diff --git a/src/bcachefs.rs b/src/bcachefs.rs index 95f5e1f0..62bfdbb7 100644 --- a/src/bcachefs.rs +++ b/src/bcachefs.rs @@ -1,3 +1,4 @@ +mod wrappers; mod commands; mod key; diff --git a/src/wrappers/handle.rs b/src/wrappers/handle.rs new file mode 100644 index 00000000..9dd66188 --- /dev/null +++ b/src/wrappers/handle.rs @@ -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>(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>(&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>(&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>(&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) }; + } +} diff --git a/src/wrappers/mod.rs b/src/wrappers/mod.rs new file mode 100644 index 00000000..b2679605 --- /dev/null +++ b/src/wrappers/mod.rs @@ -0,0 +1 @@ +pub mod handle;