#![allow(clippy::len_without_is_empty, clippy::new_ret_no_self)]
use crate::{
bindings::{
btrfs_ioctl_search_args, btrfs_ioctl_search_header, btrfs_ioctl_search_key,
BTRFS_IOC_TREE_SEARCH,
},
util::{btrfs_ioctl, OptionFd},
};
use std::{
fs::File,
io,
mem::{size_of, MaybeUninit as Uninit},
os::fd::{AsRawFd, RawFd},
path::Path,
ptr::addr_of_mut,
slice, str,
};
pub unsafe trait SearchName: Sized {
fn name_len(&self) -> usize;
}
macro_rules! impl_tree_search_name {
($( $btrfs_search_item:ident ),+) => [$(
unsafe impl SearchName for $crate::bindings::$btrfs_search_item {
#[inline]
fn name_len(&self) -> usize {
u16::from_le(self.name_len) as usize
}
}
)+]
}
impl_tree_search_name![
btrfs_dir_item,
btrfs_inode_ref,
btrfs_root_ref,
btrfs_inode_extref
];
pub struct SearchItem<'a>(&'a [i8], &'a [i8]);
impl<'a> SearchItem<'a> {
#[inline]
const fn hdr(&self) -> *const btrfs_ioctl_search_header {
self.0.as_ptr().cast()
}
#[inline]
pub const fn transid(&self) -> u64 {
unsafe { self.hdr().read_unaligned().transid }
}
#[inline]
pub const fn objectid(&self) -> u64 {
unsafe { self.hdr().read_unaligned().objectid }
}
#[inline]
pub const fn offset(&self) -> u64 {
unsafe { self.hdr().read_unaligned().offset }
}
#[inline]
pub const fn key(&self) -> u32 {
unsafe { self.hdr().read_unaligned().type_ }
}
#[inline]
pub const fn len(&self) -> usize {
unsafe { self.hdr().read_unaligned().len as usize }
}
#[inline]
pub fn get<T: From<&'a [i8]>>(&self) -> T {
self.1.into()
}
#[inline]
pub fn name_as_ibytes<T: SearchName>(&self, item: &T) -> &'a [i8] {
let buf = &self.1[size_of::<T>()..];
&buf[..item.name_len()]
}
#[inline]
pub fn name_as_bytes<T: SearchName>(&self, item: &T) -> &'a [u8] {
let ibytes = self.name_as_ibytes(item);
unsafe { slice::from_raw_parts(ibytes.as_ptr().cast(), ibytes.len()) }
}
#[inline]
pub fn name_as_str<T: SearchName>(&self, item: &T) -> Result<&str, str::Utf8Error> {
str::from_utf8(self.name_as_bytes(item))
}
}
type Callback = Box<dyn Fn(&mut btrfs_ioctl_search_key, &SearchItem)>;
struct SearchCallback<'a> {
f: Callback,
key: &'a mut btrfs_ioctl_search_key,
}
pub struct SearchBuf<'a> {
pub nr_items: u32,
buf: &'a [i8],
positon: u32,
offset: usize,
callback: Option<SearchCallback<'a>>,
}
impl<'a> Iterator for SearchBuf<'a> {
type Item = SearchItem<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.nr_items == self.positon {
return None;
}
let (hdr, buf) = &self.buf[self.offset..].split_at(size_of::<btrfs_ioctl_search_header>());
let item = SearchItem(hdr, buf);
if let Some(cb) = self.callback.as_mut() {
(cb.f)(cb.key, &item)
}
self.offset += size_of::<btrfs_ioctl_search_header>() + item.len();
self.positon += 1;
Some(item)
}
}
pub struct TreeSearch {
fd: OptionFd,
sargs: Uninit<btrfs_ioctl_search_args>,
nr_items: u32,
}
impl TreeSearch {
pub fn search_with<F>(&mut self, f: F) -> io::Result<Option<SearchBuf>>
where
F: Fn(&mut btrfs_ioctl_search_key, &SearchItem) + 'static,
{
self.nr_items(self.nr_items);
btrfs_ioctl(
self.fd.as_raw_fd(),
BTRFS_IOC_TREE_SEARCH,
self.sargs.as_mut_ptr(),
)?;
let args = unsafe { self.sargs.assume_init_mut() };
Ok(if args.key.nr_items == 0 {
None
} else {
Some(SearchBuf {
buf: &args.buf,
nr_items: args.key.nr_items,
positon: 0,
offset: 0,
callback: Some(SearchCallback {
f: Box::new(f),
key: &mut args.key,
}),
})
})
}
pub fn search(&mut self) -> io::Result<Option<SearchBuf>> {
self.nr_items(self.nr_items);
btrfs_ioctl(
self.fd.as_raw_fd(),
BTRFS_IOC_TREE_SEARCH,
self.sargs.as_mut_ptr(),
)?;
let args = unsafe { self.sargs.assume_init_ref() };
Ok(if args.key.nr_items == 0 {
None
} else {
Some(SearchBuf {
buf: &args.buf,
nr_items: args.key.nr_items,
positon: 0,
offset: 0,
callback: None,
})
})
}
pub fn try_new<F, P: AsRef<Path>>(fs: P, f: F) -> io::Result<Self>
where
F: FnOnce(&mut btrfs_ioctl_search_key),
{
let mut key = btrfs_ioctl_search_key::default();
f(&mut key);
let mut sargs = Uninit::<btrfs_ioctl_search_args>::uninit();
unsafe {
addr_of_mut!((*sargs.as_mut_ptr()).key).write(key);
}
Ok(TreeSearch {
sargs,
nr_items: key.nr_items,
fd: OptionFd::File(File::open(fs)?),
})
}
#[inline]
pub fn nr_items(&mut self, nr_items: u32) -> &mut Self {
self.nr_items = nr_items;
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.nr_items).write(self.nr_items) }
self
}
#[inline]
pub fn tree_id(&mut self, tree_id: u64) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.tree_id).write(tree_id) }
self
}
#[inline]
pub fn min_offset(&mut self, offset: u64) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.min_offset).write(offset) }
self
}
#[inline]
pub fn max_offset(&mut self, offset: u64) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.max_offset).write(offset) }
self
}
#[inline]
pub fn min_objectid(&mut self, objectid: u64) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.min_objectid).write(objectid) }
self
}
#[inline]
pub fn max_objectid(&mut self, objectid: u64) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.max_objectid).write(objectid) }
self
}
#[inline]
pub fn min_type(&mut self, ty: u32) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.min_type).write(ty) }
self
}
#[inline]
pub fn max_type(&mut self, ty: u32) -> &mut Self {
unsafe { addr_of_mut!((*self.sargs.as_mut_ptr()).key.max_type).write(ty) }
self
}
}
pub mod fd {
use super::*;
pub struct TreeSearch;
impl TreeSearch {
pub fn new<F>(fd: RawFd, f: F) -> super::TreeSearch
where
F: FnOnce(&mut btrfs_ioctl_search_key),
{
let mut key = btrfs_ioctl_search_key::default();
f(&mut key);
let mut sargs = Uninit::<btrfs_ioctl_search_args>::uninit();
unsafe {
addr_of_mut!((*sargs.as_mut_ptr()).key).write(key);
}
super::TreeSearch {
sargs,
nr_items: key.nr_items,
fd: OptionFd::Raw(fd),
}
}
}
}