mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
parent
f155205eb0
commit
81ee11a16e
22 changed files with 200 additions and 138 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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] = {
|
||||
|
|
Loading…
Reference in a new issue