summaryrefslogtreecommitdiff
path: root/src/wrappers/handle.rs
blob: 9dd66188d4dbbac54d4aa44ff2432af50cfa22bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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) };
    }
}