use btrfs_disk::items::{InodeFlags, Timespec};
use bytes::BufMut;
#[derive(Debug, Clone)]
pub struct InodeArgs {
pub generation: u64,
pub transid: u64,
pub size: u64,
pub nbytes: u64,
pub nlink: u32,
pub uid: u32,
pub gid: u32,
pub mode: u32,
pub rdev: u64,
pub flags: InodeFlags,
pub sequence: u64,
pub atime: Timespec,
pub ctime: Timespec,
pub mtime: Timespec,
pub otime: Timespec,
}
impl InodeArgs {
#[must_use]
pub fn new(transid: u64, mode: u32) -> Self {
let zero = Timespec { sec: 0, nsec: 0 };
Self {
generation: transid,
transid,
size: 0,
nbytes: 0,
nlink: 1,
uid: 0,
gid: 0,
mode,
rdev: 0,
flags: InodeFlags::empty(),
sequence: 0,
atime: zero,
ctime: zero,
mtime: zero,
otime: zero,
}
}
#[must_use]
pub fn with_uniform_time(mut self, time: Timespec) -> Self {
self.atime = time;
self.ctime = time;
self.mtime = time;
self.otime = time;
self
}
pub const SIZE: usize = 160;
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(Self::SIZE);
buf.put_u64_le(self.generation);
buf.put_u64_le(self.transid);
buf.put_u64_le(self.size);
buf.put_u64_le(self.nbytes);
buf.put_u64_le(0); buf.put_u32_le(self.nlink);
buf.put_u32_le(self.uid);
buf.put_u32_le(self.gid);
buf.put_u32_le(self.mode);
buf.put_u64_le(self.rdev);
buf.put_u64_le(self.flags.bits());
buf.put_u64_le(self.sequence);
buf.put_bytes(0, 32); for ts in [self.atime, self.ctime, self.mtime, self.otime] {
buf.put_u64_le(ts.sec);
buf.put_u32_le(ts.nsec);
}
debug_assert_eq!(buf.len(), Self::SIZE);
buf
}
}
#[cfg(test)]
mod tests {
use super::*;
use btrfs_disk::items::InodeItem;
#[test]
fn round_trip_through_parse() {
let args = InodeArgs {
generation: 42,
transid: 42,
size: 1234,
nbytes: 4096,
nlink: 2,
uid: 1000,
gid: 100,
mode: 0o100644,
rdev: 0,
flags: InodeFlags::NODATASUM | InodeFlags::IMMUTABLE,
sequence: 7,
atime: Timespec {
sec: 1_700_000_000,
nsec: 100,
},
ctime: Timespec {
sec: 1_700_000_001,
nsec: 200,
},
mtime: Timespec {
sec: 1_700_000_002,
nsec: 300,
},
otime: Timespec {
sec: 1_700_000_003,
nsec: 400,
},
};
let bytes = args.to_bytes();
let parsed = InodeItem::parse(&bytes).expect("parse");
assert_eq!(parsed.generation, args.generation);
assert_eq!(parsed.transid, args.transid);
assert_eq!(parsed.size, args.size);
assert_eq!(parsed.nbytes, args.nbytes);
assert_eq!(parsed.nlink, args.nlink);
assert_eq!(parsed.uid, args.uid);
assert_eq!(parsed.gid, args.gid);
assert_eq!(parsed.mode, args.mode);
assert_eq!(parsed.rdev, args.rdev);
assert_eq!(parsed.flags.bits(), args.flags.bits());
assert_eq!(parsed.sequence, args.sequence);
assert_eq!(parsed.atime.sec, args.atime.sec);
assert_eq!(parsed.atime.nsec, args.atime.nsec);
assert_eq!(parsed.ctime.sec, args.ctime.sec);
assert_eq!(parsed.mtime.sec, args.mtime.sec);
assert_eq!(parsed.otime.sec, args.otime.sec);
}
#[test]
fn new_defaults_are_zero() {
let a = InodeArgs::new(5, 0o040755);
assert_eq!(a.generation, 5);
assert_eq!(a.transid, 5);
assert_eq!(a.mode, 0o040755);
assert_eq!(a.nlink, 1);
assert_eq!(a.size, 0);
assert_eq!(a.nbytes, 0);
assert_eq!(a.uid, 0);
assert_eq!(a.gid, 0);
assert_eq!(a.rdev, 0);
assert_eq!(a.flags.bits(), 0);
}
#[test]
fn with_uniform_time_sets_all_four() {
let ts = Timespec {
sec: 1_700_000_000,
nsec: 99,
};
let a = InodeArgs::new(1, 0o100644).with_uniform_time(ts);
assert_eq!(a.atime.sec, ts.sec);
assert_eq!(a.ctime.sec, ts.sec);
assert_eq!(a.mtime.sec, ts.sec);
assert_eq!(a.otime.sec, ts.sec);
}
}