From 6f189d2743e9b1ab790c58fc2d3a8d0c41cf61e1 Mon Sep 17 00:00:00 2001 From: linfeng Date: Thu, 27 Jun 2024 17:43:25 +0800 Subject: [PATCH] feat(time): Add syscall support for utime* (#838) * feat(vfs): Add syscall support for utime* impl sys_utimensat impl sys_utimes add utimensat test fix some warning * fix(vfs): Verify pointer validity * fix: remove bad cfg --- kernel/src/arch/x86_64/pci/pci.rs | 2 +- kernel/src/filesystem/fat/fs.rs | 10 +++ kernel/src/filesystem/vfs/open.rs | 93 ++++++++++++++++++++-- kernel/src/filesystem/vfs/syscall.rs | 50 +++++++++++- kernel/src/syscall/mod.rs | 21 ++++- kernel/src/time/mod.rs | 10 +++ user/apps/test_utimensat/.gitignore | 1 + user/apps/test_utimensat/Makefile | 20 +++++ user/apps/test_utimensat/main.c | 12 +++ user/dadk/config/test_utimensat_0_1_0.dadk | 23 ++++++ 10 files changed, 232 insertions(+), 10 deletions(-) create mode 100644 user/apps/test_utimensat/.gitignore create mode 100644 user/apps/test_utimensat/Makefile create mode 100644 user/apps/test_utimensat/main.c create mode 100644 user/dadk/config/test_utimensat_0_1_0.dadk diff --git a/kernel/src/arch/x86_64/pci/pci.rs b/kernel/src/arch/x86_64/pci/pci.rs index 521fa9f0..427d6c88 100644 --- a/kernel/src/arch/x86_64/pci/pci.rs +++ b/kernel/src/arch/x86_64/pci/pci.rs @@ -11,7 +11,7 @@ use crate::init::initcall::INITCALL_SUBSYS; use crate::mm::PhysAddr; use acpi::mcfg::Mcfg; -use log::{error, warn}; +use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 11febb29..0c2ff956 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1472,6 +1472,16 @@ impl IndexNode for LockedFATInode { fn metadata(&self) -> Result { return Ok(self.0.lock().metadata.clone()); } + fn set_metadata(&self, metadata: &Metadata) -> Result<(), SystemError> { + let inode = &mut self.0.lock(); + inode.metadata.atime = metadata.atime; + inode.metadata.mtime = metadata.mtime; + inode.metadata.ctime = metadata.ctime; + inode.metadata.mode = metadata.mode; + inode.metadata.uid = metadata.uid; + inode.metadata.gid = metadata.gid; + Ok(()) + } fn resize(&self, len: usize) -> Result<(), SystemError> { let mut guard: SpinLockGuard = self.0.lock(); let fs: &Arc = &guard.fs.upgrade().unwrap(); diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs index 826b91be..f9490815 100644 --- a/kernel/src/filesystem/vfs/open.rs +++ b/kernel/src/filesystem/vfs/open.rs @@ -2,11 +2,6 @@ use alloc::sync::Arc; use log::warn; use system_error::SystemError; -use crate::{ - driver::base::block::SeekFrom, process::ProcessManager, - syscall::user_access::check_and_clone_cstr, -}; - use super::{ fcntl::AtFlags, file::{File, FileMode}, @@ -14,6 +9,13 @@ use super::{ utils::{rsplit_path, user_path_at}, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; +use crate::filesystem::vfs::syscall::UtimensFlags; +use crate::time::{syscall::PosixTimeval, PosixTimeSpec}; +use crate::{ + driver::base::block::SeekFrom, process::ProcessManager, + syscall::user_access::check_and_clone_cstr, +}; +use alloc::string::String; pub(super) fn do_faccessat( dirfd: i32, @@ -147,3 +149,84 @@ fn do_sys_openat2( return r; } + +/// On Linux, futimens() is a library function implemented on top of +/// the utimensat() system call. To support this, the Linux +/// utimensat() system call implements a nonstandard feature: if +/// pathname is NULL, then the call modifies the timestamps of the +/// file referred to by the file descriptor dirfd (which may refer to +/// any type of file). +pub fn do_utimensat( + dirfd: i32, + pathname: Option, + times: Option<[PosixTimeSpec; 2]>, + flags: UtimensFlags, +) -> Result { + const UTIME_NOW: i64 = (1i64 << 30) - 1i64; + const UTIME_OMIT: i64 = (1i64 << 30) - 2i64; + // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags); + let inode = match pathname { + Some(path) => { + let (inode_begin, path) = + user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?; + let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) { + inode_begin.lookup(path.as_str())? + } else { + inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)? + }; + inode + } + None => { + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.write(); + let file = fd_table_guard + .get_file_by_fd(dirfd) + .ok_or(SystemError::EBADF)?; + file.inode() + } + }; + let now = PosixTimeSpec::now(); + let mut meta = inode.metadata()?; + + if let Some([atime, mtime]) = times { + if atime.tv_nsec == UTIME_NOW { + meta.atime = now; + } else if atime.tv_nsec != UTIME_OMIT { + meta.atime = atime; + } + if mtime.tv_nsec == UTIME_NOW { + meta.mtime = now; + } else if mtime.tv_nsec != UTIME_OMIT { + meta.mtime = mtime; + } + inode.set_metadata(&meta).unwrap(); + } else { + meta.atime = now; + meta.mtime = now; + inode.set_metadata(&meta).unwrap(); + } + return Ok(0); +} + +pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result { + // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times); + let (inode_begin, path) = user_path_at( + &ProcessManager::current_pcb(), + AtFlags::AT_FDCWD.bits(), + path, + )?; + let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + let mut meta = inode.metadata()?; + + if let Some([atime, mtime]) = times { + meta.atime = PosixTimeSpec::from(atime); + meta.mtime = PosixTimeSpec::from(mtime); + inode.set_metadata(&meta)?; + } else { + let now = PosixTimeSpec::now(); + meta.atime = now; + meta.mtime = now; + inode.set_metadata(&meta)?; + } + return Ok(0); +} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 893de210..e6eeb67b 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -7,6 +7,7 @@ use log::warn; use system_error::SystemError; use crate::producefs; +use crate::syscall::user_access::UserBufferReader; use crate::{ driver::base::{block::SeekFrom, device::device_number::DeviceNumber}, filesystem::vfs::{core as Vcore, file::FileDescriptorVec}, @@ -17,14 +18,14 @@ use crate::{ user_access::{self, check_and_clone_cstr, UserBufferWriter}, Syscall, }, - time::PosixTimeSpec, + time::{syscall::PosixTimeval, PosixTimeSpec}, }; use super::{ core::{do_mkdir_at, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, - open::{do_faccessat, do_fchmodat, do_sys_open}, + open::{do_faccessat, do_fchmodat, do_sys_open, do_utimensat, do_utimes}, utils::{rsplit_path, user_path_at}, Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, @@ -323,6 +324,13 @@ bitflags! { } } +bitflags! { + pub struct UtimensFlags: u32 { + /// 不需要解释符号链接 + const AT_SYMLINK_NOFOLLOW = 0x100; + } +} + #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PosixStatfs { @@ -1620,6 +1628,44 @@ impl Syscall { )?; return Ok(()); } + + pub fn sys_utimensat( + dirfd: i32, + pathname: *const u8, + times: *const PosixTimeSpec, + flags: u32, + ) -> Result { + let pathname = if pathname.is_null() { + None + } else { + let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))?; + Some(pathname) + }; + let flags = UtimensFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; + let times = if times.is_null() { + None + } else { + let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; + let times = times_reader.read_from_user::(0)?; + Some([times[0], times[1]]) + }; + do_utimensat(dirfd, pathname, times, flags) + } + + pub fn sys_utimes( + pathname: *const u8, + times: *const PosixTimeval, + ) -> Result { + let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))?; + let times = if times.is_null() { + None + } else { + let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; + let times = times_reader.read_from_user::(0)?; + Some([times[0], times[1]]) + }; + do_utimes(&pathname, times) + } } #[repr(C)] diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 497e5084..354e38f9 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -29,7 +29,7 @@ use crate::{ filesystem::vfs::{ fcntl::{AtFlags, FcntlCommand}, file::FileMode, - syscall::{ModeType, PosixKstat}, + syscall::{ModeType, PosixKstat, UtimensFlags}, MAX_PATHLEN, }, libs::align::page_align_up, @@ -1103,7 +1103,24 @@ impl Syscall { Self::shmctl(id, cmd, user_buf, from_user) } - + SYS_UTIMENSAT => Self::sys_utimensat( + args[0] as i32, + args[1] as *const u8, + args[2] as *const PosixTimeSpec, + args[3] as u32, + ), + #[cfg(target_arch = "x86_64")] + SYS_FUTIMESAT => { + let flags = UtimensFlags::empty(); + Self::sys_utimensat( + args[0] as i32, + args[1] as *const u8, + args[2] as *const PosixTimeSpec, + flags.bits(), + ) + } + #[cfg(target_arch = "x86_64")] + SYS_UTIMES => Self::sys_utimes(args[0] as *const u8, args[1] as *const PosixTimeval), _ => panic!("Unsupported syscall ID: {}", syscall_num), }; diff --git a/kernel/src/time/mod.rs b/kernel/src/time/mod.rs index d42a6d4a..2e695a40 100644 --- a/kernel/src/time/mod.rs +++ b/kernel/src/time/mod.rs @@ -5,6 +5,7 @@ use core::{ }; use crate::arch::CurrentTimeArch; +use crate::time::syscall::PosixTimeval; use self::timekeeping::getnstimeofday; @@ -114,6 +115,15 @@ impl From for PosixTimeSpec { } } +impl From for PosixTimeSpec { + fn from(value: PosixTimeval) -> Self { + PosixTimeSpec { + tv_sec: value.tv_sec, + tv_nsec: value.tv_usec as i64 * 1000, + } + } +} + impl From for Duration { fn from(val: PosixTimeSpec) -> Self { Duration::from_micros(val.tv_sec as u64 * 1000000 + val.tv_nsec as u64 / 1000) diff --git a/user/apps/test_utimensat/.gitignore b/user/apps/test_utimensat/.gitignore new file mode 100644 index 00000000..f4910341 --- /dev/null +++ b/user/apps/test_utimensat/.gitignore @@ -0,0 +1 @@ +test_utimensat \ No newline at end of file diff --git a/user/apps/test_utimensat/Makefile b/user/apps/test_utimensat/Makefile new file mode 100644 index 00000000..985606c7 --- /dev/null +++ b/user/apps/test_utimensat/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test_utimensat main.c + +.PHONY: install clean +install: all + mv test_utimensat $(DADK_CURRENT_BUILD_DIR)/test_utimensat + +clean: + rm test_utimensat *.o + +fmt: diff --git a/user/apps/test_utimensat/main.c b/user/apps/test_utimensat/main.c new file mode 100644 index 00000000..441fdef6 --- /dev/null +++ b/user/apps/test_utimensat/main.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + + +int main(){ + int res = utimensat(AT_FDCWD, "/bin/about.elf", NULL, 0); + printf("utimensat res = %d\n", res); +} \ No newline at end of file diff --git a/user/dadk/config/test_utimensat_0_1_0.dadk b/user/dadk/config/test_utimensat_0_1_0.dadk new file mode 100644 index 00000000..098a3670 --- /dev/null +++ b/user/dadk/config/test_utimensat_0_1_0.dadk @@ -0,0 +1,23 @@ +{ + "name": "test_utimensat", + "version": "0.1.0", + "description": "test_utimensat", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_utimensat" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "clean": { + "clean_command": "make clean" + }, + "target_arch": ["x86_64"] +} \ No newline at end of file