use chrono::{DateTime, Utc};
use crate::fsrecord::parse_xfields;
const OFF_PARENT_ID: usize = 0;
const OFF_PRIVATE_ID: usize = 8;
const OFF_CREATE_TIME: usize = 16;
const OFF_MOD_TIME: usize = 24;
const OFF_CHANGE_TIME: usize = 32;
const OFF_ACCESS_TIME: usize = 40;
const OFF_INTERNAL_FLAGS: usize = 48;
const OFF_NCHILDREN: usize = 56;
const OFF_BSD_FLAGS: usize = 68;
const OFF_OWNER: usize = 72;
const OFF_GROUP: usize = 76;
const OFF_MODE: usize = 80;
const OFF_XFIELDS: usize = 92;
const INO_EXT_TYPE_NAME: u8 = 4;
const INO_EXT_TYPE_DSTREAM: u8 = 8;
const NS_PER_SEC: i64 = 1_000_000_000;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Inode {
pub oid: u64,
pub parent_id: u64,
pub private_id: u64,
pub create_time: u64,
pub mod_time: u64,
pub change_time: u64,
pub access_time: u64,
pub internal_flags: u64,
pub nlink_or_nchildren: i32,
pub bsd_flags: u32,
pub uid: u32,
pub gid: u32,
pub mode: u16,
pub name: Option<String>,
pub size: Option<u64>,
}
impl Inode {
pub fn parse(oid: u64, value: &[u8]) -> crate::Result<Self> {
let mut name = None;
let mut size = None;
if value.len() > OFF_XFIELDS {
for (x_type, data) in parse_xfields(&value[OFF_XFIELDS..]) {
match x_type {
INO_EXT_TYPE_NAME => name = Some(decode_cstr(data)),
INO_EXT_TYPE_DSTREAM => size = Some(crate::bytes::le_u64(data, 0)),
_ => {}
}
}
}
Ok(Self {
oid,
parent_id: crate::bytes::le_u64(value, OFF_PARENT_ID),
private_id: crate::bytes::le_u64(value, OFF_PRIVATE_ID),
create_time: crate::bytes::le_u64(value, OFF_CREATE_TIME),
mod_time: crate::bytes::le_u64(value, OFF_MOD_TIME),
change_time: crate::bytes::le_u64(value, OFF_CHANGE_TIME),
access_time: crate::bytes::le_u64(value, OFF_ACCESS_TIME),
internal_flags: crate::bytes::le_u64(value, OFF_INTERNAL_FLAGS),
#[allow(clippy::cast_possible_wrap)]
nlink_or_nchildren: crate::bytes::le_u32(value, OFF_NCHILDREN) as i32,
bsd_flags: crate::bytes::le_u32(value, OFF_BSD_FLAGS),
uid: crate::bytes::le_u32(value, OFF_OWNER),
gid: crate::bytes::le_u32(value, OFF_GROUP),
mode: crate::bytes::le_u16(value, OFF_MODE),
name,
size,
})
}
#[must_use]
pub fn created(&self) -> Option<DateTime<Utc>> {
ns_to_datetime(self.create_time)
}
#[must_use]
pub fn modified(&self) -> Option<DateTime<Utc>> {
ns_to_datetime(self.mod_time)
}
#[must_use]
pub fn changed(&self) -> Option<DateTime<Utc>> {
ns_to_datetime(self.change_time)
}
#[must_use]
pub fn accessed(&self) -> Option<DateTime<Utc>> {
ns_to_datetime(self.access_time)
}
}
#[must_use]
pub fn ns_to_datetime(ns: u64) -> Option<DateTime<Utc>> {
#[allow(clippy::cast_possible_wrap)]
let secs = (ns / NS_PER_SEC as u64) as i64;
#[allow(clippy::cast_possible_truncation)]
let nanos = (ns % NS_PER_SEC as u64) as u32;
DateTime::from_timestamp(secs, nanos)
}
fn decode_cstr(data: &[u8]) -> String {
let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
String::from_utf8_lossy(&data[..end]).into_owned()
}