18
18
use std:: {
19
19
collections:: HashMap ,
20
20
env,
21
+ io:: BufRead ,
21
22
ops:: { BitAnd , BitAndAssign , BitOr , BitOrAssign , Not } ,
22
23
path:: Path ,
23
24
} ;
24
25
25
26
use lazy_static:: lazy_static;
26
27
use log:: error;
27
28
use nix:: {
28
- mount:: { mount, MsFlags } ,
29
+ mount:: { mount, MntFlags , MsFlags } ,
29
30
sched:: { unshare, CloneFlags } ,
30
31
unistd:: { fork, ForkResult } ,
31
32
} ;
@@ -41,6 +42,39 @@ struct Flag {
41
42
42
43
const OVERLAY_LOWERDIR_PREFIX : & str = "lowerdir=" ;
43
44
45
+ #[ derive( Debug , Default , Clone ) ]
46
+ struct MountInfo {
47
+ /// id is a unique identifier of the mount (may be reused after umount).
48
+ pub id : u32 ,
49
+ /// parent is the ID of the parent mount (or of self for the root
50
+ /// of this mount namespace's mount tree).
51
+ pub parent : u32 ,
52
+ /// major and minor are the major and the minor components of the Dev
53
+ /// field of unix.Stat_t structure returned by unix.*Stat calls for
54
+ /// files on this filesystem.
55
+ pub major : u32 ,
56
+ pub minor : u32 ,
57
+ /// root is the pathname of the directory in the filesystem which forms
58
+ /// the root of this mount.
59
+ pub root : String ,
60
+ /// mountpoint is the pathname of the mount point relative to the
61
+ /// process's root directory.
62
+ pub mountpoint : String ,
63
+ /// options is a comma-separated list of mount options.
64
+ pub options : String ,
65
+ /// optional are zero or more fields of the form "tag[:value]",
66
+ /// separated by a space. Currently, the possible optional fields are
67
+ /// "shared", "master", "propagate_from", and "unbindable". For more
68
+ /// information, see mount_namespaces(7) Linux man page.
69
+ pub optional : String ,
70
+ /// fs_type is the filesystem type in the form "type[.subtype]".
71
+ pub fs_type : String ,
72
+ /// source is filesystem-specific information, or "none".
73
+ pub source : String ,
74
+ /// vfs_options is a comma-separated list of superblock options.
75
+ pub vfs_options : String ,
76
+ }
77
+
44
78
lazy_static ! {
45
79
static ref MOUNT_FLAGS : HashMap <& ' static str , Flag > = {
46
80
let mut mf = HashMap :: new( ) ;
@@ -596,6 +630,137 @@ pub fn mount_rootfs(
596
630
Ok ( ( ) )
597
631
}
598
632
633
+ pub fn umount_recursive ( target : Option < & str > , flags : i32 ) -> Result < ( ) > {
634
+ if let Some ( target) = target {
635
+ let mut mounts = get_mounts ( Some ( prefix_filter ( target. to_string ( ) ) ) ) ?;
636
+ mounts. sort_by ( |a, b| b. mountpoint . len ( ) . cmp ( & a. mountpoint . len ( ) ) ) ;
637
+ for ( index, target) in mounts. iter ( ) . enumerate ( ) {
638
+ umount_all ( Some ( target. clone ( ) . mountpoint ) , flags) ?;
639
+ }
640
+ } ;
641
+ Ok ( ( ) )
642
+ }
643
+
644
+ fn umount_all ( target : Option < String > , flags : i32 ) -> Result < ( ) > {
645
+ if let Some ( target) = target {
646
+ if let Err ( e) = std:: fs:: metadata ( target. clone ( ) ) {
647
+ if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
648
+ return Ok ( ( ) ) ;
649
+ }
650
+ }
651
+ loop {
652
+ if let Err ( e) = nix:: mount:: umount2 (
653
+ & std:: path:: PathBuf :: from ( & target) ,
654
+ MntFlags :: from_bits ( flags) . unwrap_or ( MntFlags :: empty ( ) ) ,
655
+ ) {
656
+ if e == nix:: errno:: Errno :: EINVAL {
657
+ return Ok ( ( ) ) ;
658
+ }
659
+ return Err ( Error :: from ( e) ) ;
660
+ }
661
+ }
662
+ } ;
663
+ Ok ( ( ) )
664
+ }
665
+
666
+ fn prefix_filter ( prefix : String ) -> impl Fn ( MountInfo ) -> bool {
667
+ move |m : MountInfo | {
668
+ if let Some ( s) = ( m. mountpoint . clone ( ) + "/" ) . strip_prefix ( & ( prefix. clone ( ) + "/" ) ) {
669
+ return false ;
670
+ }
671
+ true
672
+ }
673
+ }
674
+
675
+ fn get_mounts < F > ( f : Option < F > ) -> Result < Vec < MountInfo > >
676
+ where
677
+ F : Fn ( MountInfo ) -> bool ,
678
+ {
679
+ let mountinfo_path = "/proc/self/mountinfo" ;
680
+ let file = std:: fs:: File :: open ( mountinfo_path) . map_err ( io_error ! ( e, "io_error" ) ) ?;
681
+ let reader = std:: io:: BufReader :: new ( file) ;
682
+ let lines: Vec < String > = reader. lines ( ) . map_while ( |line| line. ok ( ) ) . collect ( ) ;
683
+ let mount_points = lines
684
+ . into_iter ( )
685
+ . filter_map ( |line| {
686
+ /*
687
+ See http://man7.org/linux/man-pages/man5/proc.5.html
688
+ 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
689
+ (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
690
+ (1) mount ID: unique identifier of the mount (may be reused after umount)
691
+ (2) parent ID: ID of parent (or of self for the top of the mount tree)
692
+ (3) major:minor: value of st_dev for files on filesystem
693
+ (4) root: root of the mount within the filesystem
694
+ (5) mount point: mount point relative to the process's root
695
+ (6) mount options: per mount options
696
+ (7) optional fields: zero or more fields of the form "tag[:value]"
697
+ (8) separator: marks the end of the optional fields
698
+ (9) filesystem type: name of filesystem of the form "type[.subtype]"
699
+ (10) mount source: filesystem specific information or "none"
700
+ (11) super options: per super block options
701
+ In other words, we have:
702
+ * 6 mandatory fields (1)..(6)
703
+ * 0 or more optional fields (7)
704
+ * a separator field (8)
705
+ * 3 mandatory fields (9)..(11)
706
+ */
707
+ let parts: Vec < & str > = line. split_whitespace ( ) . collect ( ) ;
708
+ if parts. len ( ) < 10 {
709
+ // mountpoint parse error.
710
+ return None ;
711
+ }
712
+ // separator field
713
+ let mut sep_idx = parts. len ( ) - 4 ;
714
+ // In Linux <= 3.9 mounting a cifs with spaces in a share
715
+ // name (like "//srv/My Docs") _may_ end up having a space
716
+ // in the last field of mountinfo (like "unc=//serv/My Docs").
717
+ // Since kernel 3.10-rc1, cifs option "unc=" is ignored,
718
+ // so spaces should not appear.
719
+ //
720
+ // Check for a separator, and work around the spaces bug
721
+ for i in ( 0 ..sep_idx) . rev ( ) {
722
+ if parts[ i] == "-" {
723
+ sep_idx = i;
724
+ break ;
725
+ }
726
+ if sep_idx == 5 {
727
+ // mountpoint parse error
728
+ return None ;
729
+ }
730
+ }
731
+
732
+ let mut mount_info = MountInfo {
733
+ id : str:: parse :: < u32 > ( parts[ 0 ] ) . ok ( ) ?,
734
+ parent : str:: parse :: < u32 > ( parts[ 1 ] ) . ok ( ) ?,
735
+ major : 0 ,
736
+ minor : 0 ,
737
+ root : parts[ 3 ] . to_string ( ) ,
738
+ mountpoint : parts[ 4 ] . to_string ( ) ,
739
+ options : parts[ 5 ] . to_string ( ) ,
740
+ optional : parts[ 6 ..sep_idx] . join ( " " ) ,
741
+ fs_type : parts[ sep_idx + 1 ] . to_string ( ) ,
742
+ source : parts[ sep_idx + 2 ] . to_string ( ) ,
743
+ vfs_options : parts[ sep_idx + 3 ] . to_string ( ) ,
744
+ } ;
745
+ let major_minor = parts[ 2 ] . splitn ( 3 , ':' ) . collect :: < Vec < & str > > ( ) ;
746
+ if major_minor. len ( ) != 2 {
747
+ // mountpoint parse error.
748
+ return None ;
749
+ }
750
+ mount_info. major = str:: parse :: < u32 > ( major_minor[ 0 ] ) . ok ( ) ?;
751
+ mount_info. minor = str:: parse :: < u32 > ( major_minor[ 1 ] ) . ok ( ) ?;
752
+ if let Some ( f) = & f {
753
+ if f ( mount_info. clone ( ) ) {
754
+ // skip this mountpoint. This mountpoint is not the container's mountpoint
755
+ return None ;
756
+ }
757
+ }
758
+ Some ( mount_info)
759
+ } )
760
+ . collect :: < Vec < MountInfo > > ( ) ;
761
+ Ok ( mount_points)
762
+ }
763
+
599
764
#[ cfg( test) ]
600
765
mod tests {
601
766
use super :: * ;
0 commit comments