#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(test, expect(clippy::unwrap_used, clippy::expect_used))]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
use alloc::vec::Vec;
mod block_read;
mod btree;
mod checksum;
mod chunk_tree;
mod compression;
mod dir;
mod error;
mod file;
mod format;
mod inode;
mod path;
mod resolve;
mod root_tree;
mod superblock;
mod util;
pub use block_read::{BlockRead, SliceReadError};
use chunk_tree::ChunkMap;
pub use dir::DirEntry;
pub use error::{Error, Result, SuperblockReason};
pub use inode::{Inode, Metadata};
pub use path::Path;
#[doc(hidden)]
#[cfg(fuzzing)]
pub mod __fuzz_internals {
use alloc::vec::Vec;
pub fn decode(algorithm: u8, src: &[u8], dst: &mut Vec<u8>) -> crate::Result<()> {
crate::compression::decode(algorithm, src, dst)
}
pub fn parse_system_chunk_array(bytes: &[u8]) -> crate::Result<()> {
let mut map = crate::chunk_tree::ChunkMap::default();
map.parse_system_chunk_array(bytes, bytes.len())
}
pub fn name_hash(name: &[u8]) -> u32 {
crate::checksum::crc32c_with_seed(0xFFFF_FFFE, name)
}
}
pub struct Btrfs<R: BlockRead> {
reader: R,
chunk_map: ChunkMap,
nodesize: u32,
fs_tree_root: u64,
default_subvol_objectid: u64,
}
impl<R: BlockRead> Btrfs<R> {
pub fn open(mut reader: R, device_size_bytes: u64) -> Result<Self> {
let sb = superblock::load(&mut reader, device_size_bytes)?;
let mut chunk_map = ChunkMap::default();
chunk_map
.parse_system_chunk_array(&sb.sys_chunk_array, sb.sys_chunk_array_size as usize)?;
chunk_tree::populate_from_chunk_tree(
&mut reader,
&mut chunk_map,
sb.nodesize,
sb.chunk_root,
)?;
let (fs_tree_root, _fs_tree_level, default_subvol_objectid) =
root_tree::resolve_default_subvol(
&mut reader,
&chunk_map,
sb.nodesize,
sb.root,
sb.root_dir_objectid,
)?;
Ok(Self {
reader,
chunk_map,
nodesize: sb.nodesize,
fs_tree_root,
default_subvol_objectid,
})
}
pub fn default_subvol_objectid(&self) -> u64 {
self.default_subvol_objectid
}
pub fn resolve(&mut self, path: Path<'_>) -> Result<Inode> {
let objectid = resolve::resolve_path(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
path,
)?;
Ok(Inode { objectid })
}
pub fn metadata(&mut self, inode: &Inode) -> Result<Metadata> {
file::read_metadata(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
inode.objectid,
)
}
pub fn read_file(&mut self, path: Path<'_>) -> Result<Vec<u8>> {
let inode = self.resolve(path)?;
file::read_file(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
inode.objectid,
)
}
pub fn read_file_at(&mut self, inode: &Inode, offset: u64, buf: &mut [u8]) -> Result<usize> {
file::read_file_at(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
inode.objectid,
offset,
buf,
)
}
pub fn read_link(&mut self, path: Path<'_>) -> Result<Vec<u8>> {
let inode = self.resolve(path)?;
file::read_link(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
inode.objectid,
)
}
pub fn read_dir(&mut self, path: Path<'_>) -> Result<Vec<DirEntry>> {
let inode = self.resolve(path)?;
dir::read_dir(
&mut self.reader,
&self.chunk_map,
self.nodesize,
self.fs_tree_root,
inode.objectid,
)
}
}