Write tests and fixes for utimensat()

This commit is contained in:
Justine Tunney 2022-06-17 02:43:00 -07:00
parent c06ffd458c
commit d0d9cd38c5
8 changed files with 129 additions and 41 deletions

View file

@ -21,10 +21,11 @@
/**
* Sets atime/mtime on file descriptor.
*
* This function is the same as `utimensat(fd, 0, ts, 0)`.
*
* @param ts is atime/mtime, or null for current time
* @note better than microsecond precision on most platforms
* @see fstat() for reading timestamps
* @raise ENOSYS on RHEL5
*/
int futimens(int fd, const struct timespec ts[hasatleast 2]) {
return utimensat(fd, NULL, ts, 0);
int futimens(int fd, const struct timespec ts[2]) {
return utimensat(fd, 0, ts, 0);
}

View file

@ -123,6 +123,7 @@ i32 sys_clock_gettime_xnu(i32, struct timespec *) hidden;
i32 sys_fstat(i32, struct stat *) hidden;
i32 sys_fstatat(i32, const char *, struct stat *, i32) hidden;
i32 sys_futimes(i32, const struct timeval *) hidden;
i32 sys_futimens(i32, const struct timespec *) hidden;
i32 sys_futimesat(i32, const char *, const struct timeval *) hidden;
i32 sys_getitimer(i32, struct itimerval *) hidden;
i32 sys_getrlimit(i32, struct rlimit *) hidden;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/fmt/conv.h"
#include "libc/nt/createfile.h"
@ -34,14 +35,25 @@
textwindows int sys_utimensat_nt(int dirfd, const char *path,
const struct timespec ts[2], int flags) {
int i, rc;
int64_t fh;
int64_t fh, closeme;
uint16_t path16[PATH_MAX];
struct NtFileTime ft[2], *ftp[2];
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0)) == -1) {
return __winerr();
if (path) {
if (__mkntpathat(dirfd, path, 0, path16) == -1) return -1;
if ((fh = CreateFile(path16, kNtFileWriteAttributes, kNtFileShareRead, NULL,
kNtOpenExisting, kNtFileAttributeNormal, 0)) != -1) {
closeme = fh;
} else {
return __winerr();
}
} else if (__isfdkind(dirfd, kFdFile)) {
fh = g_fds.p[dirfd].handle;
closeme = -1;
} else {
return ebadf();
}
if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
GetSystemTimeAsFileTime(ft);
}
@ -65,6 +77,10 @@ textwindows int sys_utimensat_nt(int dirfd, const char *path,
} else {
rc = __winerr();
}
CloseHandle(fh);
if (closeme != -1) {
CloseHandle(fh);
}
return rc;
}

View file

@ -27,22 +27,24 @@ int sys_utimensat(int dirfd, const char *path, const struct timespec ts[2],
int flags) {
int rc, olderr;
struct timeval tv[2];
if (weaken(__zipos_notat) && weaken(__zipos_notat)(dirfd, path) == -1) {
return -1; /* TODO(jart): implement me */
}
if (!IsXnu()) {
olderr = errno;
rc = __sys_utimensat(dirfd, path, ts, flags);
if ((rc == -1 && errno == ENOSYS) && dirfd == AT_FDCWD && !flags) {
errno = olderr;
if (ts) {
tv[0].tv_sec = ts[0].tv_sec;
tv[0].tv_usec = ts[0].tv_nsec / 1000;
tv[1].tv_sec = ts[1].tv_sec;
tv[1].tv_usec = ts[1].tv_nsec / 1000;
rc = sys_utimes(path, tv);
} else {
rc = sys_utimes(path, NULL);
if (!path && (IsFreebsd() || IsNetbsd() || IsOpenbsd())) {
rc = sys_futimens(dirfd, ts);
} else {
olderr = errno;
rc = __sys_utimensat(dirfd, path, ts, flags);
// TODO(jart): How does RHEL5 do futimes()?
if (rc == -1 && errno == ENOSYS && path) {
errno = olderr;
if (ts) {
tv[0].tv_sec = ts[0].tv_sec;
tv[0].tv_usec = ts[0].tv_nsec / 1000;
tv[1].tv_sec = ts[1].tv_sec;
tv[1].tv_usec = ts[1].tv_nsec / 1000;
rc = sys_utimes(path, tv);
} else {
rc = sys_utimes(path, NULL);
}
}
}
return rc;

View file

@ -24,25 +24,45 @@
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/errfuns.h"
#include "libc/zipos/zipos.internal.h"
/**
* Sets atime/mtime on file, the modern way.
*
* This is two functions in one. If `path` is null then this function
* becomes the same as `futimens(dirfd, ts)`.
*
* XNU and RHEL5 only have microsecond accuracy and there's no `dirfd`
* relative support.
*
* @param dirfd should AT_FDCWD
* @param ts is atime/mtime, or null for current time
* @param flags can have AT_SYMLINK_NOFOLLOW
* @note no xnu/rhel5 support if dirfdAT_FDCWDflags0
* @raise ENOSYS on RHEL5 if path is NULL
* @raise EINVAL if flags had unrecognized bits
* @raise EINVAL if path is NULL and flags has AT_SYMLINK_NOFOLLOW
* @raise EBADF if dirfd isn't a valid fd or AT_FDCWD
* @raise EFAULT if path or ts memory was invalid
* @raise EROFS if file system is read-only
* @raise ENAMETOOLONG
* @raise EPERM
* @see futimens()
* @asyncsignalsafe
*/
int utimensat(int dirfd, const char *path, const struct timespec ts[2],
int flags) {
int rc;
char buf[12];
if (IsAsan() && (!__asan_is_valid(path, 1) ||
if (IsAsan() && ((dirfd == AT_FDCWD && !__asan_is_valid(path, 1)) ||
(ts && (!__asan_is_valid_timespec(ts + 0) ||
!__asan_is_valid_timespec(ts + 1))))) {
rc = efault();
rc = efault(); // bad memory
} else if ((flags & ~AT_SYMLINK_NOFOLLOW)) {
rc = einval(); // unsupported flag
} else if (!path && flags) {
rc = einval(); // futimens() doesn't take flags
} else if (weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
STRACE("zipos mkdirat not supported yet");
} else if (!IsWindows()) {

View file

@ -7,12 +7,4 @@
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/utime.h"
#include "libc/time/time.h"
#define st_atime st_atim.tv_sec
#define st_atime_nsec st_atim.tv_nsec
#define st_mtime st_mtim.tv_sec
#define st_mtime_nsec st_mtim.tv_nsec
#define st_ctime st_ctim.tv_sec
#define st_ctime_nsec st_ctim.tv_nsec
#endif

View file

@ -0,0 +1,61 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
char testlib_enable_tmp_setup_teardown;
TEST(utimensat, test) {
struct stat st;
struct timespec ts[2] = {
{1655455857}, // atim: Fri Jun 17 2022 08:50:57 GMT+0000
{827727928}, // mtim: Mon Mar 25 1996 04:25:28 GMT+0000
};
EXPECT_SYS(0, 0, touch("boop", 0644));
EXPECT_SYS(0, 0, utimensat(AT_FDCWD, "boop", ts, 0));
EXPECT_SYS(0, 0, stat("boop", &st));
EXPECT_EQ(1655455857, st.st_atim.tv_sec);
EXPECT_EQ(827727928, st.st_mtim.tv_sec);
}
TEST(futimens, test) {
if (IsLinux() && !__is_linux_2_6_23()) {
// TODO(jart): How does RHEL5 do futimes()?
return;
}
struct stat st;
struct timespec ts[2] = {
{1655455857}, // atim: Fri Jun 17 2022 08:50:57 GMT+0000
{827727928}, // mtim: Mon Mar 25 1996 04:25:28 GMT+0000
};
EXPECT_SYS(0, 0, touch("boop", 0644));
EXPECT_SYS(0, 3, open("boop", O_RDWR));
EXPECT_SYS(0, 0, futimens(3, ts));
EXPECT_SYS(0, 0, fstat(3, &st));
EXPECT_SYS(0, 0, close(3));
EXPECT_EQ(1655455857, st.st_atim.tv_sec);
EXPECT_EQ(827727928, st.st_mtim.tv_sec);
}

View file

@ -317,8 +317,3 @@ TEST(fmt, regress) {
"User-Agent: hurl/1.o (https://github.com/jart/cosmopolitan)\r\n",
buf);
}
/* TEST(fmt, funchar) { */
/* /\* TODO(jart): fix this *\/ */
/* ASSERT_STREQ("'\\200'", gc(xasprintf("%`'c", 0200))); */
/* } */