From 3600a3a439e3ce50310c3464a4012c41caeb6803 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Mon, 3 Jul 2023 16:14:40 +0800 Subject: [PATCH] Add support for chmod, fchmod, fchmodat --- framework/jinux-frame/src/config.rs | 2 +- regression/syscall_test/Makefile | 2 +- regression/syscall_test/blocklists/chmod_test | 3 + services/libs/jinux-std/src/fs/fs_resolver.rs | 2 +- .../jinux-std/src/fs/procfs/template/dir.rs | 20 +++++-- .../jinux-std/src/fs/procfs/template/file.rs | 20 +++++-- .../jinux-std/src/fs/procfs/template/mod.rs | 36 ++++++++++-- .../jinux-std/src/fs/procfs/template/sym.rs | 20 +++++-- services/libs/jinux-std/src/fs/ramfs/fs.rs | 4 ++ .../jinux-std/src/fs/utils/dentry_cache.rs | 8 +++ services/libs/jinux-std/src/fs/utils/inode.rs | 2 + services/libs/jinux-std/src/fs/utils/vnode.rs | 4 ++ services/libs/jinux-std/src/syscall/chmod.rs | 55 +++++++++++++++++++ services/libs/jinux-std/src/syscall/mod.rs | 8 +++ 14 files changed, 160 insertions(+), 26 deletions(-) create mode 100644 regression/syscall_test/blocklists/chmod_test create mode 100644 services/libs/jinux-std/src/syscall/chmod.rs diff --git a/framework/jinux-frame/src/config.rs b/framework/jinux-frame/src/config.rs index 18b052159..b818be220 100644 --- a/framework/jinux-frame/src/config.rs +++ b/framework/jinux-frame/src/config.rs @@ -4,7 +4,7 @@ use log::Level; pub const USER_STACK_SIZE: usize = PAGE_SIZE * 4; pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 64; -pub const KERNEL_HEAP_SIZE: usize = 0x4_000_000; +pub const KERNEL_HEAP_SIZE: usize = 0x5_000_000; pub const KERNEL_OFFSET: usize = 0xffffffff80000000; diff --git a/regression/syscall_test/Makefile b/regression/syscall_test/Makefile index 280d4e2ea..d91d80822 100644 --- a/regression/syscall_test/Makefile +++ b/regression/syscall_test/Makefile @@ -1,4 +1,4 @@ -TESTS ?= open_test read_test statfs_test +TESTS ?= open_test read_test statfs_test chmod_test MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH))) diff --git a/regression/syscall_test/blocklists/chmod_test b/regression/syscall_test/blocklists/chmod_test new file mode 100644 index 000000000..dff65ae2a --- /dev/null +++ b/regression/syscall_test/blocklists/chmod_test @@ -0,0 +1,3 @@ +ChmodTest.FchmodDirSucceeds_NoRandomSave +ChmodTest.FchmodatDirAbsolutePath +ChmodTest.FchmodatDir \ No newline at end of file diff --git a/services/libs/jinux-std/src/fs/fs_resolver.rs b/services/libs/jinux-std/src/fs/fs_resolver.rs index 388f00803..b96b4a008 100644 --- a/services/libs/jinux-std/src/fs/fs_resolver.rs +++ b/services/libs/jinux-std/src/fs/fs_resolver.rs @@ -109,7 +109,7 @@ impl FsResolver { return_errno_with_message!(Errno::EISDIR, "path refers to a directory"); } if !dir_dentry.vnode().inode_mode().is_writable() { - return_errno_with_message!(Errno::EPERM, "file cannot be created"); + return_errno_with_message!(Errno::EACCES, "file cannot be created"); } let new_dentry = dir_dentry.create(&file_name, InodeType::File, inode_mode)?; new_dentry diff --git a/services/libs/jinux-std/src/fs/procfs/template/dir.rs b/services/libs/jinux-std/src/fs/procfs/template/dir.rs index 831d23d80..8ee7d8bfa 100644 --- a/services/libs/jinux-std/src/fs/procfs/template/dir.rs +++ b/services/libs/jinux-std/src/fs/procfs/template/dir.rs @@ -55,26 +55,34 @@ impl ProcDir { impl Inode for ProcDir { fn len(&self) -> usize { - self.info.metadata().size + self.info.size() } fn resize(&self, _new_size: usize) {} fn metadata(&self) -> Metadata { - self.info.metadata().clone() + self.info.metadata() } fn atime(&self) -> Duration { - self.info.metadata().atime + self.info.atime() } - fn set_atime(&self, _time: Duration) {} + fn set_atime(&self, time: Duration) { + self.info.set_atime(time) + } fn mtime(&self) -> Duration { - self.info.metadata().mtime + self.info.mtime() } - fn set_mtime(&self, _time: Duration) {} + fn set_mtime(&self, time: Duration) { + self.info.set_mtime(time) + } + + fn set_mode(&self, mode: InodeMode) { + self.info.set_mode(mode) + } fn create(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result> { Err(Error::new(Errno::EPERM)) diff --git a/services/libs/jinux-std/src/fs/procfs/template/file.rs b/services/libs/jinux-std/src/fs/procfs/template/file.rs index e15a0d897..05d4196b4 100644 --- a/services/libs/jinux-std/src/fs/procfs/template/file.rs +++ b/services/libs/jinux-std/src/fs/procfs/template/file.rs @@ -28,26 +28,34 @@ impl ProcFile { impl Inode for ProcFile { fn len(&self) -> usize { - self.info.metadata().size + self.info.size() } fn resize(&self, _new_size: usize) {} fn metadata(&self) -> Metadata { - self.info.metadata().clone() + self.info.metadata() } fn atime(&self) -> Duration { - self.info.metadata().atime + self.info.atime() } - fn set_atime(&self, _time: Duration) {} + fn set_atime(&self, time: Duration) { + self.info.set_atime(time) + } fn mtime(&self) -> Duration { - self.info.metadata().mtime + self.info.mtime() } - fn set_mtime(&self, _time: Duration) {} + fn set_mtime(&self, time: Duration) { + self.info.set_mtime(time) + } + + fn set_mode(&self, mode: InodeMode) { + self.info.set_mode(mode) + } fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> { unreachable!() diff --git a/services/libs/jinux-std/src/fs/procfs/template/mod.rs b/services/libs/jinux-std/src/fs/procfs/template/mod.rs index 53a7f95b9..b310cdf26 100644 --- a/services/libs/jinux-std/src/fs/procfs/template/mod.rs +++ b/services/libs/jinux-std/src/fs/procfs/template/mod.rs @@ -1,4 +1,6 @@ -use crate::fs::utils::{FileSystem, Metadata}; +use core::time::Duration; + +use crate::fs::utils::{FileSystem, InodeMode, Metadata}; use crate::prelude::*; use super::ProcFS; @@ -14,7 +16,7 @@ mod file; mod sym; struct ProcInodeInfo { - metadata: Metadata, + metadata: RwLock, fs: Weak, is_volatile: bool, } @@ -22,7 +24,7 @@ struct ProcInodeInfo { impl ProcInodeInfo { pub fn new(metadata: Metadata, fs: Weak, is_volatile: bool) -> Self { Self { - metadata, + metadata: RwLock::new(metadata), fs, is_volatile, } @@ -32,8 +34,32 @@ impl ProcInodeInfo { &self.fs } - pub fn metadata(&self) -> &Metadata { - &self.metadata + pub fn metadata(&self) -> Metadata { + self.metadata.read().clone() + } + + pub fn size(&self) -> usize { + self.metadata.read().size + } + + pub fn atime(&self) -> Duration { + self.metadata.read().atime + } + + pub fn set_atime(&self, time: Duration) { + self.metadata.write().atime = time; + } + + pub fn mtime(&self) -> Duration { + self.metadata.read().mtime + } + + pub fn set_mtime(&self, time: Duration) { + self.metadata.write().mtime = time; + } + + pub fn set_mode(&self, mode: InodeMode) { + self.metadata.write().mode = mode; } pub fn is_volatile(&self) -> bool { diff --git a/services/libs/jinux-std/src/fs/procfs/template/sym.rs b/services/libs/jinux-std/src/fs/procfs/template/sym.rs index d26a185d7..9210ea5dc 100644 --- a/services/libs/jinux-std/src/fs/procfs/template/sym.rs +++ b/services/libs/jinux-std/src/fs/procfs/template/sym.rs @@ -28,26 +28,34 @@ impl ProcSym { impl Inode for ProcSym { fn len(&self) -> usize { - self.info.metadata().size + self.info.size() } fn resize(&self, _new_size: usize) {} fn metadata(&self) -> Metadata { - self.info.metadata().clone() + self.info.metadata() } fn atime(&self) -> Duration { - self.info.metadata().atime + self.info.atime() } - fn set_atime(&self, _time: Duration) {} + fn set_atime(&self, time: Duration) { + self.info.set_atime(time) + } fn mtime(&self) -> Duration { - self.info.metadata().mtime + self.info.mtime() } - fn set_mtime(&self, _time: Duration) {} + fn set_mtime(&self, time: Duration) { + self.info.set_mtime(time) + } + + fn set_mode(&self, mode: InodeMode) { + self.info.set_mode(mode) + } fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> { Err(Error::new(Errno::EPERM)) diff --git a/services/libs/jinux-std/src/fs/ramfs/fs.rs b/services/libs/jinux-std/src/fs/ramfs/fs.rs index 28a7a90b1..87f2ba184 100644 --- a/services/libs/jinux-std/src/fs/ramfs/fs.rs +++ b/services/libs/jinux-std/src/fs/ramfs/fs.rs @@ -394,6 +394,10 @@ impl Inode for RamInode { self.0.write().metadata.mtime = time; } + fn set_mode(&self, mode: InodeMode) { + self.0.write().metadata.mode = mode; + } + fn mknod( &self, name: &str, diff --git a/services/libs/jinux-std/src/fs/utils/dentry_cache.rs b/services/libs/jinux-std/src/fs/utils/dentry_cache.rs index e140a840a..4620f2fb9 100644 --- a/services/libs/jinux-std/src/fs/utils/dentry_cache.rs +++ b/services/libs/jinux-std/src/fs/utils/dentry_cache.rs @@ -110,6 +110,9 @@ impl Dentry { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } + if !self.vnode.inode_mode().is_executable() { + return_errno!(Errno::EACCES); + } if name.len() > NAME_MAX { return_errno!(Errno::ENAMETOOLONG); } @@ -253,6 +256,11 @@ impl Dentry { self.vnode.inode_mode() } + /// Set the inode permission mode + pub fn set_inode_mode(&self, mode: InodeMode) { + self.vnode.set_inode_mode(mode) + } + /// Get the inode length pub fn inode_len(&self) -> usize { self.vnode.len() diff --git a/services/libs/jinux-std/src/fs/utils/inode.rs b/services/libs/jinux-std/src/fs/utils/inode.rs index 343af3c2f..ee90e1348 100644 --- a/services/libs/jinux-std/src/fs/utils/inode.rs +++ b/services/libs/jinux-std/src/fs/utils/inode.rs @@ -223,6 +223,8 @@ pub trait Inode: Any + Sync + Send { fn set_mtime(&self, time: Duration); + fn set_mode(&self, mode: InodeMode); + fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> { Err(Error::new(Errno::EISDIR)) } diff --git a/services/libs/jinux-std/src/fs/utils/vnode.rs b/services/libs/jinux-std/src/fs/utils/vnode.rs index 643f26cce..787701831 100644 --- a/services/libs/jinux-std/src/fs/utils/vnode.rs +++ b/services/libs/jinux-std/src/fs/utils/vnode.rs @@ -218,6 +218,10 @@ impl Vnode { self.inner.read().inode.metadata().mode } + pub fn set_inode_mode(&self, mode: InodeMode) { + self.inner.read().inode.set_mode(mode) + } + pub fn len(&self) -> usize { self.inner.read().inode.len() } diff --git a/services/libs/jinux-std/src/syscall/chmod.rs b/services/libs/jinux-std/src/syscall/chmod.rs new file mode 100644 index 000000000..a78992ea0 --- /dev/null +++ b/services/libs/jinux-std/src/syscall/chmod.rs @@ -0,0 +1,55 @@ +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, + inode_handle::InodeHandle, + utils::{InodeMode, PATH_MAX}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::{SYS_FCHMOD, SYS_FCHMODAT}; + +pub fn sys_fchmod(fd: FileDescripter, mode: u16) -> Result { + log_syscall_entry!(SYS_FCHMOD); + debug!("fd = {}, mode = 0o{:o}", fd, mode); + + let current = current!(); + let file_table = current.file_table().lock(); + let file = file_table.get_file(fd)?; + let inode_handle = file + .downcast_ref::() + .ok_or(Error::with_message(Errno::EBADF, "not inode"))?; + let dentry = inode_handle.dentry(); + dentry.set_inode_mode(InodeMode::from_bits_truncate(mode)); + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_chmod(path_ptr: Vaddr, mode: u16) -> Result { + self::sys_fchmodat(AT_FDCWD, path_ptr, mode) +} + +// Glibc handles the `flags` argument, so we just ignore it. +pub fn sys_fchmodat( + dirfd: FileDescripter, + path_ptr: Vaddr, + mode: u16, + /* flags: u32, */ +) -> Result { + log_syscall_entry!(SYS_FCHMODAT); + let path = read_cstring_from_user(path_ptr, PATH_MAX)?; + debug!("dirfd = {}, path = {:?}, mode = 0o{:o}", dirfd, path, mode,); + + let current = current!(); + let dentry = { + let path = path.to_string_lossy(); + if path.is_empty() { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + let fs_path = FsPath::new(dirfd, path.as_ref())?; + current.fs().read().lookup(&fs_path)? + }; + dentry.set_inode_mode(InodeMode::from_bits_truncate(mode)); + Ok(SyscallReturn::Return(0)) +} diff --git a/services/libs/jinux-std/src/syscall/mod.rs b/services/libs/jinux-std/src/syscall/mod.rs index ce2f46f5c..5ad0fee66 100644 --- a/services/libs/jinux-std/src/syscall/mod.rs +++ b/services/libs/jinux-std/src/syscall/mod.rs @@ -5,6 +5,7 @@ use crate::syscall::access::sys_access; use crate::syscall::arch_prctl::sys_arch_prctl; use crate::syscall::brk::sys_brk; use crate::syscall::chdir::{sys_chdir, sys_fchdir}; +use crate::syscall::chmod::{sys_chmod, sys_fchmod, sys_fchmodat}; use crate::syscall::clock_gettime::sys_clock_gettime; use crate::syscall::clock_nanosleep::sys_clock_nanosleep; use crate::syscall::clone::sys_clone; @@ -89,6 +90,7 @@ mod arch_prctl; mod bind; mod brk; mod chdir; +mod chmod; mod clock_gettime; mod clock_nanosleep; mod clone; @@ -252,6 +254,8 @@ define_syscall_nums!( SYS_UNLINK = 87, SYS_SYMLINK = 88, SYS_READLINK = 89, + SYS_CHMOD = 90, + SYS_FCHMOD = 91, SYS_UMASK = 95, SYS_GETTIMEOFDAY = 96, SYS_GETUID = 102, @@ -286,6 +290,7 @@ define_syscall_nums!( SYS_LINKAT = 265, SYS_SYMLINKAT = 266, SYS_READLINKAT = 267, + SYS_FCHMODAT = 268, SYS_SET_ROBUST_LIST = 273, SYS_UTIMENSAT = 280, SYS_EPOLL_CREATE1 = 291, @@ -406,6 +411,8 @@ pub fn syscall_dispatch( SYS_UNLINK => syscall_handler!(1, sys_unlink, args), SYS_SYMLINK => syscall_handler!(2, sys_symlink, args), SYS_READLINK => syscall_handler!(3, sys_readlink, args), + SYS_CHMOD => syscall_handler!(2, sys_chmod, args), + SYS_FCHMOD => syscall_handler!(2, sys_fchmod, args), SYS_UMASK => syscall_handler!(1, sys_umask, args), SYS_GETTIMEOFDAY => syscall_handler!(1, sys_gettimeofday, args), SYS_GETUID => syscall_handler!(0, sys_getuid), @@ -440,6 +447,7 @@ pub fn syscall_dispatch( SYS_LINKAT => syscall_handler!(5, sys_linkat, args), SYS_SYMLINKAT => syscall_handler!(3, sys_symlinkat, args), SYS_READLINKAT => syscall_handler!(4, sys_readlinkat, args), + SYS_FCHMODAT => syscall_handler!(3, sys_fchmodat, args), SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args), SYS_UTIMENSAT => syscall_handler!(4, sys_utimensat, args), SYS_EPOLL_CREATE1 => syscall_handler!(1, sys_epoll_create1, args),