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
|
use std::path::Path;
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 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) };
}
}
|