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