linux: use statx() to obtain file birth time
Kernels > 4.11 support the statx() system call that lets one retrieve the birth time of a file. Teach libuv about it. Fixes: https://github.com/libuv/libuv/issues/2152 PR-URL: https://github.com/libuv/libuv/pull/2184 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
parent
4fc5a0ee61
commit
19d8eb0c92
@ -1053,10 +1053,82 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int uv__fs_statx(int fd,
|
||||||
|
const char* path,
|
||||||
|
int is_fstat,
|
||||||
|
int is_lstat,
|
||||||
|
uv_stat_t* buf) {
|
||||||
|
STATIC_ASSERT(UV_ENOSYS != -1);
|
||||||
|
#ifdef __linux__
|
||||||
|
static int no_statx;
|
||||||
|
struct uv__statx statxbuf;
|
||||||
|
int dirfd;
|
||||||
|
int flags;
|
||||||
|
int mode;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (no_statx)
|
||||||
|
return UV_ENOSYS;
|
||||||
|
|
||||||
|
dirfd = AT_FDCWD;
|
||||||
|
flags = 0; /* AT_STATX_SYNC_AS_STAT */
|
||||||
|
mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */
|
||||||
|
|
||||||
|
if (is_fstat) {
|
||||||
|
dirfd = fd;
|
||||||
|
flags |= 0x1000; /* AT_EMPTY_PATH */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_lstat)
|
||||||
|
flags |= AT_SYMLINK_NOFOLLOW;
|
||||||
|
|
||||||
|
rc = uv__statx(dirfd, path, flags, mode, &statxbuf);
|
||||||
|
|
||||||
|
if (rc == -1) {
|
||||||
|
/* EPERM happens when a seccomp filter rejects the system call.
|
||||||
|
* Has been observed with libseccomp < 2.3.3 and docker < 18.04.
|
||||||
|
*/
|
||||||
|
if (errno != EINVAL && errno != EPERM && errno != ENOSYS)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
no_statx = 1;
|
||||||
|
return UV_ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor;
|
||||||
|
buf->st_mode = statxbuf.stx_mode;
|
||||||
|
buf->st_nlink = statxbuf.stx_nlink;
|
||||||
|
buf->st_uid = statxbuf.stx_uid;
|
||||||
|
buf->st_gid = statxbuf.stx_gid;
|
||||||
|
buf->st_rdev = statxbuf.stx_rdev_major;
|
||||||
|
buf->st_ino = statxbuf.stx_ino;
|
||||||
|
buf->st_size = statxbuf.stx_size;
|
||||||
|
buf->st_blksize = statxbuf.stx_blksize;
|
||||||
|
buf->st_blocks = statxbuf.stx_blocks;
|
||||||
|
buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
|
||||||
|
buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
|
||||||
|
buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
|
||||||
|
buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
|
||||||
|
buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
|
||||||
|
buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
|
||||||
|
buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;
|
||||||
|
buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return UV_ENOSYS;
|
||||||
|
#endif /* __linux__ */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int uv__fs_stat(const char *path, uv_stat_t *buf) {
|
static int uv__fs_stat(const char *path, uv_stat_t *buf) {
|
||||||
struct stat pbuf;
|
struct stat pbuf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf);
|
||||||
|
if (ret != UV_ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = stat(path, &pbuf);
|
ret = stat(path, &pbuf);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
uv__to_stat(&pbuf, buf);
|
uv__to_stat(&pbuf, buf);
|
||||||
@ -1069,6 +1141,10 @@ static int uv__fs_lstat(const char *path, uv_stat_t *buf) {
|
|||||||
struct stat pbuf;
|
struct stat pbuf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf);
|
||||||
|
if (ret != UV_ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = lstat(path, &pbuf);
|
ret = lstat(path, &pbuf);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
uv__to_stat(&pbuf, buf);
|
uv__to_stat(&pbuf, buf);
|
||||||
@ -1081,6 +1157,10 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) {
|
|||||||
struct stat pbuf;
|
struct stat pbuf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf);
|
||||||
|
if (ret != UV_ENOSYS)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = fstat(fd, &pbuf);
|
ret = fstat(fd, &pbuf);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
uv__to_stat(&pbuf, buf);
|
uv__to_stat(&pbuf, buf);
|
||||||
|
|||||||
@ -187,6 +187,21 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif /* __NR_pwritev */
|
#endif /* __NR_pwritev */
|
||||||
|
|
||||||
|
#ifndef __NR_statx
|
||||||
|
# if defined(__x86_64__)
|
||||||
|
# define __NR_statx 332
|
||||||
|
# elif defined(__i386__)
|
||||||
|
# define __NR_statx 383
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
# define __NR_statx 397
|
||||||
|
# elif defined(__arm__)
|
||||||
|
# define __NR_statx (UV_SYSCALL_BASE + 397)
|
||||||
|
# elif defined(__ppc__)
|
||||||
|
# define __NR_statx 383
|
||||||
|
# elif defined(__s390__)
|
||||||
|
# define __NR_statx 379
|
||||||
|
# endif
|
||||||
|
#endif /* __NR_statx */
|
||||||
|
|
||||||
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
|
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
@ -336,3 +351,16 @@ int uv__dup3(int oldfd, int newfd, int flags) {
|
|||||||
return errno = ENOSYS, -1;
|
return errno = ENOSYS, -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int uv__statx(int dirfd,
|
||||||
|
const char* path,
|
||||||
|
int flags,
|
||||||
|
unsigned int mask,
|
||||||
|
struct uv__statx* statxbuf) {
|
||||||
|
#if defined(__NR_statx)
|
||||||
|
return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
|
||||||
|
#else
|
||||||
|
return errno = ENOSYS, -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@ -80,6 +80,36 @@
|
|||||||
#define UV__IN_DELETE_SELF 0x400
|
#define UV__IN_DELETE_SELF 0x400
|
||||||
#define UV__IN_MOVE_SELF 0x800
|
#define UV__IN_MOVE_SELF 0x800
|
||||||
|
|
||||||
|
struct uv__statx_timestamp {
|
||||||
|
int64_t tv_sec;
|
||||||
|
uint32_t tv_nsec;
|
||||||
|
int32_t unused0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct uv__statx {
|
||||||
|
uint32_t stx_mask;
|
||||||
|
uint32_t stx_blksize;
|
||||||
|
uint64_t stx_attributes;
|
||||||
|
uint32_t stx_nlink;
|
||||||
|
uint32_t stx_uid;
|
||||||
|
uint32_t stx_gid;
|
||||||
|
uint16_t stx_mode;
|
||||||
|
uint16_t unused0;
|
||||||
|
uint64_t stx_ino;
|
||||||
|
uint64_t stx_size;
|
||||||
|
uint64_t stx_blocks;
|
||||||
|
uint64_t stx_attributes_mask;
|
||||||
|
struct uv__statx_timestamp stx_atime;
|
||||||
|
struct uv__statx_timestamp stx_btime;
|
||||||
|
struct uv__statx_timestamp stx_ctime;
|
||||||
|
struct uv__statx_timestamp stx_mtime;
|
||||||
|
uint32_t stx_rdev_major;
|
||||||
|
uint32_t stx_rdev_minor;
|
||||||
|
uint32_t stx_dev_major;
|
||||||
|
uint32_t stx_dev_minor;
|
||||||
|
uint64_t unused1[14];
|
||||||
|
};
|
||||||
|
|
||||||
struct uv__inotify_event {
|
struct uv__inotify_event {
|
||||||
int32_t wd;
|
int32_t wd;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
@ -113,5 +143,10 @@ int uv__sendmmsg(int fd,
|
|||||||
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
||||||
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset);
|
||||||
int uv__dup3(int oldfd, int newfd, int flags);
|
int uv__dup3(int oldfd, int newfd, int flags);
|
||||||
|
int uv__statx(int dirfd,
|
||||||
|
const char* path,
|
||||||
|
int flags,
|
||||||
|
unsigned int mask,
|
||||||
|
struct uv__statx* statxbuf);
|
||||||
|
|
||||||
#endif /* UV_LINUX_SYSCALL_H_ */
|
#endif /* UV_LINUX_SYSCALL_H_ */
|
||||||
|
|||||||
@ -1248,6 +1248,16 @@ TEST_IMPL(fs_fstat) {
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
/* If statx() is supported, the birth time should be equal to the change time
|
||||||
|
* because we just created the file. On older kernels, it's set to zero.
|
||||||
|
*/
|
||||||
|
ASSERT(s->st_birthtim.tv_sec == 0 ||
|
||||||
|
s->st_birthtim.tv_sec == t.st_ctim.tv_sec);
|
||||||
|
ASSERT(s->st_birthtim.tv_nsec == 0 ||
|
||||||
|
s->st_birthtim.tv_nsec == t.st_ctim.tv_nsec);
|
||||||
|
#endif
|
||||||
|
|
||||||
uv_fs_req_cleanup(&req);
|
uv_fs_req_cleanup(&req);
|
||||||
|
|
||||||
/* Now do the uv_fs_fstat call asynchronously */
|
/* Now do the uv_fs_fstat call asynchronously */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user