mod error;
mod qgroup;
mod subvol;
use std::{
ffi::CString,
os::{raw::c_int, unix::prelude::OsStrExt},
path::Path,
};
pub use error::{Error, ErrorKind};
pub use qgroup::QgroupInherit;
pub use subvol::*;
pub const FS_TREE_OBJECTID: u64 = 5;
pub fn sync<P: AsRef<Path>>(path: P) -> Result<(), Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let errcode = unsafe { ffi::btrfs_util_sync(cpath.as_ptr()) };
if errcode == ffi::btrfs_util_error::BTRFS_UTIL_OK {
Ok(())
} else {
Err(Error::new(errcode))
}
}
pub fn is_subvolume<P: AsRef<Path>>(path: P) -> Result<bool, Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let errcode = unsafe { ffi::btrfs_util_is_subvolume(cpath.as_ptr()) };
match errcode {
ffi::btrfs_util_error::BTRFS_UTIL_OK => Ok(true),
ffi::btrfs_util_error::BTRFS_UTIL_ERROR_NOT_SUBVOLUME
| ffi::btrfs_util_error::BTRFS_UTIL_ERROR_NOT_BTRFS => Ok(false),
_ => Err(Error::new(errcode)),
}
}
pub fn subvolume_id<P: AsRef<Path>>(path: P) -> Result<u64, Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let mut ret: u64 = 0;
let errcode = unsafe { ffi::btrfs_util_subvolume_id(cpath.as_ptr(), &mut ret) };
if errcode == ffi::btrfs_util_error::BTRFS_UTIL_OK {
Ok(ret)
} else {
Err(Error::new(errcode))
}
}
pub fn subvolume_info_with_id<P: AsRef<Path>>(path: P, id: u64) -> Result<SubvolumeInfo, Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let mut out = SubvolumeInfo::new();
unsafe {
let errcode = ffi::btrfs_util_subvolume_info(cpath.as_ptr(), id, out.as_ptr());
if errcode != ffi::btrfs_util_error::BTRFS_UTIL_OK {
return Err(Error::new(errcode));
}
}
Ok(out)
}
pub fn subvolume_info<P: AsRef<Path>>(path: P) -> Result<SubvolumeInfo, Error> {
subvolume_info_with_id(path, 0)
}
pub fn subvolume_read_only<P: AsRef<Path>>(path: P) -> Result<bool, Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let mut ret: bool = false;
let errcode = unsafe { ffi::btrfs_util_get_subvolume_read_only(cpath.as_ptr(), &mut ret) };
if errcode == ffi::btrfs_util_error::BTRFS_UTIL_OK {
Ok(ret)
} else {
Err(Error::new(errcode))
}
}
pub fn set_subvolume_read_only<P: AsRef<Path>>(path: P, read_only: bool) -> Result<(), Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let errcode = unsafe { ffi::btrfs_util_set_subvolume_read_only(cpath.as_ptr(), read_only) };
if errcode == ffi::btrfs_util_error::BTRFS_UTIL_OK {
Ok(())
} else {
Err(Error::new(errcode))
}
}
pub struct DeleteSubvolumeOptions {
recursive: bool,
}
impl DeleteSubvolumeOptions {
pub fn new() -> Self {
Self { recursive: false }
}
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
pub fn delete<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
let mut flags: c_int = 0;
if self.recursive {
flags |= ffi::BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE as c_int;
}
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
unsafe {
let errcode = ffi::btrfs_util_delete_subvolume(cpath.as_ptr(), flags);
if errcode != ffi::btrfs_util_error::BTRFS_UTIL_OK {
return Err(Error::new(errcode));
}
}
Ok(())
}
}
pub fn delete_subvolume<P: AsRef<Path>>(path: P) -> Result<(), Error> {
DeleteSubvolumeOptions::new().delete(path)
}
pub struct CreateSubvolumeOptions {
qgroup: Option<QgroupInherit>,
}
impl CreateSubvolumeOptions {
pub fn new() -> Self {
Self { qgroup: None }
}
pub fn qgroup(&mut self, qgroup: Option<QgroupInherit>) -> &mut Self {
self.qgroup = qgroup;
self
}
pub fn create<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let flags: c_int = 0;
let cqgroup: *mut ffi::btrfs_util_qgroup_inherit = if let Some(qg) = &self.qgroup {
qg.as_ptr()
} else {
std::ptr::null_mut()
};
let errcode = unsafe {
ffi::btrfs_util_create_subvolume(cpath.as_ptr(), flags, std::ptr::null_mut(), cqgroup)
};
if errcode != ffi::btrfs_util_error::BTRFS_UTIL_OK {
Err(Error::new(errcode))
} else {
Ok(())
}
}
}
pub fn create_subvolume<P: AsRef<Path>>(path: P) -> Result<(), Error> {
CreateSubvolumeOptions::new().create(path)
}
pub struct CreateSnapshotOptions {
qgroup: Option<QgroupInherit>,
readonly: bool,
recursive: bool,
}
impl CreateSnapshotOptions {
pub fn new() -> Self {
Self {
qgroup: None,
readonly: false,
recursive: false,
}
}
pub fn qgroup(&mut self, qgroup: Option<QgroupInherit>) -> &mut Self {
self.qgroup = qgroup;
self
}
pub fn readonly(&mut self, readonly: bool) -> &mut Self {
self.readonly = readonly;
self
}
pub fn recursive(&mut self, recursive: bool) -> &mut Self {
self.recursive = recursive;
self
}
pub fn create<P: AsRef<Path>, Q: AsRef<Path>>(
&mut self,
source: P,
path: Q,
) -> Result<(), Error> {
let csource = CString::new(source.as_ref().as_os_str().as_bytes()).unwrap();
let cpath = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
let mut flags: c_int = 0;
if self.readonly {
flags |= ffi::BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY as c_int;
}
if self.recursive {
flags |= ffi::BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE as c_int;
}
let cqgroup: *mut ffi::btrfs_util_qgroup_inherit = if let Some(qg) = &self.qgroup {
qg.as_ptr()
} else {
std::ptr::null_mut()
};
unsafe {
let errcode = ffi::btrfs_util_create_snapshot(
csource.as_ptr(),
cpath.as_ptr(),
flags,
std::ptr::null_mut(),
cqgroup,
);
if errcode != ffi::btrfs_util_error::BTRFS_UTIL_OK {
return Err(Error::new(errcode));
}
}
Ok(())
}
}