diff --git a/src/header/fcntl/redox.rs b/src/header/fcntl/redox.rs index 0d54c33883..bcdbed7305 100644 --- a/src/header/fcntl/redox.rs +++ b/src/header/fcntl/redox.rs @@ -30,7 +30,9 @@ pub const O_NDELAY: c_int = O_NONBLOCK; // Flags for capability based "at" functions pub const AT_FDCWD: c_int = -100; +// fchmodat, fchownat, fstatat, utimensat pub const AT_SYMLINK_NOFOLLOW: c_int = 0x200; +// unlinkat pub const AT_REMOVEDIR: c_int = 0x200; // Used by linkat() pub const AT_SYMLINK_FOLLOW: c_int = 0x2000; diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index a5326029c4..25a0478cbc 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -304,12 +304,13 @@ impl Pal for Sys { .map(|n| n as u32) } - unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()> { - e_raw(unsafe { syscall!(UTIMENSAT, fd, ptr::null::(), times, 0) }).map(|_| ()) - } - - unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()> { - e_raw(unsafe { syscall!(UTIMENSAT, AT_FDCWD, path.as_ptr(), times, 0) }).map(|_| ()) + unsafe fn utimensat( + dirfd: c_int, + path: CStr, + times: *const timespec, + flag: c_int, + ) -> Result<()> { + e_raw(unsafe { syscall!(UTIMENSAT, dirfd, path.as_ptr(), times, flag) }).map(|_| ()) } fn getcwd(mut buf: Out<[u8]>) -> Result<()> { diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index 57363f0a8e..c3cabc08b1 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -149,10 +149,23 @@ pub trait Pal { unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result; /// Platform implementation of [`futimens()`](crate::header::sys_stat::futimens) from [`sys/stat.h`](crate::header::sys_stat). - unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()>; + unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()> { + unsafe { Self::utimensat(fd, c"".into(), times, AT_EMPTY_PATH) } + } - /// Platform implementation of `utimens()` (TODO) from [`sys/stat.h`](crate::header::sys_stat). - unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()>; + /// Platform implementation of [`utimens()`](crate::header::unistd::utimens) (from [`sys/stat.h`](crate::header::sys_stat). + /// This is an extension of POSIX. + unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()> { + unsafe { Self::utimensat(AT_FDCWD, path, times, 0) } + } + + /// Platform implementation of [`utimensat()`](crate::header::unistd::utimensat) (from [`sys/stat.h`](crate::header::sys_stat). + unsafe fn utimensat( + dirfd: c_int, + path: CStr, + times: *const timespec, + flag: c_int, + ) -> Result<()>; /// Platform implementation of [`getcwd()`](crate::header::unistd::getcwd) from [`unistd.h`](crate::header::unistd). fn getcwd(buf: Out<[u8]>) -> Result<()>; diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index dbd89ee5a1..ca136144d2 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -543,14 +543,28 @@ impl Pal for Sys { Ok(unsafe { redox_rt::sys::sys_futex_wake(addr, num) }?) } - unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()> { - (unsafe { libredox::futimens(fd as usize, times) })?; - Ok(()) - } + unsafe fn utimensat( + dirfd: c_int, + path: CStr, + times: *const timespec, + flag: c_int, + ) -> Result<()> { + let mut path = path.to_str().map_err(|_| Errno(ENOENT))?; + if path.is_empty() { + if flag & AT_EMPTY_PATH == AT_EMPTY_PATH { + if dirfd == AT_FDCWD { + path = "."; + } else { + return Ok(unsafe { libredox::futimens(dirfd as usize, times) }?); + } + } else { + // If the path is empty but `AT_EMPTY_PATH` is **not** set, bail out. + return Err(Errno(ENOENT)); + } + } - unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()> { - let file = File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC)?; - unsafe { Self::futimens(*file, times) } + let file = openat2(dirfd, path, flag, fcntl::O_PATH | fcntl::O_CLOEXEC)?; + Ok(unsafe { libredox::futimens(*file as usize, times) }?) } fn getcwd(buf: Out<[u8]>) -> Result<()> { @@ -1491,7 +1505,7 @@ impl Pal for Sys { // \n e.g. "Redox" // \n e.g. "0.9.0" // \n e.g. "x86_64" - // \n e.g. "yyyy-mm-ddThh:mm:ssZ" + // \n e.g. " // A future file format might add the domainname.