use std::{
ffi::{CStr, CString},
path::Path,
ptr,
};
use either::Either;
use libblkid_rs_sys::blkid_cache;
use libc::{c_void, free};
use crate::{
consts::BlkidDevFlags,
dev::{BlkidDev, BlkidDevIter},
err::{BlkidErr, Result},
};
pub struct BlkidCache(blkid_cache, bool);
impl BlkidCache {
pub(crate) fn as_mut_ptr(&mut self) -> *mut blkid_cache {
&mut self.0 as *mut _
}
pub fn put_cache(&mut self) {
unsafe { libblkid_rs_sys::blkid_put_cache(self.0) };
self.1 = true;
}
pub fn get_cache(filename: Option<&Path>) -> Result<Self> {
let mut cache = ptr::null_mut();
let filename_cstring = match filename {
Some(fname) => Some(CString::new(fname.to_str().ok_or(BlkidErr::InvalidConv)?)?),
None => None,
};
errno!(unsafe {
libblkid_rs_sys::blkid_get_cache(
&mut cache as *mut _,
filename_cstring
.map(|s| s.as_ptr())
.unwrap_or(ptr::null_mut()),
)
})?;
Ok(BlkidCache(cache, false))
}
pub fn gc_cache(&mut self) {
unsafe { libblkid_rs_sys::blkid_gc_cache(self.0) }
}
pub fn iter(&self) -> BlkidDevIter {
BlkidDevIter::new(unsafe { libblkid_rs_sys::blkid_dev_iterate_begin(self.0) })
}
pub fn probe_all(&mut self) -> Result<()> {
errno!(unsafe { libblkid_rs_sys::blkid_probe_all(self.0) })
}
pub fn probe_all_new(&mut self) -> Result<()> {
errno!(unsafe { libblkid_rs_sys::blkid_probe_all_new(self.0) })
}
pub fn probe_all_removable(&mut self) -> Result<()> {
errno!(unsafe { libblkid_rs_sys::blkid_probe_all_removable(self.0) })
}
pub fn get_dev(&self, devname: &Path, flags: BlkidDevFlags) -> Result<BlkidDev> {
let devname_cstring =
CString::new(devname.to_str().ok_or(BlkidErr::InvalidConv)?.as_bytes())?;
Ok(BlkidDev::new(unsafe {
libblkid_rs_sys::blkid_get_dev(self.0, devname_cstring.as_ptr(), flags.into())
}))
}
pub fn get_tag_value(&self, tag_name: &str, devname: &Path) -> Result<String> {
let tag_name_cstring = CString::new(tag_name.as_bytes())?;
let devname_cstring =
CString::new(devname.to_str().ok_or(BlkidErr::InvalidConv)?.as_bytes())?;
let ptr = errno_ptr!(unsafe {
libblkid_rs_sys::blkid_get_tag_value(
self.0,
tag_name_cstring.as_ptr(),
devname_cstring.as_ptr(),
)
})?;
let string = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string();
unsafe { libc::free(ptr as *mut libc::c_void) };
Ok(string)
}
pub fn get_devname(&self, token_or_pair: Either<&str, (&str, &str)>) -> Result<String> {
let (name, value) = match token_or_pair {
Either::Left(token) => {
if !token.contains('=') {
return Err(BlkidErr::Other(
"Token input requires the format NAME=value".to_string(),
));
}
let mut split = token.split('=');
match (split.next(), split.next()) {
(Some(name), Some(value)) => (name, value),
(_, _) => {
return Err(BlkidErr::Other(
"Token input requires the format NAME=value".to_string(),
));
}
}
}
Either::Right((name, value)) => (name, value),
};
let name_cstring = CString::new(name.as_bytes())?;
let value_cstring = CString::new(value.as_bytes())?;
let ptr = errno_ptr!(unsafe {
libblkid_rs_sys::blkid_get_devname(
self.0,
name_cstring.as_ptr(),
value_cstring.as_ptr(),
)
})?;
let string = unsafe { CStr::from_ptr(ptr) }.to_str()?.to_string();
unsafe { libc::free(ptr as *mut libc::c_void) };
Ok(string)
}
pub fn find_dev_with_tag(&self, type_: &str, value: &str) -> Result<BlkidDev> {
let type_cstring = CString::new(type_)?;
let value_cstring = CString::new(value)?;
let ptr = errno_ptr!(unsafe {
libblkid_rs_sys::blkid_find_dev_with_tag(
self.0,
type_cstring.as_ptr(),
value_cstring.as_ptr(),
)
})?;
Ok(BlkidDev::new(ptr))
}
pub fn verify(&mut self, dev: BlkidDev) -> Option<BlkidDev> {
let ptr = unsafe { libblkid_rs_sys::blkid_verify(self.0, dev.as_ptr()) };
if ptr.is_null() {
None
} else {
Some(BlkidDev::new(ptr))
}
}
}
impl Drop for BlkidCache {
fn drop(&mut self) {
if !self.1 {
unsafe { free(self.0 as *mut c_void) }
}
}
}