@@ -27,8 +27,8 @@ use crate::{
2727 propagation:: {
2828 abort_mount_propagation, commit_mount_propagation_locked, detach_mount_propagation,
2929 ensure_subtree_shared, inherit_bind_mount_propagation,
30- prepare_mount_propagation_locked, propagate_umount , propagation_umount_busy ,
31- MountPropagation ,
30+ prepare_mount_propagation_locked, propagate_umount_sources ,
31+ propagation_umount_busy , MountPropagation ,
3232 } ,
3333 } ,
3434 ProcessManager ,
@@ -403,6 +403,7 @@ struct MountLifecycle {
403403 state : MountLifecycleState ,
404404 external_pins : usize ,
405405 construction_reserved : bool ,
406+ propagation_attached : bool ,
406407 detached_component : Option < Arc < DetachedMountComponent > > ,
407408}
408409
@@ -1115,6 +1116,7 @@ impl MountFS {
11151116 state : MountLifecycleState :: Constructing ,
11161117 external_pins : 0 ,
11171118 construction_reserved : state_init. construction_reserved ,
1119+ propagation_attached : true ,
11181120 detached_component : None ,
11191121 } ) ,
11201122 } ) ;
@@ -1159,6 +1161,7 @@ impl MountFS {
11591161 state : MountLifecycleState :: Constructing ,
11601162 external_pins : 0 ,
11611163 construction_reserved : true ,
1164+ propagation_attached : true ,
11621165 detached_component : None ,
11631166 } ) ,
11641167 } ) ;
@@ -1380,6 +1383,133 @@ impl MountFS {
13801383 }
13811384 }
13821385
1386+ /// Remove one exact mount edge while moving every mount stacked on the
1387+ /// removed mount's root back to the removed edge in chronological order.
1388+ ///
1389+ /// This is the `umount_list()` restoration operation used by propagated
1390+ /// umount. Each tuple identifies whether that root child is restored to the
1391+ /// original parent (`true`) or retained below a lazy-detached parent
1392+ /// (`false`). Commit capacity is reserved during preflight.
1393+ pub ( crate ) fn detach_exact_restoring_root_children (
1394+ & self ,
1395+ mount_fs : & Arc < MountFS > ,
1396+ root_children : Vec < ( Arc < MountFS > , bool ) > ,
1397+ reservation : & MountEdgeReservation ,
1398+ ) -> Result < Arc < MountFS > , SystemError > {
1399+ let original_mountpoint = mount_fs. self_mountpoint ( ) . ok_or ( SystemError :: EINVAL ) ?;
1400+ if !Arc :: ptr_eq ( & original_mountpoint. mount_fs , & self . self_ref ( ) )
1401+ || !Arc :: ptr_eq ( & reservation. parent , & self . self_ref ( ) )
1402+ || !Arc :: ptr_eq ( reservation. mountpoint ( ) , & original_mountpoint)
1403+ || Arc :: ptr_eq ( & self . self_ref ( ) , mount_fs)
1404+ {
1405+ return Err ( SystemError :: EINVAL ) ;
1406+ }
1407+ let root_mountpoint = mount_fs. mountpoint_root_inode ( ) ;
1408+ let original_dentry = original_mountpoint. shared_dentry ( ) ;
1409+ let root_dentry = root_mountpoint. shared_dentry ( ) ;
1410+
1411+ with_dentry_mount_gates ( Some ( & original_dentry) , Some ( & root_dentry) , || {
1412+ fn replace_edge (
1413+ parent_mountpoints : & mut HashMap < DentryId , Vec < Arc < MountFS > > > ,
1414+ child_mountpoints : & mut HashMap < DentryId , Vec < Arc < MountFS > > > ,
1415+ original_mountpoint : & Arc < MountFSInode > ,
1416+ root_mountpoint : & Arc < MountFSInode > ,
1417+ mount_fs : & Arc < MountFS > ,
1418+ root_children : & [ ( Arc < MountFS > , bool ) ] ,
1419+ ) -> Result < ( ) , SystemError > {
1420+ let parent_stack = parent_mountpoints
1421+ . get ( & original_mountpoint. dentry_id ( ) )
1422+ . ok_or ( SystemError :: ENOENT ) ?;
1423+ let parent_index = parent_stack
1424+ . iter ( )
1425+ . position ( |child| Arc :: ptr_eq ( child, mount_fs) )
1426+ . ok_or ( SystemError :: ENOENT ) ?;
1427+ let actual_root_children = child_mountpoints
1428+ . get ( & root_mountpoint. dentry_id ( ) )
1429+ . map ( Vec :: as_slice)
1430+ . unwrap_or_default ( ) ;
1431+ if actual_root_children. len ( ) != root_children. len ( )
1432+ || actual_root_children
1433+ . iter ( )
1434+ . zip ( root_children)
1435+ . any ( |( actual, ( expected, _) ) | !Arc :: ptr_eq ( actual, expected) )
1436+ {
1437+ return Err ( SystemError :: EBUSY ) ;
1438+ }
1439+
1440+ let parent_stack = parent_mountpoints
1441+ . get_mut ( & original_mountpoint. dentry_id ( ) )
1442+ . expect ( "validated propagated-umount parent stack" ) ;
1443+ parent_stack. remove ( parent_index) ;
1444+ let mut offset = 0 ;
1445+ for ( child, restore) in root_children {
1446+ if !restore {
1447+ continue ;
1448+ }
1449+ child. relocate_mountpoint ( Some ( original_mountpoint. clone ( ) ) ) ;
1450+ child. tucked_under . store ( false , Ordering :: Release ) ;
1451+ parent_stack. insert ( parent_index + offset, child. clone ( ) ) ;
1452+ offset += 1 ;
1453+ }
1454+ let remove_root_key = if root_children. is_empty ( ) {
1455+ false
1456+ } else {
1457+ let root_stack = child_mountpoints
1458+ . get_mut ( & root_mountpoint. dentry_id ( ) )
1459+ . expect ( "validated propagated-umount root stack" ) ;
1460+ let mut index = 0 ;
1461+ root_stack. retain ( |_| {
1462+ let retain = !root_children[ index] . 1 ;
1463+ index += 1 ;
1464+ retain
1465+ } ) ;
1466+ root_stack. is_empty ( )
1467+ } ;
1468+ if remove_root_key {
1469+ child_mountpoints. remove ( & root_mountpoint. dentry_id ( ) ) ;
1470+ }
1471+ Ok ( ( ) )
1472+ }
1473+
1474+ if self . mount_id . data ( ) < mount_fs. mount_id . data ( ) {
1475+ let mut parent_mountpoints = self . mountpoints . lock ( ) ;
1476+ let mut child_mountpoints = mount_fs. mountpoints . lock ( ) ;
1477+ replace_edge (
1478+ & mut parent_mountpoints,
1479+ & mut child_mountpoints,
1480+ & original_mountpoint,
1481+ & root_mountpoint,
1482+ mount_fs,
1483+ & root_children,
1484+ ) ?;
1485+ } else {
1486+ let mut child_mountpoints = mount_fs. mountpoints . lock ( ) ;
1487+ let mut parent_mountpoints = self . mountpoints . lock ( ) ;
1488+ replace_edge (
1489+ & mut parent_mountpoints,
1490+ & mut child_mountpoints,
1491+ & original_mountpoint,
1492+ & root_mountpoint,
1493+ mount_fs,
1494+ & root_children,
1495+ ) ?;
1496+ }
1497+
1498+ let restored_count = root_children. iter ( ) . filter ( |( _, restore) | * restore) . count ( ) ;
1499+ root_dentry
1500+ . mount_edges
1501+ . fetch_sub ( restored_count, Ordering :: Release ) ;
1502+ if restored_count == 0 {
1503+ original_dentry. mount_edges . fetch_sub ( 1 , Ordering :: Release ) ;
1504+ } else if restored_count > 1 {
1505+ original_dentry
1506+ . mount_edges
1507+ . fetch_add ( restored_count - 1 , Ordering :: Release ) ;
1508+ }
1509+ Ok ( mount_fs. clone ( ) )
1510+ } )
1511+ }
1512+
13831513 fn restore_exact_cover (
13841514 & self ,
13851515 mount_fs : & Arc < MountFS > ,
@@ -1755,22 +1885,29 @@ impl MountFS {
17551885 pub ( crate ) fn deactivate ( & self ) {
17561886 let ( should_remove, release_construction, should_leave_propagation) = {
17571887 let mut lifecycle = self . lifecycle . lock ( ) ;
1758- match lifecycle. state {
1888+ let should_leave_propagation = lifecycle. propagation_attached ;
1889+ lifecycle. propagation_attached = false ;
1890+ let ( should_remove, release_construction) = match lifecycle. state {
17591891 MountLifecycleState :: Constructing => {
17601892 lifecycle. state = MountLifecycleState :: Detached ;
17611893 let reserved = lifecycle. construction_reserved ;
17621894 lifecycle. construction_reserved = false ;
1763- ( false , reserved, true )
1895+ ( false , reserved)
17641896 }
17651897 MountLifecycleState :: Live
17661898 | MountLifecycleState :: Detaching
17671899 | MountLifecycleState :: DetachedConnected => {
17681900 lifecycle. state = MountLifecycleState :: Detached ;
17691901 lifecycle. detached_component = None ;
1770- ( true , false , true )
1902+ ( true , false )
17711903 }
1772- MountLifecycleState :: Detached => ( false , false , false ) ,
1773- }
1904+ MountLifecycleState :: Detached => ( false , false ) ,
1905+ } ;
1906+ (
1907+ should_remove,
1908+ release_construction,
1909+ should_leave_propagation,
1910+ )
17741911 } ;
17751912 if should_leave_propagation {
17761913 detach_mount_propagation ( & self . self_ref ( ) ) ;
@@ -1783,6 +1920,22 @@ impl MountFS {
17831920 }
17841921 }
17851922
1923+ /// Record that a prepared batch graph commit already removed this mount's
1924+ /// propagation relationships. Later lifecycle teardown must not repeat it.
1925+ pub ( crate ) fn mark_propagation_detached ( & self ) {
1926+ self . lifecycle . lock ( ) . propagation_attached = false ;
1927+ }
1928+
1929+ fn detach_propagation_once ( & self ) {
1930+ let should_detach = {
1931+ let mut lifecycle = self . lifecycle . lock ( ) ;
1932+ core:: mem:: replace ( & mut lifecycle. propagation_attached , false )
1933+ } ;
1934+ if should_detach {
1935+ detach_mount_propagation ( & self . self_ref ( ) ) ;
1936+ }
1937+ }
1938+
17861939 pub ( crate ) fn has_external_pins ( & self ) -> bool {
17871940 self . lifecycle . lock ( ) . external_pins != 0
17881941 }
@@ -1895,7 +2048,7 @@ impl MountFS {
18952048 namespace. remove_mount_exact ( mount) ;
18962049 }
18972050 mount. clear_namespace ( ) ;
1898- detach_mount_propagation ( mount) ;
2051+ mount. detach_propagation_once ( ) ;
18992052 }
19002053
19012054 let mut component_roots = vec ! [ root. clone( ) ] ;
@@ -2111,7 +2264,17 @@ impl MountFS {
21112264 }
21122265
21132266 let root_mountpoint = root. self_mountpoint ( ) . ok_or ( SystemError :: EINVAL ) ?;
2114- root. commit_umount_at ( & root_mountpoint, lazy) ?;
2267+ if let Err ( error) = propagate_umount_sources ( & mounts, lazy) {
2268+ for mount in & mounts {
2269+ let mut lifecycle = mount. lifecycle . lock ( ) ;
2270+ if lifecycle. state == MountLifecycleState :: Detaching {
2271+ lifecycle. state = MountLifecycleState :: Live ;
2272+ }
2273+ }
2274+ return Err ( error) ;
2275+ }
2276+ root. commit_umount_at ( & root_mountpoint, lazy)
2277+ . expect ( "prepared local umount edge commit cannot fail" ) ;
21152278 // Final filesystem shutdown may sleep and may itself need pathname
21162279 // operations. Never wait while holding the topology/admission lock.
21172280 drop ( _topology) ;
@@ -2127,6 +2290,7 @@ impl MountFS {
21272290 /// pre-detach sync; final shared-superblock shutdown remains deferred until
21282291 /// every mount and external pin is gone.
21292292 pub fn umount_with_mode ( & self , lazy : bool ) -> Result < Arc < MountFS > , SystemError > {
2293+ let _topology = MOUNT_LIFECYCLE_LOCK . lock ( ) ;
21302294 let mountpoint = self . self_mountpoint ( ) . ok_or ( SystemError :: EINVAL ) ?;
21312295
21322296 if !lazy && propagation_umount_busy ( & mountpoint. mount_fs , & mountpoint) {
@@ -2143,8 +2307,14 @@ impl MountFS {
21432307 }
21442308 lifecycle. state = MountLifecycleState :: Detaching ;
21452309 }
2146-
2147- self . commit_umount_at ( & mountpoint, lazy)
2310+ if let Err ( error) = propagate_umount_sources ( core:: slice:: from_ref ( & self . self_ref ( ) ) , lazy)
2311+ {
2312+ self . lifecycle . lock ( ) . state = MountLifecycleState :: Live ;
2313+ return Err ( error) ;
2314+ }
2315+ Ok ( self
2316+ . commit_umount_at ( & mountpoint, lazy)
2317+ . expect ( "prepared local umount edge commit cannot fail" ) )
21482318 }
21492319
21502320 fn commit_umount_at (
@@ -2155,12 +2325,6 @@ impl MountFS {
21552325 let parent = mountpoint. mount_fs ( ) ;
21562326 let this_mount = self . self_ref ( ) ;
21572327
2158- // Propagation performs a full preflight and commits only peer/slave
2159- // copies. Keep the local edge intact until that transaction succeeds,
2160- // so failure needs no local topology rollback.
2161- if parent. propagation ( ) . is_shared ( ) {
2162- propagate_umount ( & parent, mountpoint, & this_mount, lazy) ?;
2163- }
21642328 let result = parent. detach_exact ( & this_mount) ;
21652329
21662330 if result. is_ok ( ) {
0 commit comments