mirror of
				https://github.com/edera-dev/krata.git
				synced 2025-11-03 23:29:39 +00:00 
			
		
		
		
	hypha: implement loop device support directly to avoid devd
This commit is contained in:
		
							
								
								
									
										147
									
								
								loopdev/src/bindings.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								loopdev/src/bindings.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,147 @@
 | 
			
		||||
/* originally generated by rust-bindgen */
 | 
			
		||||
/* modified to remove unused content by Mycelium */
 | 
			
		||||
#![allow(non_camel_case_types)]
 | 
			
		||||
 | 
			
		||||
pub const __BITS_PER_LONG: u32 = 64;
 | 
			
		||||
pub const __FD_SETSIZE: u32 = 1024;
 | 
			
		||||
pub const LOOP_SET_FD: u32 = 19456;
 | 
			
		||||
pub const LOOP_CLR_FD: u32 = 19457;
 | 
			
		||||
pub const LOOP_SET_STATUS64: u32 = 19460;
 | 
			
		||||
pub const LOOP_SET_CAPACITY: u32 = 19463;
 | 
			
		||||
pub const LOOP_CTL_ADD: u32 = 19584;
 | 
			
		||||
pub const LOOP_CTL_GET_FREE: u32 = 19586;
 | 
			
		||||
pub const LO_FLAGS_READ_ONLY: _bindgen_ty_1 = 1;
 | 
			
		||||
pub const LO_FLAGS_AUTOCLEAR: _bindgen_ty_1 = 4;
 | 
			
		||||
pub const LO_FLAGS_PARTSCAN: _bindgen_ty_1 = 8;
 | 
			
		||||
pub type _bindgen_ty_1 = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_old_uid_t = ::std::os::raw::c_ushort;
 | 
			
		||||
pub type __kernel_old_gid_t = ::std::os::raw::c_ushort;
 | 
			
		||||
pub type __kernel_old_dev_t = ::std::os::raw::c_ulong;
 | 
			
		||||
pub type __kernel_long_t = ::std::os::raw::c_long;
 | 
			
		||||
pub type __kernel_ulong_t = ::std::os::raw::c_ulong;
 | 
			
		||||
pub type __kernel_ino_t = __kernel_ulong_t;
 | 
			
		||||
pub type __kernel_mode_t = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_pid_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_ipc_pid_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_uid_t = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_gid_t = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_suseconds_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_daddr_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_uid32_t = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_gid32_t = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __kernel_size_t = __kernel_ulong_t;
 | 
			
		||||
pub type __kernel_ssize_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_ptrdiff_t = __kernel_long_t;
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Default, Copy, Clone)]
 | 
			
		||||
pub struct __kernel_fsid_t {
 | 
			
		||||
    pub val: [::std::os::raw::c_int; 2usize],
 | 
			
		||||
}
 | 
			
		||||
pub type __kernel_off_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_loff_t = ::std::os::raw::c_longlong;
 | 
			
		||||
pub type __kernel_old_time_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_time_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_time64_t = ::std::os::raw::c_longlong;
 | 
			
		||||
pub type __kernel_clock_t = __kernel_long_t;
 | 
			
		||||
pub type __kernel_timer_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_clockid_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_caddr_t = *mut ::std::os::raw::c_char;
 | 
			
		||||
pub type __kernel_uid16_t = ::std::os::raw::c_ushort;
 | 
			
		||||
pub type __kernel_gid16_t = ::std::os::raw::c_ushort;
 | 
			
		||||
pub type __s8 = ::std::os::raw::c_schar;
 | 
			
		||||
pub type __u8 = ::std::os::raw::c_uchar;
 | 
			
		||||
pub type __s16 = ::std::os::raw::c_short;
 | 
			
		||||
pub type __u16 = ::std::os::raw::c_ushort;
 | 
			
		||||
pub type __s32 = ::std::os::raw::c_int;
 | 
			
		||||
pub type __u32 = ::std::os::raw::c_uint;
 | 
			
		||||
pub type __s64 = ::std::os::raw::c_longlong;
 | 
			
		||||
pub type __u64 = ::std::os::raw::c_ulonglong;
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Default, Copy, Clone)]
 | 
			
		||||
pub struct __kernel_fd_set {
 | 
			
		||||
    pub fds_bits: [::std::os::raw::c_ulong; 16usize],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type __kernel_sighandler_t = Option<unsafe extern "C" fn(arg1: ::std::os::raw::c_int)>;
 | 
			
		||||
pub type __kernel_key_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __kernel_mqd_t = ::std::os::raw::c_int;
 | 
			
		||||
pub type __le16 = __u16;
 | 
			
		||||
pub type __be16 = __u16;
 | 
			
		||||
pub type __le32 = __u32;
 | 
			
		||||
pub type __be32 = __u32;
 | 
			
		||||
pub type __le64 = __u64;
 | 
			
		||||
pub type __be64 = __u64;
 | 
			
		||||
pub type __sum16 = __u16;
 | 
			
		||||
pub type __wsum = __u32;
 | 
			
		||||
pub type __poll_t = ::std::os::raw::c_uint;
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
pub struct loop_info {
 | 
			
		||||
    pub lo_number: ::std::os::raw::c_int,
 | 
			
		||||
    pub lo_device: __kernel_old_dev_t,
 | 
			
		||||
    pub lo_inode: ::std::os::raw::c_ulong,
 | 
			
		||||
    pub lo_rdevice: __kernel_old_dev_t,
 | 
			
		||||
    pub lo_offset: ::std::os::raw::c_int,
 | 
			
		||||
    pub lo_encrypt_type: ::std::os::raw::c_int,
 | 
			
		||||
    pub lo_encrypt_key_size: ::std::os::raw::c_int,
 | 
			
		||||
    pub lo_flags: ::std::os::raw::c_int,
 | 
			
		||||
    pub lo_name: [::std::os::raw::c_char; 64usize],
 | 
			
		||||
    pub lo_encrypt_key: [::std::os::raw::c_uchar; 32usize],
 | 
			
		||||
    pub lo_init: [::std::os::raw::c_ulong; 2usize],
 | 
			
		||||
    pub reserved: [::std::os::raw::c_char; 4usize],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for loop_info {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
 | 
			
		||||
            s.assume_init()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
pub struct loop_info64 {
 | 
			
		||||
    pub lo_device: __u64,
 | 
			
		||||
    pub lo_inode: __u64,
 | 
			
		||||
    pub lo_rdevice: __u64,
 | 
			
		||||
    pub lo_offset: __u64,
 | 
			
		||||
    pub lo_sizelimit: __u64,
 | 
			
		||||
    pub lo_number: __u32,
 | 
			
		||||
    pub lo_encrypt_type: __u32,
 | 
			
		||||
    pub lo_encrypt_key_size: __u32,
 | 
			
		||||
    pub lo_flags: __u32,
 | 
			
		||||
    pub lo_file_name: [__u8; 64usize],
 | 
			
		||||
    pub lo_crypt_name: [__u8; 64usize],
 | 
			
		||||
    pub lo_encrypt_key: [__u8; 32usize],
 | 
			
		||||
    pub lo_init: [__u64; 2usize],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for loop_info64 {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
 | 
			
		||||
            s.assume_init()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
pub struct loop_config {
 | 
			
		||||
    pub fd: __u32,
 | 
			
		||||
    pub block_size: __u32,
 | 
			
		||||
    pub info: loop_info64,
 | 
			
		||||
    pub __reserved: [__u64; 8usize],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for loop_config {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
 | 
			
		||||
            s.assume_init()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										475
									
								
								loopdev/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								loopdev/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,475 @@
 | 
			
		||||
// Taken from https://github.com/stratis-storage/loopdev-3/blob/master/src/lib.rs
 | 
			
		||||
// Licensed under MIT.
 | 
			
		||||
 | 
			
		||||
//! Setup and control loop devices.
 | 
			
		||||
//!
 | 
			
		||||
//! Provides rust interface with similar functionality to the Linux utility `losetup`.
 | 
			
		||||
//!
 | 
			
		||||
//! # Examples
 | 
			
		||||
//!
 | 
			
		||||
//! Default options:
 | 
			
		||||
//!
 | 
			
		||||
//! ```no_run
 | 
			
		||||
//! use loopdev::LoopControl;
 | 
			
		||||
//! let lc = LoopControl::open().unwrap();
 | 
			
		||||
//! let ld = lc.next_free().unwrap();
 | 
			
		||||
//!
 | 
			
		||||
//! println!("{}", ld.path().unwrap().display());
 | 
			
		||||
//!
 | 
			
		||||
//! ld.attach_file("disk.img").unwrap();
 | 
			
		||||
//! // ...
 | 
			
		||||
//! ld.detach().unwrap();
 | 
			
		||||
//! ```
 | 
			
		||||
//!
 | 
			
		||||
//! Custom options:
 | 
			
		||||
//!
 | 
			
		||||
//! ```no_run
 | 
			
		||||
//! # use loopdev::LoopControl;
 | 
			
		||||
//! # let lc = LoopControl::open().unwrap();
 | 
			
		||||
//! # let ld = lc.next_free().unwrap();
 | 
			
		||||
//! #
 | 
			
		||||
//! ld.with()
 | 
			
		||||
//!     .part_scan(true)
 | 
			
		||||
//!     .offset(512 * 1024 * 1024) // 512 MiB
 | 
			
		||||
//!     .size_limit(1024 * 1024 * 1024) // 1GiB
 | 
			
		||||
//!     .attach("disk.img").unwrap();
 | 
			
		||||
//! // ...
 | 
			
		||||
//! ld.detach().unwrap();
 | 
			
		||||
//! ```
 | 
			
		||||
mod bindings;
 | 
			
		||||
mod linux;
 | 
			
		||||
 | 
			
		||||
use crate::bindings::{
 | 
			
		||||
    loop_info64, LOOP_CLR_FD, LOOP_CTL_ADD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD,
 | 
			
		||||
    LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_PARTSCAN, LO_FLAGS_READ_ONLY,
 | 
			
		||||
};
 | 
			
		||||
use libc::ioctl;
 | 
			
		||||
use std::ffi::{c_int, c_ulong};
 | 
			
		||||
use std::{
 | 
			
		||||
    default::Default,
 | 
			
		||||
    fs::{File, OpenOptions},
 | 
			
		||||
    io,
 | 
			
		||||
    os::unix::prelude::*,
 | 
			
		||||
    path::{Path, PathBuf},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[cfg(all(not(target_os = "android"), not(target_env = "musl")))]
 | 
			
		||||
type IoctlRequest = c_ulong;
 | 
			
		||||
#[cfg(any(target_os = "android", target_env = "musl"))]
 | 
			
		||||
type IoctlRequest = c_int;
 | 
			
		||||
 | 
			
		||||
const LOOP_CONTROL: &str = "/dev/loop-control";
 | 
			
		||||
#[cfg(not(target_os = "android"))]
 | 
			
		||||
const LOOP_PREFIX: &str = "/dev/loop";
 | 
			
		||||
#[cfg(target_os = "android")]
 | 
			
		||||
const LOOP_PREFIX: &str = "/dev/block/loop";
 | 
			
		||||
 | 
			
		||||
/// Interface to the loop control device: `/dev/loop-control`.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct LoopControl {
 | 
			
		||||
    dev_file: File,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoopControl {
 | 
			
		||||
    /// Opens the loop control device.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when opening
 | 
			
		||||
    /// the loop control file `/dev/loop-control`. See
 | 
			
		||||
    /// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
 | 
			
		||||
    /// for further details.
 | 
			
		||||
    pub fn open() -> io::Result<Self> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            dev_file: OpenOptions::new()
 | 
			
		||||
                .read(true)
 | 
			
		||||
                .write(true)
 | 
			
		||||
                .open(LOOP_CONTROL)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Finds and opens the next available loop device.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```no_run
 | 
			
		||||
    /// use loopdev::LoopControl;
 | 
			
		||||
    /// let lc = LoopControl::open().unwrap();
 | 
			
		||||
    /// let ld = lc.next_free().unwrap();
 | 
			
		||||
    /// println!("{}", ld.path().unwrap().display());
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when opening
 | 
			
		||||
    /// the loop device file `/dev/loopX`. See
 | 
			
		||||
    /// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
 | 
			
		||||
    /// for further details.
 | 
			
		||||
    pub fn next_free(&self) -> io::Result<LoopDevice> {
 | 
			
		||||
        let dev_num = ioctl_to_error(unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.dev_file.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_CTL_GET_FREE as IoctlRequest,
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
        LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Add and opens a new loop device.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```no_run
 | 
			
		||||
    /// use loopdev::LoopControl;
 | 
			
		||||
    /// let lc = LoopControl::open().unwrap();
 | 
			
		||||
    /// let ld = lc.add(1).unwrap();
 | 
			
		||||
    /// println!("{}", ld.path().unwrap().display());
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This funcitons will return an error when a loop device with the passed
 | 
			
		||||
    /// number exists or opening the newly created device fails.
 | 
			
		||||
    pub fn add(&self, n: u32) -> io::Result<LoopDevice> {
 | 
			
		||||
        let dev_num = ioctl_to_error(unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.dev_file.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_CTL_ADD as IoctlRequest,
 | 
			
		||||
                n as c_int,
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
        LoopDevice::open(format!("{}{}", LOOP_PREFIX, dev_num))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsRawFd for LoopControl {
 | 
			
		||||
    fn as_raw_fd(&self) -> RawFd {
 | 
			
		||||
        self.dev_file.as_raw_fd()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoRawFd for LoopControl {
 | 
			
		||||
    fn into_raw_fd(self) -> RawFd {
 | 
			
		||||
        self.dev_file.into_raw_fd()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Interface to a loop device ie `/dev/loop0`.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct LoopDevice {
 | 
			
		||||
    device: File,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AsRawFd for LoopDevice {
 | 
			
		||||
    fn as_raw_fd(&self) -> RawFd {
 | 
			
		||||
        self.device.as_raw_fd()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl IntoRawFd for LoopDevice {
 | 
			
		||||
    fn into_raw_fd(self) -> RawFd {
 | 
			
		||||
        self.device.into_raw_fd()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LoopDevice {
 | 
			
		||||
    /// Opens a loop device.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when opening
 | 
			
		||||
    /// the given loop device file. See
 | 
			
		||||
    /// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
 | 
			
		||||
    /// for further details.
 | 
			
		||||
    pub fn open<P: AsRef<Path>>(dev: P) -> io::Result<Self> {
 | 
			
		||||
        // TODO create dev if it does not exist and begins with LOOP_PREFIX
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            device: OpenOptions::new().read(true).write(true).open(dev)?,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to a file with given options.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// Attach the device to a file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```no_run
 | 
			
		||||
    /// use loopdev::LoopDevice;
 | 
			
		||||
    /// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
 | 
			
		||||
    /// ld.with().part_scan(true).attach("disk.img").unwrap();
 | 
			
		||||
    /// # ld.detach().unwrap();
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn with(&self) -> AttachOptions<'_> {
 | 
			
		||||
        AttachOptions {
 | 
			
		||||
            device: self,
 | 
			
		||||
            info: loop_info64::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to a file that maps to the whole file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// Attach the device to a file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```no_run
 | 
			
		||||
    /// use loopdev::LoopDevice;
 | 
			
		||||
    /// let ld = LoopDevice::open("/dev/loop0").unwrap();
 | 
			
		||||
    /// ld.attach_file("disk.img").unwrap();
 | 
			
		||||
    /// # ld.detach().unwrap();
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons. Either when
 | 
			
		||||
    /// opening the backing file (see
 | 
			
		||||
    /// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
 | 
			
		||||
    /// for further details) or when calling the ioctl to attach the backing
 | 
			
		||||
    /// file to the device.
 | 
			
		||||
    pub fn attach_file<P: AsRef<Path>>(&self, backing_file: P) -> io::Result<()> {
 | 
			
		||||
        let info = loop_info64 {
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Self::attach_with_loop_info(self, backing_file, info)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to a file with `loop_info64`.
 | 
			
		||||
    fn attach_with_loop_info(
 | 
			
		||||
        &self, // TODO should be mut? - but changing it is a breaking change
 | 
			
		||||
        backing_file: impl AsRef<Path>,
 | 
			
		||||
        info: loop_info64,
 | 
			
		||||
    ) -> io::Result<()> {
 | 
			
		||||
        let write_access = (info.lo_flags & LO_FLAGS_READ_ONLY) == 0;
 | 
			
		||||
        let bf = OpenOptions::new()
 | 
			
		||||
            .read(true)
 | 
			
		||||
            .write(write_access)
 | 
			
		||||
            .open(backing_file)?;
 | 
			
		||||
        self.attach_fd_with_loop_info(bf, info)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to a fd with `loop_info`.
 | 
			
		||||
    fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
 | 
			
		||||
        // Attach the file
 | 
			
		||||
        ioctl_to_error(unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.device.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_SET_FD as IoctlRequest,
 | 
			
		||||
                bf.as_raw_fd() as c_int,
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let result = unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.device.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_SET_STATUS64 as IoctlRequest,
 | 
			
		||||
                &info,
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        match ioctl_to_error(result) {
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                // Ignore the error to preserve the original error
 | 
			
		||||
                let _detach_err = self.detach();
 | 
			
		||||
                Err(err)
 | 
			
		||||
            }
 | 
			
		||||
            Ok(_) => Ok(()),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the path of the loop device.
 | 
			
		||||
    pub fn path(&self) -> Option<PathBuf> {
 | 
			
		||||
        let mut p = PathBuf::from("/proc/self/fd");
 | 
			
		||||
        p.push(self.device.as_raw_fd().to_string());
 | 
			
		||||
        std::fs::read_link(&p).ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the device major number
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function needs to stat the backing file and can fail if there is
 | 
			
		||||
    /// an IO error.
 | 
			
		||||
    #[allow(clippy::unnecessary_cast)]
 | 
			
		||||
    pub fn major(&self) -> io::Result<u32> {
 | 
			
		||||
        self.device
 | 
			
		||||
            .metadata()
 | 
			
		||||
            .map(|m| linux::major(m.rdev()))
 | 
			
		||||
            .map(|m| m as u32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the device major number
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function needs to stat the backing file and can fail if there is
 | 
			
		||||
    /// an IO error.
 | 
			
		||||
    #[allow(clippy::unnecessary_cast)]
 | 
			
		||||
    pub fn minor(&self) -> io::Result<u32> {
 | 
			
		||||
        self.device
 | 
			
		||||
            .metadata()
 | 
			
		||||
            .map(|m| linux::minor(m.rdev()))
 | 
			
		||||
            .map(|m| m as u32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Detach a loop device from its backing file.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note that the device won't fully detach until a short delay after the underling device file
 | 
			
		||||
    /// gets closed. This happens when `LoopDev` goes out of scope so you should ensure the `LoopDev`
 | 
			
		||||
    /// lives for a short a time as possible.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```no_run
 | 
			
		||||
    /// use loopdev::LoopDevice;
 | 
			
		||||
    /// let ld = LoopDevice::open("/dev/loop0").unwrap();
 | 
			
		||||
    /// # ld.attach_file("disk.img").unwrap();
 | 
			
		||||
    /// ld.detach().unwrap();
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when calling the
 | 
			
		||||
    /// ioctl to detach the backing file from the device.
 | 
			
		||||
    pub fn detach(&self) -> io::Result<()> {
 | 
			
		||||
        ioctl_to_error(unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.device.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_CLR_FD as IoctlRequest,
 | 
			
		||||
                0,
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Resize a live loop device. If the size of the backing file changes this can be called to
 | 
			
		||||
    /// inform the loop driver about the new size.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when calling the
 | 
			
		||||
    /// ioctl to set the capacity of the device.
 | 
			
		||||
    pub fn set_capacity(&self) -> io::Result<()> {
 | 
			
		||||
        ioctl_to_error(unsafe {
 | 
			
		||||
            ioctl(
 | 
			
		||||
                self.device.as_raw_fd() as c_int,
 | 
			
		||||
                LOOP_SET_CAPACITY as IoctlRequest,
 | 
			
		||||
                0,
 | 
			
		||||
            )
 | 
			
		||||
        })?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Used to set options when attaching a device. Created with [`LoopDevice::with`()].
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// Enable partition scanning on attach:
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// use loopdev::LoopDevice;
 | 
			
		||||
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
 | 
			
		||||
/// ld.with()
 | 
			
		||||
///     .part_scan(true)
 | 
			
		||||
///     .attach("disk.img")
 | 
			
		||||
///     .unwrap();
 | 
			
		||||
/// # ld.detach().unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// A 1MiB slice of the file located at 1KiB into the file.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// use loopdev::LoopDevice;
 | 
			
		||||
/// let mut ld = LoopDevice::open("/dev/loop0").unwrap();
 | 
			
		||||
/// ld.with()
 | 
			
		||||
///     .offset(1024*1024)
 | 
			
		||||
///     .size_limit(1024*1024*1024)
 | 
			
		||||
///     .attach("disk.img")
 | 
			
		||||
///     .unwrap();
 | 
			
		||||
/// # ld.detach().unwrap();
 | 
			
		||||
/// ```
 | 
			
		||||
#[must_use]
 | 
			
		||||
pub struct AttachOptions<'d> {
 | 
			
		||||
    device: &'d LoopDevice,
 | 
			
		||||
    info: loop_info64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl AttachOptions<'_> {
 | 
			
		||||
    /// Offset in bytes from the start of the backing file the data will start at.
 | 
			
		||||
    pub fn offset(mut self, offset: u64) -> Self {
 | 
			
		||||
        self.info.lo_offset = offset;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Maximum size of the data in bytes.
 | 
			
		||||
    pub fn size_limit(mut self, size_limit: u64) -> Self {
 | 
			
		||||
        self.info.lo_sizelimit = size_limit;
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set read only flag
 | 
			
		||||
    pub fn read_only(mut self, read_only: bool) -> Self {
 | 
			
		||||
        if read_only {
 | 
			
		||||
            self.info.lo_flags |= LO_FLAGS_READ_ONLY;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set autoclear flag
 | 
			
		||||
    pub fn autoclear(mut self, autoclear: bool) -> Self {
 | 
			
		||||
        if autoclear {
 | 
			
		||||
            self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Force the kernel to scan the partition table on a newly created loop device. Note that the
 | 
			
		||||
    /// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
 | 
			
		||||
    pub fn part_scan(mut self, enable: bool) -> Self {
 | 
			
		||||
        if enable {
 | 
			
		||||
            self.info.lo_flags |= LO_FLAGS_PARTSCAN;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.info.lo_flags &= !LO_FLAGS_PARTSCAN;
 | 
			
		||||
        }
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to a file with the set options.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons. Either when
 | 
			
		||||
    /// opening the backing file (see
 | 
			
		||||
    /// [`OpenOptions::open`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html)
 | 
			
		||||
    /// for further details) or when calling the ioctl to attach the backing
 | 
			
		||||
    /// file to the device.
 | 
			
		||||
    pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
 | 
			
		||||
        self.device.attach_with_loop_info(backing_file, self.info)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Attach the loop device to an fd
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Errors
 | 
			
		||||
    ///
 | 
			
		||||
    /// This function will return an error for various reasons when calling the
 | 
			
		||||
    /// ioctl to attach the backing file to the device.
 | 
			
		||||
    pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
 | 
			
		||||
        self.device
 | 
			
		||||
            .attach_fd_with_loop_info(backing_file_fd, self.info)?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn ioctl_to_error(ret: i32) -> io::Result<i32> {
 | 
			
		||||
    if ret < 0 {
 | 
			
		||||
        Err(io::Error::last_os_error())
 | 
			
		||||
    } else {
 | 
			
		||||
        Ok(ret)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								loopdev/src/linux.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								loopdev/src/linux.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
use std::ffi::c_uint;
 | 
			
		||||
 | 
			
		||||
pub fn major(dev: u64) -> c_uint {
 | 
			
		||||
    let mut major = 0;
 | 
			
		||||
    major |= (dev & 0x00000000000fff00) >> 8;
 | 
			
		||||
    major |= (dev & 0xfffff00000000000) >> 32;
 | 
			
		||||
    major as c_uint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn minor(dev: u64) -> c_uint {
 | 
			
		||||
    let mut minor = 0;
 | 
			
		||||
    minor |= dev & 0x00000000000000ff;
 | 
			
		||||
    minor |= (dev & 0x00000ffffff00000) >> 12;
 | 
			
		||||
    minor as c_uint
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user