]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/xattr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include <linux/stat.h>
10 #include <sys/xattr.h>
12 #include "alloc-util.h"
16 #include "sparse-endian.h"
17 #include "stdio-util.h"
18 #include "string-util.h"
19 #include "time-util.h"
20 #include "xattr-util.h"
22 int getxattr_malloc(const char *path
, const char *name
, char **value
, bool allow_symlink
) {
31 for (l
= 100; ; l
= (size_t) n
+ 1) {
37 n
= lgetxattr(path
, name
, v
, l
);
39 n
= getxattr(path
, name
, v
, l
);
41 if (n
>= 0 && (size_t) n
< l
) {
48 if (n
< 0 && errno
!= ERANGE
)
52 n
= lgetxattr(path
, name
, NULL
, 0);
54 n
= getxattr(path
, name
, NULL
, 0);
60 int fgetxattr_malloc(int fd
, const char *name
, char **value
) {
69 for (l
= 100; ; l
= (size_t) n
+ 1) {
74 n
= fgetxattr(fd
, name
, v
, l
);
76 if (n
>= 0 && (size_t) n
< l
) {
83 if (n
< 0 && errno
!= ERANGE
)
86 n
= fgetxattr(fd
, name
, NULL
, 0);
95 const char *attribute
,
96 void *value
, size_t size
,
100 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
101 _cleanup_close_
int fd
= -1;
104 /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
106 if (flags
& ~(AT_SYMLINK_NOFOLLOW
|AT_EMPTY_PATH
))
109 if (isempty(filename
)) {
110 if (!(flags
& AT_EMPTY_PATH
))
113 xsprintf(fn
, "/proc/self/fd/%i", dirfd
);
115 fd
= openat(dirfd
, filename
, O_CLOEXEC
|O_PATH
|(flags
& AT_SYMLINK_NOFOLLOW
? O_NOFOLLOW
: 0));
119 xsprintf(fn
, "/proc/self/fd/%i", fd
);
122 l
= getxattr(fn
, attribute
, value
, size
);
130 static int parse_crtime(le64_t le
, usec_t
*usec
) {
136 if (IN_SET(u
, 0, (uint64_t) -1))
143 int fd_getcrtime_at(int dirfd
, const char *name
, usec_t
*ret
, int flags
) {
152 if (flags
& ~(AT_EMPTY_PATH
|AT_SYMLINK_NOFOLLOW
))
155 /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
156 * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
157 * implemented on various file systems on the lower level since a while, but never was accessible). However, we
158 * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
159 * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
160 * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
161 * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
162 * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
165 if (statx(dirfd
, strempty(name
), flags
|AT_STATX_DONT_SYNC
, STATX_BTIME
, &sx
) >= 0 &&
166 (sx
.stx_mask
& STATX_BTIME
) &&
167 sx
.stx_btime
.tv_sec
!= 0)
168 a
= (usec_t
) sx
.stx_btime
.tv_sec
* USEC_PER_SEC
+
169 (usec_t
) sx
.stx_btime
.tv_nsec
/ NSEC_PER_USEC
;
173 r
= fgetxattrat_fake(dirfd
, name
, "user.crtime_usec", &le
, sizeof(le
), flags
, &n
);
178 r
= parse_crtime(le
, &b
);
181 if (a
!= USEC_INFINITY
) {
189 if (a
!= USEC_INFINITY
)
197 int fd_getcrtime(int fd
, usec_t
*ret
) {
198 return fd_getcrtime_at(fd
, NULL
, ret
, AT_EMPTY_PATH
);
201 int path_getcrtime(const char *p
, usec_t
*ret
) {
202 return fd_getcrtime_at(AT_FDCWD
, p
, ret
, 0);
205 int fd_setcrtime(int fd
, usec_t usec
) {
210 if (IN_SET(usec
, 0, USEC_INFINITY
))
211 usec
= now(CLOCK_REALTIME
);
213 le
= htole64((uint64_t) usec
);
214 if (fsetxattr(fd
, "user.crtime_usec", &le
, sizeof(le
), 0) < 0)