1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-22 06:50:18 +03:00

xattr-util: modernize getcrtime_at() and friends

- Drop fd_ prefix for openat()-like function
- Make fd_setcrtime() accept O_PATH fds too
- Use statx_timestamp_load()
This commit is contained in:
Mike Yuan 2025-01-26 17:46:30 +01:00
parent a0924d96cb
commit baca3581ea
No known key found for this signature in database
GPG Key ID: 417471C0A40F58B3
3 changed files with 93 additions and 96 deletions

View File

@ -133,94 +133,6 @@ int getxattr_at_bool(int fd, const char *path, const char *name, int flags) {
return parse_boolean(v);
}
static int parse_crtime(le64_t le, usec_t *usec) {
uint64_t u;
assert(usec);
u = le64toh(le);
if (IN_SET(u, 0, UINT64_MAX))
return -EIO;
*usec = (usec_t) u;
return 0;
}
int fd_getcrtime_at(
int fd,
const char *path,
int flags,
usec_t *ret) {
_cleanup_free_ le64_t *le = NULL;
STRUCT_STATX_DEFINE(sx);
usec_t a, b;
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
assert(ret);
if (!path)
flags |= AT_EMPTY_PATH;
/* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
* on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
* implemented on various file systems on the lower level since a while, but never was accessible). However, we
* needed a concept like that for vacuuming algorithms and such, hence we emulated it via a user xattr for a
* long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
* time, where it is available. This function will read it, but it tries to keep some compatibility with older
* systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
* concept is useful for determining how "old" a file really is, and hence using the older of the two makes
* most sense. */
if (statx(fd, strempty(path),
at_flags_normalize_nofollow(flags)|AT_STATX_DONT_SYNC,
STATX_BTIME,
&sx) >= 0 &&
(sx.stx_mask & STATX_BTIME) &&
sx.stx_btime.tv_sec != 0)
a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
(usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
else
a = USEC_INFINITY;
r = getxattr_at_malloc(fd, path, "user.crtime_usec", flags, (char**) &le);
if (r >= 0) {
if (r != sizeof(*le))
r = -EIO;
else
r = parse_crtime(*le, &b);
}
if (r < 0) {
if (a != USEC_INFINITY) {
*ret = a;
return 0;
}
return r;
}
if (a != USEC_INFINITY)
*ret = MIN(a, b);
else
*ret = b;
return 0;
}
int fd_setcrtime(int fd, usec_t usec) {
le64_t le;
assert(fd >= 0);
if (!timestamp_is_set(usec))
usec = now(CLOCK_REALTIME);
le = htole64((uint64_t) usec);
return RET_NERRNO(fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0));
}
int listxattr_at_malloc(
int fd,
const char *path,
@ -377,3 +289,89 @@ int xsetxattr(int fd,
return 0;
}
static int parse_crtime(le64_t le, usec_t *ret) {
usec_t u;
assert(ret);
assert_cc(sizeof(usec_t) == sizeof(uint64_t));
assert_cc((usec_t) UINT64_MAX == USEC_INFINITY);
u = (usec_t) le64toh(le);
if (!timestamp_is_set(u))
return -EIO;
*ret = u;
return 0;
}
int getcrtime_at(
int fd,
const char *path,
int at_flags,
usec_t *ret) {
_cleanup_free_ le64_t *le = NULL;
STRUCT_STATX_DEFINE(sx);
usec_t a, b;
int r;
assert(fd >= 0 || fd == AT_FDCWD);
assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
if (isempty(path))
at_flags |= AT_EMPTY_PATH;
/* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
* on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
* implemented on various file systems on the lower level since a while, but never was accessible). However, we
* needed a concept like that for vacuuming algorithms and such, hence we emulated it via a user xattr for a
* long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
* time, where it is available. This function will read it, but it tries to keep some compatibility with older
* systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
* concept is useful for determining how "old" a file really is, and hence using the older of the two makes
* most sense. */
if (statx(fd, strempty(path),
at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC,
STATX_BTIME,
&sx) >= 0 &&
FLAGS_SET(sx.stx_mask, STATX_BTIME) && sx.stx_btime.tv_sec != 0)
a = statx_timestamp_load(&sx.stx_btime);
else
a = USEC_INFINITY;
r = getxattr_at_malloc(fd, path, "user.crtime_usec", at_flags, (char**) &le);
if (r >= 0) {
if (r != sizeof(*le))
r = -EIO;
else
r = parse_crtime(*le, &b);
}
if (r < 0) {
if (a != USEC_INFINITY) {
if (ret)
*ret = a;
return 0;
}
return r;
}
if (ret)
*ret = MIN(a, b);
return 0;
}
int fd_setcrtime(int fd, usec_t usec) {
le64_t le;
assert(fd >= 0);
if (!timestamp_is_set(usec))
usec = now(CLOCK_REALTIME);
le = htole64((uint64_t) usec);
return xsetxattr(fd, /* path = */ NULL, "user.crtime_usec", (const char*) &le, sizeof(le), AT_EMPTY_PATH);
}

View File

@ -20,13 +20,6 @@ static inline int fgetxattr_malloc(int fd, const char *name, char **ret) {
int getxattr_at_bool(int fd, const char *path, const char *name, int flags);
int fd_setcrtime(int fd, usec_t usec);
int fd_getcrtime_at(int fd, const char *name, int flags, usec_t *ret);
static inline int fd_getcrtime(int fd, usec_t *ret) {
return fd_getcrtime_at(fd, NULL, 0, ret);
}
int listxattr_at_malloc(int fd, const char *path, int flags, char **ret);
static inline int listxattr_malloc(const char *path, char **ret) {
return listxattr_at_malloc(AT_FDCWD, path, AT_SYMLINK_FOLLOW, ret);
@ -39,3 +32,9 @@ static inline int flistxattr_malloc(int fd, char **ret) {
}
int xsetxattr(int fd, const char *path, const char *name, const char *value, size_t size, int flags);
int fd_setcrtime(int fd, usec_t usec);
int getcrtime_at(int fd, const char *path, int at_flags, usec_t *ret);
static inline int fd_getcrtime(int fd, usec_t *ret) {
return getcrtime_at(fd, NULL, 0, ret);
}

View File

@ -90,7 +90,7 @@ static void patch_realtime(
* FS might provide, but unfortunately there's currently no sane API to query it. Hence let's
* implement this manually... */
if (fd_getcrtime_at(fd, fn, AT_SYMLINK_FOLLOW, &x) >= 0 && x < *realtime)
if (getcrtime_at(fd, fn, AT_SYMLINK_FOLLOW, &x) >= 0 && x < *realtime)
*realtime = x;
}