use bitflags::bitflags; use fuser::{FileAttr, FileType}; use std::fs::File; const DIRECT_NUMBER: usize = 15; #[derive(Debug, Clone, Copy)] pub struct InodeMode(u16); bitflags! { impl InodeMode: u16 { const IXOTH = 0x0001; const IWOTH = 0x0002; const IROTH = 0x0004; const IXGRP = 0x0008; const IWGRP = 0x0010; const IRGRP = 0x0020; const IXUSR = 0x0040; const IWUSR = 0x0080; const IRUSR = 0x0100; const ISVTX = 0x0200; const ISGID = 0x0400; const ISUID = 0x0800; // These are mutually-exclusive: const IFIFO = 0x1000; const IFCHR = 0x2000; const IFDIR = 0x4000; const IFBLK = 0x6000; const IFREG = 0x8000; const IFLNK = 0xA000; const IFSOCK = 0xC000; } } impl InodeMode { pub(crate) fn perm(&self) -> u16 { self.0 & 0x0FFF } pub(crate) fn is_file(&self) -> bool { (self.0 & 0xF000) == Self::IFREG.0 } pub(crate) fn is_dir(&self) -> bool { (self.0 & 0xF000) == Self::IFDIR.0 } pub(crate) fn validate(mode_value: u16) -> Option { let valid_flags: [u16; 7] = [0x1000, 0x2000, 0x4000, 0x6000, 0x8000, 0xA000, 0xC000]; valid_flags .contains(&(mode_value & 0xF000)) .then(|| Self(mode_value)) } } impl From for FileType { fn from(value: InodeMode) -> Self { let type_flag = value.0 & 0xF000; match type_flag { 0x1000 => FileType::NamedPipe, 0x2000 => FileType::CharDevice, 0x4000 => FileType::Directory, 0x6000 => FileType::BlockDevice, 0x8000 => FileType::RegularFile, 0xA000 => FileType::Symlink, 0xC000 => FileType::Socket, _ => panic!("Invalid inode mode {:x}", type_flag), } } } /// Pretty much the same with ext2, with minor changes: /// - removed OS dependent attributes (osd1 & osd2) /// - removed i_faddr since fragmentation is not supported /// - changed uid and gid from u16 to u32 /// - added more direct blocks for a total size of 128 bytes /// TODO: do we need to extend time precision? #[repr(C)] #[derive(Debug, Clone)] pub struct Inode { pub mode: InodeMode, pub uid: u32, pub size: u32, pub atime: u32, // access time, in seconds pub ctime: u32, // change time, in seconds pub mtime: u32, // modify time, in seconds pub crtime: u32, // create time, in seconds pub gid: u32, pub n_links: u16, pub n_blocks: u32, pub flags: u32, // TODO: do we actually need this? maybe just return 0 pub direct: [u32; DIRECT_NUMBER], pub single_indirect: u32, pub double_indirect: u32, pub triple_indirect: u32, pub generation: u32, pub file_acl: u32, pub dir_acl: u32, // TODO do we have to implement ACL......? } impl Inode { fn new( mode: InodeMode, uid: u32, gid: u32, time: u32, flags: u32, generation: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self { mode, uid, size: 0, atime: time, ctime: time, mtime: time, crtime: time, gid, n_links: 0, n_blocks: 0, flags, direct: [0; DIRECT_NUMBER], single_indirect: 0, double_indirect: 0, triple_indirect: 0, generation, file_acl, dir_acl, } } pub fn make_inode( permissions: u16, mode: InodeMode, uid: u32, gid: u32, time: u32, flags: u32, generation: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | mode, uid, gid, time, flags, generation, file_acl, dir_acl, ) } pub fn directory( permissions: u16, uid: u32, gid: u32, time: u32, flags: u32, generation: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | InodeMode::IFDIR, uid, gid, time, flags, generation, file_acl, dir_acl, ) } pub fn file( permissions: u16, uid: u32, gid: u32, time: u32, flags: u32, generation: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | InodeMode::IFREG, uid, gid, time, flags, generation, file_acl, dir_acl, ) } pub(crate) fn is_file(&self) -> bool { self.mode.is_file() } pub(crate) fn is_dir(&self) -> bool { self.mode.is_dir() } pub fn empty() -> Self { Self::new(InodeMode(0), 0, 0, 0, 0, 0, 0, 0) } } pub const INODE_SIZE: usize = std::mem::size_of::();