Skip to content

Commit 11e9780

Browse files
zzzzzzzzzy9mxpv
authored andcommitted
support loop-dev mount in mount_rootfs
Signed-off-by: zzzzzzzzzy9 <[email protected]>
1 parent 15fbabc commit 11e9780

File tree

1 file changed

+223
-1
lines changed

1 file changed

+223
-1
lines changed

crates/shim/src/mount_linux.rs

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
use std::{
1919
collections::HashMap,
2020
env,
21+
fs::File,
2122
io::BufRead,
2223
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not},
24+
os::fd::AsRawFd,
2325
path::Path,
2426
};
2527

@@ -40,6 +42,55 @@ struct Flag {
4042
flags: MsFlags,
4143
}
4244

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";
4394
const OVERLAY_LOWERDIR_PREFIX: &str = "lowerdir=";
4495

4596
#[derive(Debug, Default, Clone)]
@@ -568,13 +619,17 @@ pub fn mount_rootfs(
568619

569620
let mut flags: MsFlags = MsFlags::empty();
570621
let mut data = Vec::new();
622+
let mut lo_setup = false;
623+
let mut loop_params = LoopParams::default();
571624
options.iter().for_each(|x| {
572625
if let Some(f) = MOUNT_FLAGS.get(x.as_str()) {
573626
if f.clear {
574627
flags.bitand_assign(f.flags.not());
575628
} else {
576629
flags.bitor_assign(f.flags)
577630
}
631+
} else if x.as_str() == "loop" {
632+
lo_setup = true;
578633
} else {
579634
data.push(x.as_str())
580635
}
@@ -595,9 +650,23 @@ pub fn mount_rootfs(
595650
}
596651
// mount with non-propagation first, or remount with changed data
597652
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+
}
598660
let zero: MsFlags = MsFlags::empty();
599661
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!(
601670
e,
602671
"Mount {:?} to {}",
603672
source,
@@ -630,6 +699,159 @@ pub fn mount_rootfs(
630699
Ok(())
631700
}
632701

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+
633855
pub fn umount_recursive(target: Option<&str>, flags: i32) -> Result<()> {
634856
if let Some(target) = target {
635857
let mut mounts = get_mounts(Some(prefix_filter(target.to_string())))?;

0 commit comments

Comments
 (0)