Skip to content

Commit

Permalink
Add read_only, autoclear and direct IO
Browse files Browse the repository at this point in the history
Allow passing the read only and autoclear flag when attaching a backing
file. Allow to pass a raw fd instead of a Path when attaching. Add a fn
to set the direct IO flag.
  • Loading branch information
Felix Obenhuber authored and mdaffin committed Jun 1, 2021
1 parent ef2132a commit 87a6cb2
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 16 deletions.
28 changes: 20 additions & 8 deletions losetup/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?;
Expand All @@ -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(())
Expand Down Expand Up @@ -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")
Expand All @@ -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(),
Expand Down
78 changes: 70 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -141,6 +142,7 @@ impl LoopDevice {
AttachOptions {
device: self,
info: Default::default(),
direct_io: false,
}
}

Expand Down Expand Up @@ -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(
Expand All @@ -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.
Expand Down Expand Up @@ -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()].
Expand Down Expand Up @@ -347,6 +368,7 @@ impl LoopDevice {
pub struct AttachOptions<'d> {
device: &'d mut LoopDevice,
info: loop_info64,
direct_io: bool,
}

impl AttachOptions<'_> {
Expand All @@ -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 {
Expand All @@ -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<Path>) -> 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(())
}
}

Expand Down

0 comments on commit 87a6cb2

Please sign in to comment.