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