use crate::tree::Key;
use btrfs_disk::{
raw,
util::{write_le_u16, write_le_u32, write_le_u64, write_uuid},
};
use std::mem;
use uuid::Uuid;
pub fn root_item(generation: u64, bytenr: u64, root_dirid: u64) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_root_item>();
let mut buf = vec![0u8; size];
let inode_size = mem::size_of::<raw::btrfs_inode_item>();
write_le_u64(&mut buf, 0, generation); write_le_u32(&mut buf, 40, 1); write_le_u32(&mut buf, 52, 0o40755);
write_le_u64(&mut buf, inode_size, generation);
write_le_u64(&mut buf, inode_size + 8, root_dirid);
write_le_u64(&mut buf, inode_size + 16, bytenr);
write_le_u32(&mut buf, inode_size + 56, 1);
let level_off = mem::offset_of!(raw::btrfs_root_item, level);
write_le_u64(&mut buf, level_off + 1, generation);
buf
}
pub fn chunk_tree_root_item(generation: u64, bytenr: u64) -> Vec<u8> {
root_item(
generation,
bytenr,
raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID as u64,
)
}
pub fn extent_item(refs: u64, generation: u64, skinny: bool) -> Vec<u8> {
let base_size = mem::size_of::<raw::btrfs_extent_item>();
let extra = if skinny {
0
} else {
mem::size_of::<raw::btrfs_tree_block_info>()
};
let size = base_size + extra;
let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, refs);
write_le_u64(&mut buf, 8, generation);
write_le_u64(&mut buf, 16, raw::BTRFS_EXTENT_FLAG_TREE_BLOCK as u64);
buf
}
pub fn block_group_item(used: u64, chunk_objectid: u64, flags: u64) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_block_group_item>();
let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, used);
write_le_u64(&mut buf, 8, chunk_objectid);
write_le_u64(&mut buf, 16, flags);
buf
}
pub fn dev_item(
devid: u64,
total_bytes: u64,
bytes_used: u64,
sector_size: u32,
dev_uuid: &Uuid,
fsid: &Uuid,
) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_dev_item>();
let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, devid);
write_le_u64(&mut buf, 8, total_bytes);
write_le_u64(&mut buf, 16, bytes_used);
write_le_u32(&mut buf, 24, sector_size); write_le_u32(&mut buf, 28, sector_size); write_le_u32(&mut buf, 32, sector_size); write_uuid(&mut buf, 66, dev_uuid);
write_uuid(&mut buf, 82, fsid);
buf
}
pub fn chunk_item_single(
length: u64,
owner: u64,
chunk_type: u64,
sector_size: u32,
stripe_devid: u64,
stripe_offset: u64,
stripe_dev_uuid: &Uuid,
) -> Vec<u8> {
let base_size = mem::offset_of!(raw::btrfs_chunk, stripe);
let stripe_size = mem::size_of::<raw::btrfs_stripe>();
let size = base_size + stripe_size;
let mut buf = vec![0u8; size];
const STRIPE_LEN: u64 = 64 * 1024;
write_le_u64(&mut buf, 0, length);
write_le_u64(&mut buf, 8, owner);
write_le_u64(&mut buf, 16, STRIPE_LEN);
write_le_u64(&mut buf, 24, chunk_type);
write_le_u32(&mut buf, 32, sector_size); write_le_u32(&mut buf, 36, sector_size); write_le_u32(&mut buf, 40, sector_size); write_le_u16(&mut buf, 44, 1); write_le_u16(&mut buf, 46, 0);
write_le_u64(&mut buf, base_size, stripe_devid);
write_le_u64(&mut buf, base_size + 8, stripe_offset);
write_uuid(&mut buf, base_size + 16, stripe_dev_uuid);
buf
}
pub fn dev_extent(
chunk_tree: u64,
chunk_objectid: u64,
chunk_offset: u64,
length: u64,
chunk_tree_uuid: &Uuid,
) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_dev_extent>();
let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, chunk_tree);
write_le_u64(&mut buf, 8, chunk_objectid);
write_le_u64(&mut buf, 16, chunk_offset);
write_le_u64(&mut buf, 24, length);
write_uuid(&mut buf, 32, chunk_tree_uuid);
buf
}
pub fn dev_stats_zeroed() -> Vec<u8> {
vec![0u8; 5 * 8]
}
pub fn free_space_info(extent_count: u32, flags: u32) -> Vec<u8> {
let mut buf = vec![0u8; 8];
write_le_u32(&mut buf, 0, extent_count);
write_le_u32(&mut buf, 4, flags);
buf
}
pub fn inode_item_dir(generation: u64, nbytes: u64, now_sec: u64) -> Vec<u8> {
let size = mem::size_of::<raw::btrfs_inode_item>();
let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, generation); write_le_u64(&mut buf, 24, nbytes); write_le_u32(&mut buf, 40, 1); write_le_u32(&mut buf, 52, 0o40755);
let ts_off = mem::offset_of!(raw::btrfs_inode_item, atime);
let ts_size = mem::size_of::<raw::btrfs_timespec>();
for i in 0..4 {
write_le_u64(&mut buf, ts_off + i * ts_size, now_sec);
write_le_u32(&mut buf, ts_off + i * ts_size + 8, 0);
}
buf
}
pub fn inode_ref(index: u64, name: &[u8]) -> Vec<u8> {
let size = 8 + 2 + name.len(); let mut buf = vec![0u8; size];
write_le_u64(&mut buf, 0, index);
write_le_u16(&mut buf, 8, name.len() as u16);
buf[10..10 + name.len()].copy_from_slice(name);
buf
}
pub fn disk_key(key: &Key) -> Vec<u8> {
let mut buf = vec![0u8; 17];
key.write_to(&mut buf, 0);
buf
}
#[cfg(test)]
mod tests {
use super::*;
use btrfs_disk::items;
#[test]
fn roundtrip_block_group_item() {
let data = block_group_item(
4096,
raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID as u64,
raw::BTRFS_BLOCK_GROUP_SYSTEM as u64,
);
let parsed = items::BlockGroupItem::parse(&data).unwrap();
assert_eq!(parsed.used, 4096);
assert_eq!(
parsed.chunk_objectid,
raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID as u64
);
assert_eq!(parsed.flags, raw::BTRFS_BLOCK_GROUP_SYSTEM as u64);
}
#[test]
fn roundtrip_dev_item() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let fsid =
Uuid::parse_str("cafebabe-cafe-babe-cafe-babecafebabe").unwrap();
let data = dev_item(1, 1_000_000_000, 4_000_000, 4096, &uuid, &fsid);
let parsed = items::DevItem::parse(&data).unwrap();
assert_eq!(parsed.devid, 1);
assert_eq!(parsed.total_bytes, 1_000_000_000);
assert_eq!(parsed.bytes_used, 4_000_000);
assert_eq!(parsed.io_align, 4096);
assert_eq!(parsed.io_width, 4096);
assert_eq!(parsed.sector_size, 4096);
assert_eq!(parsed.uuid, uuid);
assert_eq!(parsed.fsid, fsid);
}
#[test]
fn roundtrip_chunk_item() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let data = chunk_item_single(
4 * 1024 * 1024,
raw::BTRFS_EXTENT_TREE_OBJECTID as u64,
raw::BTRFS_BLOCK_GROUP_SYSTEM as u64,
4096,
1,
0x100000,
&uuid,
);
let parsed = items::ChunkItem::parse(&data).unwrap();
assert_eq!(parsed.length, 4 * 1024 * 1024);
assert_eq!(parsed.owner, raw::BTRFS_EXTENT_TREE_OBJECTID as u64);
assert_eq!(parsed.chunk_type, raw::BTRFS_BLOCK_GROUP_SYSTEM as u64);
assert_eq!(parsed.num_stripes, 1);
assert_eq!(parsed.stripes.len(), 1);
assert_eq!(parsed.stripes[0].devid, 1);
assert_eq!(parsed.stripes[0].offset, 0x100000);
assert_eq!(parsed.stripes[0].dev_uuid, uuid);
}
#[test]
fn roundtrip_dev_extent() {
let uuid =
Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
let data = dev_extent(
raw::BTRFS_CHUNK_TREE_OBJECTID as u64,
raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID as u64,
0x100000,
4 * 1024 * 1024,
&uuid,
);
let parsed = items::DevExtent::parse(&data).unwrap();
assert_eq!(parsed.chunk_tree, raw::BTRFS_CHUNK_TREE_OBJECTID as u64);
assert_eq!(
parsed.chunk_objectid,
raw::BTRFS_FIRST_CHUNK_TREE_OBJECTID as u64
);
assert_eq!(parsed.chunk_offset, 0x100000);
assert_eq!(parsed.length, 4 * 1024 * 1024);
assert_eq!(parsed.chunk_tree_uuid, uuid);
}
#[test]
fn roundtrip_free_space_info() {
let data = free_space_info(3, 0);
let parsed = items::FreeSpaceInfo::parse(&data).unwrap();
assert_eq!(parsed.extent_count, 3);
assert_eq!(parsed.flags, 0);
}
#[test]
fn roundtrip_root_item() {
let data =
root_item(1, 0x100000, raw::BTRFS_FIRST_FREE_OBJECTID as u64);
let parsed = items::RootItem::parse(&data).unwrap();
assert_eq!(parsed.generation, 1);
assert_eq!(parsed.bytenr, 0x100000);
assert_eq!(parsed.root_dirid, raw::BTRFS_FIRST_FREE_OBJECTID as u64);
assert_eq!(parsed.refs, 1);
assert_eq!(parsed.generation_v2, 1);
}
#[test]
fn extent_item_skinny_size() {
let data = extent_item(1, 1, true);
assert_eq!(data.len(), mem::size_of::<raw::btrfs_extent_item>());
}
#[test]
fn extent_item_non_skinny_size() {
let data = extent_item(1, 1, false);
assert_eq!(
data.len(),
mem::size_of::<raw::btrfs_extent_item>()
+ mem::size_of::<raw::btrfs_tree_block_info>()
);
}
#[test]
fn dev_stats_zeroed_size() {
let data = dev_stats_zeroed();
assert_eq!(data.len(), 40);
assert!(data.iter().all(|&b| b == 0));
}
}