diff --git a/libkernel/src/fs/attr.rs b/libkernel/src/fs/attr.rs index a385fcf..37d1e6a 100644 --- a/libkernel/src/fs/attr.rs +++ b/libkernel/src/fs/attr.rs @@ -41,6 +41,28 @@ bitflags! { const S_ISGID = 0x0400; const S_ISUID = 0x0800; + } +} + +bitflags! { + #[derive(Clone, Copy, Debug)] + pub struct FileMode: u16 { + const S_IXOTH = 0x0001; + const S_IWOTH = 0x0002; + const S_IROTH = 0x0004; + + const S_IXGRP = 0x0008; + const S_IWGRP = 0x0010; + const S_IRGRP = 0x0020; + + const S_IXUSR = 0x0040; + const S_IWUSR = 0x0080; + const S_IRUSR = 0x0100; + + const S_ISVTX = 0x0200; + + const S_ISGID = 0x0400; + const S_ISUID = 0x0800; // Mutually-exclusive file types: const S_IFIFO = 0x1000; @@ -53,6 +75,28 @@ bitflags! { } } +impl From for FilePermissions { + fn from(mode: FileMode) -> Self { + FilePermissions::from_bits_truncate(mode.bits()) + } +} + +impl FileMode { + pub fn new(file_type: FileType, permissions: FilePermissions) -> Self { + let mut mode = FileMode::from_bits_truncate(permissions.bits()); + mode |= match file_type { + FileType::Directory => FileMode::S_IFDIR, + FileType::File => FileMode::S_IFREG, + FileType::Symlink => FileMode::S_IFLNK, + FileType::BlockDevice(_) => FileMode::S_IFBLK, + FileType::CharDevice(_) => FileMode::S_IFCHR, + FileType::Fifo => FileMode::S_IFIFO, + FileType::Socket => FileMode::S_IFSOCK, + }; + mode + } +} + /// Represents file metadata, similar to `stat`. #[derive(Debug, Clone)] pub struct FileAttr { @@ -65,12 +109,18 @@ pub struct FileAttr { pub mtime: Duration, // Modification time pub ctime: Duration, // Change time pub file_type: FileType, - pub mode: FilePermissions, + pub permissions: FilePermissions, pub nlinks: u32, pub uid: Uid, pub gid: Gid, } +impl FileAttr { + pub fn mode(&self) -> FileMode { + FileMode::new(self.file_type, self.permissions) + } +} + impl Default for FileAttr { fn default() -> Self { Self { @@ -83,7 +133,7 @@ impl Default for FileAttr { mtime: Duration::new(0, 0), ctime: Duration::new(0, 0), file_type: FileType::File, - mode: FilePermissions::empty(), + permissions: FilePermissions::empty(), nlinks: 1, uid: Uid::new_root(), gid: Gid::new_root_group(), @@ -116,7 +166,7 @@ impl FileAttr { if uid.is_root() { if requested_mode.contains(AccessMode::X_OK) { // Root still needs at least one execute bit to be set for X_OK - if self.mode.intersects( + if self.permissions.intersects( FilePermissions::S_IXUSR | FilePermissions::S_IXGRP | FilePermissions::S_IXOTH, ) { return Ok(()); @@ -129,13 +179,13 @@ impl FileAttr { // Determine which set of permission bits to use (owner, group, or other) let perms_to_check = if self.uid == uid { // User is the owner - self.mode + self.permissions } else if self.gid == gid { // User is in the file's group. Shift group bits to align with owner bits for easier checking. - FilePermissions::from_bits_truncate(self.mode.bits() << 3) + FilePermissions::from_bits_truncate(self.permissions.bits() << 3) } else { // Others. Shift other bits to align with owner bits. - FilePermissions::from_bits_truncate(self.mode.bits() << 6) + FilePermissions::from_bits_truncate(self.permissions.bits() << 6) }; if requested_mode.contains(AccessMode::R_OK) @@ -175,11 +225,11 @@ mod tests { const OTHER_UID: Uid = Uid::new(1002); const OTHER_GID: Gid = Gid::new(3000); - fn setup_file(mode: FilePermissions) -> FileAttr { + fn setup_file(permissions: FilePermissions) -> FileAttr { FileAttr { uid: OWNER_UID, gid: FILE_GROUP_GID, - mode, + permissions, ..Default::default() } } diff --git a/libkernel/src/fs/filesystems/ext4/mod.rs b/libkernel/src/fs/filesystems/ext4/mod.rs index e20d844..9c717c7 100644 --- a/libkernel/src/fs/filesystems/ext4/mod.rs +++ b/libkernel/src/fs/filesystems/ext4/mod.rs @@ -96,8 +96,7 @@ impl From for FileAttr { FileAttr { size: meta.size_in_bytes, file_type: meta.file_type.into(), - // Infallible, since they are identical - mode: FilePermissions::from_bits(meta.mode.bits()).unwrap(), + permissions: FilePermissions::from_bits_truncate(meta.mode.bits()), uid: Uid::new(meta.uid), gid: Gid::new(meta.gid), atime: meta.atime, diff --git a/libkernel/src/fs/filesystems/fat32/dir.rs b/libkernel/src/fs/filesystems/fat32/dir.rs index e1e5f06..0d5327f 100644 --- a/libkernel/src/fs/filesystems/fat32/dir.rs +++ b/libkernel/src/fs/filesystems/fat32/dir.rs @@ -221,7 +221,7 @@ impl Fat32DirStream { let attr = FileAttr { size: dir_entry.size as u64, file_type, - mode: FilePermissions::from_bits_retain(0o755), + permissions: FilePermissions::from_bits_retain(0o755), atime: fat_date_to_duration(dir_entry.adate), mtime: fat_datetime_to_duration(dir_entry.mdate, dir_entry.mtime, 0), ctime: fat_datetime_to_duration( diff --git a/libkernel/src/fs/filesystems/tmpfs.rs b/libkernel/src/fs/filesystems/tmpfs.rs index fcbd75a..37d3e30 100644 --- a/libkernel/src/fs/filesystems/tmpfs.rs +++ b/libkernel/src/fs/filesystems/tmpfs.rs @@ -126,14 +126,14 @@ where G: PageAllocGetter, T: AddressTranslator<()>, { - fn new(id: InodeId, mode: FilePermissions) -> Result { + fn new(id: InodeId, permissions: FilePermissions) -> Result { Ok(Self { id, attr: SpinLockIrq::new(FileAttr { file_type: FileType::File, size: 0, nlinks: 1, - mode, + permissions, ..Default::default() }), inner: SpinLockIrq::new(TmpFsRegInner { @@ -666,14 +666,14 @@ where G: PageAllocGetter, T: AddressTranslator<()>, { - pub fn new(id: u64, fs: Weak>, mode: FilePermissions) -> Arc { + pub fn new(id: u64, fs: Weak>, permissions: FilePermissions) -> Arc { Arc::new_cyclic(|weak_this| Self { entries: SpinLockIrq::new(Vec::new()), attrs: SpinLockIrq::new(FileAttr { size: 0, file_type: FileType::Directory, block_size: BLOCK_SZ as _, - mode, + permissions, ..Default::default() }), id, diff --git a/src/drivers/fs/dev.rs b/src/drivers/fs/dev.rs index 3f8ed7f..ea490e2 100644 --- a/src/drivers/fs/dev.rs +++ b/src/drivers/fs/dev.rs @@ -28,7 +28,7 @@ impl DevFs { id: InodeId::from_fsid_and_inodeid(DEVFS_ID, 0), attr: SpinLock::new(FileAttr { file_type: FileType::Directory, - mode: FilePermissions::from_bits_retain(0o755), + permissions: FilePermissions::from_bits_retain(0o755), ..FileAttr::default() }), kind: InodeKind::Directory(SpinLock::new(BTreeMap::new())), @@ -44,7 +44,7 @@ impl DevFs { &self, name: String, device_id: CharDevDescriptor, - mode: FilePermissions, + permissions: FilePermissions, ) -> Result<()> { let InodeKind::Directory(ref children) = self.root.kind else { // This should be impossible as the root is always a directory. @@ -66,7 +66,7 @@ impl DevFs { attr: SpinLock::new(FileAttr { id, file_type: FileType::CharDevice(device_id), - mode, + permissions, ..FileAttr::default() }), // This is the crucial part: we store the device handle. diff --git a/src/drivers/fs/proc/cmdline.rs b/src/drivers/fs/proc/cmdline.rs index f7f4b9c..e5230bc 100644 --- a/src/drivers/fs/proc/cmdline.rs +++ b/src/drivers/fs/proc/cmdline.rs @@ -16,7 +16,7 @@ impl ProcCmdlineInode { id, attr: FileAttr { file_type: libkernel::fs::FileType::File, - mode: libkernel::fs::attr::FilePermissions::from_bits_retain(0o444), + permissions: libkernel::fs::attr::FilePermissions::from_bits_retain(0o444), ..FileAttr::default() }, } diff --git a/src/drivers/fs/proc/root.rs b/src/drivers/fs/proc/root.rs index 7de6375..919358a 100644 --- a/src/drivers/fs/proc/root.rs +++ b/src/drivers/fs/proc/root.rs @@ -26,7 +26,7 @@ impl ProcRootInode { id: InodeId::from_fsid_and_inodeid(PROCFS_ID, 0), attr: FileAttr { file_type: FileType::Directory, - mode: FilePermissions::from_bits_retain(0o555), + permissions: FilePermissions::from_bits_retain(0o555), ..FileAttr::default() }, } diff --git a/src/drivers/fs/proc/task/mod.rs b/src/drivers/fs/proc/task/mod.rs index 61e8630..97a6de7 100644 --- a/src/drivers/fs/proc/task/mod.rs +++ b/src/drivers/fs/proc/task/mod.rs @@ -31,7 +31,7 @@ impl ProcTaskInode { id: inode_id, attr: FileAttr { file_type: FileType::Directory, - mode: FilePermissions::from_bits_retain(0o555), + permissions: FilePermissions::from_bits_retain(0o555), ..FileAttr::default() }, desc, diff --git a/src/drivers/fs/proc/task/task_file.rs b/src/drivers/fs/proc/task/task_file.rs index 095904e..f7d76bf 100644 --- a/src/drivers/fs/proc/task/task_file.rs +++ b/src/drivers/fs/proc/task/task_file.rs @@ -58,7 +58,7 @@ impl ProcTaskFileInode { | TaskFileType::Stat => FileType::File, TaskFileType::Cwd | TaskFileType::Root => FileType::Symlink, }, - mode: FilePermissions::from_bits_retain(0o444), + permissions: FilePermissions::from_bits_retain(0o444), ..FileAttr::default() }, process_stats, diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 9dc7918..c1931b2 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -507,7 +507,7 @@ impl VFS { { let creds = task.creds.lock_save_irq(); - if attr.mode.contains(FilePermissions::S_ISVTX) + if attr.permissions.contains(FilePermissions::S_ISVTX) && attr.uid != creds.euid() && parent_attr.uid != creds.euid() { diff --git a/src/fs/pipe.rs b/src/fs/pipe.rs index 14cab70..5d85eeb 100644 --- a/src/fs/pipe.rs +++ b/src/fs/pipe.rs @@ -62,7 +62,7 @@ impl Inode for PipeInode { mtime: self.time, ctime: self.time, file_type: FileType::Fifo, - mode: FilePermissions::from_bits_retain(0o0600), + permissions: FilePermissions::from_bits_retain(0o0600), nlinks: 1, uid: self.uid, gid: self.gid, diff --git a/src/fs/syscalls/at/chmod.rs b/src/fs/syscalls/at/chmod.rs index b5c00c3..d003868 100644 --- a/src/fs/syscalls/at/chmod.rs +++ b/src/fs/syscalls/at/chmod.rs @@ -37,7 +37,7 @@ pub async fn sys_fchmodat(dirfd: Fd, path: TUA, mode: u16, flags: i32) - return Err(KernelError::NotPermitted); } - attr.mode = mode; + attr.permissions = mode; node.setattr(attr).await?; Ok(0) diff --git a/src/fs/syscalls/at/rename.rs b/src/fs/syscalls/at/rename.rs index b6237e5..203ddf6 100644 --- a/src/fs/syscalls/at/rename.rs +++ b/src/fs/syscalls/at/rename.rs @@ -103,14 +103,14 @@ pub async fn sys_renameat2( let creds = task.creds.lock_save_irq(); - if (old_attr.mode.contains(FilePermissions::S_ISVTX) + if (old_attr.permissions.contains(FilePermissions::S_ISVTX) && old_attr.uid != creds.euid() && old_parent_attr.uid != creds.euid()) || new_parent_attr.uid != creds.euid() { creds.caps().check_capable(CapabilitiesFlags::CAP_FOWNER)?; } else if let Some(new_attr) = new_attr - && new_attr.mode.contains(FilePermissions::S_ISVTX) + && new_attr.permissions.contains(FilePermissions::S_ISVTX) && new_attr.uid != creds.euid() { creds.caps().check_capable(CapabilitiesFlags::CAP_FOWNER)?; diff --git a/src/fs/syscalls/at/stat.rs b/src/fs/syscalls/at/stat.rs index 77ae911..e13da14 100644 --- a/src/fs/syscalls/at/stat.rs +++ b/src/fs/syscalls/at/stat.rs @@ -45,7 +45,7 @@ impl From for Stat { Self { st_dev: value.id.fs_id(), st_ino: value.id.inode_id(), - st_mode: value.mode.bits() as u32 | u32::from(value.file_type), + st_mode: value.mode().bits() as u32 | u32::from(value.file_type), st_nlink: value.nlinks, st_uid: value.uid.into(), st_gid: value.gid.into(), diff --git a/src/fs/syscalls/at/statx.rs b/src/fs/syscalls/at/statx.rs index 29a4ea0..929f171 100644 --- a/src/fs/syscalls/at/statx.rs +++ b/src/fs/syscalls/at/statx.rs @@ -146,7 +146,7 @@ pub async fn sys_statx( if mask.contains(StatXMask::STATX_MODE) { stat_x.stx_mask |= StatXMask::STATX_MODE.bits(); - stat_x.stx_mode |= attr.mode.bits() as u16; + stat_x.stx_mode |= attr.mode().bits() as u16; } if mask.contains(StatXMask::STATX_NLINK) { diff --git a/src/fs/syscalls/chmod.rs b/src/fs/syscalls/chmod.rs index 713dd7e..6218db9 100644 --- a/src/fs/syscalls/chmod.rs +++ b/src/fs/syscalls/chmod.rs @@ -13,7 +13,7 @@ pub async fn sys_fchmod(fd: Fd, mode: u16) -> Result { .lock_save_irq() .get(fd) .ok_or(KernelError::BadFd)?; - let mode = FilePermissions::from_bits_retain(mode); + let permissions = FilePermissions::from_bits_retain(mode); let inode = file.inode().ok_or(KernelError::BadFd)?; let mut attr = inode.getattr().await?; @@ -22,7 +22,7 @@ pub async fn sys_fchmod(fd: Fd, mode: u16) -> Result { return Err(KernelError::NotPermitted); } - attr.mode = mode; + attr.permissions = permissions; inode.setattr(attr).await?; Ok(0)