Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Parsing

The btrfs-disk crate parses btrfs on-disk structures from raw byte buffers. It is platform-independent — it works on any OS and can be used to inspect filesystem images without a running kernel.

Reading a filesystem

The typical entry point is filesystem_open, which bootstraps from the superblock:

superblock → sys_chunk_array → chunk tree → root tree

The returned OpenFilesystem contains a BlockReader (for reading tree blocks by logical address) and a map of tree root locations. From there, tree_walk traverses any tree in BFS or DFS order, calling a visitor callback for each block:

#![allow(unused)]
fn main() {
let open = filesystem_open(file)?;
let mut reader = open.reader;
tree_walk(&mut reader, root_bytenr, Traversal::Bfs, &mut |block| {
    // block: &TreeBlock — either a Node (internal) or Leaf
    Ok(())
})?;
}

Item payloads

Leaf blocks contain items, each with a DiskKey (objectid, type, offset) and a raw payload. parse_item_payload dispatches to a typed parser based on the key type:

#![allow(unused)]
fn main() {
let payload = parse_item_payload(&key, data);
match payload {
    ItemPayload::InodeItem(inode) => { /* ... */ }
    ItemPayload::RootItem(root) => { /* ... */ }
    ItemPayload::FileExtentItem(extent) => { /* ... */ }
    // ...
}
}

Reading on-disk fields safely

On-disk structs are packed and little-endian. Casting a *const u8 pointer directly to a packed struct is undefined behaviour due to potential misalignment.

btrfs-disk: bytes::Buf / bytes::BufMut

The disk crate uses the bytes crate for all parsing and serialization. A &[u8] implements Buf, so you can read fields sequentially with methods like get_u64_le(), which advances the cursor automatically:

#![allow(unused)]
fn main() {
let mut buf = data;
let generation = buf.get_u64_le();
let size = buf.get_u64_le();
let mode = buf.get_u32_le();
}

For serialization, BufMut provides the inverse (put_u64_le, put_slice, etc.). This approach avoids manual offset arithmetic and makes it impossible to read past the end of the buffer (it panics instead of silently producing garbage).

btrfs-uapi: offset-based LE readers

The uapi crate parses tree search results returned by the kernel, which are raw &[u8] buffers at known offsets. It uses explicit offset-based helpers from uapi/src/util.rs:

#![allow(unused)]
fn main() {
use btrfs_uapi::util::read_le_u64;
use std::mem::offset_of;

let size = read_le_u64(data, offset_of!(raw::btrfs_inode_item, size));
}

Always use std::mem::offset_of! and std::mem::size_of to derive offsets and sizes from the bindgen struct definitions — never hard-code numeric byte offsets. The field_size!(T, field) macro (from crate::util) gives the size of an individual field.

Superblock mirrors

btrfs writes up to three superblock copies at fixed offsets. super_mirror_offset(n) returns the byte offset for mirror n (0, 1, or 2). read_superblock reads and validates a superblock — checking the magic number and CRC — from any seekable reader.

Display logic belongs in cli/

The disk/ crate only produces typed structs. All formatting and human-readable output lives in cli/src/inspect/. The disk/ crate never calls println! or constructs output strings.