]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - src/wrappers/handle.rs
feat(rust/wrappers): init `BcachefsHandle`
[bcachefs-tools-debian] / src / wrappers / handle.rs
diff --git a/src/wrappers/handle.rs b/src/wrappers/handle.rs
new file mode 100644 (file)
index 0000000..9dd6618
--- /dev/null
@@ -0,0 +1,103 @@
+use std::{path::Path, os::unix::ffi::OsStrExt, ffi::CString};
+
+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};
+use errno::Errno;
+
+/// A handle to a bcachefs filesystem
+/// This can be used to send [`libc::ioctl`] to the underlying filesystem.
+pub(crate) struct BcachefsHandle {
+    inner: bchfs_handle
+}
+
+impl BcachefsHandle {
+    /// Opens a bcachefs filesystem and returns its handle
+    /// TODO(raitobezarius): how can this not be faillible?
+    pub(crate) unsafe fn open<P: AsRef<Path>>(path: P) -> Self {
+        let path = CString::new(path.as_ref().as_os_str().as_bytes()).expect("Failed to cast path into a C-style string");
+        Self {
+            inner: bcache_fs_open(path.as_ptr())
+        }
+    }
+}
+
+/// I/O control commands that can be sent to a bcachefs filesystem
+/// Those are non-exhaustive 
+#[repr(u64)]
+#[non_exhaustive]
+pub enum BcachefsIoctl {
+    SubvolumeCreate = BCH_IOCTL_SUBVOLUME_CREATE,
+    SubvolumeDestroy = BCH_IOCTL_SUBVOLUME_DESTROY,
+}
+
+/// I/O control commands payloads
+#[non_exhaustive]
+pub enum BcachefsIoctlPayload {
+    Subvolume(bch_ioctl_subvolume),
+}
+
+impl From<&BcachefsIoctlPayload> for *const libc::c_void {
+    fn from(value: &BcachefsIoctlPayload) -> Self {
+        match value {
+            BcachefsIoctlPayload::Subvolume(p) => p as *const _ as *const libc::c_void
+        }
+    }
+}
+
+impl BcachefsHandle {
+    /// Type-safe [`libc::ioctl`] for bcachefs filesystems
+    pub fn ioctl(&self, request: BcachefsIoctl, payload: &BcachefsIoctlPayload) -> Result<(), Errno> {
+        let payload_ptr: *const libc::c_void = payload.into();
+        let ret = unsafe { libc::ioctl(self.inner.ioctl_fd, request as u64, payload_ptr) };
+
+        if ret == -1 {
+            Err(errno::errno())
+        } else {
+            Ok(())
+        }
+    }
+
+    /// Create a subvolume for this bcachefs filesystem
+    /// at the given path
+    pub fn create_subvolume<P: AsRef<Path>>(&self, dst: P) -> Result<(), Errno> {
+        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");
+        self.ioctl(BcachefsIoctl::SubvolumeCreate, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
+            dirfd: libc::AT_FDCWD,
+            mode: 0o777,
+            dst_ptr: dst.as_ptr() as u64,
+            ..Default::default()
+        }))
+    }
+
+    /// Delete the subvolume at the given path
+    /// for this bcachefs filesystem
+    pub fn delete_subvolume<P: AsRef<Path>>(&self, dst: P) -> Result<(), Errno> {
+        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");
+        self.ioctl(BcachefsIoctl::SubvolumeDestroy, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
+            dirfd: libc::AT_FDCWD,
+            mode: 0o777,
+            dst_ptr: dst.as_ptr() as u64,
+            ..Default::default()
+        }))
+    }
+
+    /// Snapshot a subvolume for this bcachefs filesystem
+    /// at the given path
+    pub fn snapshot_subvolume<P: AsRef<Path>>(&self, extra_flags: u32, src: P, dst: P) -> Result<(), Errno> {
+        let src = CString::new(src.as_ref().as_os_str().as_bytes()).expect("Failed to cast source path for subvolume in a C-style string");
+        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");
+        self.ioctl(BcachefsIoctl::SubvolumeCreate, &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
+            flags: BCH_SUBVOL_SNAPSHOT_CREATE | extra_flags,
+            dirfd: libc::AT_FDCWD,
+            mode: 0o777,
+            src_ptr: src.as_ptr() as u64,
+            dst_ptr: dst.as_ptr() as u64,
+            ..Default::default()
+        }))
+    }
+}
+
+impl Drop for BcachefsHandle {
+    fn drop(&mut self) {
+        unsafe { bcache_fs_close(self.inner) };
+    }
+}