support loop-dev mount in mount_rootfs
Signed-off-by: zzzzzzzzzy9 <zhang.yu58@zte.com.cn>
This commit is contained in:
parent
15fbabcf8e
commit
11e97809b8
|
|
@ -18,8 +18,10 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
fs::File,
|
||||
io::BufRead,
|
||||
ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not},
|
||||
os::fd::AsRawFd,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
|
|
@ -40,6 +42,55 @@ struct Flag {
|
|||
flags: MsFlags,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LoopParams {
|
||||
readonly: bool,
|
||||
auto_clear: bool,
|
||||
direct: bool,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub struct LoopInfo {
|
||||
device: u64,
|
||||
inode: u64,
|
||||
rdevice: u64,
|
||||
offset: u64,
|
||||
size_limit: u64,
|
||||
number: u32,
|
||||
encrypt_type: u32,
|
||||
encrypt_key_size: u32,
|
||||
flags: u32,
|
||||
file_name: [u8; 64],
|
||||
crypt_name: [u8; 64],
|
||||
encrypt_key: [u8; 32],
|
||||
init: [u64; 2],
|
||||
}
|
||||
|
||||
impl Default for LoopInfo {
|
||||
fn default() -> Self {
|
||||
LoopInfo {
|
||||
device: 0,
|
||||
inode: 0,
|
||||
rdevice: 0,
|
||||
offset: 0,
|
||||
size_limit: 0,
|
||||
number: 0,
|
||||
encrypt_type: 0,
|
||||
encrypt_key_size: 0,
|
||||
flags: 0,
|
||||
file_name: [0; 64],
|
||||
crypt_name: [0; 64],
|
||||
encrypt_key: [0; 32],
|
||||
init: [0; 2],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LOOP_CONTROL_PATH: &str = "/dev/loop-control";
|
||||
const LOOP_DEV_FORMAT: &str = "/dev/loop";
|
||||
const EBUSY_STRING: &str = "device or resource busy";
|
||||
const OVERLAY_LOWERDIR_PREFIX: &str = "lowerdir=";
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
|
|
@ -568,6 +619,8 @@ pub fn mount_rootfs(
|
|||
|
||||
let mut flags: MsFlags = MsFlags::empty();
|
||||
let mut data = Vec::new();
|
||||
let mut lo_setup = false;
|
||||
let mut loop_params = LoopParams::default();
|
||||
options.iter().for_each(|x| {
|
||||
if let Some(f) = MOUNT_FLAGS.get(x.as_str()) {
|
||||
if f.clear {
|
||||
|
|
@ -575,6 +628,8 @@ pub fn mount_rootfs(
|
|||
} else {
|
||||
flags.bitor_assign(f.flags)
|
||||
}
|
||||
} else if x.as_str() == "loop" {
|
||||
lo_setup = true;
|
||||
} else {
|
||||
data.push(x.as_str())
|
||||
}
|
||||
|
|
@ -595,9 +650,23 @@ pub fn mount_rootfs(
|
|||
}
|
||||
// mount with non-propagation first, or remount with changed data
|
||||
let oflags = flags.bitand(PROPAGATION_TYPES.not());
|
||||
if lo_setup {
|
||||
loop_params = LoopParams {
|
||||
readonly: oflags.bitand(MsFlags::MS_RDONLY) == MsFlags::MS_RDONLY,
|
||||
auto_clear: true,
|
||||
direct: false,
|
||||
};
|
||||
}
|
||||
let zero: MsFlags = MsFlags::empty();
|
||||
if flags.bitand(MsFlags::MS_REMOUNT).eq(&zero) || data.is_some() {
|
||||
mount(source, target.as_ref(), fs_type, oflags, data).map_err(mount_error!(
|
||||
let mut lo_file = String::new();
|
||||
let s = if lo_setup {
|
||||
lo_file = setup_loop(source, loop_params)?;
|
||||
Some(lo_file.as_str())
|
||||
} else {
|
||||
source
|
||||
};
|
||||
mount(s, target.as_ref(), fs_type, oflags, data).map_err(mount_error!(
|
||||
e,
|
||||
"Mount {:?} to {}",
|
||||
source,
|
||||
|
|
@ -630,6 +699,159 @@ pub fn mount_rootfs(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_loop(source: Option<&str>, params: LoopParams) -> Result<String> {
|
||||
let src = source.ok_or(other!("loop source is None"))?;
|
||||
for _ in 0..100 {
|
||||
let num = get_free_loop_dev()?;
|
||||
let loop_dev = format!("{}{}", LOOP_DEV_FORMAT, num);
|
||||
match setup_loop_dev(src, loop_dev.as_str(), ¶ms) {
|
||||
Ok(_) => return Ok(loop_dev),
|
||||
Err(e) => {
|
||||
if e.to_string().contains(EBUSY_STRING) {
|
||||
continue;
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::Other(
|
||||
"creating new loopback device after 100 times".to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_free_loop_dev() -> Result<i32> {
|
||||
const LOOP_CTL_GET_FREE: i32 = 0x4c82;
|
||||
let loop_control = File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(LOOP_CONTROL_PATH)
|
||||
.map_err(|e| Error::IoError {
|
||||
context: format!("open {} error: ", LOOP_CONTROL_PATH),
|
||||
err: e,
|
||||
})?;
|
||||
unsafe {
|
||||
#[cfg(target_env = "gnu")]
|
||||
let ret = libc::ioctl(
|
||||
loop_control.as_raw_fd() as libc::c_int,
|
||||
LOOP_CTL_GET_FREE as libc::c_ulong,
|
||||
) as i32;
|
||||
#[cfg(target_env = "musl")]
|
||||
let ret = libc::ioctl(
|
||||
loop_control.as_raw_fd() as libc::c_int,
|
||||
LOOP_CTL_GET_FREE as libc::c_int,
|
||||
) as i32;
|
||||
match nix::errno::Errno::result(ret) {
|
||||
Ok(ret) => Ok(ret),
|
||||
Err(e) => Err(Error::Nix(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_loop_dev(backing_file: &str, loop_dev: &str, params: &LoopParams) -> Result<File> {
|
||||
const LOOP_SET_FD: u32 = 0x4c00;
|
||||
const LOOP_CLR_FD: u32 = 0x4c01;
|
||||
const LOOP_SET_STATUS64: u32 = 0x4c04;
|
||||
const LOOP_SET_DIRECT_IO: u32 = 0x4c08;
|
||||
const LO_FLAGS_READ_ONLY: u32 = 0x1;
|
||||
const LO_FLAGS_AUTOCLEAR: u32 = 0x4;
|
||||
let mut open_options = File::options();
|
||||
open_options.read(true);
|
||||
if !params.readonly {
|
||||
open_options.write(true);
|
||||
}
|
||||
// 1. open backing file
|
||||
let back = open_options
|
||||
.open(backing_file)
|
||||
.map_err(|e| Error::IoError {
|
||||
context: format!("open {} error: ", backing_file),
|
||||
err: e,
|
||||
})?;
|
||||
let loop_dev = open_options.open(loop_dev).map_err(|e| Error::IoError {
|
||||
context: format!("open {} error: ", loop_dev),
|
||||
err: e,
|
||||
})?;
|
||||
// 2. set FD
|
||||
unsafe {
|
||||
#[cfg(target_env = "gnu")]
|
||||
let ret = libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_SET_FD as libc::c_ulong,
|
||||
back.as_raw_fd() as libc::c_int,
|
||||
);
|
||||
#[cfg(target_env = "musl")]
|
||||
let ret = libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_SET_FD as libc::c_int,
|
||||
back.as_raw_fd() as libc::c_int,
|
||||
);
|
||||
if let Err(e) = nix::errno::Errno::result(ret) {
|
||||
return Err(Error::Nix(e));
|
||||
}
|
||||
}
|
||||
// 3. set info
|
||||
let mut info = LoopInfo::default();
|
||||
info.file_name[..backing_file.as_bytes().len()].copy_from_slice(backing_file.as_bytes());
|
||||
if params.readonly {
|
||||
info.flags |= LO_FLAGS_READ_ONLY;
|
||||
}
|
||||
|
||||
if params.auto_clear {
|
||||
info.flags |= LO_FLAGS_AUTOCLEAR;
|
||||
}
|
||||
unsafe {
|
||||
#[cfg(target_env = "gnu")]
|
||||
let ret = libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_SET_STATUS64 as libc::c_ulong,
|
||||
info,
|
||||
);
|
||||
#[cfg(target_env = "musl")]
|
||||
if let Err(e) = nix::errno::Errno::result(ret) {
|
||||
libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_CLR_FD as libc::c_int,
|
||||
0,
|
||||
);
|
||||
return Err(Error::Nix(e));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Set Direct IO
|
||||
if params.direct {
|
||||
unsafe {
|
||||
#[cfg(target_env = "gnu")]
|
||||
let ret = libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_SET_DIRECT_IO as libc::c_ulong,
|
||||
1,
|
||||
);
|
||||
#[cfg(target_env = "musl")]
|
||||
let ret = libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_SET_DIRECT_IO as libc::c_int,
|
||||
1,
|
||||
);
|
||||
if let Err(e) = nix::errno::Errno::result(ret) {
|
||||
#[cfg(target_env = "gnu")]
|
||||
libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_CLR_FD as libc::c_ulong,
|
||||
0,
|
||||
);
|
||||
#[cfg(target_env = "musl")]
|
||||
libc::ioctl(
|
||||
loop_dev.as_raw_fd() as libc::c_int,
|
||||
LOOP_CLR_FD as libc::c_int,
|
||||
0,
|
||||
);
|
||||
return Err(Error::Nix(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(loop_dev)
|
||||
}
|
||||
|
||||
pub fn umount_recursive(target: Option<&str>, flags: i32) -> Result<()> {
|
||||
if let Some(target) = target {
|
||||
let mut mounts = get_mounts(Some(prefix_filter(target.to_string())))?;
|
||||
|
|
|
|||
Loading…
Reference in New Issue