]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/xattr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
10 #include <linux/stat.h>
15 #include <sys/xattr.h>
17 #include "alloc-util.h"
21 #include "sparse-endian.h"
22 #include "stdio-util.h"
23 #include "string-util.h"
24 #include "time-util.h"
25 #include "xattr-util.h"
27 int getxattr_malloc(const char *path
, const char *name
, char **value
, bool allow_symlink
) {
36 for (l
= 100; ; l
= (size_t) n
+ 1) {
42 n
= lgetxattr(path
, name
, v
, l
);
44 n
= getxattr(path
, name
, v
, l
);
46 if (n
>= 0 && (size_t) n
< l
) {
53 if (n
< 0 && errno
!= ERANGE
)
57 n
= lgetxattr(path
, name
, NULL
, 0);
59 n
= getxattr(path
, name
, NULL
, 0);
65 int fgetxattr_malloc(int fd
, const char *name
, char **value
) {
74 for (l
= 100; ; l
= (size_t) n
+ 1) {
79 n
= fgetxattr(fd
, name
, v
, l
);
81 if (n
>= 0 && (size_t) n
< l
) {
88 if (n
< 0 && errno
!= ERANGE
)
91 n
= fgetxattr(fd
, name
, NULL
, 0);
100 const char *attribute
,
101 void *value
, size_t size
,
105 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
106 _cleanup_close_
int fd
= -1;
109 /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
111 if (flags
& ~(AT_SYMLINK_NOFOLLOW
|AT_EMPTY_PATH
))
114 if (isempty(filename
)) {
115 if (!(flags
& AT_EMPTY_PATH
))
118 xsprintf(fn
, "/proc/self/fd/%i", dirfd
);
120 fd
= openat(dirfd
, filename
, O_CLOEXEC
|O_PATH
|(flags
& AT_SYMLINK_NOFOLLOW
? O_NOFOLLOW
: 0));
124 xsprintf(fn
, "/proc/self/fd/%i", fd
);
127 l
= getxattr(fn
, attribute
, value
, size
);
135 static int parse_crtime(le64_t le
, usec_t
*usec
) {
141 if (IN_SET(u
, 0, (uint64_t) -1))
148 int fd_getcrtime_at(int dirfd
, const char *name
, usec_t
*ret
, int flags
) {
157 if (flags
& ~(AT_EMPTY_PATH
|AT_SYMLINK_NOFOLLOW
))
160 /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
161 * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
162 * implemented on various file systems on the lower level since a while, but never was accessible). However, we
163 * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
164 * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
165 * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
166 * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
167 * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
170 if (statx(dirfd
, strempty(name
), flags
|AT_STATX_DONT_SYNC
, STATX_BTIME
, &sx
) >= 0 &&
171 (sx
.stx_mask
& STATX_BTIME
) &&
172 sx
.stx_btime
.tv_sec
!= 0)
173 a
= (usec_t
) sx
.stx_btime
.tv_sec
* USEC_PER_SEC
+
174 (usec_t
) sx
.stx_btime
.tv_nsec
/ NSEC_PER_USEC
;
178 r
= fgetxattrat_fake(dirfd
, name
, "user.crtime_usec", &le
, sizeof(le
), flags
, &n
);
183 r
= parse_crtime(le
, &b
);
186 if (a
!= USEC_INFINITY
) {
194 if (a
!= USEC_INFINITY
)
202 int fd_getcrtime(int fd
, usec_t
*ret
) {
203 return fd_getcrtime_at(fd
, NULL
, ret
, AT_EMPTY_PATH
);
206 int path_getcrtime(const char *p
, usec_t
*ret
) {
207 return fd_getcrtime_at(AT_FDCWD
, p
, ret
, 0);
210 int fd_setcrtime(int fd
, usec_t usec
) {
215 if (IN_SET(usec
, 0, USEC_INFINITY
))
216 usec
= now(CLOCK_REALTIME
);
218 le
= htole64((uint64_t) usec
);
219 if (fsetxattr(fd
, "user.crtime_usec", &le
, sizeof(le
), 0) < 0)