Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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