diff --git a/book/src/kernel/linux-compatibility/README.md b/book/src/kernel/linux-compatibility/README.md index 002deab40..4ba8b3ec4 100644 --- a/book/src/kernel/linux-compatibility/README.md +++ b/book/src/kernel/linux-compatibility/README.md @@ -288,7 +288,7 @@ which are summarized in the table below. | 265 | linkat | ✅ | ❓ | | 266 | symlinkat | ✅ | 💯 | | 267 | readlinkat | ✅ | 💯 | -| 268 | fchmodat | ✅ | ❓ | +| 268 | fchmodat | ✅ | 💯 | | 269 | faccessat | ✅ | ❓ | | 270 | pselect6 | ✅ | 💯 | | 271 | ppoll | ✅ | ❓ | @@ -348,6 +348,7 @@ which are summarized in the table below. | 436 | close_range | ✅ | ❓ | | 439 | faccessat2 | ✅ | ❓ | | 441 | epoll_pwait2 | ✅ | ❓ | +| 452 | fchmodat2 | ✅ | 💯 | - Supported: - ✅ = syscall supported diff --git a/book/src/kernel/linux-compatibility/syscall-flag-coverage/file-and-directory-operations/fully_covered.scml b/book/src/kernel/linux-compatibility/syscall-flag-coverage/file-and-directory-operations/fully_covered.scml index c39262289..2d34addb9 100644 --- a/book/src/kernel/linux-compatibility/syscall-flag-coverage/file-and-directory-operations/fully_covered.scml +++ b/book/src/kernel/linux-compatibility/syscall-flag-coverage/file-and-directory-operations/fully_covered.scml @@ -68,6 +68,8 @@ readlinkat(dirfd, path, buf, bufsiz); // Change permissions of a file chmod(path, mode); fchmod(fd, mode); +fchmodat(dirfd, path_ptr, mode); +fchmodat2(dirfd, path_ptr, mode, flags = AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); // Change ownership of a file chown(path, owner, group); diff --git a/kernel/src/syscall/arch/loongarch.rs b/kernel/src/syscall/arch/loongarch.rs index 67c2da20d..a4373d719 100644 --- a/kernel/src/syscall/arch/loongarch.rs +++ b/kernel/src/syscall/arch/loongarch.rs @@ -10,7 +10,7 @@ use super::{ capget::sys_capget, capset::sys_capset, chdir::{sys_chdir, sys_fchdir}, - chmod::{sys_fchmod, sys_fchmodat}, + chmod::{sys_fchmod, sys_fchmodat, sys_fchmodat2}, chown::{sys_fchown, sys_fchownat}, chroot::sys_chroot, clock_gettime::sys_clock_gettime, @@ -356,4 +356,5 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]); SYS_FACCESSAT2 = 439 => sys_faccessat2(args[..4]); SYS_EPOLL_PWAIT2 = 441 => sys_epoll_pwait2(args[..5]); + SYS_FCHMODAT2 = 452 => sys_fchmodat2(args[..4]); } diff --git a/kernel/src/syscall/arch/riscv.rs b/kernel/src/syscall/arch/riscv.rs index 586eb5187..203985f01 100644 --- a/kernel/src/syscall/arch/riscv.rs +++ b/kernel/src/syscall/arch/riscv.rs @@ -10,7 +10,7 @@ use super::{ capget::sys_capget, capset::sys_capset, chdir::{sys_chdir, sys_fchdir}, - chmod::{sys_fchmod, sys_fchmodat}, + chmod::{sys_fchmod, sys_fchmodat, sys_fchmodat2}, chown::{sys_fchown, sys_fchownat}, chroot::sys_chroot, clock_gettime::sys_clock_gettime, @@ -358,4 +358,5 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]); SYS_FACCESSAT2 = 439 => sys_faccessat2(args[..4]); SYS_EPOLL_PWAIT2 = 441 => sys_epoll_pwait2(args[..5]); + SYS_FCHMODAT2 = 452 => sys_fchmodat2(args[..4]); } diff --git a/kernel/src/syscall/arch/x86.rs b/kernel/src/syscall/arch/x86.rs index 062f299bb..74953a001 100644 --- a/kernel/src/syscall/arch/x86.rs +++ b/kernel/src/syscall/arch/x86.rs @@ -12,7 +12,7 @@ use super::{ capget::sys_capget, capset::sys_capset, chdir::{sys_chdir, sys_fchdir}, - chmod::{sys_chmod, sys_fchmod, sys_fchmodat}, + chmod::{sys_chmod, sys_fchmod, sys_fchmodat, sys_fchmodat2}, chown::{sys_chown, sys_fchown, sys_fchownat, sys_lchown}, chroot::sys_chroot, clock_gettime::sys_clock_gettime, @@ -407,4 +407,5 @@ impl_syscall_nums_and_dispatch_fn! { SYS_CLOSE_RANGE = 436 => sys_close_range(args[..3]); SYS_FACCESSAT2 = 439 => sys_faccessat2(args[..4]); SYS_EPOLL_PWAIT2 = 441 => sys_epoll_pwait2(args[..5]); + SYS_FCHMODAT2 = 452 => sys_fchmodat2(args[..4]); } diff --git a/kernel/src/syscall/chmod.rs b/kernel/src/syscall/chmod.rs index 39a888b17..77b709281 100644 --- a/kernel/src/syscall/chmod.rs +++ b/kernel/src/syscall/chmod.rs @@ -24,32 +24,62 @@ pub fn sys_fchmod(fd: FileDesc, mode: u16, ctx: &Context) -> Result Result { - self::sys_fchmodat(AT_FDCWD, path_ptr, mode, ctx) + do_fchmodat(AT_FDCWD, path_ptr, mode, ChmodFlags::empty(), ctx) } -// Glibc handles the `flags` argument, so we just ignore it. pub fn sys_fchmodat( dirfd: FileDesc, path_ptr: Vaddr, mode: u16, - /* flags: u32, */ + ctx: &Context, +) -> Result { + do_fchmodat(dirfd, path_ptr, mode, ChmodFlags::empty(), ctx) +} + +pub fn sys_fchmodat2( + dirfd: FileDesc, + path_ptr: Vaddr, + mode: u16, + flags: u32, + ctx: &Context, +) -> Result { + let flags = ChmodFlags::from_bits(flags) + .ok_or_else(|| Error::with_message(Errno::EINVAL, "invalid chmod flags"))?; + + do_fchmodat(dirfd, path_ptr, mode, flags, ctx) +} + +fn do_fchmodat( + dirfd: FileDesc, + path_ptr: Vaddr, + mode: u16, + flags: ChmodFlags, ctx: &Context, ) -> Result { let path_name = ctx.user_space().read_cstring(path_ptr, PATH_MAX)?; + debug!( - "dirfd = {}, path_name = {:?}, mode = 0o{:o}", - dirfd, path_name, mode, + "dirfd = {}, path_name = {:?}, mode = 0o{:o}, flags = {:?}", + dirfd, path_name, mode, flags, ); let path_or_inode = { let path_name = path_name.to_string_lossy(); - let fs_path = FsPath::from_fd_and_path(dirfd, &path_name)?; - ctx.thread_local - .borrow_fs() - .resolver() - .read() - .lookup_inode(&fs_path)? + let fs_path = if flags.contains(ChmodFlags::AT_EMPTY_PATH) && path_name.is_empty() { + FsPath::from_fd(dirfd)? + } else { + FsPath::from_fd_and_path(dirfd, &path_name)? + }; + + let fs_ref = ctx.thread_local.borrow_fs(); + let fs = fs_ref.resolver().read(); + if flags.contains(ChmodFlags::AT_SYMLINK_NOFOLLOW) { + fs.lookup_inode_no_follow(&fs_path)? + } else { + fs.lookup_inode(&fs_path)? + } }; + path_or_inode .inode() .set_mode(InodeMode::from_bits_truncate(mode))?; @@ -58,3 +88,10 @@ pub fn sys_fchmodat( } Ok(SyscallReturn::Return(0)) } + +bitflags::bitflags! { + struct ChmodFlags: u32 { + const AT_EMPTY_PATH = 1 << 12; + const AT_SYMLINK_NOFOLLOW = 1 << 8; + } +}