btrfs crate

This commit is contained in:
Janis 2023-03-15 19:25:19 +01:00
parent c22758f860
commit 47710fe4fa
5 changed files with 1237 additions and 0 deletions

27
btrfs/Cargo.toml Normal file
View file

@ -0,0 +1,27 @@
[package]
name = "btrfs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = []
std = []
[dependencies]
log = "*"
uuid = {version = "1.3.0", default-features = false}
bytemuck = {version = "1.13.1", features = ["derive"]}
byteorder = {version = "1.4.3", default-features = false}
scroll = {version = "0.11.0", features = ["derive"], default-features = false}
derivative = {version = "2.2.0", features = ["use_core"]}
hex = {version = "0.4.3", default-features = false}
zerocopy = "0.6.1"
crc = "3.0.1"
thiserror = { version = "1.0", package = "thiserror-core", default-features = false }
num_enum = {version = "0.5.11", default-features = false}
[dev-dependencies]
env_logger = "*"
test-log = "*"

257
btrfs/src/lib.rs Normal file
View file

@ -0,0 +1,257 @@
#![feature(error_in_core)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
use core::{borrow::Borrow, mem::size_of, ops::RangeBounds};
use alloc::{
collections::{btree_map::Entry, BTreeMap},
vec,
vec::Vec,
};
use scroll::{Endian, Pread};
use thiserror::Error;
use structs::{BTreeNode, Chunk, Header, Item, Key, KeyPtr, ObjectType, Stripe, Superblock};
extern crate alloc;
pub mod structs;
#[cfg(feature = "std")]
pub mod std_io {
use std::io::{Cursor, Read, Seek};
use crate::{Error, VolumeIo};
impl<T: Read + Seek> VolumeIo for T {
fn read(&mut self, dst: &mut [u8], address: u64) -> Result<(), Error> {
self.seek(std::io::SeekFrom::Start(address))
.map_err(|a| Error::SeekFailed)?;
self.read_exact(dst).map_err(|_| Error::ReadFailed)
}
fn alignment(&self) -> usize {
1
}
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("read failed")]
ReadFailed,
#[error("seek failed")]
SeekFailed,
#[error("invalid magic signature")]
InvalidMagic,
#[error("invalid offset")]
InvalidOffset,
#[error("Expected an internal node")]
ExpectedInternalNode,
#[error("Expected a leaf node")]
ExpectedLeafNode,
#[error("{0}")]
ScrollError(scroll::Error),
}
impl From<scroll::Error> for Error {
fn from(value: scroll::Error) -> Self {
Self::ScrollError(value)
}
}
pub type Result<T> = core::result::Result<T, Error>;
pub trait VolumeIo {
fn read(&mut self, dst: &mut [u8], address: u64) -> Result<()>;
fn alignment(&self) -> usize;
}
/// equal if overlapping, ordered by lower bound
#[derive(Debug)]
pub struct ChunkTreeKey {
range: core::ops::Range<u64>,
}
impl From<u64> for ChunkTreeKey {
fn from(value: u64) -> Self {
Self {
range: value..value,
}
}
}
impl ChunkTreeKey {
pub fn start(&self) -> u64 {
self.range.start
}
pub fn end(&self) -> u64 {
self.range.end
}
pub fn size(&self) -> u64 {
self.range.end - self.range.start
}
pub fn delta(&self, point: u64) -> u64 {
point - self.range.start
}
pub fn sub_range(&self, point: u64) -> core::ops::Range<u64> {
self.delta(point)..self.end()
}
}
impl Eq for ChunkTreeKey {}
impl Ord for ChunkTreeKey {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.partial_cmp(other).unwrap()
}
}
impl PartialEq for ChunkTreeKey {
fn eq(&self, other: &Self) -> bool {
(self.range.contains(&other.range.start) || self.range.contains(&other.range.end))
|| (other.range.contains(&self.range.start) || other.range.contains(&self.range.end))
}
}
impl PartialOrd for ChunkTreeKey {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
self.eq(other)
.then_some(core::cmp::Ordering::Equal)
.or_else(|| self.range.start.partial_cmp(&other.range.start))
}
}
pub struct ChunkCacheTree {
inner: BTreeMap<ChunkTreeKey, u64>,
}
type ChunkTree = BTreeMap<ChunkTreeKey, u64>;
pub struct Volume<R: VolumeIo> {
reader: R,
superblock: Superblock,
chunk_cache: ChunkTree,
}
impl<R: VolumeIo> Volume<R> {
pub fn new(mut reader: R) -> Result<Self> {
let mut buf = vec![0; size_of::<Superblock>()];
reader.read(&mut buf, Superblock::SUPERBLOCK_BASE_OFFSET as _)?;
let superblock = Superblock::parse(&buf)?;
let chunk_cache = Self::bootstrap_chunk_tree(&superblock)?;
Ok(Self {
reader,
superblock,
chunk_cache,
})
}
pub fn size_from_logical(&self, logical: u64) -> Option<u64> {
self.chunk_cache
.get_key_value(&logical.into())
.map(|(key, _)| key.size())
}
pub fn offset_from_logical(&self, logical: u64) -> Option<u64> {
self.chunk_cache
.get_key_value(&logical.into())
.map(|(key, offset)| offset + key.delta(logical))
}
pub fn range_from_logical(&self, logical: u64) -> Option<core::ops::Range<u64>> {
self.chunk_cache
.get_key_value(&logical.into())
.map(|(key, offset)| {
let delta = key.delta(logical);
(offset + delta)..(offset + key.size() - delta)
})
}
pub fn read_range(&mut self, range: core::ops::Range<u64>) -> Result<Vec<u8>> {
let mut buf = vec![0; (range.end - range.start) as usize];
self.reader.read(&mut buf, range.start)?;
Ok(buf)
}
pub fn asdf(&mut self) -> Result<()> {
let chunk_root = self.superblock.chunk_root;
// let size = self.size_from_logical(chunk_root).expect("size");
log::debug!("chunk_root: {chunk_root}");
let contains = self.chunk_cache.contains_key(&chunk_root.into());
log::debug!("chunk_cache: {contains} {:?}", self.chunk_cache);
let physical = self.range_from_logical(chunk_root).expect("range");
let root = self.read_range(physical)?;
let node = BTreeNode::parse(&root)?;
log::debug!("{node:#?}");
Ok(())
}
pub fn bootstrap_chunk_tree(superblock: &Superblock) -> Result<ChunkTree> {
let array_size = superblock.sys_chunk_array_size as usize;
let mut offset: usize = 0;
let key_size = size_of::<Key>();
let mut chunk_tree = ChunkTree::new();
let bytes = &superblock.sys_chunk_array;
while offset < array_size {
if offset + key_size > array_size {
log::error!("short key read");
return Err(Error::InvalidOffset);
}
let key = bytes.gread_with::<Key>(&mut offset, scroll::LE)?;
if key.ty.as_type() != Some(ObjectType::ChunkItem) {
log::error!("key is not of type ChunkItem");
return Err(Error::InvalidOffset);
}
let chunk = bytes.gread_with::<Chunk>(&mut offset, scroll::LE)?;
if chunk.num_stripes == 0 {
log::error!("num_stripes cannot be 0");
return Err(Error::InvalidOffset);
}
let num_stripes = chunk.num_stripes; // copy to prevent unaligned access
if num_stripes != 1 {
log::warn!(
"warning: {} stripes detected but only processing 1",
num_stripes
);
}
match chunk_tree.entry(ChunkTreeKey {
range: key.offset..(key.offset + chunk.length),
}) {
Entry::Vacant(entry) => {
entry.insert(chunk.stripe.offset);
}
Entry::Occupied(_) => {
log::error!("overlapping stripes!");
return Err(Error::InvalidOffset);
}
};
offset += (num_stripes - 1) as usize * size_of::<Stripe>();
if offset > array_size {
log::error!("short chunk item + stripes read");
return Err(Error::InvalidOffset);
}
}
Ok(chunk_tree)
}
pub fn superblock(&self) -> &Superblock {
&self.superblock
}
}

789
btrfs/src/structs.rs Normal file
View file

@ -0,0 +1,789 @@
use core::{mem::size_of, ops::Deref};
use bytemuck::{Pod, Zeroable};
use derivative::Derivative;
use num_enum::{FromPrimitive, TryFromPrimitive};
use scroll::{
ctx::{SizeWith, TryFromCtx},
Pread, SizeWith,
};
use zerocopy::{AsBytes, FromBytes};
use crate::{Error, Result};
#[repr(u8)]
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive)]
//#[rustc_nonnull_optimization_guaranteed]
pub enum ObjectType {
INodeItem = 0x01,
InodeRef = 0x0C,
InodeExtref = 0x0D,
XattrItem = 0x18,
OrphanInode = 0x30,
DirItem = 0x54,
DirIndex = 0x60,
ExtentData = 0x6C,
ExtentCsum = 0x80,
RootItem = 0x84,
TypeRootBackref = 0x90,
RootRef = 0x9C,
ExtentItem = 0xA8,
MetadataItem = 0xA9,
TreeBlockRef = 0xB0,
ExtentDataRef = 0xB2,
ExtentRefV0 = 0xB4,
SharedBlockRef = 0xB6,
SharedDataRef = 0xB8,
BlockGroupItem = 0xC0,
FreeSpaceInfo = 0xC6,
FreeSpaceExtent = 0xC7,
FreeSpaceBitmap = 0xC8,
DevExtent = 0xCC,
DevItem = 0xD8,
ChunkItem = 0xE4,
TempItem = 0xF8,
DevStats = 0xF9,
SubvolUuid = 0xFB,
SubvolRecUuid = 0xFC,
}
#[repr(C, packed)]
#[derive(Clone, Copy, PartialEq, Eq, Pread, SizeWith)]
pub struct ObjectTypeWrapper {
inner: u8,
}
impl core::fmt::Debug for ObjectTypeWrapper {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ObjectTypeWrapper")
.field("inner", &self.as_type())
.finish()
}
}
impl ObjectTypeWrapper {
pub fn as_type(self) -> Option<ObjectType> {
ObjectType::try_from_primitive(self.inner).ok()
}
}
unsafe impl Pod for ObjectTypeWrapper {}
unsafe impl Zeroable for ObjectTypeWrapper {}
#[repr(transparent)]
#[derive(Debug, Clone, Copy)]
pub struct Uuid(uuid::Uuid);
impl SizeWith<scroll::Endian> for Uuid {
fn size_with(_: &scroll::Endian) -> usize {
size_of::<Self>()
}
}
impl<'a> TryFromCtx<'a, scroll::Endian> for Uuid {
type Error = scroll::Error;
fn try_from_ctx(
from: &'a [u8],
_: scroll::Endian,
) -> core::result::Result<(Self, usize), Self::Error> {
let size = size_of::<Self>();
Ok((*bytemuck::from_bytes::<Self>(&from[..size]), size))
}
}
impl Deref for Uuid {
type Target = uuid::Uuid;
fn deref(&self) -> &Self::Target {
&self.0
}
}
unsafe impl Pod for Uuid {}
unsafe impl Zeroable for Uuid {}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread, SizeWith)]
pub struct Key {
pub id: u64,
pub ty: ObjectTypeWrapper,
pub offset: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct TreeHeader {
csum: [u8; 32],
fs_uuid: Uuid,
address: u64,
flags: u64,
chunk_tree_uuid: Uuid,
generation: u64,
tree_id: u64,
num_items: u32,
level: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct LeafNode {
key: Key,
offset: u32,
size: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InternalNode {
key: Key,
address: u64,
generation: u64,
}
const MAX_LABEL_SIZE: usize = 0x100;
const SYS_CHUNK_ARRAY_SIZE: usize = 0x800;
const BTRFS_NUM_BACKUP_ROOTS: usize = 4;
fn format_u8str<T: AsRef<[u8]>>(s: &T, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let bytes = s.as_ref();
let end = bytes
.iter()
.position(|&b| b == 0)
.map(|i| i + 1)
.unwrap_or(bytes.len());
core::ffi::CStr::from_bytes_with_nul(&bytes[..end])
.map(|s| write!(f, "{:?}", s))
.map_err(|_| core::fmt::Error)?
}
#[repr(C, packed(1))]
#[derive(Derivative, Clone, Copy, Pod, Zeroable)]
#[derivative(Debug)]
pub struct INodeItem {
generation: u64,
transid: u64,
st_size: u64,
st_blocks: u64,
block_group: u64,
st_nlink: u32,
st_uid: u32,
st_gid: u32,
st_mode: u32,
st_rdev: u64,
flags: u64,
sequence: u64,
#[derivative(Debug = "ignore")]
reserved: [u8; 32],
st_atime: Timespec,
st_ctime: Timespec,
st_mtime: Timespec,
otime: Timespec,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ChunkItem {
size: u64,
root_id: u64,
stripe_length: u64,
ty: u64,
opt_io_alignment: u32,
opt_io_width: u32,
sector_size: u32,
num_stripes: u16,
sub_stripes: u16,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ChunkItemStripe {
dev_id: u64,
offset: u64,
dev_uuid: Uuid,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentData {
generation: u64,
decoded_size: u64,
compression: u8,
encryption: u8,
encoding: u16,
ty: u8,
data: [u8; 1],
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentData2 {
address: u64,
size: u64,
offset: u64,
num_bytes: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct INodeRef {
index: u64,
n: u16,
name: [u8; 1],
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct INodeExtRef {
dir: u64,
index: u64,
n: u16,
name: [u8; 1],
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentItem {
ref_count: u64,
generation: u64,
flags: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentItem2 {
first_item: Key,
level: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentItemV0 {
ref_count: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentItemTree {
extent_item: ExtentItem,
first_item: Key,
level: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct TreeBlockRef {
offset: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentDataRef {
root: u64,
objid: u64,
offset: u64,
count: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct BlockGroupItem {
used: u64,
chunk_tree: u64,
flags: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct ExtentRefV0 {
root: u64,
gen: u64,
objid: u64,
count: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct SharedBlockRef {
offset: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct SharedDataRef {
offset: u64,
count: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct FreeSpaceEntry {
offset: u64,
size: u64,
ty: u8,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct FreeSpaceItem {
key: Key,
generation: u64,
num_entries: u64,
num_bitmaps: u64,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct RootRef {
dir: u64,
index: u64,
n: u16,
name: [u8; 1],
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct DevExtent {
chunktree: u64,
objid: u64,
address: u64,
length: u64,
chunktree_uuid: Uuid,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct BalanceArgs {}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct BalanceItem {}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct FreeSpaceInfo {
count: u32,
flags: u32,
}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct SendHeader {}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct SendCommand {}
#[repr(C, packed(1))]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct SendTlv {}
const CSUM_SIZE: usize = 32;
const LABEL_SIZE: usize = 256;
const SYSTEM_CHUNK_ARRAY_SIZE: usize = 2048;
pub const FS_TREE_OBJECTID: u64 = 5;
pub const INODE_REF_KEY: u8 = 12;
pub const DIR_ITEM_KEY: u8 = 84;
pub const ROOT_ITEM_KEY: u8 = 132;
pub const CHUNK_ITEM_KEY: u8 = 228;
pub const FT_REG_FILE: u8 = 1;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread, SizeWith)]
pub struct DevItem {
/// the internal btrfs device id
pub devid: u64,
/// size of the device
pub total_bytes: u64,
/// bytes used
pub bytes_used: u64,
/// optimal io alignment for this device
pub io_align: u32,
/// optimal io width for this device
pub io_width: u32,
/// minimal io size for this device
pub sector_size: u32,
/// type and info about this device
pub ty: u64,
/// expected generation for this device
pub generation: u64,
/// starting byte of this partition on the device, to allow for stripe alignment in the future
pub start_offset: u64,
/// grouping information for allocation decisions
pub dev_group: u32,
/// seek speed 0-100 where 100 is fastest
pub seek_speed: u8,
/// bandwidth 0-100 where 100 is fastest
pub bandwidth: u8,
/// btrfs generated uuid for this device
pub uuid: Uuid,
/// uuid of FS who owns this device
pub fsid: Uuid,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread, SizeWith)]
pub struct RootBackup {
pub tree_root: u64,
pub tree_root_gen: u64,
pub chunk_root: u64,
pub chunk_root_gen: u64,
pub extent_root: u64,
pub extent_root_gen: u64,
pub fs_root: u64,
pub fs_root_gen: u64,
pub dev_root: u64,
pub dev_root_gen: u64,
pub csum_root: u64,
pub csum_root_gen: u64,
pub total_bytes: u64,
pub bytes_used: u64,
pub num_devices: u64,
/// future
pub unused_64: [u64; 4],
pub tree_root_level: u8,
pub chunk_root_level: u8,
pub extent_root_level: u8,
pub fs_root_level: u8,
pub dev_root_level: u8,
pub csum_root_level: u8,
/// future and to align
pub unused_8: [u8; 10],
}
#[repr(C, packed)]
#[derive(Derivative, Clone, Copy, Pod, Zeroable, Pread, SizeWith)]
#[derivative(Debug)]
pub struct Superblock {
pub csum: [u8; 32],
pub fsid: Uuid,
/// Physical address of this block
pub bytenr: u64,
pub flags: u64,
pub magic: [u8; 0x8],
pub generation: u64,
/// Logical address of the root tree root
pub root: u64,
/// Logical address of the chunk tree root
pub chunk_root: u64,
/// Logical address of the log tree root
pub log_root: u64,
pub log_root_transid: u64,
pub total_bytes: u64,
pub bytes_used: u64,
pub root_dir_objectid: u64,
pub num_devices: u64,
pub sector_size: u32,
pub node_size: u32,
/// Unused and must be equal to `nodesize`
pub leafsize: u32,
pub stripesize: u32,
pub sys_chunk_array_size: u32,
pub chunk_root_generation: u64,
pub compat_flags: u64,
pub compat_ro_flags: u64,
pub incompat_flags: u64,
pub csum_type: u16,
pub root_level: u8,
pub chunk_root_level: u8,
pub log_root_level: u8,
pub dev_item: DevItem,
#[derivative(Debug(format_with = "format_u8str"))]
pub label: [u8; 0x100],
pub cache_generation: u64,
pub uuid_tree_generation: u64,
pub metadata_uuid: Uuid,
/// Future expansion
reserved: [u64; 28],
#[derivative(Debug = "ignore")]
pub sys_chunk_array: [u8; 0x800],
pub root_backup0: RootBackup,
pub root_backup1: RootBackup,
pub root_backup2: RootBackup,
pub root_backup3: RootBackup,
}
#[repr(u16)]
#[derive(Derivative, Clone, Copy, TryFromPrimitive)]
#[derivative(Debug)]
pub enum ChecksumType {
Crc32 = 0,
XxHash64,
Sha256,
Blake2B,
}
impl Superblock {
pub const SUPERBLOCK_BASE_OFFSET: usize = 0x10000;
pub const SUPERBLOCK_OFFSETS: [usize; 4] = [
Self::SUPERBLOCK_BASE_OFFSET,
0x4000000,
0x4000000000,
0x4000000000000,
];
pub const MAGIC: [u8; 8] = *b"_BHRfS_M";
pub fn parse(bytes: &[u8]) -> Result<Self> {
let superblock = bytes.pread_with::<Self>(0, scroll::LE)?;
if !superblock.verify_magic() {
return Err(Error::InvalidMagic);
}
Ok(superblock)
}
pub fn checksum_type(&self) -> Option<ChecksumType> {
ChecksumType::try_from_primitive(self.csum_type).ok()
}
pub fn verify_magic(&self) -> bool {
self.magic == Self::MAGIC
}
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
pub struct Stripe {
pub devid: u64,
pub offset: u64,
pub dev_uuid: Uuid,
}
impl Stripe {
pub fn parse(bytes: &[u8]) -> Result<Self> {
Ok(bytes.pread_with(0, scroll::LE)?)
}
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
pub struct Chunk {
/// size of this chunk in bytes
pub length: u64,
/// objectid of the root referencing this chunk
pub owner: u64,
pub stripe_len: u64,
pub ty: u64,
/// optimal io alignment for this chunk
pub io_align: u32,
/// optimal io width for this chunk
pub io_width: u32,
/// minimal io size for this chunk
pub sector_size: u32,
/// 2^16 stripes is quite a lot, a second limit is the size of a single item in the btree
pub num_stripes: u16,
/// sub stripes only matter for raid10
pub sub_stripes: u16,
pub stripe: Stripe,
// additional stripes go here
}
impl Chunk {
pub fn parse(bytes: &[u8]) -> Result<Self> {
Ok(bytes.pread_with(0, scroll::LE)?)
}
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
pub struct Timespec {
pub sec: u64,
pub nsec: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InodeItem {
/// nfs style generation number
pub generation: u64,
/// transid that last touched this inode
pub transid: u64,
pub size: u64,
pub nbytes: u64,
pub block_group: u64,
pub nlink: u32,
pub uid: u32,
pub gid: u32,
pub mode: u32,
pub rdev: u64,
pub flags: u64,
/// modification sequence number for NFS
pub sequence: u64,
pub reserved: [u64; 4],
pub atime: Timespec,
pub ctime: Timespec,
pub mtime: Timespec,
pub otime: Timespec,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct RootItem {
pub inode: InodeItem,
pub generation: u64,
pub root_dirid: u64,
pub bytenr: u64,
pub byte_limit: u64,
pub bytes_used: u64,
pub last_snapshot: u64,
pub flags: u64,
pub refs: u32,
pub drop_progress: Key,
pub drop_level: u8,
pub level: u8,
pub generation_v2: u64,
pub uuid: Uuid,
pub parent_uuid: Uuid,
pub received_uuid: Uuid,
/// updated when an inode changes
pub ctransid: u64,
/// trans when created
pub otransid: u64,
/// trans when sent. non-zero for received subvol
pub stransid: u64,
/// trans when received. non-zero for received subvol
pub rtransid: u64,
pub ctime: Timespec,
pub otime: Timespec,
pub stime: Timespec,
pub rtime: Timespec,
pub reserved: [u64; 8],
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct DirItem {
pub location: Key,
pub transid: u64,
pub data_len: u16,
pub name_len: u16,
pub ty: u8,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct InodeRef {
pub index: u64,
pub name_len: u16,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
pub struct Header {
pub csum: [u8; 32],
pub fsid: Uuid,
/// Which block this node is supposed to live in
pub bytenr: u64,
pub flags: u64,
pub chunk_tree_uuid: Uuid,
pub generation: u64,
pub owner: u64,
pub nritems: u32,
pub level: u8,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
/// A `BtrfsLeaf` is full of `BtrfsItem`s. `offset` and `size` (relative to start of data area)
/// tell us where to find the item in the leaf.
pub struct Item {
pub key: Key,
pub offset: u32,
pub size: u32,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct Leaf {
pub header: Header,
// `Item`s begin here
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Pread)]
/// All non-leaf blocks are nodes and they hold only keys are pointers to other blocks
pub struct KeyPtr {
pub key: Key,
pub blockptr: u64,
pub generation: u64,
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, Pod, Zeroable)]
pub struct Node {
pub header: Header,
// `KeyPtr`s begin here
}
use alloc::vec::Vec;
#[derive(Debug)]
pub struct BTreeLeafNode {
header: Header,
/// actual leaf data
items: Vec<Item>,
}
impl BTreeLeafNode {
pub fn parse(header: Header, bytes: &[u8]) -> Result<Self> {
let offset = &mut 0;
let items = core::iter::from_fn(|| {
if *offset as usize + size_of::<Item>() < bytes.len() {
Some(bytes.gread_with::<Item>(offset, scroll::LE))
} else {
None
}
})
.take(header.nritems as usize)
.collect::<core::result::Result<Vec<_>, _>>()?;
Ok(Self { header, items })
}
}
#[derive(Debug)]
pub struct BTreeInternalNode {
header: Header,
children: Vec<KeyPtr>,
}
impl BTreeInternalNode {
pub fn parse(header: Header, bytes: &[u8]) -> Result<Self> {
let offset = &mut 0;
let children = core::iter::from_fn(|| {
if *offset as usize + size_of::<KeyPtr>() < bytes.len() {
Some(bytes.gread_with::<KeyPtr>(offset, scroll::LE))
} else {
None
}
})
.take(header.nritems as usize)
.collect::<core::result::Result<Vec<_>, _>>()?;
Ok(Self { header, children })
}
}
#[derive(Debug)]
pub enum BTreeNode {
Internal(BTreeInternalNode),
Leaf(BTreeLeafNode),
}
impl BTreeNode {
pub fn parse(bytes: &[u8]) -> Result<Self> {
let offset = &mut 0;
let header = bytes.gread_with::<Header>(offset, scroll::LE)?;
if header.level == 0 {
Ok(Self::Leaf(BTreeLeafNode::parse(header, &bytes[*offset..])?))
} else {
Ok(Self::Internal(BTreeInternalNode::parse(
header,
&bytes[*offset..],
)?))
}
}
}

View file

@ -0,0 +1,44 @@
#![cfg(feature = "std")]
use std::{
io::{BufReader, Read, Seek},
mem::size_of,
};
use test_log::test;
use btrfs::{structs::*, Volume};
#[test]
fn superblock() {
let mut file = std::fs::File::open("btrfs.img").expect("btrfs image");
let reader = BufReader::new(file);
let volume = Volume::new(reader).expect("volume");
let sb = volume.superblock();
println!("{sb:#?}");
}
#[test]
fn iter_sys_chunks() {
let mut file = std::fs::File::open("btrfs.img").expect("btrfs image");
let reader = BufReader::new(file);
let volume = Volume::new(reader).expect("volume");
let sb = volume.superblock();
let result = Volume::<BufReader<std::fs::File>>::bootstrap_chunk_tree(&sb);
println!("{result:#?}");
}
#[test]
fn iter_root() {
let mut file = std::fs::File::open("btrfs.img").expect("btrfs image");
let reader = BufReader::new(file);
let mut volume = Volume::new(reader).expect("volume");
let sb = volume.superblock();
volume.asdf();
}

120
notes.org Normal file
View file

@ -0,0 +1,120 @@
#+TITLE:Quibble bootloader reasoned
#+AUTHOR: Janis Böhm
#+email: janis@nirgendwo.xyz
#+STARTUP: indent inlineimages
#+INFOJS_OPT:
#+BABEL: :session *R* :cache yes :results output graphics :exports both :tangle yes
--------------------------
* Btrfs driver
** structures
*** volume
**** EFI_SIMPLE_FILE_SYSTEM_PROTOCOL: proto
**** EFI_QUIBBLE_PROTOCOL: quibble_proto
**** EFI_OPEN_SUBVOL_PROTOCOL: open_subvol_proto
**** superblock*: sb
***** checksum
***** uuid
**** EFI_HANDLE: controller
**** EFI_BLOCK_IO_PROTOCOL*: block
**** EFI_DISK_IO_PROTOCOL*: disk_io
**** bool: chunks_loaded
**** LIST_ENTRY: chunks
**** LIST_ENTRY: roots
**** root*: root_root
**** root*: chunk_root
**** LIST_ENTRY: list_entry
**** root*: fsroot
** efi_main
- initialize volumes list
- construct global driver bind structure
- install driver protocol
** drv_start
- walk list of volumes and check for controller handle
- open block_io protocol and ensure media->blocksize isnt 0
- open disk_io protocol
- read superblock and check magic and crc32
- check for incompat flags
- setup volume struct with protocols/functions:
- install protocols and store volume in global list
*** open_volume
- ensure chunks are loaded
- populate file handle
- construct inode from fsroot and vol
**** load_chunks
**** populate_file_handle
*** get_arc_name
returns superblock uuid field
*** get_driver_name
just returns "btrfs"
*** open_subvol
** drv_stop
- cleanup / deallocating
- close diskio/blockio protocols
** drv_supported
- checks for disk_io and block_io
* boot function
find and open btrfs disk and subvol, if specified
check for, and open system32 folder
add_image kernel executable and hal library
** add_image
*** args
- images list
- image name and dir
- memory type
SystemCode for kernel, HalCode for hal.dll
- is dll?
- bdle / boot driver list entry
always null
- order
load order of drivers
- no_reloc
false for kernel and HAL, and drivers
*** function
keeps track of image in a linked list
** add kernel and hal to image list
*** kernel image
should we look for alternate kernel images? *mp,*pa,*amp
** process memory map
** load kernel
** load registry
** load apiset if >= win8
** add crashdmp driver if >= win8 blue to image list
** load image list, drivers, add dependencies to image list
** fix image order
** iterate thru images again, resolve imports
** make images contiguous
** construct kernel stack/arguments
** map images?
** stuff
** call kernel entry