Browse Source

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
linfeng 8 months ago
parent
commit
6f189d2743

+ 1 - 1
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;
 

+ 10 - 0
kernel/src/filesystem/fat/fs.rs

@@ -1472,6 +1472,16 @@ impl IndexNode for LockedFATInode {
     fn metadata(&self) -> Result<Metadata, SystemError> {
         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<FATInode> = self.0.lock();
         let fs: &Arc<FATFileSystem> = &guard.fs.upgrade().unwrap();

+ 88 - 5
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<String>,
+    times: Option<[PosixTimeSpec; 2]>,
+    flags: UtimensFlags,
+) -> Result<usize, SystemError> {
+    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<usize, SystemError> {
+    // 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);
+}

+ 48 - 2
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<usize, SystemError> {
+        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::<PosixTimeSpec>() * 2, true)?;
+            let times = times_reader.read_from_user::<PosixTimeSpec>(0)?;
+            Some([times[0], times[1]])
+        };
+        do_utimensat(dirfd, pathname, times, flags)
+    }
+
+    pub fn sys_utimes(
+        pathname: *const u8,
+        times: *const PosixTimeval,
+    ) -> Result<usize, SystemError> {
+        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::<PosixTimeval>() * 2, true)?;
+            let times = times_reader.read_from_user::<PosixTimeval>(0)?;
+            Some([times[0], times[1]])
+        };
+        do_utimes(&pathname, times)
+    }
 }
 
 #[repr(C)]

+ 19 - 2
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),
         };
 

+ 10 - 0
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<Duration> for PosixTimeSpec {
     }
 }
 
+impl From<PosixTimeval> for PosixTimeSpec {
+    fn from(value: PosixTimeval) -> Self {
+        PosixTimeSpec {
+            tv_sec: value.tv_sec,
+            tv_nsec: value.tv_usec as i64 * 1000,
+        }
+    }
+}
+
 impl From<PosixTimeSpec> for Duration {
     fn from(val: PosixTimeSpec) -> Self {
         Duration::from_micros(val.tv_sec as u64 * 1000000 + val.tv_nsec as u64 / 1000)

+ 1 - 0
user/apps/test_utimensat/.gitignore

@@ -0,0 +1 @@
+test_utimensat

+ 20 - 0
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:

+ 12 - 0
user/apps/test_utimensat/main.c

@@ -0,0 +1,12 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+
+int main(){
+    int res = utimensat(AT_FDCWD, "/bin/about.elf", NULL, 0);
+    printf("utimensat res = %d\n", res);
+}

+ 23 - 0
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"]
+}