diff --git a/libc/calls/_timespec_get.c b/libc/calls/_timespec_get.c index a7b433347..1bd948775 100644 --- a/libc/calls/_timespec_get.c +++ b/libc/calls/_timespec_get.c @@ -26,6 +26,7 @@ * @param ts receives `CLOCK_REALTIME` timestamp * @param base must be `TIME_UTC` * @return `base` on success, or `0` on failure + * @see _timespec_real() */ int timespec_get(struct timespec *ts, int base) { if (base == TIME_UTC && !clock_gettime(CLOCK_REALTIME, ts)) { diff --git a/libc/calls/_timespec_mono.c b/libc/calls/_timespec_mono.c index 376966d81..b0e46c612 100644 --- a/libc/calls/_timespec_mono.c +++ b/libc/calls/_timespec_mono.c @@ -20,6 +20,13 @@ #include "libc/calls/struct/timespec.h" #include "libc/sysv/consts/clock.h" +/** + * Returns current monotonic time. + * + * This function uses a `CLOCK_MONOTONIC` clock and never fails. + * + * @see _timespec_real() + */ struct timespec _timespec_mono(void) { struct timespec ts; _npassert(!clock_gettime(CLOCK_MONOTONIC_FAST, &ts)); diff --git a/libc/calls/_timespec_real.c b/libc/calls/_timespec_real.c index 2e49004c6..f54a11a70 100644 --- a/libc/calls/_timespec_real.c +++ b/libc/calls/_timespec_real.c @@ -20,6 +20,15 @@ #include "libc/calls/struct/timespec.h" #include "libc/sysv/consts/clock.h" +/** + * Returns current time. + * + * This function uses a `CLOCK_REALTIME` clock and never fails. Unlike + * clock_gettime() or timespec_real() this interface avoids the use of + * pointers which lets time handling code become more elegant. + * + * @see _timespec_mono() + */ struct timespec _timespec_real(void) { struct timespec ts; _npassert(!clock_gettime(CLOCK_REALTIME_FAST, &ts)); diff --git a/libc/calls/_timespec_tomicros.c b/libc/calls/_timespec_tomicros.c index 72d591858..2155be4c0 100644 --- a/libc/calls/_timespec_tomicros.c +++ b/libc/calls/_timespec_tomicros.c @@ -21,6 +21,17 @@ /** * Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding. + * + * This function uses ceiling rounding. For example, if `ts` is one + * nanosecond, then one microsecond will be returned. Ceil rounding + * is needed by many interfaces, e.g. setitimer(), because the zero + * timestamp has a special meaning. + * + * This function also detects overflow in which case `INT64_MAX` or + * `INT64_MIN` may be returned. The `errno` variable isn't changed. + * + * @return 64-bit scalar holding microseconds since epoch + * @see _timespec_totimeval() */ int64_t _timespec_tomicros(struct timespec ts) { int64_t us; diff --git a/libc/calls/_timespec_tomillis.c b/libc/calls/_timespec_tomillis.c index 31a2ecda8..7fb07dc35 100644 --- a/libc/calls/_timespec_tomillis.c +++ b/libc/calls/_timespec_tomillis.c @@ -21,6 +21,16 @@ /** * Reduces `ts` from 1e-9 to 1e-3 granularity w/ ceil rounding. + * + * This function uses ceiling rounding. For example, if `ts` is one + * nanosecond, then one millisecond will be returned. Ceil rounding + * is needed by many interfaces, e.g. setitimer(), because the zero + * timestamp has a special meaning. + * + * This function also detects overflow in which case `INT64_MAX` or + * `INT64_MIN` may be returned. The `errno` variable isn't changed. + * + * @return 64-bit scalar milliseconds since epoch */ int64_t _timespec_tomillis(struct timespec ts) { int64_t ms; diff --git a/libc/calls/_timespec_tonanos.c b/libc/calls/_timespec_tonanos.c index 369b66ed0..6edac6ac1 100644 --- a/libc/calls/_timespec_tonanos.c +++ b/libc/calls/_timespec_tonanos.c @@ -20,7 +20,12 @@ #include "libc/limits.h" /** - * Converts timespec interval to nanoseconds. + * Converts timespec to scalar. + * + * This function will detect overflow in which case `INT64_MAX` or + * `INT64_MIN` may be returned. The `errno` variable isn't changed. + * + * @return 64-bit integer holding nanoseconds since epoch */ int64_t _timespec_tonanos(struct timespec x) { int64_t ns; diff --git a/libc/calls/_timespec_totimeval.c b/libc/calls/_timespec_totimeval.c index 32bb83e31..5786513a5 100644 --- a/libc/calls/_timespec_totimeval.c +++ b/libc/calls/_timespec_totimeval.c @@ -20,6 +20,14 @@ /** * Reduces `ts` from 1e-9 to 1e-6 granularity w/ ceil rounding. + * + * This function uses ceiling rounding. For example, if `ts` is one + * nanosecond, then one microsecond will be returned. Ceil rounding + * is needed by many interfaces, e.g. setitimer(), because the zero + * timestamp has a special meaning. + * + * @return microseconds since epoch + * @see _timespec_tomicros() */ struct timeval _timespec_totimeval(struct timespec ts) { if (ts.tv_nsec < 1000000000 - 999) { diff --git a/libc/calls/_timeval_totimespec.c b/libc/calls/_timeval_totimespec.c index 65a992bb4..e508155fd 100644 --- a/libc/calls/_timeval_totimespec.c +++ b/libc/calls/_timeval_totimespec.c @@ -18,6 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timeval.h" +/** + * Coerces `tv` from 1e-6 to 1e-9 granularity. + */ struct timespec _timeval_totimespec(struct timeval tv) { return (struct timespec){tv.tv_sec, tv.tv_usec * 1000}; } diff --git a/libc/calls/clock_nanosleep.c b/libc/calls/clock_nanosleep.c index 6eb147f00..3a587725c 100644 --- a/libc/calls/clock_nanosleep.c +++ b/libc/calls/clock_nanosleep.c @@ -65,14 +65,10 @@ * of your preferred platforms to see what other clocks might work * @param flags can be 0 for relative and `TIMER_ABSTIME` for absolute * @param req can be a relative or absolute time, depending on `flags` - * @param rem will be updated with the unslept time only when `flags` - * is zero, otherwise `rem` is ignored; when this call completes, - * that means `rem` will be set to `{0, 0}`, and shall only have - * something else when -1 is returned with `EINTR`, which means - * there was a signal delivery that happened mid-sleep and `rem` - * reflects (poorly) how much remaining time was left over, in - * case the caller wishes to retry the sleep operation, noting - * this isn't recommended since relative timestamps can drift + * @param rem shall be updated with the remainder of unslept time when + * (1) it's non-null; (2) `flags` is 0; and (3) -1 w/ `EINTR` is + * returned; if this function returns 0 then `rem` is undefined; + * if flags is `TIMER_ABSTIME` then `rem` is ignored * @return 0 on success, or -1 w/ errno * @raise EINTR when a signal got delivered while we were waiting * @raise ENOTSUP if `clock` is known but we can't use it here @@ -107,14 +103,6 @@ int clock_nanosleep(int clock, int flags, const struct timespec *req, rc = sys_clock_nanosleep_nt(clock, flags, req, rem); } - // Linux Kernel doesn't change the remainder value on success, but - // some kernels like OpenBSD will. POSIX doesn't specify the Linux - // behavior. So we polyfill it here. - if (!rc && !flags && rem) { - rem->tv_sec = 0; - rem->tv_nsec = 0; - } - STRACE("clock_nanosleep(%s, %s, %s, [%s]) → %d% m", DescribeClockName(clock), DescribeSleepFlags(flags), flags, DescribeTimespec(0, req), DescribeTimespec(rc, rem), rc); diff --git a/libc/calls/futimens.c b/libc/calls/futimens.c index 952f8a9b5..d53957468 100644 --- a/libc/calls/futimens.c +++ b/libc/calls/futimens.c @@ -26,8 +26,8 @@ * Changes access/modified time on open file, the modern way. * * XNU only has microsecond (1e-6) accuracy. Windows only has - * hectonanosecond (1e-7) accuracy. RHEL5 is somewhat broken so utimes() - * is recommended if portability to old versions of Linux is desired. + * hectonanosecond (1e-7) accuracy. RHEL5 (Linux c. 2007) doesn't + * support this system call. * * @param fd is file descriptor of file whose timestamps will change * @param ts is {access, modified} timestamps, or null for current time diff --git a/libc/calls/futimes.c b/libc/calls/futimes.c index 6e2fd03db..83547baee 100644 --- a/libc/calls/futimes.c +++ b/libc/calls/futimes.c @@ -16,26 +16,45 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/asan.internal.h" +#include "libc/calls/struct/itimerval.internal.h" #include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timespec.internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" +#include "libc/sysv/errfuns.h" /** * Sets atime/mtime on file descriptor. * * @param ts is atime/mtime, or null for current time - * @note better than microsecond precision on most platforms - * @see fstat() for reading timestamps + * @return 0 on success, or -1 w/ errno + * @raise ENOTSUP if `fd` is on zip filesystem + * @raise EBADF if `fd` isn't an open file descriptor + * @raise EPERM if pledge() is in play without `fattr` promise + * @raise EINVAL if `tv` specifies a microsecond value that's out of range + * @raise ENOSYS on RHEL5 or bare metal + * @see futimens() for modern version + * @asyncsignalsafe + * @threadsafe */ int futimes(int fd, const struct timeval tv[2]) { - // TODO(jart): does this work on rhel5? what's up with this? + int rc; struct timespec ts[2]; + if (tv) { ts[0].tv_sec = tv[0].tv_sec; ts[0].tv_nsec = tv[0].tv_usec * 1000; ts[1].tv_sec = tv[1].tv_sec; ts[1].tv_nsec = tv[1].tv_usec * 1000; - return utimensat(fd, NULL, ts, 0); + rc = __utimens(fd, 0, ts, 0); } else { - return utimensat(fd, NULL, NULL, 0); + rc = __utimens(fd, 0, 0, 0); } + + STRACE("futimes(%d, {%s, %s}) → %d% m", fd, DescribeTimeval(0, tv), + DescribeTimeval(0, tv ? tv + 1 : 0), rc); + + return rc; } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index b39620b82..12184ada2 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -30,13 +30,11 @@ * Sleeps for relative amount of time. * * @param req is the duration of time we should sleep - * @param rem if non-NULL will receive the amount of time that wasn't - * slept because a signal was delivered. If no signal's delivered - * then this value will be set to `{0, 0}`. It's also fine to set - * this value to the same pointer as `req` + * @param rem if non-null will be updated with the remainder of unslept + * time when -1 w/ `EINTR` is returned otherwise `rem` is undefined * @return 0 on success, or -1 w/ errno * @raise EINVAL if `req->tv_nsec ∉ [0,1000000000)` - * @raise EINTR if a signal was delivered, and `rem` is updated + * @raise EINTR if a signal was delivered and `rem` is updated * @raise EFAULT if `req` is NULL or `req` / `rem` is a bad pointer * @raise ENOSYS on bare metal * @see clock_nanosleep() @@ -58,19 +56,11 @@ int nanosleep(const struct timespec *req, struct timespec *rem) { } else if (IsXnu()) { rc = sys_nanosleep_xnu(req, rem); } else if (IsMetal()) { - rc = enosys(); /* TODO: Sleep on Metal */ + rc = enosys(); } else { rc = sys_nanosleep_nt(req, rem); } - // Linux Kernel doesn't change the remainder value on success, but - // some kernels like OpenBSD will. POSIX doesn't specify the Linux - // behavior. So we polyfill it here. - if (!rc && rem) { - rem->tv_sec = 0; - rem->tv_nsec = 0; - } - #ifdef SYSDEBUG if (!__time_critical) { STRACE("nanosleep(%s, [%s]) → %d% m", DescribeTimespec(rc, req), diff --git a/libc/calls/setitimer.c b/libc/calls/setitimer.c index d7538c2f0..725ad24c8 100644 --- a/libc/calls/setitimer.c +++ b/libc/calls/setitimer.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/intrin/strace.internal.h" #include "libc/calls/struct/itimerval.h" #include "libc/calls/struct/itimerval.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/errfuns.h" #include "libc/time/time.h" @@ -29,28 +29,28 @@ * * Raise SIGALRM every 1.5s: * - * CHECK_NE(-1, sigaction(SIGALRM, - * &(struct sigaction){.sa_sigaction = _missingno}, - * NULL)); - * CHECK_NE(-1, setitimer(ITIMER_REAL, - * &(const struct itimerval){{1, 500000}, - * {1, 500000}}, - * NULL)); + * sigaction(SIGALRM, + * &(struct sigaction){.sa_sigaction = _missingno}, + * NULL); + * setitimer(ITIMER_REAL, + * &(const struct itimerval){{1, 500000}, + * {1, 500000}}, + * NULL); * * Set single-shot 50ms timer callback to interrupt laggy connect(): * - * CHECK_NE(-1, sigaction(SIGALRM, - * &(struct sigaction){.sa_sigaction = _missingno, - * .sa_flags = SA_RESETHAND}, - * NULL)); - * CHECK_NE(-1, setitimer(ITIMER_REAL, - * &(const struct itimerval){{0, 0}, {0, 50000}}, - * NULL)); + * sigaction(SIGALRM, + * &(struct sigaction){.sa_sigaction = _missingno, + * .sa_flags = SA_RESETHAND}, + * NULL); + * setitimer(ITIMER_REAL, + * &(const struct itimerval){{0, 0}, {0, 50000}}, + * NULL); * if (connect(...) == -1 && errno == EINTR) { ... } * * Disarm timer: * - * CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL)); + * setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL); * * Be sure to check for EINTR on your i/o calls, for best low latency. * @@ -81,6 +81,7 @@ int setitimer(int which, const struct itimerval *newvalue, rc = sys_setitimer_nt(which, newvalue, oldvalue); } +#ifdef SYSDEBUG if (newvalue && oldvalue) { STRACE("setitimer(%d, " "{{%'ld, %'ld}, {%'ld, %'ld}}, " @@ -100,6 +101,7 @@ int setitimer(int which, const struct itimerval *newvalue, } else { STRACE("setitimer(%d, NULL, NULL) → %d% m", which, rc); } +#endif return rc; } diff --git a/libc/calls/sys_utimes_nt.c b/libc/calls/sys_utimes_nt.c deleted file mode 100644 index 9d1fd71c6..000000000 --- a/libc/calls/sys_utimes_nt.c +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- 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 2021 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/struct/timespec.h" -#include "libc/calls/struct/timespec.internal.h" -#include "libc/calls/struct/timeval.h" -#include "libc/calls/struct/timeval.internal.h" -#include "libc/sysv/consts/at.h" - -textwindows int sys_utimes_nt(const char *path, const struct timeval tv[2]) { - struct timespec ts[2]; - if (tv) { - ts[0].tv_sec = tv[0].tv_sec; - ts[0].tv_nsec = tv[0].tv_usec * 1000; - ts[1].tv_sec = tv[1].tv_sec; - ts[1].tv_nsec = tv[1].tv_usec * 1000; - return sys_utimensat_nt(AT_FDCWD, path, ts, 0); - } else { - return sys_utimensat_nt(AT_FDCWD, path, NULL, 0); - } -} diff --git a/libc/calls/utime.c b/libc/calls/utime.c index ac8c97d69..363f6eff7 100644 --- a/libc/calls/utime.c +++ b/libc/calls/utime.c @@ -17,16 +17,16 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timeval.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/at.h" #include "libc/time/struct/utimbuf.h" /** * Changes last accessed/modified times on file. * * @param times if NULL means now - * @return 0 on success or -1 w/ errno + * @return 0 on success, or -1 w/ errno + * @see utimensat() for modern version * @asyncsignalsafe + * @threadsafe */ int utime(const char *path, const struct utimbuf *times) { struct timeval tv[2]; diff --git a/libc/calls/utimens.c b/libc/calls/utimens.c index 5be86ea96..a4830ce37 100644 --- a/libc/calls/utimens.c +++ b/libc/calls/utimens.c @@ -21,6 +21,8 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/struct/timespec.internal.h" +#include "libc/calls/struct/timeval.internal.h" +#include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" @@ -46,6 +48,8 @@ int __utimens(int fd, const char *path, const struct timespec ts[2], (path && (_weaken(__zipos_parseuri) && _weaken(__zipos_parseuri)(path, &zipname) != -1))) { rc = enotsup(); + } else if (IsLinux() && !__is_linux_2_6_23() && fd == AT_FDCWD && !flags) { + rc = sys_utimes(path, (void *)ts); // rhel5 truncates to seconds } else if (!IsWindows()) { rc = sys_utimensat(fd, path, ts, flags); } else { diff --git a/libc/calls/utimensat.c b/libc/calls/utimensat.c index 893cbe8a7..06f39f06f 100644 --- a/libc/calls/utimensat.c +++ b/libc/calls/utimensat.c @@ -34,10 +34,13 @@ * * XNU only has microsecond (1e-6) accuracy and there's no * `dirfd`-relative support. Windows only has hectonanosecond (1e-7) - * accuracy. RHEL5 is somewhat broken so utimes() is recommended if - * portability to old versions of Linux is desired. + * accuracy. RHEL5 doesn't support `dirfd` or `flags` and will truncate + * timestamps to seconds. * - * @param dirfd is usually `AT_FDCWD` + * If you'd rather specify an open file descriptor rather than its + * filesystem path, then consider using futimens(). + * + * @param dirfd can be `AT_FDCWD` or an open directory * @param path is filename whose timestamps should be modified * @param ts is {access, modified} timestamps, or null for current time * @param flags can have `AT_SYMLINK_NOFOLLOW` when `path` is specified @@ -52,21 +55,23 @@ * @raise EBADF if `dirfd` isn't a valid fd or `AT_FDCWD` * @raise EFAULT if `path` or `ts` memory was invalid * @raise EROFS if `path` is on read-only filesystem - * @raise ENOSYS on bare metal - * @see futimens() + * @raise ENOSYS on bare metal or on rhel5 when `dirfd` or `flags` is used * @asyncsignalsafe * @threadsafe */ int utimensat(int dirfd, const char *path, const struct timespec ts[2], int flags) { int rc; + if (!path) { rc = efault(); // linux kernel abi behavior isn't supported } else { rc = __utimens(dirfd, path, ts, flags); } + STRACE("utimensat(%s, %#s, {%s, %s}, %#o) → %d% m", DescribeDirfd(dirfd), path, DescribeTimespec(0, ts), DescribeTimespec(0, ts ? ts + 1 : 0), flags, rc); + return rc; } diff --git a/libc/calls/utimes.c b/libc/calls/utimes.c index 7d804e9ef..6ab0bdb7b 100644 --- a/libc/calls/utimes.c +++ b/libc/calls/utimes.c @@ -16,57 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/asan.internal.h" #include "libc/calls/struct/itimerval.internal.h" +#include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timespec.internal.h" #include "libc/calls/struct/timeval.h" -#include "libc/calls/struct/timeval.internal.h" -#include "libc/dce.h" -#include "libc/intrin/asan.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" #include "libc/sysv/consts/at.h" -#include "libc/sysv/errfuns.h" -#include "libc/zipos/zipos.internal.h" /** * Changes last accessed/modified timestamps on file. * * @param tv is access/modified timestamps, or NULL which means now * @return 0 on success, or -1 w/ errno - * @raise ENOTSUP if `path` is a zip filesystem path - * @raise EROFS if `path` is on a read-only filesystem - * @raise EFAULT if `path` or `tv` points to invalid memory - * @raise EPERM if pledge() is in play without fattr promise - * @raise ENOENT if `path` doesn't exist or is an empty string - * @raise EACCES if unveil() is in play and `path` isn't unveiled - * @raise ELOOP if a loop was detected resolving components of `path` - * @raise ENAMETOOLONG if symlink-resolved `path` length exceeds `PATH_MAX` - * @raise ENAMETOOLONG if component in `path` exists longer than `NAME_MAX` - * @raise EINVAL if `tv` specifies a microseconds value that's out of range - * @raise EACCES if we don't have permission to search a component of `path` - * @raise ENOTDIR if a directory component in `path` exists as non-directory - * @raise ENOSYS on bare metal + * @note truncates to second precision on rhel5 + * @see utimensat() for modern version * @asyncsignalsafe - * @see stat() + * @threadsafe */ int utimes(const char *path, const struct timeval tv[2]) { int rc; - struct ZiposUri zipname; - if (IsMetal()) { - rc = enosys(); - } else if (IsAsan() && tv && - (!__asan_is_valid_timeval(tv + 0) || - !__asan_is_valid_timeval(tv + 1))) { - rc = efault(); - } else if (_weaken(__zipos_parseuri) && - _weaken(__zipos_parseuri)(path, &zipname) != -1) { - rc = enotsup(); - } else if (!IsWindows()) { - // we don't modernize utimes() into utimensat() because the - // latter is poorly supported and utimes() works everywhere - rc = sys_utimes(path, tv); + struct timespec ts[2]; + if (tv) { + ts[0].tv_sec = tv[0].tv_sec; + ts[0].tv_nsec = tv[0].tv_usec * 1000; + ts[1].tv_sec = tv[1].tv_sec; + ts[1].tv_nsec = tv[1].tv_usec * 1000; + rc = __utimens(AT_FDCWD, path, ts, 0); } else { - rc = sys_utimes_nt(path, tv); + rc = __utimens(AT_FDCWD, path, 0, 0); } STRACE("utimes(%#s, {%s, %s}) → %d% m", path, DescribeTimeval(0, tv), DescribeTimeval(0, tv ? tv + 1 : 0), rc); diff --git a/libc/intrin/bzero.c b/libc/intrin/bzero.c index 40becb89b..c65c9cd42 100644 --- a/libc/intrin/bzero.c +++ b/libc/intrin/bzero.c @@ -130,7 +130,7 @@ microarchitecture("avx") static void bzero_avx(char *p, size_t n) { * @return p * @asyncsignalsafe */ -void(bzero)(void *p, size_t n) { +void bzero(void *p, size_t n) { char *b; uint64_t x; b = p; diff --git a/test/libc/calls/_timespec_test.c b/test/libc/calls/_timespec_test.c index bd2d586d9..e51b3b4f5 100644 --- a/test/libc/calls/_timespec_test.c +++ b/test/libc/calls/_timespec_test.c @@ -64,12 +64,19 @@ TEST(_timespec_frommicros, test) { } TEST(_timespec_tomillis, test) { + EXPECT_EQ(0, _timespec_tomillis((struct timespec){0, 0})); + EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 1})); + EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 999999})); + EXPECT_EQ(1, _timespec_tomillis((struct timespec){0, 1000000})); + EXPECT_EQ(1000, _timespec_tomillis((struct timespec){0, 999999999})); EXPECT_EQ(2123, _timespec_tomillis((struct timespec){2, 123000000})); EXPECT_EQ(INT64_MAX, _timespec_tomillis((struct timespec){INT64_MAX, 0})); EXPECT_EQ(INT64_MIN, _timespec_tomillis((struct timespec){INT64_MIN, 0})); } TEST(_timespec_tomicros, test) { + EXPECT_EQ(0, _timespec_tomicros((struct timespec){0, 0})); + EXPECT_EQ(1, _timespec_tomicros((struct timespec){0, 1})); EXPECT_EQ(2000123, _timespec_tomicros((struct timespec){2, 123000})); EXPECT_EQ(INT64_MAX, _timespec_tomicros((struct timespec){INT64_MAX, 0})); EXPECT_EQ(INT64_MIN, _timespec_tomicros((struct timespec){INT64_MIN, 0})); diff --git a/test/libc/calls/nanosleep_test.c b/test/libc/calls/nanosleep_test.c index 369a259f3..a57197ebc 100644 --- a/test/libc/calls/nanosleep_test.c +++ b/test/libc/calls/nanosleep_test.c @@ -22,6 +22,7 @@ #include "libc/calls/struct/timespec.h" #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" +#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sig.h" @@ -41,11 +42,16 @@ TEST(nanosleep, testInvalid) { EXPECT_SYS(EINVAL, -1, nanosleep(&ts, 0)); } -TEST(nanosleep, testNoSignalIsDelivered_remIsSetToZero) { +TEST(nanosleep, testNoSignalIsDelivered) { struct timespec ts = {0, 1}; ASSERT_SYS(0, 0, nanosleep(&ts, &ts)); - EXPECT_EQ(0, ts.tv_sec); - EXPECT_EQ(0, ts.tv_nsec); + ASSERT_SYS(0, 0, nanosleep(&ts, 0)); +} + +TEST(clock_nanosleep, testNoSignalIsDelivered) { + struct timespec ts = {0, 1}; + ASSERT_SYS(0, 0, clock_nanosleep(CLOCK_REALTIME, 0, &ts, &ts)); + ASSERT_SYS(0, 0, clock_nanosleep(CLOCK_REALTIME, 0, &ts, 0)); } TEST(nanosleep, testInterrupt_remIsUpdated) { diff --git a/test/libc/calls/utimensat_test.c b/test/libc/calls/utimensat_test.c index ff46276fb..59f3f0e35 100644 --- a/test/libc/calls/utimensat_test.c +++ b/test/libc/calls/utimensat_test.c @@ -19,8 +19,10 @@ #include "libc/calls/calls.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timespec.h" +#include "libc/calls/struct/timeval.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/utime.h" @@ -33,6 +35,50 @@ void SetUpOnce(void) { ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath fattr", 0)); } +TEST(utimes, test) { + struct stat st; + struct timeval tv[2] = { + {1655455857, 1}, // atim: Fri Jun 17 2022 08:50:57 GMT+0000 + {827727928, 2}, // mtim: Mon Mar 25 1996 04:25:28 GMT+0000 + }; + EXPECT_SYS(0, 0, touch("boop", 0644)); + EXPECT_SYS(0, 0, utimes("boop", tv)); + EXPECT_SYS(0, 0, stat("boop", &st)); + EXPECT_EQ(1655455857, st.st_atim.tv_sec); + EXPECT_EQ(827727928, st.st_mtim.tv_sec); + if (IsLinux() && !__is_linux_2_6_23()) { + // rhel5 only seems to have second granularity + EXPECT_EQ(0, st.st_atim.tv_nsec); + EXPECT_EQ(0, st.st_mtim.tv_nsec); + } else { + EXPECT_EQ(1000, st.st_atim.tv_nsec); + EXPECT_EQ(2000, st.st_mtim.tv_nsec); + } +} + +TEST(futimes, test) { + if (IsLinux() && !__is_linux_2_6_23()) return; + struct stat st; + struct timeval tv[2] = {{1655455857, 1}, {827727928, 2}}; + EXPECT_SYS(0, 3, creat("boop", 0644)); + EXPECT_SYS(0, 0, futimes(3, tv)); + EXPECT_SYS(0, 0, fstat(3, &st)); + EXPECT_EQ(1655455857, st.st_atim.tv_sec); + EXPECT_EQ(827727928, st.st_mtim.tv_sec); + EXPECT_EQ(1000, st.st_atim.tv_nsec); + EXPECT_EQ(2000, st.st_mtim.tv_nsec); + EXPECT_SYS(0, 0, close(3)); +} + +TEST(futimes, rhel5_enosys) { + if (IsLinux() && !__is_linux_2_6_23()) { + struct timeval tv[2] = {{1655455857}, {827727928}}; + EXPECT_SYS(0, 3, creat("boop", 0644)); + EXPECT_SYS(ENOSYS, -1, futimes(3, tv)); + EXPECT_SYS(0, 0, close(3)); + } +} + TEST(utimensat, test) { struct stat st; struct timespec ts[2] = {