Skip to content

Commit 87a6cb2

Browse files
Felix Obenhubermdaffin
authored andcommitted
Add read_only, autoclear and direct IO
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.
1 parent ef2132a commit 87a6cb2

2 files changed

Lines changed: 90 additions & 16 deletions

File tree

losetup/src/main.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
extern crate clap;
33
extern crate loopdev;
44

5+
use loopdev::{LoopControl, LoopDevice};
56
use std::io::{self, Write};
67
use std::process::exit;
7-
use loopdev::{LoopControl, LoopDevice};
88

99
fn find() -> io::Result<()> {
1010
let loopdev = LoopControl::open()?.next_free()?;
@@ -13,16 +13,25 @@ fn find() -> io::Result<()> {
1313
}
1414

1515
fn attach(matches: &clap::ArgMatches) -> io::Result<()> {
16-
let quite = matches.is_present("quite");
16+
let quiet = matches.is_present("quiet");
1717
let image = matches.value_of("image").unwrap();
1818
let offset = value_t!(matches.value_of("offset"), u64).unwrap_or(0);
19-
let sizelimit = value_t!(matches.value_of("sizelimit"), u64).unwrap_or(0);
20-
let loopdev = match matches.value_of("loopdev") {
19+
let size_limit = value_t!(matches.value_of("sizelimit"), u64).unwrap_or(0);
20+
let read_only = matches.is_present("read-only");
21+
let autoclear = matches.is_present("autoclear");
22+
let mut loopdev = match matches.value_of("loopdev") {
2123
Some(loopdev) => LoopDevice::open(&loopdev)?,
2224
None => LoopControl::open().and_then(|lc| lc.next_free())?,
2325
};
24-
loopdev.attach_with_sizelimit(&image, offset, sizelimit)?;
25-
if !quite {
26+
loopdev
27+
.with()
28+
.offset(offset)
29+
.size_limit(size_limit)
30+
.read_only(read_only)
31+
.autoclear(autoclear)
32+
.attach(image)?;
33+
34+
if !quiet {
2635
println!("{}", loopdev.path().unwrap().display());
2736
}
2837
Ok(())
@@ -60,7 +69,9 @@ fn main() {
6069
(@arg loopdev: "the loop device to attach")
6170
(@arg offset: -o --offset +takes_value "the offset within the file to start at")
6271
(@arg sizelimit: -s --sizelimit +takes_value "the file is limited to this size")
63-
(@arg quite: -q --quite "don't print the device name")
72+
(@arg read_only: -r --read-only "set up a read-only loop device")
73+
(@arg autoclear: -a --autoclear "set the autoclear flag")
74+
(@arg quiet: -q --quiet "don't print the device name")
6475
)
6576
(@subcommand detach =>
6677
(about: "detach the loop device from the backing file")
@@ -75,7 +86,8 @@ fn main() {
7586
(@arg free: -f --free "find free devices")
7687
(@arg used: -u --used "find used devices")
7788
)
78-
).get_matches();
89+
)
90+
.get_matches();
7991

8092
let result = match matches.subcommand() {
8193
("find", _) => find(),

src/lib.rs

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
extern crate libc;
2020

2121
use bindings::{
22-
loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_FD, LOOP_SET_STATUS64,
22+
loop_info64, LOOP_CLR_FD, LOOP_CTL_GET_FREE, LOOP_SET_CAPACITY, LOOP_SET_DIRECT_IO,
23+
LOOP_SET_FD, LOOP_SET_STATUS64, LO_FLAGS_AUTOCLEAR, LO_FLAGS_READ_ONLY,
2324
};
2425
use libc::{c_int, ioctl};
2526
use std::{
@@ -141,6 +142,7 @@ impl LoopDevice {
141142
AttachOptions {
142143
device: self,
143144
info: Default::default(),
145+
direct_io: false,
144146
}
145147
}
146148

@@ -226,7 +228,11 @@ impl LoopDevice {
226228
.read(true)
227229
.write(true)
228230
.open(backing_file)?;
231+
self.attach_fd_with_loop_info(bf, info)
232+
}
229233

234+
/// Attach the loop device to a fd with loop_info.
235+
fn attach_fd_with_loop_info(&self, bf: impl AsRawFd, info: loop_info64) -> io::Result<()> {
230236
// Attach the file
231237
ioctl_to_error(unsafe {
232238
ioctl(
@@ -236,18 +242,21 @@ impl LoopDevice {
236242
)
237243
})?;
238244

239-
if let Err(err) = ioctl_to_error(unsafe {
245+
let result = unsafe {
240246
ioctl(
241247
self.device.as_raw_fd() as c_int,
242248
LOOP_SET_STATUS64 as IoctlRequest,
243249
&info,
244250
)
245-
}) {
246-
// Ignore the error to preserve the original error
247-
let _ = self.detach();
248-
return Err(err);
251+
};
252+
match ioctl_to_error(result) {
253+
Err(err) => {
254+
// Ignore the error to preserve the original error
255+
let _ = self.detach();
256+
Err(err)
257+
}
258+
Ok(_) => Ok(()),
249259
}
250-
Ok(())
251260
}
252261

253262
/// Get the path of the loop device.
@@ -314,6 +323,18 @@ impl LoopDevice {
314323
})?;
315324
Ok(())
316325
}
326+
327+
// Enable or disable direct I/O for the backing file.
328+
pub fn set_direct_io(&self, direct_io: bool) -> io::Result<()> {
329+
ioctl_to_error(unsafe {
330+
ioctl(
331+
self.device.as_raw_fd() as c_int,
332+
LOOP_SET_DIRECT_IO as IoctlRequest,
333+
if direct_io { 1 } else { 0 },
334+
)
335+
})?;
336+
Ok(())
337+
}
317338
}
318339

319340
/// Used to set options when attaching a device. Created with [LoopDevice::with()].
@@ -347,6 +368,7 @@ impl LoopDevice {
347368
pub struct AttachOptions<'d> {
348369
device: &'d mut LoopDevice,
349370
info: loop_info64,
371+
direct_io: bool,
350372
}
351373

352374
impl AttachOptions<'_> {
@@ -362,6 +384,32 @@ impl AttachOptions<'_> {
362384
self
363385
}
364386

387+
/// Set read only flag
388+
pub fn read_only(mut self, read_only: bool) -> Self {
389+
if read_only {
390+
self.info.lo_flags |= LO_FLAGS_READ_ONLY;
391+
} else {
392+
self.info.lo_flags &= !LO_FLAGS_READ_ONLY;
393+
}
394+
self
395+
}
396+
397+
/// Set autoclear flag
398+
pub fn autoclear(mut self, read_only: bool) -> Self {
399+
if read_only {
400+
self.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
401+
} else {
402+
self.info.lo_flags &= !LO_FLAGS_AUTOCLEAR;
403+
}
404+
self
405+
}
406+
407+
// Enable or disable direct I/O for the backing file.
408+
pub fn set_direct_io(mut self, direct_io: bool) -> Self {
409+
self.direct_io = direct_io;
410+
self
411+
}
412+
365413
/// Force the kernel to scan the partition table on a newly created loop device. Note that the
366414
/// partition table parsing depends on sector sizes. The default is sector size is 512 bytes
367415
pub fn part_scan(mut self, enable: bool) -> Self {
@@ -375,7 +423,21 @@ impl AttachOptions<'_> {
375423

376424
/// Attach the loop device to a file with the set options.
377425
pub fn attach(self, backing_file: impl AsRef<Path>) -> io::Result<()> {
378-
self.device.attach_with_loop_info(backing_file, self.info)
426+
self.device.attach_with_loop_info(backing_file, self.info)?;
427+
if self.direct_io {
428+
self.device.set_direct_io(self.direct_io)?;
429+
}
430+
Ok(())
431+
}
432+
433+
/// Attach the loop device to an fd
434+
pub fn attach_fd(self, backing_file_fd: impl AsRawFd) -> io::Result<()> {
435+
self.device
436+
.attach_fd_with_loop_info(backing_file_fd, self.info)?;
437+
if self.direct_io {
438+
self.device.set_direct_io(self.direct_io)?;
439+
}
440+
Ok(())
379441
}
380442
}
381443

0 commit comments

Comments
 (0)