]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/xattr-util.c
socket-util: re-implement socket_address_parse_netlink() by using extract_first_word()
[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) {
5187dd2c 143 struct_statx sx;
4c2e1b39 144 usec_t a, b;
89a5a90c 145 le64_t le;
e4de6259 146 size_t n;
4c2e1b39 147 int r;
89a5a90c 148
4c2e1b39 149 assert(ret);
89a5a90c 150
4c2e1b39
LP
151 if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
152 return -EINVAL;
89a5a90c 153
4c2e1b39
LP
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
162 * most sense. */
163
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;
169 else
170 a = USEC_INFINITY;
89a5a90c 171
e4de6259
ZJS
172 r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
173 if (r >= 0) {
174 if (n != sizeof(le))
175 r = -EIO;
176 else
177 r = parse_crtime(le, &b);
178 }
4c2e1b39
LP
179 if (r < 0) {
180 if (a != USEC_INFINITY) {
181 *ret = a;
182 return 0;
183 }
89a5a90c 184
4c2e1b39
LP
185 return r;
186 }
89a5a90c 187
4c2e1b39
LP
188 if (a != USEC_INFINITY)
189 *ret = MIN(a, b);
190 else
191 *ret = b;
89a5a90c 192
4c2e1b39
LP
193 return 0;
194}
89a5a90c 195
4c2e1b39
LP
196int fd_getcrtime(int fd, usec_t *ret) {
197 return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
198}
89a5a90c 199
4c2e1b39
LP
200int path_getcrtime(const char *p, usec_t *ret) {
201 return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
89a5a90c
LP
202}
203
204int fd_setcrtime(int fd, usec_t usec) {
205 le64_t le;
206
207 assert(fd >= 0);
208
4c2e1b39 209 if (IN_SET(usec, 0, USEC_INFINITY))
89a5a90c
LP
210 usec = now(CLOCK_REALTIME);
211
212 le = htole64((uint64_t) usec);
213 if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
214 return -errno;
215
216 return 0;
217}