summaryrefslogtreecommitdiff
path: root/src/wrappers/handle.rs
blob: fa065dec344ba9faee0784e939b447cf7fa17939 (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::path::Path;

use bch_bindgen::c::{
    bcache_fs_close, bcache_fs_open, bch_ioctl_subvolume, bchfs_handle, BCH_IOCTL_SUBVOLUME_CREATE,
    BCH_IOCTL_SUBVOLUME_DESTROY, BCH_SUBVOL_SNAPSHOT_CREATE,
};
use bch_bindgen::path_to_cstr;
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 = path_to_cstr(path);
        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(u32)]
#[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 libc::Ioctl, 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 = path_to_cstr(dst);
        self.ioctl(
            BcachefsIoctl::SubvolumeCreate,
            &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
                dirfd: libc::AT_FDCWD as u32,
                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 = path_to_cstr(dst);
        self.ioctl(
            BcachefsIoctl::SubvolumeDestroy,
            &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
                dirfd: libc::AT_FDCWD as u32,
                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: Option<P>,
        dst: P,
    ) -> Result<(), Errno> {
        let src = src.map(|src| path_to_cstr(src));
        let dst = path_to_cstr(dst);

        let res = self.ioctl(
            BcachefsIoctl::SubvolumeCreate,
            &BcachefsIoctlPayload::Subvolume(bch_ioctl_subvolume {
                flags: BCH_SUBVOL_SNAPSHOT_CREATE | extra_flags,
                dirfd: libc::AT_FDCWD as u32,
                mode: 0o777,
                src_ptr: src.as_ref().map_or(0, |x| x.as_ptr() as u64),
                //src_ptr: if let Some(src) = src { src.as_ptr() } else { std::ptr::null() } as u64,
                dst_ptr: dst.as_ptr() as u64,
                ..Default::default()
            }),
        );

        drop(src);
        drop(dst);
        res
    }
}

impl Drop for BcachefsHandle {
    fn drop(&mut self) {
        unsafe { bcache_fs_close(self.inner) };
    }
}