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