use crate::bindings;
use crate::common;
use crate::error::GlueError;
use crate::error::LibError;
use crate::error::LibErrorCode;
use crate::qgroup::QgroupInherit;
use crate::subvolume::SubvolumeInfo;
use crate::subvolume::SubvolumeIterator;
use crate::Result;
use std::convert::TryFrom;
use std::ffi::CString;
use std::path::PathBuf;
use bindings::btrfs_util_create_snapshot;
use bindings::btrfs_util_create_subvolume;
use bindings::btrfs_util_delete_subvolume;
use bindings::btrfs_util_deleted_subvolumes;
use bindings::btrfs_util_get_default_subvolume;
use bindings::btrfs_util_get_subvolume_read_only;
use bindings::btrfs_util_is_subvolume;
use bindings::btrfs_util_qgroup_inherit;
use bindings::btrfs_util_set_default_subvolume;
use bindings::btrfs_util_set_subvolume_read_only;
use bindings::btrfs_util_subvolume_id;
use bindings::btrfs_util_subvolume_path;
bitflags! {
pub struct DeleteFlags: i32 {
const RECURSIVE = bindings::BTRFS_UTIL_DELETE_SUBVOLUME_RECURSIVE as i32;
}
}
bitflags! {
pub struct SnapshotFlags: i32 {
const READ_ONLY = bindings::BTRFS_UTIL_CREATE_SNAPSHOT_READ_ONLY as i32;
const RECURSIVE = bindings::BTRFS_UTIL_CREATE_SNAPSHOT_RECURSIVE as i32;
}
}
#[derive(Clone, Debug)]
pub struct Subvolume(u64);
impl Subvolume {
pub fn create<T: Into<PathBuf> + Clone>(
path: T,
qgroup: Option<QgroupInherit>,
) -> Result<Self> {
let path_cstr = common::into_path_to_cstr(path.clone())?;
let qgroup_ptr: *mut btrfs_util_qgroup_inherit =
if_let_some!(qgroup, val, val.into(), std::ptr::null_mut());
unsafe_wrapper!(errcode, {
errcode = btrfs_util_create_subvolume(
path_cstr.as_ptr(),
0,
std::ptr::null_mut(),
qgroup_ptr,
);
});
Self::get(path)
}
pub fn delete(self, flags: Option<DeleteFlags>) -> Result<()> {
let path_cstr = common::path_to_cstr(self.path()?)?;
let flags_val = if_let_some!(flags, val, val.bits(), 0);
unsafe_wrapper!(errcode, {
errcode = btrfs_util_delete_subvolume(path_cstr.as_ptr(), flags_val);
});
Ok(())
}
pub fn deleted<T: Into<PathBuf>>(path: Option<T>) -> Result<Vec<Subvolume>> {
let path_cstr = common::optional_into_path_to_cstr(path)?;
let mut ids_ptr: *mut u64 = std::ptr::null_mut();
let mut ids_count: u64 = 0;
unsafe_wrapper!(errcode, {
errcode =
btrfs_util_deleted_subvolumes(path_cstr.as_ptr(), &mut ids_ptr, &mut ids_count);
});
if ids_count == 0 {
return Ok(Vec::new());
}
glue_error!(ids_ptr.is_null(), GlueError::NullPointerReceived);
let subvolume_ids: Vec<u64> =
unsafe { std::slice::from_raw_parts(ids_ptr, ids_count as usize).to_owned() };
let subvolumes: Vec<Subvolume> = {
let mut subvolumes: Vec<Subvolume> = Vec::with_capacity(ids_count as usize);
for item in subvolume_ids {
subvolumes.push(Subvolume(item));
}
subvolumes
};
Ok(subvolumes)
}
pub fn get_default<T: Into<PathBuf>>(path: Option<T>) -> Result<Self> {
let path_cstr = common::optional_into_path_to_cstr(path)?;
let mut id: u64 = 0;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_get_default_subvolume(path_cstr.as_ptr(), &mut id);
});
Ok(Subvolume(id))
}
pub fn set_default(&self) -> Result<()> {
let path_cstr = common::into_path_to_cstr("/")?;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_set_default_subvolume(path_cstr.as_ptr(), self.0);
});
Ok(())
}
pub fn is_ro(&self) -> Result<bool> {
let path_cstr = common::path_to_cstr(self.path()?)?;
let mut ro: bool = false;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_get_subvolume_read_only(path_cstr.as_ptr(), &mut ro);
});
Ok(ro)
}
pub fn set_ro(&self, ro: bool) -> Result<()> {
let path_cstr = common::path_to_cstr(self.path()?)?;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_set_subvolume_read_only(path_cstr.as_ptr(), ro);
});
Ok(())
}
pub fn get<T: Into<PathBuf>>(path: T) -> Result<Self> {
let path_cstr = common::into_path_to_cstr(path)?;
let id: *mut u64 = &mut 0;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_subvolume_id(path_cstr.as_ptr(), id);
});
glue_error!(id.is_null(), GlueError::NullPointerReceived);
let subvol_id: u64 = unsafe { *id };
Ok(Self(subvol_id))
}
pub fn is_subvolume<T: Into<PathBuf>>(path: T) -> Result<()> {
let path_cstr = common::into_path_to_cstr(path)?;
unsafe_wrapper!(errcode, {
errcode = btrfs_util_is_subvolume(path_cstr.as_ptr());
});
Ok(())
}
pub fn info(&self) -> Result<SubvolumeInfo> {
SubvolumeInfo::try_from(self)
}
pub fn path(&self) -> Result<PathBuf> {
let path_cstr = common::into_path_to_cstr("/")?;
let mut str_ptr: *mut std::os::raw::c_char = std::ptr::null_mut();
unsafe_wrapper!(errcode, {
errcode = btrfs_util_subvolume_path(path_cstr.as_ptr(), self.0, &mut str_ptr);
});
glue_error!(str_ptr.is_null(), GlueError::NullPointerReceived);
let cstr: CString = unsafe { CString::from_raw(str_ptr) };
match cstr.to_str() {
Ok(val) => Ok(PathBuf::from(format!("/{}", val))),
Err(e) => glue_error!(GlueError::Utf8Error(e)),
}
}
pub fn snapshot<T: Into<PathBuf> + Clone>(
&self,
path: T,
flags: Option<SnapshotFlags>,
qgroup: Option<QgroupInherit>,
) -> Result<Self> {
let path_src_cstr = common::path_to_cstr(self.path()?)?;
let path_dest_cstr = common::into_path_to_cstr(path.clone())?;
let flags_val = if_let_some!(flags, val, val.bits(), 0);
let qgroup_ptr: *mut btrfs_util_qgroup_inherit =
if_let_some!(qgroup, val, val.into(), std::ptr::null_mut());
unsafe_wrapper!(errcode, {
errcode = btrfs_util_create_snapshot(
path_src_cstr.as_ptr(),
path_dest_cstr.as_ptr(),
flags_val,
std::ptr::null_mut(), qgroup_ptr,
);
});
Ok(Self::get(path)?)
}
pub fn id(&self) -> u64 {
self.0
}
pub(crate) fn new(id: u64) -> Self {
Self(id)
}
}
impl Into<Result<SubvolumeIterator>> for Subvolume {
fn into(self) -> Result<SubvolumeIterator> {
SubvolumeIterator::create(self, None)
}
}