summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2019-07-28 18:51:20 -0400
committerRich Felker <dalias@aerifal.cx>2019-07-29 00:19:16 -0400
commit01f3480d377a764135a1bd28af0d901d765ded50 (patch)
tree41531866975b416fc1636318dd5ce1d8a8104f85
parent7aeecf3e0b5462a05f7173993618eb59ba1a86d5 (diff)
downloadmusl-01f3480d377a764135a1bd28af0d901d765ded50.tar.gz
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.
-rw-r--r--src/stat/utimensat.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/src/stat/utimensat.c b/src/stat/utimensat.c
index 49d74c22..730723a9 100644
--- a/src/stat/utimensat.c
+++ b/src/stat/utimensat.c
@@ -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;
}
}