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