]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/xattr-util.c
Merge pull request #14081 from poettering/xattr-list-rework
[thirdparty/systemd.git] / src / basic / xattr-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89a5a90c 2
11c3a366
TA
3#include <errno.h>
4#include <fcntl.h>
5#include <stdint.h>
6#include <stdlib.h>
11c3a366 7#include <sys/time.h>
89a5a90c
LP
8#include <sys/xattr.h>
9
b5efdb8a 10#include "alloc-util.h"
89a5a90c 11#include "fd-util.h"
11c3a366 12#include "macro.h"
f5947a5e 13#include "missing_syscall.h"
93cc7779 14#include "sparse-endian.h"
15a5e950 15#include "stdio-util.h"
1133dea4 16#include "string-util.h"
93cc7779 17#include "time-util.h"
89a5a90c
LP
18#include "xattr-util.h"
19
6ac99d9d
LP
20int getxattr_malloc(
21 const char *path,
22 const char *name,
23 char **ret,
24 bool allow_symlink) {
25
26 size_t l = 100;
89a5a90c
LP
27
28 assert(path);
29 assert(name);
6ac99d9d
LP
30 assert(ret);
31
32 for(;;) {
33 _cleanup_free_ char *v = NULL;
34 ssize_t n;
89a5a90c 35
6ac99d9d 36 v = new0(char, l+1);
89a5a90c
LP
37 if (!v)
38 return -ENOMEM;
39
40 if (allow_symlink)
41 n = lgetxattr(path, name, v, l);
42 else
43 n = getxattr(path, name, v, l);
6ac99d9d
LP
44 if (n < 0) {
45 if (errno != ERANGE)
46 return -errno;
47 } else {
48 v[n] = 0; /* NUL terminate */
49 *ret = TAKE_PTR(v);
50 return (int) n;
89a5a90c
LP
51 }
52
89a5a90c
LP
53 if (allow_symlink)
54 n = lgetxattr(path, name, NULL, 0);
55 else
56 n = getxattr(path, name, NULL, 0);
57 if (n < 0)
58 return -errno;
6ac99d9d
LP
59 if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
60 return -E2BIG;
61
62 l = (size_t) n;
89a5a90c
LP
63 }
64}
65
6ac99d9d
LP
66int fgetxattr_malloc(
67 int fd,
68 const char *name,
69 char **ret) {
70
71 size_t l = 100;
89a5a90c
LP
72
73 assert(fd >= 0);
74 assert(name);
6ac99d9d
LP
75 assert(ret);
76
77 for (;;) {
78 _cleanup_free_ char *v = NULL;
79 ssize_t n;
89a5a90c 80
6ac99d9d 81 v = new(char, l+1);
89a5a90c
LP
82 if (!v)
83 return -ENOMEM;
84
85 n = fgetxattr(fd, name, v, l);
6ac99d9d
LP
86 if (n < 0) {
87 if (errno != ERANGE)
88 return -errno;
89 } else {
90 v[n] = 0; /* NUL terminate */
91 *ret = TAKE_PTR(v);
92 return (int) n;
89a5a90c
LP
93 }
94
89a5a90c
LP
95 n = fgetxattr(fd, name, NULL, 0);
96 if (n < 0)
97 return -errno;
6ac99d9d
LP
98 if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
99 return -E2BIG;
100
101 l = (size_t) n;
89a5a90c
LP
102 }
103}
104
e4de6259
ZJS
105int fgetxattrat_fake(
106 int dirfd,
107 const char *filename,
108 const char *attribute,
109 void *value, size_t size,
110 int flags,
111 size_t *ret_size) {
112
fbd0b64f 113 char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
89a5a90c
LP
114 _cleanup_close_ int fd = -1;
115 ssize_t l;
116
117 /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
118
1133dea4
LP
119 if (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH))
120 return -EINVAL;
121
122 if (isempty(filename)) {
123 if (!(flags & AT_EMPTY_PATH))
124 return -EINVAL;
89a5a90c 125
1133dea4
LP
126 xsprintf(fn, "/proc/self/fd/%i", dirfd);
127 } else {
128 fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
129 if (fd < 0)
130 return -errno;
131
132 xsprintf(fn, "/proc/self/fd/%i", fd);
133 }
89a5a90c
LP
134
135 l = getxattr(fn, attribute, value, size);
136 if (l < 0)
137 return -errno;
138
e4de6259
ZJS
139 *ret_size = l;
140 return 0;
89a5a90c
LP
141}
142
143static int parse_crtime(le64_t le, usec_t *usec) {
144 uint64_t u;
145
146 assert(usec);
147
148 u = le64toh(le);
4c701096 149 if (IN_SET(u, 0, (uint64_t) -1))
89a5a90c
LP
150 return -EIO;
151
152 *usec = (usec_t) u;
153 return 0;
154}
155
4c2e1b39 156int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) {
08995a6b
ZJS
157 struct_statx sx
158#if HAS_FEATURE_MEMORY_SANITIZER
159 = {}
160# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
161#endif
162 ;
4c2e1b39 163 usec_t a, b;
89a5a90c 164 le64_t le;
e4de6259 165 size_t n;
4c2e1b39 166 int r;
89a5a90c 167
4c2e1b39 168 assert(ret);
89a5a90c 169
4c2e1b39
LP
170 if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
171 return -EINVAL;
89a5a90c 172
4c2e1b39
LP
173 /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
174 * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
175 * implemented on various file systems on the lower level since a while, but never was accessible). However, we
176 * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
177 * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
178 * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
179 * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
180 * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
181 * most sense. */
182
183 if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
184 (sx.stx_mask & STATX_BTIME) &&
185 sx.stx_btime.tv_sec != 0)
186 a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
187 (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
188 else
189 a = USEC_INFINITY;
89a5a90c 190
e4de6259
ZJS
191 r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
192 if (r >= 0) {
193 if (n != sizeof(le))
194 r = -EIO;
195 else
196 r = parse_crtime(le, &b);
197 }
4c2e1b39
LP
198 if (r < 0) {
199 if (a != USEC_INFINITY) {
200 *ret = a;
201 return 0;
202 }
89a5a90c 203
4c2e1b39
LP
204 return r;
205 }
89a5a90c 206
4c2e1b39
LP
207 if (a != USEC_INFINITY)
208 *ret = MIN(a, b);
209 else
210 *ret = b;
89a5a90c 211
4c2e1b39
LP
212 return 0;
213}
89a5a90c 214
4c2e1b39
LP
215int fd_getcrtime(int fd, usec_t *ret) {
216 return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
217}
89a5a90c 218
4c2e1b39
LP
219int path_getcrtime(const char *p, usec_t *ret) {
220 return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
89a5a90c
LP
221}
222
223int fd_setcrtime(int fd, usec_t usec) {
224 le64_t le;
225
226 assert(fd >= 0);
227
4c2e1b39 228 if (IN_SET(usec, 0, USEC_INFINITY))
89a5a90c
LP
229 usec = now(CLOCK_REALTIME);
230
231 le = htole64((uint64_t) usec);
232 if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
233 return -errno;
234
235 return 0;
236}
7de2d2e1
LP
237
238int flistxattr_malloc(int fd, char **ret) {
239 size_t l = 100;
240
241 assert(fd >= 0);
242 assert(ret);
243
244 for (;;) {
245 _cleanup_free_ char *v = NULL;
246 ssize_t n;
247
248 v = new(char, l+1);
249 if (!v)
250 return -ENOMEM;
251
252 n = flistxattr(fd, v, l);
253 if (n < 0) {
254 if (errno != ERANGE)
255 return -errno;
256 } else {
257 v[n] = 0; /* NUL terminate */
258 *ret = TAKE_PTR(v);
259 return (int) n;
260 }
261
262 n = flistxattr(fd, NULL, 0);
263 if (n < 0)
264 return -errno;
265 if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
266 return -E2BIG;
267
268 l = (size_t) n;
269 }
270}