mirror of git://git.musl-libc.org/musl
utimensat: add time64 syscall support, decouple 32-bit time_t
time64 syscall is used only if it's the only one defined for the arch, or if either of the requested times does not fit in 32 bits. care is taken to normalize the inputs to account for UTIME_NOW or UTIME_OMIT in tv_nsec, in which case tv_sec should be ignored. this is needed not only to avoid spurious time64 syscalls that might waste time failing with ENOSYS, but also to accurately decide whether fallback is possible. if the requested time cannot be represented, the function fails with ENOTSUP, defined in general as "The implementation does not support the requested feature or value". neither the time64 syscall, nor this error, can happen on current 32-bit archs where time_t is a 32-bit type, and both are statically unreachable. on 64-bit archs, there are only superficial changes to the SYS_futimesat fallback path, which has been modified to pass long[4] instead of struct timeval[2] to the kernel, making it suitable for use on 32-bit archs even once time_t is changed to 64-bit. for 32-bit archs, the call to SYS_utimensat has also been changed to copy the timespecs through an array of long[4] rather than passing the timespec[2] in-place.
This commit is contained in:
parent
7aeecf3e0b
commit
01f3480d37
|
@ -4,26 +4,51 @@
|
|||
#include <errno.h>
|
||||
#include "syscall.h"
|
||||
|
||||
#define IS32BIT(x) !((x)+0x80000000ULL>>32)
|
||||
#define NS_SPECIAL(ns) ((ns)==UTIME_NOW || (ns)==UTIME_OMIT)
|
||||
|
||||
int utimensat(int fd, const char *path, const struct timespec times[2], int flags)
|
||||
{
|
||||
int r;
|
||||
if (times && times[0].tv_nsec==UTIME_NOW && times[1].tv_nsec==UTIME_NOW)
|
||||
times = 0;
|
||||
int r = __syscall(SYS_utimensat, fd, path, times, flags);
|
||||
#ifdef SYS_utimensat_time64
|
||||
r = -ENOSYS;
|
||||
time_t s0=0, s1=0;
|
||||
long ns0=0, ns1=0;
|
||||
if (times) {
|
||||
ns0 = times[0].tv_nsec;
|
||||
ns1 = times[1].tv_nsec;
|
||||
if (!NS_SPECIAL(ns0)) s0 = times[0].tv_sec;
|
||||
if (!NS_SPECIAL(ns1)) s1 = times[1].tv_sec;
|
||||
}
|
||||
if (SYS_utimensat == SYS_utimensat_time64 || !IS32BIT(s0) || !IS32BIT(s1))
|
||||
r = __syscall(SYS_utimensat_time64, fd, path, times ?
|
||||
((long long[]){s0, ns0, s1, ns1}) : 0, flags);
|
||||
if (SYS_utimensat == SYS_utimensat_time64 || r!=-ENOSYS)
|
||||
return __syscall_ret(r);
|
||||
if (!IS32BIT(s0) || !IS32BIT(s1))
|
||||
return __syscall_ret(-ENOTSUP);
|
||||
r = __syscall(SYS_utimensat, fd, path,
|
||||
times ? ((long[]){s0, ns0, s1, ns1}) : 0, flags);
|
||||
#else
|
||||
r = __syscall(SYS_utimensat, fd, path, times, flags);
|
||||
#endif
|
||||
|
||||
#ifdef SYS_futimesat
|
||||
if (r != -ENOSYS || flags) return __syscall_ret(r);
|
||||
struct timeval *tv = 0, tmp[2];
|
||||
long *tv=0, tmp[4];
|
||||
if (times) {
|
||||
int i;
|
||||
tv = tmp;
|
||||
for (i=0; i<2; i++) {
|
||||
if (times[i].tv_nsec >= 1000000000ULL) {
|
||||
if (times[i].tv_nsec == UTIME_NOW
|
||||
|| times[i].tv_nsec == UTIME_OMIT)
|
||||
if (NS_SPECIAL(times[i].tv_nsec))
|
||||
return __syscall_ret(-ENOSYS);
|
||||
return __syscall_ret(-EINVAL);
|
||||
}
|
||||
tmp[i].tv_sec = times[i].tv_sec;
|
||||
tmp[i].tv_usec = times[i].tv_nsec / 1000;
|
||||
tmp[2*i+0] = times[i].tv_sec;
|
||||
tmp[2*i+1] = times[i].tv_nsec / 1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue