use std::fmt;
use reverie::syscalls::StatxMask;
use reverie::syscalls::Timespec;
use serde::Deserialize;
use serde::Serialize;
bitflags! {
#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct StatxAttributes: u64 {
const STATX_ATTR_COMPRESSED = 0x4;
const STATX_ATTR_IMMUTABLE = 0x00000010;
const STATX_ATTR_APPEND = 0x00000020;
const STATX_ATTR_NODUMP = 0x00000040;
const STATX_ATTR_ENCRYPTED = 0x00000800;
const STATX_ATTR_AUTOMOUNT = 0x00001000;
const STATX_ATTR_MOUNT_ROOT = 0x00002000;
const STATX_ATTR_VERITY = 0x00100000;
const STATX_ATTR_DAX = 0x00200000;
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct DetStat {
pub mask: StatxMask,
pub mode: u32,
pub uid: u32,
pub gid: u32,
pub attributes: StatxAttributes,
pub attributes_mask: StatxAttributes,
pub dev: u64,
pub rdev: u64,
pub inode: u64,
pub nlink: u64,
pub size: i64,
pub blksize: i64,
pub blocks: i64,
pub atime: Timespec,
pub btime: Timespec,
pub ctime: Timespec,
pub mtime: Timespec,
}
impl Default for DetStat {
fn default() -> Self {
let statx: libc::statx = unsafe { std::mem::zeroed() };
let mut stat: Self = statx.into();
stat.mask = StatxMask::STATX_BASIC_STATS;
stat.nlink = 1;
stat
}
}
impl fmt::Debug for DetStat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("DetStat")
.field("mode", &self.mode)
.field("uid", &self.uid)
.field("gid", &self.gid)
.field("dev", &self.dev)
.field("rdev", &self.rdev)
.field("inode", &self.inode)
.field("nlink", &self.nlink)
.field("size", &self.size)
.field("blksize", &self.blksize)
.field("blocks", &self.blocks)
.field("atime", &self.atime)
.field("btime", &self.btime)
.field("ctime", &self.ctime)
.field("mtime", &self.mtime)
.finish()
}
}
impl From<&libc::stat> for DetStat {
fn from(st: &libc::stat) -> Self {
DetStat {
mask: StatxMask::STATX_BASIC_STATS,
mode: st.st_mode,
uid: st.st_uid,
gid: st.st_gid,
attributes: StatxAttributes::empty(),
attributes_mask: StatxAttributes::empty(),
dev: st.st_dev,
rdev: st.st_rdev,
inode: st.st_ino,
nlink: st.st_nlink,
size: st.st_size,
blksize: st.st_blksize,
blocks: st.st_blocks,
atime: Timespec {
tv_sec: st.st_atime as _,
tv_nsec: st.st_atime_nsec as _,
},
btime: Timespec {
tv_sec: 0,
tv_nsec: 0,
},
ctime: Timespec {
tv_sec: st.st_ctime as _,
tv_nsec: st.st_ctime_nsec as _,
},
mtime: Timespec {
tv_sec: st.st_mtime as _,
tv_nsec: st.st_mtime_nsec as _,
},
}
}
}
impl From<libc::stat> for DetStat {
fn from(st: libc::stat) -> Self {
(&st).into()
}
}
impl From<&DetStat> for libc::stat {
fn from(st: &DetStat) -> Self {
let mut stat: libc::stat = unsafe { std::mem::zeroed() };
stat.st_dev = st.dev;
stat.st_ino = st.inode;
stat.st_nlink = st.nlink;
stat.st_mode = st.mode;
stat.st_uid = st.uid;
stat.st_gid = st.gid;
stat.st_rdev = st.rdev;
stat.st_size = st.size;
stat.st_blksize = st.blksize;
stat.st_blocks = st.blocks;
stat.st_atime = st.atime.tv_sec;
stat.st_atime_nsec = st.atime.tv_nsec;
stat.st_mtime = st.mtime.tv_sec;
stat.st_mtime_nsec = st.mtime.tv_nsec;
stat.st_ctime = st.ctime.tv_sec;
stat.st_ctime_nsec = st.ctime.tv_nsec;
stat
}
}
impl From<DetStat> for libc::stat {
fn from(st: DetStat) -> Self {
(&st).into()
}
}
impl From<&libc::statx> for DetStat {
fn from(st: &libc::statx) -> Self {
DetStat {
mask: StatxMask::from_bits_truncate(st.stx_mask),
mode: st.stx_mode as _,
uid: st.stx_uid,
gid: st.stx_gid,
attributes: StatxAttributes::from_bits_truncate(st.stx_attributes),
attributes_mask: StatxAttributes::from_bits_truncate(st.stx_attributes_mask),
dev: libc::makedev(st.stx_dev_major, st.stx_dev_minor),
rdev: libc::makedev(st.stx_rdev_major, st.stx_rdev_minor),
inode: st.stx_ino,
nlink: st.stx_nlink as u64,
size: st.stx_size as i64,
blksize: st.stx_blksize as i64,
blocks: st.stx_blocks as i64,
atime: st.stx_atime.into(),
btime: st.stx_btime.into(),
ctime: st.stx_ctime.into(),
mtime: st.stx_mtime.into(),
}
}
}
impl From<libc::statx> for DetStat {
fn from(st: libc::statx) -> Self {
(&st).into()
}
}
impl From<&DetStat> for libc::statx {
fn from(st: &DetStat) -> Self {
let mut statx: libc::statx = unsafe { std::mem::zeroed() };
statx.stx_mask = st.mask.bits();
statx.stx_blksize = st.blksize as u32;
statx.stx_attributes = st.attributes.bits();
statx.stx_nlink = st.nlink as u32;
statx.stx_uid = st.uid;
statx.stx_gid = st.gid;
statx.stx_mode = st.mode as u16;
statx.stx_ino = st.inode;
statx.stx_size = st.size as u64;
statx.stx_blocks = st.blocks as u64;
statx.stx_attributes_mask = st.attributes_mask.bits();
statx.stx_atime = st.atime.into();
statx.stx_btime = st.btime.into();
statx.stx_ctime = st.ctime.into();
statx.stx_mtime = st.mtime.into();
statx.stx_rdev_major = libc::major(st.rdev);
statx.stx_rdev_minor = libc::minor(st.rdev);
statx.stx_dev_major = libc::major(st.dev);
statx.stx_dev_minor = libc::minor(st.dev);
statx
}
}
impl From<DetStat> for libc::statx {
fn from(st: DetStat) -> Self {
(&st).into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stat_and_statx_use_the_same_device_encoding() {
let dev = libc::makedev(259, 5);
let rdev = libc::makedev(8, 1);
let mut stat: libc::stat = unsafe { std::mem::zeroed() };
stat.st_dev = dev;
stat.st_rdev = rdev;
let mut statx: libc::statx = unsafe { std::mem::zeroed() };
statx.stx_dev_major = 259;
statx.stx_dev_minor = 5;
statx.stx_rdev_major = 8;
statx.stx_rdev_minor = 1;
let from_stat = DetStat::from(&stat);
let from_statx = DetStat::from(&statx);
assert_eq!(from_stat.dev, from_statx.dev);
assert_eq!(from_stat.rdev, from_statx.rdev);
let round_trip = libc::statx::from(&from_statx);
assert_eq!(
(round_trip.stx_dev_major, round_trip.stx_dev_minor),
(259, 5)
);
assert_eq!(
(round_trip.stx_rdev_major, round_trip.stx_rdev_minor),
(8, 1)
);
}
}