use core::{fmt, mem, ops, slice}; use redox_simple_endian::*; use crate::{BlockList, BlockPtr, BlockRaw}; pub enum NodeLevel { L0(usize), L1(usize, usize), L2(usize, usize, usize), L3(usize, usize, usize, usize), L4(usize, usize, usize, usize, usize), } impl NodeLevel { // Warning: this uses constant block offsets, make sure to sync with Node pub fn new(mut block_offset: u64) -> Option { // 1 << 8 = 256, this is the number of entries in a BlockList const SHIFT: u64 = 8; const NUM: u64 = 1 << SHIFT; const MASK: u64 = NUM - 1; const L0: u64 = 128; if block_offset < L0 { return Some(Self::L0((block_offset & MASK) as usize)); } else { block_offset -= L0; } const L1: u64 = 64 * NUM; if block_offset < L1 { return Some(Self::L1( ((block_offset >> SHIFT) & MASK) as usize, (block_offset & MASK) as usize, )); } else { block_offset -= L1; } const L2: u64 = 32 * NUM * NUM; if block_offset < L2 { return Some(Self::L2( ((block_offset >> (2 * SHIFT)) & MASK) as usize, ((block_offset >> SHIFT) & MASK) as usize, (block_offset & MASK) as usize, )); } else { block_offset -= L2; } const L3: u64 = 16 * NUM * NUM * NUM; if block_offset < L3 { return Some(Self::L3( ((block_offset >> (3 * SHIFT)) & MASK) as usize, ((block_offset >> (2 * SHIFT)) & MASK) as usize, ((block_offset >> SHIFT) & MASK) as usize, (block_offset & MASK) as usize, )); } else { block_offset -= L3; } const L4: u64 = 12 * NUM * NUM * NUM * NUM; if block_offset < L4 { Some(Self::L4( ((block_offset >> (4 * SHIFT)) & MASK) as usize, ((block_offset >> (3 * SHIFT)) & MASK) as usize, ((block_offset >> (2 * SHIFT)) & MASK) as usize, ((block_offset >> SHIFT) & MASK) as usize, (block_offset & MASK) as usize, )) } else { None } } } type BlockListL1 = BlockList; type BlockListL2 = BlockList; type BlockListL3 = BlockList; type BlockListL4 = BlockList; /// A file/folder node #[repr(packed)] pub struct Node { pub mode: u16le, pub uid: u32le, pub gid: u32le, pub links: u32le, pub size: u64le, pub ctime: u64le, pub ctime_nsec: u32le, pub mtime: u64le, pub mtime_nsec: u32le, pub atime: u64le, pub atime_nsec: u32le, pub padding: [u8; 6], // 128 * BLOCK_SIZE (512 KiB, 4 KiB each) pub level0: [BlockPtr; 128], // 64 * 256 * BLOCK_SIZE (64 MiB, 1 MiB each) pub level1: [BlockPtr; 64], // 32 * 256 * 256 * BLOCK_SIZE (8 GiB, 256 MiB each) pub level2: [BlockPtr; 32], // 16 * 256 * 256 * 256 * BLOCK_SIZE (1 TiB, 64 GiB each) pub level3: [BlockPtr; 16], // 12 * 256 * 256 * 256 * 256 * BLOCK_SIZE (192 TiB, 16 TiB each) pub level4: [BlockPtr; 12], } impl Default for Node { fn default() -> Self { Self { mode: 0.into(), uid: 0.into(), gid: 0.into(), links: 0.into(), size: 0.into(), ctime: 0.into(), ctime_nsec: 0.into(), mtime: 0.into(), mtime_nsec: 0.into(), atime: 0.into(), atime_nsec: 0.into(), padding: [0; 6], level0: [BlockPtr::default(); 128], level1: [BlockPtr::default(); 64], level2: [BlockPtr::default(); 32], level3: [BlockPtr::default(); 16], level4: [BlockPtr::default(); 12], } } } impl Node { pub const MODE_TYPE: u16 = 0xF000; pub const MODE_FILE: u16 = 0x8000; pub const MODE_DIR: u16 = 0x4000; pub const MODE_SYMLINK: u16 = 0xA000; pub const MODE_PERM: u16 = 0x0FFF; pub const MODE_EXEC: u16 = 0o1; pub const MODE_WRITE: u16 = 0o2; pub const MODE_READ: u16 = 0o4; pub fn new(mode: u16, uid: u32, gid: u32, ctime: u64, ctime_nsec: u32) -> Self { Self { mode: mode.into(), uid: uid.into(), gid: gid.into(), links: 0.into(), ctime: ctime.into(), ctime_nsec: ctime_nsec.into(), mtime: ctime.into(), mtime_nsec: ctime_nsec.into(), atime: ctime.into(), atime_nsec: ctime_nsec.into(), ..Default::default() } } pub fn mode(&self) -> u16 { { self.mode }.to_native() } pub fn uid(&self) -> u32 { { self.uid }.to_native() } pub fn gid(&self) -> u32 { { self.gid }.to_native() } pub fn links(&self) -> u32 { { self.links }.to_native() } pub fn size(&self) -> u64 { { self.size }.to_native() } pub fn ctime(&self) -> (u64, u32) { ({ self.ctime }.to_native(), { self.ctime_nsec }.to_native()) } pub fn mtime(&self) -> (u64, u32) { ({ self.mtime }.to_native(), { self.mtime_nsec }.to_native()) } pub fn atime(&self) -> (u64, u32) { ({ self.atime }.to_native(), { self.atime_nsec }.to_native()) } pub fn set_mode(&mut self, mode: u16) { self.mode = mode.into(); } pub fn set_uid(&mut self, uid: u32) { self.uid = uid.into(); } pub fn set_gid(&mut self, gid: u32) { self.gid = gid.into(); } pub fn set_links(&mut self, links: u32) { self.links = links.into(); } pub fn set_size(&mut self, size: u64) { self.size = size.into(); } pub fn set_mtime(&mut self, mtime: u64, mtime_nsec: u32) { self.mtime = mtime.into(); self.mtime_nsec = mtime_nsec.into(); } pub fn set_atime(&mut self, atime: u64, atime_nsec: u32) { self.atime = atime.into(); self.atime_nsec = atime_nsec.into(); } pub fn is_dir(&self) -> bool { self.mode() & Self::MODE_TYPE == Self::MODE_DIR } pub fn is_file(&self) -> bool { self.mode() & Self::MODE_TYPE == Self::MODE_FILE } pub fn is_symlink(&self) -> bool { self.mode() & Self::MODE_TYPE == Self::MODE_SYMLINK } /// Tests if UID is the owner of that file, only true when uid=0 or when the UID stored in metadata is equal to the UID you supply pub fn owner(&self, uid: u32) -> bool { uid == 0 || self.uid() == uid } /// Tests if the current user has enough permissions to view the file, op is the operation, /// like read and write, these modes are MODE_EXEC, MODE_READ, and MODE_WRITE pub fn permission(&self, uid: u32, gid: u32, op: u16) -> bool { let mut perm = self.mode() & 0o7; if self.uid() == uid { // If self.mode is 101100110, >> 6 would be 000000101 // 0o7 is octal for 111, or, when expanded to 9 digits is 000000111 perm |= (self.mode() >> 6) & 0o7; // Since we erased the GID and OTHER bits when >>6'ing, |= will keep those bits in place. } if self.gid() == gid || gid == 0 { perm |= (self.mode() >> 3) & 0o7; } if uid == 0 { //set the `other` bits to 111 perm |= 0o7; } perm & op == op } } impl fmt::Debug for Node { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mode = self.mode; let uid = self.uid; let gid = self.gid; let links = self.links; let size = self.size; let ctime = self.ctime; let ctime_nsec = self.ctime_nsec; let mtime = self.mtime; let mtime_nsec = self.mtime_nsec; let atime = self.atime; let atime_nsec = self.atime_nsec; f.debug_struct("Node") .field("mode", &mode) .field("uid", &uid) .field("gid", &gid) .field("links", &links) .field("size", &size) .field("ctime", &ctime) .field("ctime_nsec", &ctime_nsec) .field("mtime", &mtime) .field("mtime_nsec", &mtime_nsec) .field("atime", &atime) .field("atime_nsec", &atime_nsec) //TODO: level0/1/2/3 .finish() } } impl ops::Deref for Node { type Target = [u8]; fn deref(&self) -> &[u8] { unsafe { slice::from_raw_parts(self as *const Node as *const u8, mem::size_of::()) as &[u8] } } } impl ops::DerefMut for Node { fn deref_mut(&mut self) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(self as *mut Node as *mut u8, mem::size_of::()) as &mut [u8] } } } #[test] fn node_size_test() { assert_eq!(mem::size_of::(), crate::BLOCK_SIZE as usize); } #[kani::proof] fn check_node_level() { let offset = kani::any(); NodeLevel::new(offset); } #[kani::proof] fn check_node_perms() { let mode = 0o750; let uid = kani::any(); let gid = kani::any(); let ctime = kani::any(); let ctime_nsec = kani::any(); let node = Node::new(mode, uid, gid, ctime, ctime_nsec); let root_uid = 0; let root_gid = 0; let other_uid = kani::any(); kani::assume(other_uid != uid); kani::assume(other_uid != root_uid); let other_gid = kani::any(); kani::assume(other_gid != gid); kani::assume(other_gid != root_gid); assert!(node.owner(uid)); assert!(node.permission(uid, gid, 0o7)); assert!(node.permission(uid, gid, 0o5)); assert!(node.permission(uid, other_gid, 0o7)); assert!(node.permission(uid, other_gid, 0o5)); assert!(!node.permission(other_uid, gid, 0o7)); assert!(node.permission(other_uid, gid, 0o5)); assert!(node.owner(root_uid)); assert!(node.permission(root_uid, root_gid, 0o7)); assert!(node.permission(root_uid, root_gid, 0o5)); assert!(node.permission(root_uid, other_gid, 0o7)); assert!(node.permission(root_uid, other_gid, 0o5)); assert!(!node.permission(other_uid, root_gid, 0o7)); assert!(node.permission(other_uid, root_gid, 0o5)); assert!(!node.owner(other_uid)); assert!(!node.permission(other_uid, other_gid, 0o7)); assert!(!node.permission(other_uid, other_gid, 0o5)); }