]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/xattr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
11 #include "alloc-util.h"
15 #include "sparse-endian.h"
16 #include "stdio-util.h"
17 #include "string-util.h"
18 #include "time-util.h"
19 #include "xattr-util.h"
21 int getxattr_malloc(const char *path
, const char *name
, char **value
, bool allow_symlink
) {
30 for (l
= 100; ; l
= (size_t) n
+ 1) {
36 n
= lgetxattr(path
, name
, v
, l
);
38 n
= getxattr(path
, name
, v
, l
);
40 if (n
>= 0 && (size_t) n
< l
) {
47 if (n
< 0 && errno
!= ERANGE
)
51 n
= lgetxattr(path
, name
, NULL
, 0);
53 n
= getxattr(path
, name
, NULL
, 0);
59 int fgetxattr_malloc(int fd
, const char *name
, char **value
) {
68 for (l
= 100; ; l
= (size_t) n
+ 1) {
73 n
= fgetxattr(fd
, name
, v
, l
);
75 if (n
>= 0 && (size_t) n
< l
) {
82 if (n
< 0 && errno
!= ERANGE
)
85 n
= fgetxattr(fd
, name
, NULL
, 0);
94 const char *attribute
,
95 void *value
, size_t size
,
99 char fn
[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
100 _cleanup_close_
int fd
= -1;
103 /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
105 if (flags
& ~(AT_SYMLINK_NOFOLLOW
|AT_EMPTY_PATH
))
108 if (isempty(filename
)) {
109 if (!(flags
& AT_EMPTY_PATH
))
112 xsprintf(fn
, "/proc/self/fd/%i", dirfd
);
114 fd
= openat(dirfd
, filename
, O_CLOEXEC
|O_PATH
|(flags
& AT_SYMLINK_NOFOLLOW
? O_NOFOLLOW
: 0));
118 xsprintf(fn
, "/proc/self/fd/%i", fd
);
121 l
= getxattr(fn
, attribute
, value
, size
);
129 static int parse_crtime(le64_t le
, usec_t
*usec
) {
135 if (IN_SET(u
, 0, (uint64_t) -1))
142 int fd_getcrtime_at(int dirfd
, const char *name
, usec_t
*ret
, int flags
) {
151 if (flags
& ~(AT_EMPTY_PATH
|AT_SYMLINK_NOFOLLOW
))
154 /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
155 * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
156 * implemented on various file systems on the lower level since a while, but never was accessible). However, we
157 * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
158 * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
159 * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
160 * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
161 * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
164 if (statx(dirfd
, strempty(name
), flags
|AT_STATX_DONT_SYNC
, STATX_BTIME
, &sx
) >= 0 &&
165 (sx
.stx_mask
& STATX_BTIME
) &&
166 sx
.stx_btime
.tv_sec
!= 0)
167 a
= (usec_t
) sx
.stx_btime
.tv_sec
* USEC_PER_SEC
+
168 (usec_t
) sx
.stx_btime
.tv_nsec
/ NSEC_PER_USEC
;
172 r
= fgetxattrat_fake(dirfd
, name
, "user.crtime_usec", &le
, sizeof(le
), flags
, &n
);
177 r
= parse_crtime(le
, &b
);
180 if (a
!= USEC_INFINITY
) {
188 if (a
!= USEC_INFINITY
)
196 int fd_getcrtime(int fd
, usec_t
*ret
) {
197 return fd_getcrtime_at(fd
, NULL
, ret
, AT_EMPTY_PATH
);
200 int path_getcrtime(const char *p
, usec_t
*ret
) {
201 return fd_getcrtime_at(AT_FDCWD
, p
, ret
, 0);
204 int fd_setcrtime(int fd
, usec_t usec
) {
209 if (IN_SET(usec
, 0, USEC_INFINITY
))
210 usec
= now(CLOCK_REALTIME
);
212 le
= htole64((uint64_t) usec
);
213 if (fsetxattr(fd
, "user.crtime_usec", &le
, sizeof(le
), 0) < 0)