18
18
use std:: {
19
19
collections:: HashMap ,
20
20
env,
21
+ fs:: File ,
21
22
io:: BufRead ,
22
23
ops:: { BitAnd , BitAndAssign , BitOr , BitOrAssign , Not } ,
24
+ os:: fd:: AsRawFd ,
23
25
path:: Path ,
24
26
} ;
25
27
@@ -40,6 +42,55 @@ struct Flag {
40
42
flags : MsFlags ,
41
43
}
42
44
45
+ #[ cfg( target_os = "linux" ) ]
46
+ #[ derive( Debug , Default ) ]
47
+ pub struct LoopParams {
48
+ readonly : bool ,
49
+ auto_clear : bool ,
50
+ direct : bool ,
51
+ }
52
+
53
+ #[ repr( C ) ]
54
+ #[ derive( Debug ) ]
55
+ pub struct LoopInfo {
56
+ device : u64 ,
57
+ inode : u64 ,
58
+ rdevice : u64 ,
59
+ offset : u64 ,
60
+ size_limit : u64 ,
61
+ number : u32 ,
62
+ encrypt_type : u32 ,
63
+ encrypt_key_size : u32 ,
64
+ flags : u32 ,
65
+ file_name : [ u8 ; 64 ] ,
66
+ crypt_name : [ u8 ; 64 ] ,
67
+ encrypt_key : [ u8 ; 32 ] ,
68
+ init : [ u64 ; 2 ] ,
69
+ }
70
+
71
+ impl Default for LoopInfo {
72
+ fn default ( ) -> Self {
73
+ LoopInfo {
74
+ device : 0 ,
75
+ inode : 0 ,
76
+ rdevice : 0 ,
77
+ offset : 0 ,
78
+ size_limit : 0 ,
79
+ number : 0 ,
80
+ encrypt_type : 0 ,
81
+ encrypt_key_size : 0 ,
82
+ flags : 0 ,
83
+ file_name : [ 0 ; 64 ] ,
84
+ crypt_name : [ 0 ; 64 ] ,
85
+ encrypt_key : [ 0 ; 32 ] ,
86
+ init : [ 0 ; 2 ] ,
87
+ }
88
+ }
89
+ }
90
+
91
+ const LOOP_CONTROL_PATH : & str = "/dev/loop-control" ;
92
+ const LOOP_DEV_FORMAT : & str = "/dev/loop" ;
93
+ const EBUSY_STRING : & str = "device or resource busy" ;
43
94
const OVERLAY_LOWERDIR_PREFIX : & str = "lowerdir=" ;
44
95
45
96
#[ derive( Debug , Default , Clone ) ]
@@ -568,13 +619,17 @@ pub fn mount_rootfs(
568
619
569
620
let mut flags: MsFlags = MsFlags :: empty ( ) ;
570
621
let mut data = Vec :: new ( ) ;
622
+ let mut lo_setup = false ;
623
+ let mut loop_params = LoopParams :: default ( ) ;
571
624
options. iter ( ) . for_each ( |x| {
572
625
if let Some ( f) = MOUNT_FLAGS . get ( x. as_str ( ) ) {
573
626
if f. clear {
574
627
flags. bitand_assign ( f. flags . not ( ) ) ;
575
628
} else {
576
629
flags. bitor_assign ( f. flags )
577
630
}
631
+ } else if x. as_str ( ) == "loop" {
632
+ lo_setup = true ;
578
633
} else {
579
634
data. push ( x. as_str ( ) )
580
635
}
@@ -595,9 +650,23 @@ pub fn mount_rootfs(
595
650
}
596
651
// mount with non-propagation first, or remount with changed data
597
652
let oflags = flags. bitand ( PROPAGATION_TYPES . not ( ) ) ;
653
+ if lo_setup {
654
+ loop_params = LoopParams {
655
+ readonly : oflags. bitand ( MsFlags :: MS_RDONLY ) == MsFlags :: MS_RDONLY ,
656
+ auto_clear : true ,
657
+ direct : false ,
658
+ } ;
659
+ }
598
660
let zero: MsFlags = MsFlags :: empty ( ) ;
599
661
if flags. bitand ( MsFlags :: MS_REMOUNT ) . eq ( & zero) || data. is_some ( ) {
600
- mount ( source, target. as_ref ( ) , fs_type, oflags, data) . map_err ( mount_error ! (
662
+ let mut lo_file = String :: new ( ) ;
663
+ let s = if lo_setup {
664
+ lo_file = setup_loop ( source, loop_params) ?;
665
+ Some ( lo_file. as_str ( ) )
666
+ } else {
667
+ source
668
+ } ;
669
+ mount ( s, target. as_ref ( ) , fs_type, oflags, data) . map_err ( mount_error ! (
601
670
e,
602
671
"Mount {:?} to {}" ,
603
672
source,
@@ -630,6 +699,159 @@ pub fn mount_rootfs(
630
699
Ok ( ( ) )
631
700
}
632
701
702
+ fn setup_loop ( source : Option < & str > , params : LoopParams ) -> Result < String > {
703
+ let src = source. ok_or ( other ! ( "loop source is None" ) ) ?;
704
+ for _ in 0 ..100 {
705
+ let num = get_free_loop_dev ( ) ?;
706
+ let loop_dev = format ! ( "{}{}" , LOOP_DEV_FORMAT , num) ;
707
+ match setup_loop_dev ( src, loop_dev. as_str ( ) , & params) {
708
+ Ok ( _) => return Ok ( loop_dev) ,
709
+ Err ( e) => {
710
+ if e. to_string ( ) . contains ( EBUSY_STRING ) {
711
+ continue ;
712
+ } else {
713
+ return Err ( e) ;
714
+ }
715
+ }
716
+ }
717
+ }
718
+ Err ( Error :: Other (
719
+ "creating new loopback device after 100 times" . to_string ( ) ,
720
+ ) )
721
+ }
722
+
723
+ pub fn get_free_loop_dev ( ) -> Result < i32 > {
724
+ const LOOP_CTL_GET_FREE : i32 = 0x4c82 ;
725
+ let loop_control = File :: options ( )
726
+ . read ( true )
727
+ . write ( true )
728
+ . open ( LOOP_CONTROL_PATH )
729
+ . map_err ( |e| Error :: IoError {
730
+ context : format ! ( "open {} error: " , LOOP_CONTROL_PATH ) ,
731
+ err : e,
732
+ } ) ?;
733
+ unsafe {
734
+ #[ cfg( target_env = "gnu" ) ]
735
+ let ret = libc:: ioctl (
736
+ loop_control. as_raw_fd ( ) as libc:: c_int ,
737
+ LOOP_CTL_GET_FREE as libc:: c_ulong ,
738
+ ) as i32 ;
739
+ #[ cfg( target_env = "musl" ) ]
740
+ let ret = libc:: ioctl (
741
+ loop_control. as_raw_fd ( ) as libc:: c_int ,
742
+ LOOP_CTL_GET_FREE as libc:: c_int ,
743
+ ) as i32 ;
744
+ match nix:: errno:: Errno :: result ( ret) {
745
+ Ok ( ret) => Ok ( ret) ,
746
+ Err ( e) => Err ( Error :: Nix ( e) ) ,
747
+ }
748
+ }
749
+ }
750
+
751
+ pub fn setup_loop_dev ( backing_file : & str , loop_dev : & str , params : & LoopParams ) -> Result < File > {
752
+ const LOOP_SET_FD : u32 = 0x4c00 ;
753
+ const LOOP_CLR_FD : u32 = 0x4c01 ;
754
+ const LOOP_SET_STATUS64 : u32 = 0x4c04 ;
755
+ const LOOP_SET_DIRECT_IO : u32 = 0x4c08 ;
756
+ const LO_FLAGS_READ_ONLY : u32 = 0x1 ;
757
+ const LO_FLAGS_AUTOCLEAR : u32 = 0x4 ;
758
+ let mut open_options = File :: options ( ) ;
759
+ open_options. read ( true ) ;
760
+ if !params. readonly {
761
+ open_options. write ( true ) ;
762
+ }
763
+ // 1. open backing file
764
+ let back = open_options
765
+ . open ( backing_file)
766
+ . map_err ( |e| Error :: IoError {
767
+ context : format ! ( "open {} error: " , backing_file) ,
768
+ err : e,
769
+ } ) ?;
770
+ let loop_dev = open_options. open ( loop_dev) . map_err ( |e| Error :: IoError {
771
+ context : format ! ( "open {} error: " , loop_dev) ,
772
+ err : e,
773
+ } ) ?;
774
+ // 2. set FD
775
+ unsafe {
776
+ #[ cfg( target_env = "gnu" ) ]
777
+ let ret = libc:: ioctl (
778
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
779
+ LOOP_SET_FD as libc:: c_ulong ,
780
+ back. as_raw_fd ( ) as libc:: c_int ,
781
+ ) ;
782
+ #[ cfg( target_env = "musl" ) ]
783
+ let ret = libc:: ioctl (
784
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
785
+ LOOP_SET_FD as libc:: c_int ,
786
+ back. as_raw_fd ( ) as libc:: c_int ,
787
+ ) ;
788
+ if let Err ( e) = nix:: errno:: Errno :: result ( ret) {
789
+ return Err ( Error :: Nix ( e) ) ;
790
+ }
791
+ }
792
+ // 3. set info
793
+ let mut info = LoopInfo :: default ( ) ;
794
+ info. file_name [ ..backing_file. as_bytes ( ) . len ( ) ] . copy_from_slice ( backing_file. as_bytes ( ) ) ;
795
+ if params. readonly {
796
+ info. flags |= LO_FLAGS_READ_ONLY ;
797
+ }
798
+
799
+ if params. auto_clear {
800
+ info. flags |= LO_FLAGS_AUTOCLEAR ;
801
+ }
802
+ unsafe {
803
+ #[ cfg( target_env = "gnu" ) ]
804
+ let ret = libc:: ioctl (
805
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
806
+ LOOP_SET_STATUS64 as libc:: c_ulong ,
807
+ info,
808
+ ) ;
809
+ #[ cfg( target_env = "musl" ) ]
810
+ if let Err ( e) = nix:: errno:: Errno :: result ( ret) {
811
+ libc:: ioctl (
812
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
813
+ LOOP_CLR_FD as libc:: c_int ,
814
+ 0 ,
815
+ ) ;
816
+ return Err ( Error :: Nix ( e) ) ;
817
+ }
818
+ }
819
+
820
+ // 4. Set Direct IO
821
+ if params. direct {
822
+ unsafe {
823
+ #[ cfg( target_env = "gnu" ) ]
824
+ let ret = libc:: ioctl (
825
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
826
+ LOOP_SET_DIRECT_IO as libc:: c_ulong ,
827
+ 1 ,
828
+ ) ;
829
+ #[ cfg( target_env = "musl" ) ]
830
+ let ret = libc:: ioctl (
831
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
832
+ LOOP_SET_DIRECT_IO as libc:: c_int ,
833
+ 1 ,
834
+ ) ;
835
+ if let Err ( e) = nix:: errno:: Errno :: result ( ret) {
836
+ #[ cfg( target_env = "gnu" ) ]
837
+ libc:: ioctl (
838
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
839
+ LOOP_CLR_FD as libc:: c_ulong ,
840
+ 0 ,
841
+ ) ;
842
+ #[ cfg( target_env = "musl" ) ]
843
+ libc:: ioctl (
844
+ loop_dev. as_raw_fd ( ) as libc:: c_int ,
845
+ LOOP_CLR_FD as libc:: c_int ,
846
+ 0 ,
847
+ ) ;
848
+ return Err ( Error :: Nix ( e) ) ;
849
+ }
850
+ }
851
+ }
852
+ Ok ( loop_dev)
853
+ }
854
+
633
855
pub fn umount_recursive ( target : Option < & str > , flags : i32 ) -> Result < ( ) > {
634
856
if let Some ( target) = target {
635
857
let mut mounts = get_mounts ( Some ( prefix_filter ( target. to_string ( ) ) ) ) ?;
0 commit comments