use crate::block_device::BLOCK_SIZE; use crate::utils; use bitflags::bitflags; use fuser::FileType; pub(crate) const DIRECT_NUMBER: usize = 15; pub(crate) const INODE_PER_BLOCK: usize = BLOCK_SIZE / INODE_SIZE; #[derive(Debug, Clone, Copy)] pub struct InodeMode(pub 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 { #[allow(unused)] pub(crate) fn exec_other(&self) -> bool { self.0 & Self::IXOTH.0 != 0 } #[allow(unused)] pub(crate) fn write_other(&self) -> bool { self.0 & Self::IWOTH.0 != 0 } #[allow(unused)] pub(crate) fn read_other(&self) -> bool { self.0 & Self::IROTH.0 != 0 } #[allow(unused)] pub(crate) fn exec_group(&self) -> bool { self.0 & Self::IXGRP.0 != 0 } #[allow(unused)] pub(crate) fn write_group(&self) -> bool { self.0 & Self::IWGRP.0 != 0 } #[allow(unused)] pub(crate) fn read_group(&self) -> bool { self.0 & Self::IRGRP.0 != 0 } #[allow(unused)] pub(crate) fn exec_user(&self) -> bool { self.0 & Self::IXUSR.0 != 0 } #[allow(unused)] pub(crate) fn write_user(&self) -> bool { self.0 & Self::IWUSR.0 != 0 } #[allow(unused)] pub(crate) fn read_user(&self) -> bool { self.0 & Self::IRUSR.0 != 0 } 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 is_symlink(&self) -> bool { self.0 & 0xF000 == Self::IFLNK.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), } } } impl From for u8 { fn from(value: InodeMode) -> Self { utils::from_filetype(value.into()) } } /// 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: u64, 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 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, file_acl: u32, dir_acl: u32, ) -> Self { Self { mode, uid, size: 0, atime: time, ctime: time, mtime: time, crtime: time, gid, n_links: 1, n_blocks: 0, flags, direct: [0; DIRECT_NUMBER], single_indirect: 0, double_indirect: 0, triple_indirect: 0, file_acl, dir_acl, } } #[allow(unused)] pub fn make_inode( permissions: u16, mode: InodeMode, uid: u32, gid: u32, time: u32, flags: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | mode, uid, gid, time, flags, file_acl, dir_acl, ) } pub fn directory( permissions: u16, uid: u32, gid: u32, time: u32, flags: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | InodeMode::IFDIR, uid, gid, time, flags, file_acl, dir_acl, ) } pub fn file( permissions: u16, uid: u32, gid: u32, time: u32, flags: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | InodeMode::IFREG, uid, gid, time, flags, file_acl, dir_acl, ) } pub fn symlink( permissions: u16, uid: u32, gid: u32, time: u32, flags: u32, file_acl: u32, dir_acl: u32, ) -> Self { Self::new( InodeMode(permissions) | InodeMode::IFLNK, uid, gid, time, flags, 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(crate) fn is_symlink(&self) -> bool { self.mode.is_symlink() } pub(crate) fn file_type(&self) -> FileType { self.mode.into() } pub fn empty() -> Self { Self { mode: InodeMode(0), uid: 0, size: 0, atime: 0, ctime: 0, mtime: 0, crtime: 0, gid: 0, n_links: 0, n_blocks: 0, flags: 0, direct: [0; 15], single_indirect: 0, double_indirect: 0, triple_indirect: 0, file_acl: 0, dir_acl: 0, } } } pub const INODE_SIZE: usize = std::mem::size_of::(); pub const ENTRY_PER_BLOCK: usize = BLOCK_SIZE / 4;