@@ -593,92 +593,64 @@ fn do_bind_mount(
593593 . downcast_arc :: < MountFSInode > ( )
594594 . ok_or ( SystemError :: EINVAL ) ?;
595595
596- // Get the source's filesystem
597- let source_fs = source_inode. fs ( ) ;
596+ let source_mfs = source_inode
597+ . fs ( )
598+ . downcast_arc :: < MountFS > ( )
599+ . ok_or ( SystemError :: EINVAL ) ?;
598600
599- // Check if source is on a MountFS
600- let source_mfs = source_fs. clone ( ) . downcast_arc :: < MountFS > ( ) ;
601+ let target_mountpoint = target_inode
602+ . clone ( )
603+ . downcast_arc :: < crate :: filesystem:: vfs:: mount:: MountFSInode > ( )
604+ . ok_or ( SystemError :: EINVAL ) ?;
605+ let target_mount_fs = target_mountpoint. mount_fs ( ) ;
606+ let current_mntns = ProcessManager :: current_mntns ( ) ;
601607
602- // The source mount must belong to the current mount namespace.
603- if let Some ( ref mfs) = source_mfs {
604- let current_mntns = ProcessManager :: current_mntns ( ) ;
605- if !mfs. is_belongs_to_mntns ( & current_mntns) {
608+ // Linux holds namespace_lock across check_mnt(), clone_mnt(root), and
609+ // copy_tree(children). Take one mount+dentry snapshot for the equivalent
610+ // source validation and complete detached clone so root and descendants
611+ // cannot observe different lifecycle or propagation states.
612+ let target_mfs = with_topology_snapshot ( || {
613+ if !source_mfs. is_live ( )
614+ || !source_mfs. is_belongs_to_mntns ( & current_mntns)
615+ || !target_mount_fs. is_live ( )
616+ || !target_mount_fs. is_belongs_to_mntns ( & current_mntns)
617+ {
606618 return Err ( SystemError :: EINVAL ) ;
607619 }
608- }
609-
610- // Check if source is unbindable - if so, reject the bind mount
611- if let Some ( ref mfs) = source_mfs {
612- if mfs. propagation ( ) . is_unbindable ( ) {
620+ if source_mfs. propagation ( ) . is_unbindable ( ) {
613621 return Err ( SystemError :: EINVAL ) ;
614622 }
615623 if !flags. contains ( MountFlags :: REC )
616- && has_locked_children_in_view ( mfs , & source_mount_inode) ?
624+ && has_locked_children_in_view_locked ( & source_mfs , & source_mount_inode) ?
617625 {
618626 return Err ( SystemError :: EINVAL ) ;
619627 }
620- }
621-
622- // Clone source_mfs for recursive bind mount (need to keep it for later use)
623- let source_mfs_for_recursive = source_mfs. clone ( ) ;
624-
625- let root_inner_inode = if is_mountpoint_root ( & source_inode) {
626- source_mfs
627- . as_ref ( )
628- . map ( |mfs| mfs. root_inner_inode ( ) )
629- . unwrap_or_else ( || source_inode. clone ( ) )
630- } else {
631- source_inode
632- . clone ( )
633- . downcast_arc :: < MountFSInode > ( )
634- . map ( |inode| inode. underlying_inode ( ) )
635- . unwrap_or_else ( || source_inode. clone ( ) )
636- } ;
637-
638- // Get the inner filesystem for mounting while preserving the source subtree root.
639- let inner_fs = source_mfs
640- . map ( |mfs| mfs. inner_filesystem ( ) )
641- . unwrap_or ( source_fs) ;
642-
643- // do_loopback: the target mount point must belong to the current mount namespace.
644- let current_mntns = ProcessManager :: current_mntns ( ) ;
645- let target_mount_fs = target_inode
646- . fs ( )
647- . downcast_arc :: < MountFS > ( )
648- . ok_or ( SystemError :: EINVAL ) ?;
649- if !target_mount_fs. is_belongs_to_mntns ( & current_mntns) {
650- return Err ( SystemError :: EINVAL ) ;
651- }
652628
653- let target_mountpoint = target_inode
654- . clone ( )
655- . downcast_arc :: < crate :: filesystem:: vfs:: mount:: MountFSInode > ( )
656- . ok_or ( SystemError :: EINVAL ) ?;
657- let target_mfs = target_mountpoint. prepare_subtree_with_root_dentry (
658- inner_fs,
659- root_inner_inode,
660- Some ( source_mount_inode. shared_dentry ( ) ) ,
661- source_mfs_for_recursive
662- . as_ref ( )
663- . map ( |mount| mount. mount_flags ( ) )
664- . unwrap_or_else ( MountFlags :: empty) ,
665- source_mfs_for_recursive
666- . as_ref ( )
667- . map ( |mfs| mfs. super_block_state ( ) ) ,
668- source_mfs_for_recursive. as_ref ( ) ,
669- ) ?;
670- target_mfs. set_mount_source ( Some ( source_path. clone ( ) ) ) ;
671-
672- // Build the complete recursive clone while detached. The root edge is the
673- // publication point, so lookup never observes a half-copied bind tree.
674- if flags. contains ( MountFlags :: REC ) {
675- if let Some ( ref mfs) = source_mfs_for_recursive {
676- if let Err ( e) = do_recursive_bind_mount ( mfs, & target_mfs) {
629+ let root_inner_inode = if is_mountpoint_root ( & source_inode) {
630+ source_mfs. root_inner_inode ( )
631+ } else {
632+ source_mount_inode. underlying_inode ( )
633+ } ;
634+ let target_mfs = target_mountpoint. prepare_subtree_with_root_dentry_prevalidated (
635+ source_mfs. inner_filesystem ( ) ,
636+ root_inner_inode,
637+ Some ( source_mount_inode. shared_dentry ( ) ) ,
638+ source_mfs. mount_flags ( ) ,
639+ Some ( source_mfs. super_block_state ( ) ) ,
640+ Some ( & source_mfs) ,
641+ ) ?;
642+ target_mfs. set_mount_source ( Some ( source_path. clone ( ) ) ) ;
643+
644+ // The root edge is the publication point, so lookup never observes a
645+ // half-copied recursive bind tree.
646+ if flags. contains ( MountFlags :: REC ) {
647+ if let Err ( error) = do_recursive_bind_mount_locked ( & source_mfs, & target_mfs) {
677648 MountFS :: deactivate_disconnected_subtree ( & target_mfs) ;
678- return Err ( e ) ;
649+ return Err ( error ) ;
679650 }
680651 }
681- }
652+ Ok ( target_mfs)
653+ } ) ?;
682654
683655 if let Err ( error) = target_mountpoint. publish_prepared_subtree ( & target_mfs) {
684656 MountFS :: deactivate_disconnected_subtree ( & target_mfs) ;
@@ -690,26 +662,25 @@ fn do_bind_mount(
690662
691663/// Linux rejects a non-recursive bind when it would uncover locked child
692664/// mounts below the selected source dentry (has_locked_children()).
693- fn has_locked_children_in_view (
665+ /// Caller holds the mount+dentry topology snapshot.
666+ fn has_locked_children_in_view_locked (
694667 source_mount : & Arc < MountFS > ,
695668 source_root : & Arc < MountFSInode > ,
696669) -> Result < bool , SystemError > {
697- with_topology_snapshot ( || {
698- let mut pending = source_mount. mount_children ( ) ;
699- while let Some ( child) = pending. pop ( ) {
700- let mountpoint = child. self_mountpoint ( ) . ok_or ( SystemError :: EINVAL ) ?;
701- if mountpoint
702- . relative_path_from_snapshot ( source_root) ?
703- . is_none ( )
704- {
705- continue ;
706- }
707- if child. is_locked ( ) {
708- return Ok ( true ) ;
709- }
670+ let mut pending = source_mount. mount_children ( ) ;
671+ while let Some ( child) = pending. pop ( ) {
672+ let mountpoint = child. self_mountpoint ( ) . ok_or ( SystemError :: EINVAL ) ?;
673+ if mountpoint
674+ . relative_path_from_snapshot ( source_root) ?
675+ . is_none ( )
676+ {
677+ continue ;
678+ }
679+ if child. is_locked ( ) {
680+ return Ok ( true ) ;
710681 }
711- Ok ( false )
712- } )
682+ }
683+ Ok ( false )
713684}
714685
715686/// Change the propagation type of a mount point.
@@ -829,13 +800,12 @@ fn do_move_mount(
829800/// # Returns
830801/// * `Ok(())` on success
831802/// * `Err(SystemError)` on failure
832- fn do_recursive_bind_mount (
803+ ///
804+ /// Caller holds the mount+dentry topology snapshot for the complete detached copy.
805+ fn do_recursive_bind_mount_locked (
833806 source_mfs : & Arc < MountFS > ,
834807 target_mfs : & Arc < MountFS > ,
835808) -> Result < ( ) , SystemError > {
836- // Mount edge mutations use the same lock. Holding it for the complete
837- // detached copy gives MS_BIND|MS_REC one coherent source-tree snapshot.
838- let _topology = MOUNT_LIFECYCLE_LOCK . lock ( ) ;
839809 let mut pending = vec ! [ ( source_mfs. clone( ) , target_mfs. clone( ) ) ] ;
840810 while let Some ( ( source_parent, target_parent) ) = pending. pop ( ) {
841811 for source_child in source_parent. mount_children ( ) {
0 commit comments