use crate::fs::{Attrs, NodeKind};
const S_IFCHR: u32 = 0o020_000;
const S_IFIFO: u32 = 0o010_000;
const S_IFSOCK: u32 = 0o140_000;
#[must_use]
#[allow(dead_code)]
pub fn makedev(major: u64, minor: u64) -> u64 {
((major & 0xfff) << 8) | (minor & 0xff) | ((minor & !0xff) << 12) | ((major & !0xfff) << 32)
}
pub fn encode_stat(attrs: &Attrs) -> [u8; 128] {
let mut b = [0u8; 128];
let put64 =
|b: &mut [u8; 128], off: usize, v: u64| b[off..off + 8].copy_from_slice(&v.to_le_bytes());
let put32 =
|b: &mut [u8; 128], off: usize, v: u32| b[off..off + 4].copy_from_slice(&v.to_le_bytes());
put64(&mut b, 0, 1); put64(&mut b, 8, attrs.inode); put32(&mut b, 16, attrs.mode); put32(&mut b, 20, attrs.nlink); put32(&mut b, 24, attrs.uid);
put32(&mut b, 28, attrs.gid);
put64(&mut b, 32, attrs.rdev); put64(&mut b, 48, attrs.size); put32(&mut b, 56, 4096); put64(&mut b, 64, attrs.size.div_ceil(512)); let t = attrs.mtime as u64;
put64(&mut b, 72, t); put64(&mut b, 88, t); put64(&mut b, 104, t); b
}
pub fn encode_statfs() -> [u8; 120] {
let mut b = [0u8; 120];
let put64 =
|b: &mut [u8; 120], off: usize, v: u64| b[off..off + 8].copy_from_slice(&v.to_le_bytes());
put64(&mut b, 0, 0x0102_1994); put64(&mut b, 8, 4096); put64(&mut b, 16, 1 << 20); put64(&mut b, 24, 1 << 19); put64(&mut b, 32, 1 << 19); put64(&mut b, 40, 1 << 16); put64(&mut b, 48, 1 << 15); put64(&mut b, 64, 255); put64(&mut b, 72, 4096); b
}
pub fn char_device_attrs() -> Attrs {
Attrs {
kind: NodeKind::CharDevice,
size: 0,
mode: S_IFCHR | 0o620,
uid: 0,
gid: 0,
mtime: 0,
inode: 0,
nlink: 1,
rdev: 0,
}
}
pub fn fifo_attrs() -> Attrs {
Attrs {
kind: NodeKind::Fifo,
size: 0,
mode: S_IFIFO | 0o600,
uid: 0,
gid: 0,
mtime: 0,
inode: 0,
nlink: 1,
rdev: 0,
}
}
pub fn socket_attrs() -> Attrs {
Attrs {
kind: NodeKind::Socket,
size: 0,
mode: S_IFSOCK | 0o777,
uid: 0,
gid: 0,
mtime: 0,
inode: 0,
nlink: 1,
rdev: 0,
}
}
fn d_type(kind: NodeKind) -> u8 {
match kind {
NodeKind::Fifo => 1,
NodeKind::CharDevice => 2,
NodeKind::Dir => 4,
NodeKind::BlockDevice => 6,
NodeKind::File => 8,
NodeKind::Symlink => 10,
NodeKind::Socket => 12,
}
}
pub fn encode_dirents(
entries: &[(String, NodeKind, u64)],
pos: usize,
cap: usize,
) -> (Vec<u8>, usize) {
let mut out = Vec::new();
let mut i = pos;
while i < entries.len() {
let (name, kind, ino) = &entries[i];
let reclen = (19 + name.len() + 1).div_ceil(8) * 8;
if out.len() + reclen > cap {
break;
}
let start = out.len();
out.resize(start + reclen, 0);
out[start..start + 8].copy_from_slice(&ino.to_le_bytes());
out[start + 8..start + 16].copy_from_slice(&((i + 1) as i64).to_le_bytes());
out[start + 16..start + 18].copy_from_slice(&(reclen as u16).to_le_bytes());
out[start + 18] = d_type(*kind);
out[start + 19..start + 19 + name.len()].copy_from_slice(name.as_bytes());
i += 1;
}
(out, i)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stat_size_and_mode() {
let attrs = Attrs {
kind: NodeKind::File,
size: 1234,
mode: 0o100_644,
uid: 0,
gid: 0,
mtime: 0,
inode: 42,
nlink: 1,
rdev: 0,
};
let b = encode_stat(&attrs);
assert_eq!(u64::from_le_bytes(b[8..16].try_into().unwrap()), 42); assert_eq!(u32::from_le_bytes(b[16..20].try_into().unwrap()), 0o100_644);
assert_eq!(u64::from_le_bytes(b[48..56].try_into().unwrap()), 1234); }
#[test]
fn stat_encodes_rdev() {
let attrs = Attrs {
kind: NodeKind::CharDevice,
size: 0,
mode: S_IFCHR | 0o666,
uid: 0,
gid: 0,
mtime: 0,
inode: 7,
nlink: 1,
rdev: makedev(1, 3), };
let b = encode_stat(&attrs);
assert_eq!(
u64::from_le_bytes(b[32..40].try_into().unwrap()),
attrs.rdev
);
assert_eq!(attrs.rdev, makedev(1, 3));
}
#[test]
fn makedev_small_values_are_major_times_256_plus_minor() {
assert_eq!(makedev(1, 3), 256 + 3);
assert_eq!(makedev(5, 0), 5 * 256);
assert_eq!(makedev(1, 9), 256 + 9);
}
#[test]
fn dirents_paginate() {
let entries = vec![
(".".to_string(), NodeKind::Dir, 1),
("file".to_string(), NodeKind::File, 2),
];
let (bytes, next) = encode_dirents(&entries, 0, 4096);
assert_eq!(next, 2);
assert_eq!(bytes[18], 4);
let (empty, n) = encode_dirents(&entries, 0, 8);
assert!(empty.is_empty());
assert_eq!(n, 0);
}
}