@@ -594,6 +594,7 @@ impl SandboxManager {
594594 . stdout ( std:: process:: Stdio :: piped ( ) )
595595 . stderr ( std:: process:: Stdio :: piped ( ) )
596596 . kill_on_drop ( true )
597+ . process_group ( 0 )
597598 . spawn ( )
598599 . map_err ( |e| {
599600 SandboxError :: CreateFailed ( format ! ( "failed to spawn firecracker: {}" , e) )
@@ -1080,6 +1081,7 @@ impl SandboxManager {
10801081 . stdout ( std:: process:: Stdio :: piped ( ) )
10811082 . stderr ( std:: process:: Stdio :: piped ( ) )
10821083 . kill_on_drop ( true )
1084+ . process_group ( 0 )
10831085 . spawn ( )
10841086 {
10851087 Ok ( c) => c,
@@ -1214,6 +1216,7 @@ impl SandboxManager {
12141216 info ! ( sandbox_id = %sandbox_id, "destroying sandbox" ) ;
12151217
12161218 self . set_status ( sandbox_id, SandboxStatus :: Stopping ) . await ;
1219+ let paths = self . sandbox_paths ( sandbox_id) ;
12171220
12181221 // Get the network slot before removing sandbox info
12191222 let network_slot = {
@@ -1229,6 +1232,18 @@ impl SandboxManager {
12291232 }
12301233 }
12311234
1235+ // Best-effort filesystem cleanup even if the VM handle was missing.
1236+ if Path :: new ( & paths. data_dir ) . exists ( ) {
1237+ if let Err ( e) = tokio:: fs:: remove_dir_all ( & paths. data_dir ) . await {
1238+ error ! (
1239+ sandbox_id = %sandbox_id,
1240+ dir = %paths. data_dir,
1241+ error = %e,
1242+ "failed to clean up sandbox data directory"
1243+ ) ;
1244+ }
1245+ }
1246+
12321247 // Tear down networking
12331248 if let Some ( slot) = network_slot {
12341249 network:: teardown_network ( sandbox_id, slot) . await ;
@@ -1872,6 +1887,21 @@ mod tests {
18721887 assert ! ( result. is_ok( ) ) ;
18731888 }
18741889
1890+ #[ tokio:: test]
1891+ async fn destroy_sandbox_cleans_up_orphaned_disk_without_vm_handle ( ) {
1892+ let manager = SandboxManager :: new ( test_node_config ( ) ) ;
1893+ let sandbox_dir = std:: path:: Path :: new ( "/tmp/sandchest-test" )
1894+ . join ( "sandboxes" )
1895+ . join ( "sb_orphaned" ) ;
1896+ let _ = std:: fs:: remove_dir_all ( & sandbox_dir) ;
1897+ std:: fs:: create_dir_all ( & sandbox_dir) . unwrap ( ) ;
1898+ std:: fs:: write ( sandbox_dir. join ( "rootfs.ext4" ) , b"orphaned" ) . unwrap ( ) ;
1899+
1900+ let result = manager. destroy_sandbox ( "sb_orphaned" ) . await ;
1901+ assert ! ( result. is_ok( ) ) ;
1902+ assert ! ( !sandbox_dir. exists( ) ) ;
1903+ }
1904+
18751905 #[ tokio:: test]
18761906 async fn event_dropped_when_channel_full ( ) {
18771907 let ( tx, _rx) = crate :: events:: channel ( 1 ) ;
0 commit comments