]> git.sesse.net Git - bcachefs-tools-debian/blob - src/wrappers/handle.rs
336a029f5fae9632eba3e0520729bc1c511ed8f5
[bcachefs-tools-debian] / src / wrappers / handle.rs
1 use std::{path::Path, os::unix::ffi::OsStrExt, ffi::CString};
2
3 use bch_bindgen::c::{bchfs_handle, BCH_IOCTL_SUBVOLUME_CREATE, BCH_IOCTL_SUBVOLUME_DESTROY, bch_ioctl_subvolume, bcache_fs_open, BCH_SUBVOL_SNAPSHOT_CREATE, bcache_fs_close};
4 use errno::Errno;
5
6 /// A handle to a bcachefs filesystem
7 /// This can be used to send [`libc::ioctl`] to the underlying filesystem.
8 pub(crate) struct BcachefsHandle {
9     inner: bchfs_handle
10 }
11
12 impl BcachefsHandle {
13     /// Opens a bcachefs filesystem and returns its handle
14     /// TODO(raitobezarius): how can this not be faillible?
15     pub(crate) unsafe fn open<P: AsRef<Path>>(path: P) -> Self {
16         let path = CString::new(path.as_ref().as_os_str().as_bytes()).expect("Failed to cast path into a C-style string");
17         Self {
18             inner: bcache_fs_open(path.as_ptr())
19         }
20     }
21 }
22
23 /// I/O control commands that can be sent to a bcachefs filesystem
24 /// Those are non-exhaustive 
25 #[repr(u64)]
26 #[non_exhaustive]
27 pub enum BcachefsIoctl {
28     SubvolumeCreate = BCH_IOCTL_SUBVOLUME_CREATE,
29     SubvolumeDestroy = BCH_IOCTL_SUBVOLUME_DESTROY,
30 }
31
32 /// I/O control commands payloads
33 #[non_exhaustive]
34 pub enum BcachefsIoctlPayload {
35     Subvolume(bch_ioctl_subvolume),
36 }
37
38 impl From<&BcachefsIoctlPayload> for *const libc::c_void {
39     fn from(value: &BcachefsIoctlPayload) -> Self {
40         match value {
41             BcachefsIoctlPayload::Subvolume(p) => p as *const _ as *const libc::c_void
42         }
43     }
44 }
45
46 impl BcachefsHandle {
47     /// Type-safe [`libc::ioctl`] for bcachefs filesystems
48     pub fn ioctl(&self, request: BcachefsIoctl, payload: &BcachefsIoctlPayload) -> Result<(), Errno> {
49         let payload_ptr: *const libc::c_void = payload.into();
50         let ret = unsafe { libc::ioctl(self.inner.ioctl_fd, request as u64, payload_ptr) };
51
52         if ret == -1 {
53             Err(errno::errno())
54         } else {
55             Ok(())
56         }
57     }
58
59     /// Create a subvolume for this bcachefs filesystem
60     /// at the given path
61     pub fn create_subvolume<P: AsRef<Path>>(&self, dst: P) -> Result<(), Errno> {
62         let dst = CString::new(dst.as_ref().as_os_str().as_bytes()).expect("Failed to cast destination path for subvolume in a C-style string");
63         self.ioctl(BcachefsIoctl::SubvolumeCreate, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
64             dirfd: libc::AT_FDCWD,
65             mode: 0o777,
66             dst_ptr: dst.as_ptr() as u64,
67             ..Default::default()
68         }))
69     }
70
71     /// Delete the subvolume at the given path
72     /// for this bcachefs filesystem
73     pub fn delete_subvolume<P: AsRef<Path>>(&self, dst: P) -> Result<(), Errno> {
74         let dst = CString::new(dst.as_ref().as_os_str().as_bytes()).expect("Failed to cast destination path for subvolume in a C-style string");
75         self.ioctl(BcachefsIoctl::SubvolumeDestroy, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
76             dirfd: libc::AT_FDCWD,
77             mode: 0o777,
78             dst_ptr: dst.as_ptr() as u64,
79             ..Default::default()
80         }))
81     }
82
83     /// Snapshot a subvolume for this bcachefs filesystem
84     /// at the given path
85     pub fn snapshot_subvolume<P: AsRef<Path>>(&self, extra_flags: u32, src: Option<P>, dst: P) -> Result<(), Errno> {
86         let src = src.map(|src| CString::new(src.as_ref().as_os_str().as_bytes()).expect("Failed to cast source path for subvolume in a C-style string"));
87         let dst = CString::new(dst.as_ref().as_os_str().as_bytes()).expect("Failed to cast destination path for subvolume in a C-style string");
88
89         let res = self.ioctl(BcachefsIoctl::SubvolumeCreate, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
90             flags: BCH_SUBVOL_SNAPSHOT_CREATE | extra_flags,
91             dirfd: libc::AT_FDCWD,
92             mode: 0o777,
93             src_ptr: src.as_ref().map_or(0, |x| x.as_ptr() as u64),
94             //src_ptr: if let Some(src) = src { src.as_ptr() } else { std::ptr::null() } as u64,
95             dst_ptr: dst.as_ptr() as u64,
96             ..Default::default()
97         }));
98
99         drop(src);
100         drop(dst);
101         res
102     }
103 }
104
105 impl Drop for BcachefsHandle {
106     fn drop(&mut self) {
107         unsafe { bcache_fs_close(self.inner) };
108     }
109 }