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