1 use std::{path::Path, os::unix::ffi::OsStrExt, ffi::CString};
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};
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 {
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");
18 inner: bcache_fs_open(path.as_ptr())
23 /// I/O control commands that can be sent to a bcachefs filesystem
24 /// Those are non-exhaustive
27 pub enum BcachefsIoctl {
28 SubvolumeCreate = BCH_IOCTL_SUBVOLUME_CREATE,
29 SubvolumeDestroy = BCH_IOCTL_SUBVOLUME_DESTROY,
32 /// I/O control commands payloads
34 pub enum BcachefsIoctlPayload {
35 Subvolume(bch_ioctl_subvolume),
38 impl From<&BcachefsIoctlPayload> for *const libc::c_void {
39 fn from(value: &BcachefsIoctlPayload) -> Self {
41 BcachefsIoctlPayload::Subvolume(p) => p as *const _ as *const libc::c_void
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) };
59 /// Create a subvolume for this bcachefs filesystem
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,
66 dst_ptr: dst.as_ptr() as u64,
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,
78 dst_ptr: dst.as_ptr() as u64,
83 /// Snapshot a subvolume for this bcachefs filesystem
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");
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,
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,
105 impl Drop for BcachefsHandle {
107 unsafe { bcache_fs_close(self.inner) };