]> git.proxmox.com Git - proxmox-backup.git/blob - src/tools/loopdev.rs
d/control: add ',' after qrencode dependency
[proxmox-backup.git] / src / tools / loopdev.rs
1 use anyhow::Error;
2 use std::fs::{File, OpenOptions};
3 use std::path::Path;
4 use std::os::unix::io::{RawFd, AsRawFd};
5
6 const LOOP_CONTROL: &str = "/dev/loop-control";
7 const LOOP_NAME: &str = "/dev/loop";
8
9 /// Implements a subset of loop device ioctls necessary to assign and release
10 /// a single file from a free loopdev.
11 mod loop_ioctl {
12 use nix::{ioctl_none, ioctl_write_int_bad, ioctl_write_ptr_bad};
13
14 const LOOP_IOCTL: u16 = 0x4C; // 'L'
15 const LOOP_SET_FD: u16 = 0x00;
16 const LOOP_CLR_FD: u16 = 0x01;
17 const LOOP_SET_STATUS64: u16 = 0x04;
18
19 const LOOP_CTRL_GET_FREE: u16 = 0x82;
20
21 ioctl_write_int_bad!(ioctl_set_fd, (LOOP_IOCTL << 8) | LOOP_SET_FD);
22 ioctl_none!(ioctl_clr_fd, LOOP_IOCTL, LOOP_CLR_FD);
23 ioctl_none!(ioctl_ctrl_get_free, LOOP_IOCTL, LOOP_CTRL_GET_FREE);
24 ioctl_write_ptr_bad!(ioctl_set_status64, (LOOP_IOCTL << 8) | LOOP_SET_STATUS64, LoopInfo64);
25
26 pub const LO_FLAGS_READ_ONLY: u32 = 1;
27 pub const LO_FLAGS_PARTSCAN: u32 = 8;
28
29 const LO_NAME_SIZE: usize = 64;
30 const LO_KEY_SIZE: usize = 32;
31
32 #[repr(C)]
33 pub struct LoopInfo64 {
34 pub lo_device: u64,
35 pub lo_inode: u64,
36 pub lo_rdevice: u64,
37 pub lo_offset: u64,
38 pub lo_sizelimit: u64,
39 pub lo_number: u32,
40 pub lo_encrypt_type: u32,
41 pub lo_encrypt_key_size: u32,
42 pub lo_flags: u32,
43 pub lo_file_name: [u8; LO_NAME_SIZE],
44 pub lo_crypt_name: [u8; LO_NAME_SIZE],
45 pub lo_encrypt_key: [u8; LO_KEY_SIZE],
46 pub lo_init: [u64; 2],
47 }
48 }
49
50 // ioctl helpers create public fns, do not export them outside the module
51 // users should use the wrapper functions below
52 use loop_ioctl::*;
53
54 /// Use the GET_FREE ioctl to get or add a free loop device, of which the
55 /// /dev/loopN path will be returned. This is inherently racy because of the
56 /// delay between this and calling assign, but since assigning is atomic it
57 /// does not matter much and will simply cause assign to fail.
58 pub fn get_or_create_free_dev() -> Result<String, Error> {
59 let ctrl_file = File::open(LOOP_CONTROL)?;
60 let free_num = unsafe { ioctl_ctrl_get_free(ctrl_file.as_raw_fd())? };
61 let loop_file_path = format!("{}{}", LOOP_NAME, free_num);
62 Ok(loop_file_path)
63 }
64
65 fn assign_dev(fd: RawFd, backing_fd: RawFd) -> Result<(), Error> {
66 unsafe { ioctl_set_fd(fd, backing_fd)?; }
67
68 // set required read-only flag and partscan for convenience
69 let mut info: LoopInfo64 = unsafe { std::mem::zeroed() };
70 info.lo_flags = LO_FLAGS_READ_ONLY | LO_FLAGS_PARTSCAN;
71 unsafe { ioctl_set_status64(fd, &info)?; }
72
73 Ok(())
74 }
75
76 /// Open the next available /dev/loopN file and assign the given path to
77 /// it as it's backing file in read-only mode.
78 pub fn assign<P: AsRef<Path>>(loop_dev: P, backing: P) -> Result<(), Error> {
79 let loop_file = File::open(loop_dev)?;
80 let backing_file = OpenOptions::new()
81 .read(true)
82 .open(backing)?;
83 assign_dev(loop_file.as_raw_fd(), backing_file.as_raw_fd())?;
84 Ok(())
85 }
86
87 /// Unassign any file descriptors currently attached to the given
88 /// /dev/loopN device.
89 pub fn unassign<P: AsRef<Path>>(path: P) -> Result<(), Error> {
90 let loop_file = File::open(path)?;
91 unsafe { ioctl_clr_fd(loop_file.as_raw_fd())?; }
92 Ok(())
93 }