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:
parent
a0924d96cb
commit
baca3581ea
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user