unix,fs: use utimes & friends for uv_fs_utime

This should improve uv_fs_utime resolution and reliability, as
utime(2)'s precision is left more to the implementing platform than the
newer but well supported alternatives.

Related to https://github.com/nodejs/node/issues/22070
PR-URL: https://github.com/libuv/libuv/pull/1940
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
This commit is contained in:
Jeremiah Senkpiel 2018-08-07 15:06:34 -04:00 committed by Santiago Gimeno
parent ff45b0d789
commit 3646624e5e
No known key found for this signature in database
GPG Key ID: F28C3C8DA33C03BE

View File

@ -43,7 +43,6 @@
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <utime.h>
#include <poll.h>
#if defined(__DragonFly__) || \
@ -67,6 +66,10 @@
# define FICLONE _IOW(0x94, 9, int)
#endif
#if defined(_AIX) && !defined(_AIX71)
# include <utime.h>
#endif
#define INIT(subtype) \
do { \
if (req == NULL) \
@ -702,10 +705,90 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
static ssize_t uv__fs_utime(uv_fs_t* req) {
#if defined(__linux__)
/* utimesat() has nanosecond resolution but we stick to microseconds
* for the sake of consistency with other platforms.
*/
static int no_utimesat;
struct timespec ts[2];
struct timeval tv[2];
int r;
if (no_utimesat)
goto skip;
ts[0].tv_sec = req->atime;
ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000;
ts[1].tv_sec = req->mtime;
ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000;
r = uv__utimesat(AT_FDCWD, req->path, ts, 0);
if (r == 0)
return r;
if (errno != ENOSYS)
return r;
no_utimesat = 1;
skip:
tv[0].tv_sec = req->atime;
tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
tv[1].tv_sec = req->mtime;
tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
r = utimes(req->path, tv);
if (r == 0)
return r;
switch (errno) {
case EACCES:
case ENOTDIR:
errno = ENOSYS;
break;
}
return r;
#elif defined(__APPLE__) \
|| defined(__DragonFly__) \
|| defined(__FreeBSD__) \
|| defined(__FreeBSD_kernel__) \
|| defined(__NetBSD__) \
|| defined(__OpenBSD__)
struct timeval tv[2];
tv[0].tv_sec = req->atime;
tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000;
tv[1].tv_sec = req->mtime;
tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000;
return utimes(req->path, tv);
#elif defined(_AIX) \
&& !defined(_AIX71)
struct utimbuf buf;
buf.actime = req->atime;
buf.modtime = req->mtime;
return utime(req->path, &buf); /* TODO use utimes() where available */
return utime(req->path, &buf);
#elif defined(_AIX71) \
|| defined(__sun)
struct timespec ts[2];
ts[0].tv_sec = req->atime;
ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000;
ts[1].tv_sec = req->mtime;
ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000;
return utimensat(AT_FDCWD, req->path, ts, 0);
#elif defined(__MVS__)
attrib_t atr;
memset(&atr, 0, sizeof(atr));
atr.att_mtimechg = 1;
atr.att_atimechg = 1;
atr.att_mtime = req->mtime;
atr.att_atime = req->atime;
return __lchattr(req->path, &atr, sizeof(atr));
#else
errno = ENOSYS;
return -1;
#endif
}