diff --git a/losetup/src/main.rs b/losetup/src/main.rs index ce71ea09..3af1260c 100644 --- a/losetup/src/main.rs +++ b/losetup/src/main.rs @@ -2,9 +2,9 @@ extern crate clap; extern crate loopdev; +use loopdev::{LoopControl, LoopDevice}; use std::io::{self, Write}; use std::process::exit; -use loopdev::{LoopControl, LoopDevice}; fn find() -> io::Result<()> { let loopdev = LoopControl::open()?.next_free()?; @@ -13,16 +13,25 @@ fn find() -> io::Result<()> { } fn attach(matches: &clap::ArgMatches) -> io::Result<()> { - let quite = matches.is_present("quite"); + let quiet = matches.is_present("quiet"); let image = matches.value_of("image").unwrap(); let offset = value_t!(matches.value_of("offset"), u64).unwrap_or(0); - let sizelimit = value_t!(matches.value_of("sizelimit"), u64).unwrap_or(0); - let loopdev = match matches.value_of("loopdev") { + let size_limit = value_t!(matches.value_of("sizelimit"), u64).unwrap_or(0); + let read_only = matches.is_present("read-only"); + let autoclear = matches.is_present("autoclear"); + let mut loopdev = match matches.value_of("loopdev") { Some(loopdev) => LoopDevice::open(&loopdev)?, None => LoopControl::open().and_then(|lc| lc.next_free())?, }; - loopdev.attach_with_sizelimit(&image, offset, sizelimit)?; - if !quite { + loopdev + .with() + .offset(offset) + .size_limit(size_limit) + .read_only(read_only) + .autoclear(autoclear) + .attach(image)?; + + if !quiet { println!("{}", loopdev.path().unwrap().display()); } Ok(()) @@ -60,7 +69,9 @@ fn main() { (@arg loopdev: "the loop device to attach") (@arg offset: -o --offset +takes_value "the offset within the file to start at") (@arg sizelimit: -s --sizelimit +takes_value "the file is limited to this size") - (@arg quite: -q --quite "don't print the device name") + (@arg read_only: -r --read-only "set up a read-only loop device") + (@arg autoclear: -a --autoclear "set the autoclear flag") + (@arg quiet: -q --quiet "don't print the device name") ) (@subcommand detach => (about: "detach the loop device from the backing file") @@ -75,7 +86,8 @@ fn main() { (@arg free: -f --free "find free devices") (@arg used: -u --used "find used devices") ) - ).get_matches(); + ) + .get_matches(); let result = match matches.subcommand() { ("find", _) => find(), diff --git a/src/lib.rs b/src/lib.rs index 8fe54285..240c7bf2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,8 @@ extern crate libc; use bindings::{ - loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64, + loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_DIRECT_IO, + LOOP_SET_FD, LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_READ_ONLY, }; use libc::{c_int, ioctl}; use std::{ @@ -141,6 +142,7 @@ impl LoopDevice { AttachOptions { device: self, info: Default::default(), + direct_io: false, } } @@ -226,7 +228,11 @@ impl LoopDevice { .read(true) .write(true) .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( @@ -236,18 +242,21 @@ impl LoopDevice { ) })?; - if let Err(err) = ioctl_to_error(unsafe { + let result = unsafe { ioctl( self.device.as_raw_fd() as c_int, LOOP_SET_STATUS64 as IoctlRequest, &info, ) - }) { - // Ignore the error to preserve the original error - let _ = self.detach(); - return Err(err); + }; + match ioctl_to_error(result) { + Err(err) => { + // Ignore the error to preserve the original error + let _ = self.detach(); + Err(err) + } + Ok(_) => Ok(()), } - Ok(()) } /// Get the path of the loop device. @@ -314,6 +323,18 @@ impl LoopDevice { })?; Ok(()) } + + // Enable or disable direct I/O for the backing file. + pub fn set_direct_io(&self, direct_io: bool) -> io::Result<()> { + ioctl_to_error(unsafe { + ioctl( + self.device.as_raw_fd() as c_int, + LOOP_SET_DIRECT_IO as IoctlRequest, + if direct_io { 1 } else { 0 }, + ) + })?; + Ok(()) + } } /// Used to set options when attaching a device. Created with [LoopDevice::with()]. @@ -347,6 +368,7 @@ impl LoopDevice { pub struct AttachOptions<'d> { device: &'d mut LoopDevice, info: loop_info64, + direct_io: bool, } impl AttachOptions<'_> { @@ -362,6 +384,32 @@ impl AttachOptions<'_> { 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, read_only: bool) -> Self { + if read_only { + self.info.lo_flags |= LO_FLAGS_AUTOCLEAR; + } else { + self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR; + } + self + } + + // Enable or disable direct I/O for the backing file. + pub fn set_direct_io(mut self, direct_io: bool) -> Self { + self.direct_io = direct_io; + 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 { @@ -375,7 +423,21 @@ impl AttachOptions<'_> { /// Attach the loop device to a file with the set options. pub fn attach(self, backing_file: impl AsRef) -> io::Result<()> { - self.device.attach_with_loop_info(backing_file, self.info) + self.device.attach_with_loop_info(backing_file, self.info)?; + if self.direct_io { + self.device.set_direct_io(self.direct_io)?; + } + Ok(()) + } + + /// Attach the loop device to an fd + pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> { + self.device + .attach_fd_with_loop_info(backing_file_fd, self.info)?; + if self.direct_io { + self.device.set_direct_io(self.direct_io)?; + } + Ok(()) } }