mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 23:26:32 +00:00
Reconstruct utime-like syscalls and fix filetime implementations
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
3de8a9330a
commit
5edc110f9d
@ -152,7 +152,7 @@ provided by Linux on x86-64 architecture.
|
||||
| 129 | rt_sigqueueinfo | ❌ |
|
||||
| 130 | rt_sigsuspend | ✅ |
|
||||
| 131 | sigaltstack | ✅ |
|
||||
| 132 | utime | ❌ |
|
||||
| 132 | utime | ✅ |
|
||||
| 133 | mknod | ❌ |
|
||||
| 134 | uselib | ❌ |
|
||||
| 135 | personality | ❌ |
|
||||
@ -255,7 +255,7 @@ provided by Linux on x86-64 architecture.
|
||||
| 232 | epoll_wait | ✅ |
|
||||
| 233 | epoll_ctl | ✅ |
|
||||
| 234 | tgkill | ✅ |
|
||||
| 235 | utimes | ❌ |
|
||||
| 235 | utimes | ✅ |
|
||||
| 236 | vserver | ❌ |
|
||||
| 237 | mbind | ❌ |
|
||||
| 238 | set_mempolicy | ❌ |
|
||||
@ -281,7 +281,7 @@ provided by Linux on x86-64 architecture.
|
||||
| 258 | mkdirat | ✅ |
|
||||
| 259 | mknodat | ❌ |
|
||||
| 260 | fchownat | ✅ |
|
||||
| 261 | futimesat | ❌ |
|
||||
| 261 | futimesat | ✅ |
|
||||
| 262 | newfstatat | ✅ |
|
||||
| 263 | unlinkat | ✅ |
|
||||
| 264 | renameat | ✅ |
|
||||
|
@ -210,6 +210,14 @@ impl Inode for RootInode {
|
||||
self.metadata.write().mtime = time;
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.metadata.read().ctime
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.metadata.write().ctime = time;
|
||||
}
|
||||
|
||||
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
@ -124,6 +124,14 @@ impl Inode for Ptmx {
|
||||
self.metadata.write().mtime = time;
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.metadata.read().ctime
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.metadata.write().ctime = time;
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
@ -105,6 +105,14 @@ impl Inode for PtySlaveInode {
|
||||
self.metadata.write().mtime = time;
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.metadata.read().ctime
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.metadata.write().ctime = time;
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.device.read(buf)
|
||||
}
|
||||
|
@ -1175,6 +1175,14 @@ impl Inode for ExfatInode {
|
||||
self.inner.write().mtime = DosTimestamp::from_duration(time).unwrap_or_default();
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.inner.read().ctime.as_duration().unwrap_or_default()
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.inner.write().ctime = DosTimestamp::from_duration(time).unwrap_or_default();
|
||||
}
|
||||
|
||||
fn owner(&self) -> Result<Uid> {
|
||||
Ok(Uid::new(
|
||||
self.inner.read().fs().mount_option().fs_uid as u32,
|
||||
|
@ -61,6 +61,14 @@ impl Inode for Ext2Inode {
|
||||
self.set_mtime(time)
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.ctime()
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.set_ctime(time)
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.ino() as _
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ use super::{
|
||||
indirect_block_cache::{IndirectBlock, IndirectBlockCache},
|
||||
prelude::*,
|
||||
};
|
||||
use crate::time::clocks::RealTimeCoarseClock;
|
||||
|
||||
/// Max length of file name.
|
||||
pub const MAX_FNAME_LEN: usize = 255;
|
||||
@ -649,6 +650,7 @@ impl Inode {
|
||||
pub fn set_gid(&self, gid: u32);
|
||||
pub fn set_atime(&self, time: Duration);
|
||||
pub fn set_mtime(&self, time: Duration);
|
||||
pub fn set_ctime(&self, time: Duration);
|
||||
}
|
||||
|
||||
impl Debug for Inode {
|
||||
@ -728,6 +730,7 @@ impl Inner {
|
||||
pub fn mtime(&self) -> Duration;
|
||||
pub fn set_mtime(&mut self, time: Duration);
|
||||
pub fn ctime(&self) -> Duration;
|
||||
pub fn set_ctime(&mut self, time: Duration);
|
||||
pub fn set_device_id(&mut self, device_id: u64);
|
||||
pub fn device_id(&self) -> u64;
|
||||
pub fn sync_metadata(&self) -> Result<()>;
|
||||
@ -1653,6 +1656,11 @@ impl InodeImpl {
|
||||
self.0.read().desc.ctime
|
||||
}
|
||||
|
||||
pub fn set_ctime(&self, time: Duration) {
|
||||
let mut inner = self.0.write();
|
||||
inner.desc.ctime = time;
|
||||
}
|
||||
|
||||
pub fn read_block_sync(&self, bid: Ext2Bid, block: &Frame) -> Result<()> {
|
||||
self.0.read().read_block_sync(bid, block)
|
||||
}
|
||||
@ -1829,15 +1837,16 @@ impl TryFrom<RawInode> for InodeDesc {
|
||||
|
||||
impl InodeDesc {
|
||||
pub fn new(type_: FileType, perm: FilePerm) -> Dirty<Self> {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Dirty::new_dirty(Self {
|
||||
type_,
|
||||
perm,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
size: 0,
|
||||
atime: Duration::ZERO,
|
||||
ctime: Duration::ZERO,
|
||||
mtime: Duration::ZERO,
|
||||
atime: now,
|
||||
ctime: now,
|
||||
mtime: now,
|
||||
dtime: Duration::ZERO,
|
||||
hard_links: 1,
|
||||
blocks_count: 0,
|
||||
|
@ -314,6 +314,8 @@ impl Dentry_ {
|
||||
pub fn set_atime(&self, time: Duration);
|
||||
pub fn mtime(&self) -> Duration;
|
||||
pub fn set_mtime(&self, time: Duration);
|
||||
pub fn ctime(&self) -> Duration;
|
||||
pub fn set_ctime(&self, time: Duration);
|
||||
}
|
||||
|
||||
impl Debug for Dentry_ {
|
||||
@ -693,6 +695,8 @@ impl Dentry {
|
||||
pub fn set_atime(&self, time: Duration);
|
||||
pub fn mtime(&self) -> Duration;
|
||||
pub fn set_mtime(&self, time: Duration);
|
||||
pub fn ctime(&self) -> Duration;
|
||||
pub fn set_ctime(&self, time: Duration);
|
||||
pub fn key(&self) -> DentryKey;
|
||||
pub fn inode(&self) -> &Arc<dyn Inode>;
|
||||
pub fn is_root_of_mount(&self) -> bool;
|
||||
|
@ -10,6 +10,7 @@ use crate::{
|
||||
events::{IoEvents, Observer},
|
||||
prelude::*,
|
||||
process::{signal::Poller, Gid, Uid},
|
||||
time::clocks::RealTimeCoarseClock,
|
||||
};
|
||||
|
||||
pub struct PipeReader {
|
||||
@ -44,15 +45,16 @@ impl FileLike for PipeReader {
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::NamedPipe,
|
||||
mode: InodeMode::from_bits_truncate(0o400),
|
||||
nlinks: 1,
|
||||
@ -110,15 +112,16 @@ impl FileLike for PipeWriter {
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::NamedPipe,
|
||||
mode: InodeMode::from_bits_truncate(0o200),
|
||||
nlinks: 1,
|
||||
|
@ -81,6 +81,8 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
|
||||
fn set_atime(&self, time: Duration);
|
||||
fn mtime(&self) -> Duration;
|
||||
fn set_mtime(&self, time: Duration);
|
||||
fn ctime(&self) -> Duration;
|
||||
fn set_ctime(&self, time: Duration);
|
||||
fn fs(&self) -> Arc<dyn FileSystem>;
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
|
@ -50,6 +50,8 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
|
||||
fn set_atime(&self, time: Duration);
|
||||
fn mtime(&self) -> Duration;
|
||||
fn set_mtime(&self, time: Duration);
|
||||
fn ctime(&self) -> Duration;
|
||||
fn set_ctime(&self, time: Duration);
|
||||
fn fs(&self) -> Arc<dyn FileSystem>;
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
|
@ -71,6 +71,14 @@ impl Common {
|
||||
self.metadata.write().mtime = time;
|
||||
}
|
||||
|
||||
pub fn ctime(&self) -> Duration {
|
||||
self.metadata.read().ctime
|
||||
}
|
||||
|
||||
pub fn set_ctime(&self, time: Duration) {
|
||||
self.metadata.write().ctime = time;
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> Result<InodeMode> {
|
||||
Ok(self.metadata.read().mode)
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
|
||||
fn set_atime(&self, time: Duration);
|
||||
fn mtime(&self) -> Duration;
|
||||
fn set_mtime(&self, time: Duration);
|
||||
fn ctime(&self) -> Duration;
|
||||
fn set_ctime(&self, time: Duration);
|
||||
fn fs(&self) -> Arc<dyn FileSystem>;
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
|
@ -25,6 +25,7 @@ use crate::{
|
||||
},
|
||||
prelude::*,
|
||||
process::{signal::Poller, Gid, Uid},
|
||||
time::clocks::RealTimeCoarseClock,
|
||||
vm::vmo::Vmo,
|
||||
};
|
||||
|
||||
@ -188,12 +189,13 @@ struct InodeMeta {
|
||||
|
||||
impl InodeMeta {
|
||||
pub fn new(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
size: 0,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid,
|
||||
@ -202,12 +204,13 @@ impl InodeMeta {
|
||||
}
|
||||
|
||||
pub fn new_dir(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
size: 2,
|
||||
blocks: 1,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
mode,
|
||||
nlinks: 2,
|
||||
uid,
|
||||
@ -574,6 +577,14 @@ impl Inode for RamInode {
|
||||
self.node.write().metadata.mtime = time;
|
||||
}
|
||||
|
||||
fn ctime(&self) -> Duration {
|
||||
self.node.read().metadata.ctime
|
||||
}
|
||||
|
||||
fn set_ctime(&self, time: Duration) {
|
||||
self.node.write().metadata.ctime = time;
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.ino
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||
fs::device::{Device, DeviceType},
|
||||
prelude::*,
|
||||
process::{signal::Poller, Gid, Uid},
|
||||
time::clocks::RealTimeCoarseClock,
|
||||
vm::vmo::Vmo,
|
||||
};
|
||||
|
||||
@ -137,15 +138,16 @@ pub struct Metadata {
|
||||
|
||||
impl Metadata {
|
||||
pub fn new_dir(ino: u64, mode: InodeMode, blk_size: usize) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 2,
|
||||
blk_size,
|
||||
blocks: 1,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::Dir,
|
||||
mode,
|
||||
nlinks: 2,
|
||||
@ -156,15 +158,16 @@ impl Metadata {
|
||||
}
|
||||
|
||||
pub fn new_file(ino: u64, mode: InodeMode, blk_size: usize) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::File,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
@ -175,15 +178,16 @@ impl Metadata {
|
||||
}
|
||||
|
||||
pub fn new_symlink(ino: u64, mode: InodeMode, blk_size: usize) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::SymLink,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
@ -193,15 +197,16 @@ impl Metadata {
|
||||
}
|
||||
}
|
||||
pub fn new_device(ino: u64, mode: InodeMode, blk_size: usize, device: &dyn Device) -> Self {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::from(device.type_()),
|
||||
mode,
|
||||
nlinks: 1,
|
||||
@ -212,15 +217,16 @@ impl Metadata {
|
||||
}
|
||||
|
||||
pub fn new_socket(ino: u64, mode: InodeMode, blk_size: usize) -> Metadata {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
atime: now,
|
||||
mtime: now,
|
||||
ctime: now,
|
||||
type_: InodeType::Socket,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
@ -262,6 +268,10 @@ pub trait Inode: Any + Sync + Send {
|
||||
|
||||
fn set_mtime(&self, time: Duration);
|
||||
|
||||
fn ctime(&self) -> Duration;
|
||||
|
||||
fn set_ctime(&self, time: Duration);
|
||||
|
||||
fn page_cache(&self) -> Option<Vmo<Full>> {
|
||||
None
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ use crate::syscall::{
|
||||
umount::sys_umount,
|
||||
uname::sys_uname,
|
||||
unlink::{sys_unlink, sys_unlinkat},
|
||||
utimens::sys_utimensat,
|
||||
utimens::{sys_futimesat, sys_utime, sys_utimensat, sys_utimes},
|
||||
wait4::sys_wait4,
|
||||
waitid::sys_waitid,
|
||||
write::sys_write,
|
||||
@ -230,6 +230,7 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||
SYS_RT_SIGPENDING = 127 => sys_rt_sigpending(args[..2]);
|
||||
SYS_RT_SIGSUSPEND = 130 => sys_rt_sigsuspend(args[..2]);
|
||||
SYS_SIGALTSTACK = 131 => sys_sigaltstack(args[..2]);
|
||||
SYS_UTIME = 132 => sys_utime(args[..2]);
|
||||
SYS_STATFS = 137 => sys_statfs(args[..2]);
|
||||
SYS_FSTATFS = 138 => sys_fstatfs(args[..2]);
|
||||
SYS_GET_PRIORITY = 140 => sys_get_priority(args[..2]);
|
||||
@ -257,10 +258,12 @@ impl_syscall_nums_and_dispatch_fn! {
|
||||
SYS_EPOLL_WAIT = 232 => sys_epoll_wait(args[..4]);
|
||||
SYS_EPOLL_CTL = 233 => sys_epoll_ctl(args[..4]);
|
||||
SYS_TGKILL = 234 => sys_tgkill(args[..3]);
|
||||
SYS_UTIMES = 235 => sys_utimes(args[..2]);
|
||||
SYS_WAITID = 247 => sys_waitid(args[..5]);
|
||||
SYS_OPENAT = 257 => sys_openat(args[..4]);
|
||||
SYS_MKDIRAT = 258 => sys_mkdirat(args[..3]);
|
||||
SYS_FCHOWNAT = 260 => sys_fchownat(args[..5]);
|
||||
SYS_FUTIMESAT = 261 => sys_futimesat(args[..3]);
|
||||
SYS_FSTATAT = 262 => sys_fstatat(args[..4]);
|
||||
SYS_UNLINKAT = 263 => sys_unlinkat(args[..3]);
|
||||
SYS_RENAMEAT = 264 => sys_renameat(args[..4]);
|
||||
|
@ -2,69 +2,168 @@
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use super::{constants::MAX_FILENAME_LEN, SyscallReturn};
|
||||
use crate::{
|
||||
fs::{file_table::FileDesc, fs_resolver::FsPath},
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
path::Dentry,
|
||||
},
|
||||
prelude::*,
|
||||
syscall::constants::MAX_FILENAME_LEN,
|
||||
time::timespec_t,
|
||||
time::{clocks::RealTimeCoarseClock, timespec_t, timeval_t},
|
||||
util::{read_cstring_from_user, read_val_from_user},
|
||||
};
|
||||
|
||||
/// The 'sys_utimensat' system call sets the access and modification times of a file.
|
||||
/// The times are defined by an array of two timespec structures, where times[0] represents the access time,
|
||||
/// and times[1] represents the modification time.
|
||||
/// The `flags` argument is a bit mask that can include the following values:
|
||||
/// - `AT_SYMLINK_NOFOLLOW`: If set, the file is not dereferenced if it is a symbolic link.
|
||||
pub fn sys_utimensat(
|
||||
dirfd: FileDesc,
|
||||
path_addr: Vaddr,
|
||||
pathname_ptr: Vaddr,
|
||||
timespecs_ptr: Vaddr,
|
||||
flags: u32,
|
||||
) -> Result<SyscallReturn> {
|
||||
let path = read_cstring_from_user(path_addr, MAX_FILENAME_LEN)?;
|
||||
let (atime, mtime) = {
|
||||
let (autime, mutime) = if timespecs_ptr == 0 {
|
||||
(timespec_t::utime_now(), timespec_t::utime_now())
|
||||
} else {
|
||||
let mut timespecs_addr = timespecs_ptr;
|
||||
let autime = read_val_from_user::<timespec_t>(timespecs_addr)?;
|
||||
timespecs_addr += core::mem::size_of::<timespec_t>();
|
||||
let mutime = read_val_from_user::<timespec_t>(timespecs_addr)?;
|
||||
(autime, mutime)
|
||||
};
|
||||
|
||||
// TODO: Get current time
|
||||
let current_time: timespec_t = Default::default();
|
||||
|
||||
let atime = if autime.is_utime_omit() {
|
||||
None
|
||||
} else if autime.is_utime_now() {
|
||||
Some(current_time)
|
||||
} else {
|
||||
Some(autime)
|
||||
};
|
||||
let mtime = if mutime.is_utime_omit() {
|
||||
None
|
||||
} else if mutime.is_utime_now() {
|
||||
Some(current_time)
|
||||
} else {
|
||||
Some(mutime)
|
||||
};
|
||||
(atime, mtime)
|
||||
};
|
||||
let flags = UtimensFlags::from_bits(flags)
|
||||
.ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?;
|
||||
debug!(
|
||||
"dirfd = {}, path = {:?}, atime = {:?}, mtime = {:?}, flags = {:?}",
|
||||
dirfd, path, atime, mtime, flags
|
||||
"utimensat: dirfd: {}, pathname_ptr: {:#x}, timespecs_ptr: {:#x}, flags: {:#x}",
|
||||
dirfd, pathname_ptr, timespecs_ptr, flags
|
||||
);
|
||||
|
||||
if atime.is_none() && mtime.is_none() {
|
||||
let times = if timespecs_ptr != 0 {
|
||||
let (autime, mutime) = read_time_from_user::<timespec_t>(timespecs_ptr)?;
|
||||
if autime.is_utime_omit() && mutime.is_utime_omit() {
|
||||
return Ok(SyscallReturn::Return(0));
|
||||
}
|
||||
Some(TimeSpecPair {
|
||||
atime: autime,
|
||||
mtime: mutime,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
do_utimes(dirfd, pathname_ptr, times, flags)
|
||||
}
|
||||
|
||||
/// The 'sys_futimesat' system call sets the access and modification times of a file.
|
||||
/// Unlike 'sys_utimensat', it receives time values in the form of timeval structures,
|
||||
/// and it does not support the 'flags' argument.
|
||||
pub fn sys_futimesat(
|
||||
dirfd: FileDesc,
|
||||
pathname_ptr: Vaddr,
|
||||
timeval_ptr: Vaddr,
|
||||
) -> Result<SyscallReturn> {
|
||||
debug!(
|
||||
"futimesat: dirfd: {}, pathname_ptr: {:#x}, timeval_ptr: {:#x}",
|
||||
dirfd, pathname_ptr, timeval_ptr
|
||||
);
|
||||
do_futimesat(dirfd, pathname_ptr, timeval_ptr)
|
||||
}
|
||||
|
||||
/// The 'sys_utimes' system call sets the access and modification times of a file.
|
||||
/// It receives time values in the form of timeval structures like 'sys_futimesat',
|
||||
/// but it uses the current working directory as the base directory.
|
||||
pub fn sys_utimes(pathname_ptr: Vaddr, timeval_ptr: Vaddr) -> Result<SyscallReturn> {
|
||||
debug!(
|
||||
"utimes: pathname_ptr: {:#x}, timeval_ptr: {:#x}",
|
||||
pathname_ptr, timeval_ptr
|
||||
);
|
||||
do_futimesat(AT_FDCWD, pathname_ptr, timeval_ptr)
|
||||
}
|
||||
|
||||
/// The 'sys_utime' system call is similar to 'sys_utimes' but uses the older 'utimbuf' structure to specify times.
|
||||
pub fn sys_utime(pathname_ptr: Vaddr, utimbuf_ptr: Vaddr) -> Result<SyscallReturn> {
|
||||
debug!(
|
||||
"utime: pathname_ptr: {:#x}, utimbuf_ptr: {:#x}",
|
||||
pathname_ptr, utimbuf_ptr
|
||||
);
|
||||
let times = if utimbuf_ptr != 0 {
|
||||
let utimbuf = read_val_from_user::<Utimbuf>(utimbuf_ptr)?;
|
||||
let atime = timespec_t {
|
||||
sec: utimbuf.actime,
|
||||
nsec: 0,
|
||||
};
|
||||
let mtime = timespec_t {
|
||||
sec: utimbuf.modtime,
|
||||
nsec: 0,
|
||||
};
|
||||
Some(TimeSpecPair { atime, mtime })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
do_utimes(AT_FDCWD, pathname_ptr, times, 0)
|
||||
}
|
||||
|
||||
// Structure to hold access and modification times
|
||||
#[derive(Debug)]
|
||||
struct TimeSpecPair {
|
||||
atime: timespec_t,
|
||||
mtime: timespec_t,
|
||||
}
|
||||
|
||||
/// This struct is corresponding to the `utimbuf` struct in Linux.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default, Copy, Clone, Pod)]
|
||||
struct Utimbuf {
|
||||
actime: i64,
|
||||
modtime: i64,
|
||||
}
|
||||
|
||||
fn vfs_utimes(dentry: &Arc<Dentry>, times: Option<TimeSpecPair>) -> Result<SyscallReturn> {
|
||||
let (atime, mtime) = match times {
|
||||
Some(times) => {
|
||||
if !times.atime.is_valid() || !times.mtime.is_valid() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid time")
|
||||
}
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
let atime = if times.atime.is_utime_omit() {
|
||||
dentry.atime()
|
||||
} else if times.atime.is_utime_now() {
|
||||
now
|
||||
} else {
|
||||
Duration::from(times.atime)
|
||||
};
|
||||
let mtime = if times.mtime.is_utime_omit() {
|
||||
dentry.mtime()
|
||||
} else if times.mtime.is_utime_now() {
|
||||
now
|
||||
} else {
|
||||
Duration::from(times.mtime)
|
||||
};
|
||||
(atime, mtime)
|
||||
}
|
||||
None => {
|
||||
let now = RealTimeCoarseClock::get().read_time();
|
||||
(now, now)
|
||||
}
|
||||
};
|
||||
|
||||
// Update times
|
||||
dentry.set_atime(atime);
|
||||
dentry.set_mtime(mtime);
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
// Common function to handle updating file times, supporting both fd and path based operations
|
||||
fn do_utimes(
|
||||
dirfd: FileDesc,
|
||||
pathname_ptr: Vaddr,
|
||||
times: Option<TimeSpecPair>,
|
||||
flags: u32,
|
||||
) -> Result<SyscallReturn> {
|
||||
let flags = UtimensFlags::from_bits(flags)
|
||||
.ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?;
|
||||
|
||||
let pathname = if pathname_ptr == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
let cstring = read_cstring_from_user(pathname_ptr, MAX_FILENAME_LEN)?;
|
||||
cstring.to_string_lossy().into_owned()
|
||||
};
|
||||
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())?;
|
||||
// Determine the file system path and the corresponding entry
|
||||
let fs_path = FsPath::new(dirfd, pathname.as_ref())?;
|
||||
let fs = current.fs().read();
|
||||
if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) {
|
||||
fs.lookup_no_follow(&fs_path)?
|
||||
@ -72,13 +171,35 @@ pub fn sys_utimensat(
|
||||
fs.lookup(&fs_path)?
|
||||
}
|
||||
};
|
||||
if let Some(time) = atime {
|
||||
dentry.set_atime(Duration::from(time));
|
||||
|
||||
vfs_utimes(&dentry, times)
|
||||
}
|
||||
|
||||
// Sets the access and modification times for a file,
|
||||
// specified by a pathname relative to the directory file descriptor `dirfd`.
|
||||
fn do_futimesat(dirfd: FileDesc, pathname_ptr: Vaddr, timeval_ptr: Vaddr) -> Result<SyscallReturn> {
|
||||
let times = if timeval_ptr != 0 {
|
||||
let (autime, mutime) = read_time_from_user::<timeval_t>(timeval_ptr)?;
|
||||
if autime.usec >= 1000000 || autime.usec < 0 || mutime.usec >= 1000000 || mutime.usec < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "Invalid time");
|
||||
}
|
||||
if let Some(time) = mtime {
|
||||
dentry.set_mtime(Duration::from(time));
|
||||
}
|
||||
Ok(SyscallReturn::Return(0))
|
||||
let (autime, mutime) = (timespec_t::from(autime), timespec_t::from(mutime));
|
||||
Some(TimeSpecPair {
|
||||
atime: autime,
|
||||
mtime: mutime,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
do_utimes(dirfd, pathname_ptr, times, 0)
|
||||
}
|
||||
|
||||
fn read_time_from_user<T: Pod>(time_ptr: Vaddr) -> Result<(T, T)> {
|
||||
let mut time_addr = time_ptr;
|
||||
let autime = read_val_from_user::<T>(time_addr)?;
|
||||
time_addr += core::mem::size_of::<T>();
|
||||
let mutime = read_val_from_user::<T>(time_addr)?;
|
||||
Ok((autime, mutime))
|
||||
}
|
||||
|
||||
trait UtimeExt {
|
||||
@ -87,6 +208,7 @@ trait UtimeExt {
|
||||
fn utime_omit() -> Self;
|
||||
fn is_utime_now(&self) -> bool;
|
||||
fn is_utime_omit(&self) -> bool;
|
||||
fn is_valid(&self) -> bool;
|
||||
}
|
||||
|
||||
impl UtimeExt for timespec_t {
|
||||
@ -111,6 +233,12 @@ impl UtimeExt for timespec_t {
|
||||
fn is_utime_omit(&self) -> bool {
|
||||
self.nsec == UTIME_OMIT
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
self.nsec == UTIME_OMIT
|
||||
|| self.nsec == UTIME_NOW
|
||||
|| (self.nsec >= 0 && self.nsec <= 999_999_999)
|
||||
}
|
||||
}
|
||||
|
||||
const UTIME_NOW: i64 = (1i64 << 30) - 1i64;
|
||||
|
@ -43,6 +43,15 @@ impl From<Duration> for timespec_t {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<timeval_t> for timespec_t {
|
||||
fn from(timeval: timeval_t) -> timespec_t {
|
||||
let sec = timeval.sec;
|
||||
let nsec = timeval.usec * 1000;
|
||||
debug_assert!(sec >= 0); // nsec >= 0 always holds
|
||||
timespec_t { sec, nsec }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<timespec_t> for Duration {
|
||||
fn from(timespec: timespec_t) -> Self {
|
||||
Duration::new(timespec.sec as u64, timespec.nsec as u32)
|
||||
|
@ -37,6 +37,7 @@ TESTS ?= \
|
||||
unlink_test \
|
||||
vdso_clock_gettime_test \
|
||||
write_test \
|
||||
utimes_test \
|
||||
# The end of the list
|
||||
|
||||
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
|
||||
|
8
regression/syscall_test/blocklists.exfat/utimes_test
Normal file
8
regression/syscall_test/blocklists.exfat/utimes_test
Normal file
@ -0,0 +1,8 @@
|
||||
UtimesTest.OnFile
|
||||
UtimesTest.OnDir
|
||||
FutimesatTest.OnAbsPath
|
||||
FutimesatTest.OnRelPath
|
||||
UtimensatTest.OnAbsPath
|
||||
UtimensatTest.OnRelPath
|
||||
UtimeTest.ZeroAtimeandMtime
|
||||
Utimensat.NullPath
|
0
regression/syscall_test/blocklists/utimes_test
Normal file
0
regression/syscall_test/blocklists/utimes_test
Normal file
Reference in New Issue
Block a user