mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-09 00:00:04 +03:00
feat(rust/wrappers): init BcachefsHandle
We propose a simple low-level wrapper which can perform various subvolume-related operations as an example for the API surface. This will be used in an upcoming commit to migrate the subvolume CLI fully to Rust. The API design is the following: - `BcachefsHandle` is meant as a low level handle to carry around whenever you need a filesystem handle to send ioctl to. - it possess type-safe operations Type safe operations are handled by having type safe wrappers for ioctl commands *and* their payloads. We assume that all ioctl payloads only use *one* argument, this can easily be changed if needed. When the handle goes out of scope, we automatically close it à la C++ RAII. Signed-off-by: Ryan Lahfa <bcachefs@lahfa.xyz>
This commit is contained in:
parent
930646e8dd
commit
9282cb953c
@ -1,3 +1,4 @@
|
||||
mod wrappers;
|
||||
mod commands;
|
||||
mod key;
|
||||
|
||||
|
103
src/wrappers/handle.rs
Normal file
103
src/wrappers/handle.rs
Normal file
@ -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<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) };
|
||||
}
|
||||
}
|
1
src/wrappers/mod.rs
Normal file
1
src/wrappers/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod handle;
|
Loading…
Reference in New Issue
Block a user