From 4fab3681273f133440b2903b3cee1adb34cfe0da Mon Sep 17 00:00:00 2001 From: zhubojun Date: Wed, 16 Mar 2022 15:59:11 +0800 Subject: [PATCH] [libos] Add support for UTIME --- src/libos/src/fs/file_ops/mod.rs | 4 + src/libos/src/fs/file_ops/utimes.rs | 138 ++++++++++++++++++++++++++++ src/libos/src/fs/mod.rs | 4 +- src/libos/src/fs/syscalls.rs | 90 +++++++++++++++++- src/libos/src/syscall/mod.rs | 23 ++--- src/libos/src/time/mod.rs | 23 +++++ 6 files changed, 266 insertions(+), 16 deletions(-) create mode 100644 src/libos/src/fs/file_ops/utimes.rs diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index e1090b16..e36596d1 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -29,6 +29,9 @@ pub use self::stat::{do_fstat, do_fstatat, Stat, StatFlags}; pub use self::symlink::{do_readlinkat, do_symlinkat}; pub use self::truncate::{do_ftruncate, do_truncate}; pub use self::unlink::{do_unlinkat, UnlinkFlags}; +pub use self::utimes::{ + do_utimes_fd, do_utimes_path, get_utimes, utimbuf_t, Utime, UtimeFlags, UTIME_OMIT, +}; pub use self::write::{do_pwrite, do_write, do_writev}; mod access; @@ -56,4 +59,5 @@ mod stat; mod symlink; mod truncate; mod unlink; +mod utimes; mod write; diff --git a/src/libos/src/fs/file_ops/utimes.rs b/src/libos/src/fs/file_ops/utimes.rs new file mode 100644 index 00000000..b3e476dc --- /dev/null +++ b/src/libos/src/fs/file_ops/utimes.rs @@ -0,0 +1,138 @@ +use crate::util::mem_util::from_user; + +use super::time::{timespec_t, OcclumTimeProvider}; +use super::*; + +use rcore_fs::dev::TimeProvider; + +const UTIME_NOW: i64 = (1i64 << 30) - 1i64; +pub const UTIME_OMIT: i64 = (1i64 << 30) - 2i64; + +bitflags! { + pub struct UtimeFlags: i32 { + const AT_SYMLINK_NOFOLLOW = 1 << 8; + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +#[allow(non_camel_case_types)] +pub struct utimbuf_t { + atime: time_t, + mtime: time_t, +} + +impl utimbuf_t { + pub fn atime(&self) -> time_t { + self.atime + } + + pub fn mtime(&self) -> time_t { + self.mtime + } +} + +#[derive(Debug)] +#[allow(non_camel_case_types)] +pub enum Utime { + UTIME_OMIT, + UTIME_NOW, + UTIME(Timespec), +} + +pub fn get_utimes(times: Option<(timespec_t, timespec_t)>) -> Result<(Utime, Utime)> { + let now = OcclumTimeProvider.current_time(); + if let Some(times) = times { + let (atime, mtime) = times; + + if (!timespec_valid(atime)) || (!timespec_valid(mtime)) { + return_errno!(EINVAL, "parameter: times is invalid") + } + + let atime = if atime.nsec() == UTIME_OMIT { + Utime::UTIME_OMIT + } else if atime.nsec() == UTIME_NOW { + Utime::UTIME(now) + } else { + Utime::UTIME(Timespec { + sec: atime.sec(), + nsec: atime.nsec(), + }) + }; + let mtime = if mtime.nsec() == UTIME_OMIT { + Utime::UTIME_OMIT + } else if mtime.nsec() == UTIME_NOW { + Utime::UTIME(now) + } else { + Utime::UTIME(Timespec { + sec: mtime.sec(), + nsec: mtime.nsec(), + }) + }; + Ok((atime, mtime)) + } else { + Ok((Utime::UTIME(now), Utime::UTIME(now))) + } +} + +fn timespec_valid(time: timespec_t) -> bool { + if (time.nsec() == UTIME_NOW || time.nsec() == UTIME_OMIT) { + true + } else { + time.sec() >= 0 && time.nsec() >= 0 && time.nsec() < 1_000_000_000 + } +} + +pub fn do_utimes_fd(fd: FileDesc, atime: Utime, mtime: Utime, flags: i32) -> Result { + debug!( + "utimes_fd: fd: {:?}, atime: {:?}, mtime: {:?}, flags: {:?}", + fd, atime, mtime, flags + ); + + if flags != 0 { + return_errno!(EINVAL, "parameter: flags is invalid"); + } + + let file_ref = current!().file(fd)?; + let mut info = file_ref.metadata()?; + if let Utime::UTIME(atime) = atime { + info.atime = atime; + } + if let Utime::UTIME(mtime) = mtime { + info.mtime = mtime; + } + file_ref.set_metadata(&info)?; + Ok(0) +} + +pub fn do_utimes_path( + fs_path: &FsPath, + atime: Utime, + mtime: Utime, + flags: UtimeFlags, +) -> Result { + debug!( + "utimes_path: fs_path: {:?}, atime: {:?}, mtime: {:?}, flags: {:?}", + fs_path, atime, mtime, flags + ); + + let inode = { + let path = fs_path.to_abs_path()?; + let current = current!(); + let fs = current.fs().read().unwrap(); + if flags.contains(UtimeFlags::AT_SYMLINK_NOFOLLOW) { + fs.lookup_inode_no_follow(&path)? + } else { + fs.lookup_inode(&path)? + } + }; + let mut info = inode.metadata()?; + if let Utime::UTIME(atime) = atime { + info.atime = atime; + } + if let Utime::UTIME(mtime) = mtime { + info.mtime = mtime; + } + inode.set_metadata(&info)?; + Ok(0) +} diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index bfd75169..55ab8618 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -19,8 +19,8 @@ pub use self::event_file::{AsEvent, EventCreationFlags, EventFile}; pub use self::events::{AtomicIoEvents, IoEvents, IoNotifier}; pub use self::file::{File, FileRef}; pub use self::file_ops::{ - occlum_ocall_ioctl, AccessMode, BuiltinIoctlNum, CreationFlags, FallocateFlags, FileMode, - IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, + occlum_ocall_ioctl, utimbuf_t, AccessMode, BuiltinIoctlNum, CreationFlags, FallocateFlags, + FileMode, IfConf, IoctlCmd, Stat, StatusFlags, StructuredIoctlArgType, StructuredIoctlNum, }; pub use self::file_table::{FileDesc, FileTable, FileTableEvent, FileTableNotifier}; pub use self::fs_ops::Statfs; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index dc13ebb6..df62dc7a 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -1,12 +1,12 @@ use super::event_file::EventCreationFlags; use super::file_ops; use super::file_ops::{ - get_abs_path_by_fd, AccessibilityCheckFlags, AccessibilityCheckMode, ChownFlags, FcntlCmd, - FsPath, LinkFlags, StatFlags, UnlinkFlags, AT_FDCWD, + get_abs_path_by_fd, get_utimes, AccessibilityCheckFlags, AccessibilityCheckMode, ChownFlags, + FcntlCmd, FsPath, LinkFlags, StatFlags, UnlinkFlags, Utime, UtimeFlags, AT_FDCWD, UTIME_OMIT, }; use super::fs_ops; use super::fs_ops::{MountFlags, MountOptions, UmountFlags}; -use super::time::{clockid_t, itimerspec_t, ClockID}; +use super::time::{clockid_t, itimerspec_t, timespec_t, timeval_t, ClockID}; use super::timer_file::{TimerCreationFlags, TimerSetFlags}; use super::*; use config::ConfigMountFsType; @@ -700,3 +700,87 @@ pub fn do_flock(fd: FileDesc, operation: i32) -> Result { file_ops::do_flock(fd, flock_ops)?; Ok(0) } + +pub fn do_utime(path: *const i8, times_u: *const utimbuf_t) -> Result { + let times = if !times_u.is_null() { + from_user::check_ptr(times_u)?; + let utimbuf = unsafe { *times_u }; + let atime = timespec_t::from(utimbuf.atime()); + atime.validate()?; + let mtime = timespec_t::from(utimbuf.mtime()); + mtime.validate()?; + Some((atime, mtime)) + } else { + None + }; + + let (atime, mtime) = file_ops::get_utimes(times)?; + self::do_utimes_wrapper(AT_FDCWD, path, atime, mtime, 0) +} + +pub fn do_utimes(path: *const i8, times: *const timeval_t) -> Result { + self::do_futimesat(AT_FDCWD, path, times) +} + +pub fn do_futimesat(dirfd: i32, path: *const i8, times_u: *const timeval_t) -> Result { + let times = if !times_u.is_null() { + from_user::check_array(times_u, 2)?; + let atime_ptr = unsafe { times_u.offset(0) }; + let atime = unsafe { *atime_ptr }; + let atime = timespec_t::from(atime); + atime.validate()?; + let mtime_ptr = unsafe { times_u.offset(1) }; + let mtime = unsafe { *mtime_ptr }; + let mtime = timespec_t::from(mtime); + mtime.validate()?; + Some((atime, mtime)) + } else { + None + }; + + let (atime, mtime) = file_ops::get_utimes(times)?; + self::do_utimes_wrapper(dirfd, path, atime, mtime, 0) +} + +pub fn do_utimensat( + dirfd: i32, + path: *const i8, + times_u: *const timespec_t, + flags: i32, +) -> Result { + let times = if !times_u.is_null() { + from_user::check_array(times_u, 2)?; + let atime_ptr = unsafe { times_u.offset(0) }; + let atime = unsafe { *atime_ptr }; + let mtime_ptr = unsafe { times_u.offset(1) }; + let mtime = unsafe { *mtime_ptr }; + if atime.nsec() == UTIME_OMIT && mtime.nsec() == UTIME_OMIT { + return Ok(0); + } + Some((atime, mtime)) + } else { + None + }; + + let (atime, mtime) = file_ops::get_utimes(times)?; + self::do_utimes_wrapper(dirfd, path, atime, mtime, flags) +} + +fn do_utimes_wrapper( + dirfd: i32, + path: *const i8, + atime: Utime, + mtime: Utime, + flags: i32, +) -> Result { + if path.is_null() && dirfd != AT_FDCWD { + file_ops::do_utimes_fd(dirfd as FileDesc, atime, mtime, flags) + } else { + let path = from_user::clone_cstring_safely(path)? + .to_string_lossy() + .into_owned(); + let flags = UtimeFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?; + let fs_path = FsPath::new(&path, dirfd, false)?; + file_ops::do_utimes_path(&fs_path, atime, mtime, flags) + } +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index cdc24a0d..0ea8d0c1 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -25,13 +25,14 @@ use crate::fs::{ do_access, do_chdir, do_chmod, do_chown, do_close, do_creat, do_dup, do_dup2, do_dup3, do_eventfd, do_eventfd2, do_faccessat, do_fallocate, do_fchdir, do_fchmod, do_fchmodat, do_fchown, do_fchownat, do_fcntl, do_fdatasync, do_flock, do_fstat, do_fstatat, do_fstatfs, - do_fsync, do_ftruncate, do_getcwd, do_getdents, do_getdents64, do_ioctl, do_lchown, do_link, - do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount, do_mount_rootfs, do_open, - do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, - do_readv, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_statfs, do_symlink, - do_symlinkat, do_sync, do_timerfd_create, do_timerfd_gettime, do_timerfd_settime, do_truncate, - do_umask, do_umount, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, AsTimer, File, - FileDesc, FileRef, HostStdioFds, Stat, Statfs, + do_fsync, do_ftruncate, do_futimesat, do_getcwd, do_getdents, do_getdents64, do_ioctl, + do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_mount, + do_mount_rootfs, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, + do_readlink, do_readlinkat, do_readv, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, + do_statfs, do_symlink, do_symlinkat, do_sync, do_timerfd_create, do_timerfd_gettime, + do_timerfd_settime, do_truncate, do_umask, do_umount, do_unlink, do_unlinkat, do_utime, + do_utimensat, do_utimes, do_write, do_writev, iovec_t, utimbuf_t, AsTimer, File, FileDesc, + FileRef, HostStdioFds, Stat, Statfs, }; use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t, RandFlags}; @@ -220,7 +221,7 @@ macro_rules! process_syscall_table_with_callback { (RtSigqueueinfo = 129) => handle_unsupported(), (RtSigsuspend = 130) => handle_unsupported(), (Sigaltstack = 131) => do_sigaltstack(ss: *const stack_t, old_ss: *mut stack_t, context: *const CpuContext), - (Utime = 132) => handle_unsupported(), + (Utime = 132) => do_utime(path: *const i8, times: *const utimbuf_t), (Mknod = 133) => handle_unsupported(), (Uselib = 134) => handle_unsupported(), (Personality = 135) => handle_unsupported(), @@ -323,7 +324,7 @@ macro_rules! process_syscall_table_with_callback { (EpollWait = 232) => do_epoll_wait(epfd: c_int, events: *mut libc::epoll_event, maxevents: c_int, timeout: c_int), (EpollCtl = 233) => do_epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *const libc::epoll_event), (Tgkill = 234) => do_tgkill(pid: i32, tid: pid_t, sig: c_int), - (Utimes = 235) => handle_unsupported(), + (Utimes = 235) => do_utimes(path: *const i8, times: *const timeval_t), (Vserver = 236) => handle_unsupported(), (Mbind = 237) => handle_unsupported(), (SetMempolicy = 238) => handle_unsupported(), @@ -349,7 +350,7 @@ macro_rules! process_syscall_table_with_callback { (Mkdirat = 258) => do_mkdirat(dirfd: i32, path: *const i8, mode: u16), (Mknodat = 259) => handle_unsupported(), (Fchownat = 260) => do_fchownat(dirfd: i32, path: *const i8, uid: u32, gid: u32, flags: i32), - (Futimesat = 261) => handle_unsupported(), + (Futimesat = 261) => do_futimesat(dirfd: i32, path: *const i8, times: *const timeval_t), (Fstatat = 262) => do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32), (Unlinkat = 263) => do_unlinkat(dirfd: i32, path: *const i8, flags: i32), (Renameat = 264) => do_renameat(olddirfd: i32, oldpath: *const i8, newdirfd: i32, newpath: *const i8), @@ -368,7 +369,7 @@ macro_rules! process_syscall_table_with_callback { (SyncFileRange = 277) => handle_unsupported(), (Vmsplice = 278) => handle_unsupported(), (MovePages = 279) => handle_unsupported(), - (Utimensat = 280) => handle_unsupported(), + (Utimensat = 280) => do_utimensat(dirfd: i32, path: *const i8, times: *const timespec_t, flags: i32), (EpollPwait = 281) => do_epoll_pwait(epfd: c_int, events: *mut libc::epoll_event, maxevents: c_int, timeout: c_int, sigmask: *const usize), (Signalfd = 282) => handle_unsupported(), (TimerfdCreate = 283) => do_timerfd_create(clockid: clockid_t, flags: i32 ), diff --git a/src/libos/src/time/mod.rs b/src/libos/src/time/mod.rs index 45b2e021..ef90b3a4 100644 --- a/src/libos/src/time/mod.rs +++ b/src/libos/src/time/mod.rs @@ -48,6 +48,14 @@ impl timeval_t { } } + pub fn sec(&self) -> time_t { + self.sec + } + + pub fn usec(&self) -> suseconds_t { + self.usec + } + pub fn as_duration(&self) -> Duration { Duration::new(self.sec as u64, (self.usec * 1_000) as u32) } @@ -92,6 +100,21 @@ impl From for timespec_t { } } +impl From for timespec_t { + fn from(timval: timeval_t) -> timespec_t { + timespec_t { + sec: timval.sec(), + nsec: timval.usec() * 1_000, + } + } +} + +impl From for timespec_t { + fn from(time: time_t) -> timespec_t { + timespec_t { sec: time, nsec: 0 } + } +} + impl timespec_t { pub fn from_raw_ptr(ptr: *const timespec_t) -> Result { let ts = unsafe { *ptr };