Skip to main content

btrfs_disk/
util.rs

1//! # Shared helpers for on-disk structures
2//!
3//! Little-endian writer functions for placing typed values into raw byte
4//! buffers at known offsets, and a raw CRC32C matching the kernel's format.
5
6use uuid::Uuid;
7
8/// Write a little-endian u64 into `buf` at byte offset `off`.
9pub fn write_le_u64(buf: &mut [u8], off: usize, val: u64) {
10    buf[off..off + 8].copy_from_slice(&val.to_le_bytes());
11}
12
13/// Write a little-endian u32 into `buf` at byte offset `off`.
14pub fn write_le_u32(buf: &mut [u8], off: usize, val: u32) {
15    buf[off..off + 4].copy_from_slice(&val.to_le_bytes());
16}
17
18/// Write a little-endian u16 into `buf` at byte offset `off`.
19pub fn write_le_u16(buf: &mut [u8], off: usize, val: u16) {
20    buf[off..off + 2].copy_from_slice(&val.to_le_bytes());
21}
22
23/// Write a UUID (16 bytes) into `buf` at byte offset `off`.
24pub fn write_uuid(buf: &mut [u8], off: usize, uuid: &Uuid) {
25    buf[off..off + 16].copy_from_slice(uuid.as_bytes());
26}
27
28/// Raw CRC32C matching the kernel's `crc32c()` function.
29///
30/// The seed is passed through directly with no inversion on input or output,
31/// unlike the standard ISO 3309 CRC32C which inverts both. Use this when
32/// computing btrfs on-disk checksums.
33pub fn raw_crc32c(seed: u32, data: &[u8]) -> u32 {
34    // crc32c::crc32c_append(seed) computes: !crc32c_hw(!seed, data)
35    // We want: crc32c_hw(seed, data)
36    // So: !crc32c::crc32c_append(!seed, data)
37    !crc32c::crc32c_append(!seed, data)
38}
39
40/// Recompute the CRC32C checksum of a tree block and write it into the header.
41///
42/// The checksum covers `buf[32..]` (everything after the csum field).
43/// The 4-byte LE result is written to `buf[0..4]` and `buf[4..32]` is zeroed.
44/// This is the same algorithm as `superblock::csum_superblock` but for
45/// arbitrary-length tree blocks (nodesize bytes).
46pub fn csum_tree_block(buf: &mut [u8]) {
47    assert!(buf.len() > 32, "buffer too small for tree block checksum");
48    let csum = raw_crc32c(0, &buf[32..]);
49    buf[0..4].copy_from_slice(&csum.to_le_bytes());
50    buf[4..32].fill(0);
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_write_le_u64() {
59        let mut buf = [0u8; 8];
60        write_le_u64(&mut buf, 0, 0x0807060504030201);
61        assert_eq!(buf, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
62    }
63
64    #[test]
65    fn test_write_le_u32() {
66        let mut buf = [0u8; 4];
67        write_le_u32(&mut buf, 0, 0x04030201);
68        assert_eq!(buf, [0x01, 0x02, 0x03, 0x04]);
69    }
70
71    #[test]
72    fn test_write_le_u16() {
73        let mut buf = [0u8; 2];
74        write_le_u16(&mut buf, 0, 0x0201);
75        assert_eq!(buf, [0x01, 0x02]);
76    }
77
78    #[test]
79    fn test_write_uuid() {
80        let uuid =
81            Uuid::parse_str("deadbeef-dead-beef-dead-beefdeadbeef").unwrap();
82        let mut buf = [0u8; 16];
83        write_uuid(&mut buf, 0, &uuid);
84        assert_eq!(buf, *uuid.as_bytes());
85    }
86
87    #[test]
88    fn test_roundtrip_u64() {
89        let mut buf = [0u8; 16];
90        write_le_u64(&mut buf, 4, 0xDEADBEEF_CAFEBABE);
91        assert_eq!(
92            u64::from_le_bytes(buf[4..12].try_into().unwrap()),
93            0xDEADBEEF_CAFEBABE
94        );
95    }
96
97    #[test]
98    fn test_roundtrip_uuid() {
99        let uuid =
100            Uuid::parse_str("01234567-89ab-cdef-0123-456789abcdef").unwrap();
101        let mut buf = [0u8; 16];
102        write_uuid(&mut buf, 0, &uuid);
103        assert_eq!(Uuid::from_bytes(buf), uuid);
104    }
105}