Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
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>
9 : #include <sys/xattr.h>
10 :
11 : #include "alloc-util.h"
12 : #include "fd-util.h"
13 : #include "macro.h"
14 : #include "missing.h"
15 : #include "sparse-endian.h"
16 : #include "stdio-util.h"
17 : #include "string-util.h"
18 : #include "time-util.h"
19 : #include "xattr-util.h"
20 :
21 0 : int 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 0 : assert(path);
27 0 : assert(name);
28 0 : assert(value);
29 :
30 0 : for (l = 100; ; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) {
31 0 : v = new0(char, l);
32 0 : if (!v)
33 0 : return -ENOMEM;
34 :
35 0 : if (allow_symlink)
36 0 : n = lgetxattr(path, name, v, l);
37 : else
38 0 : n = getxattr(path, name, v, l);
39 0 : if (n >= 0 && (size_t) n < l) {
40 0 : *value = v;
41 0 : return n;
42 : }
43 :
44 0 : free(v);
45 :
46 0 : if (n < 0 && errno != ERANGE)
47 0 : return -errno;
48 :
49 0 : if (allow_symlink)
50 0 : n = lgetxattr(path, name, NULL, 0);
51 : else
52 0 : n = getxattr(path, name, NULL, 0);
53 0 : if (n < 0)
54 0 : return -errno;
55 : }
56 : }
57 :
58 0 : int fgetxattr_malloc(int fd, const char *name, char **value) {
59 : char *v;
60 : size_t l;
61 : ssize_t n;
62 :
63 0 : assert(fd >= 0);
64 0 : assert(name);
65 0 : assert(value);
66 :
67 0 : for (l = 100;; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) {
68 0 : v = new0(char, l);
69 0 : if (!v)
70 0 : return -ENOMEM;
71 :
72 0 : n = fgetxattr(fd, name, v, l);
73 0 : if (n >= 0 && (size_t) n < l) {
74 0 : *value = v;
75 0 : return n;
76 : }
77 :
78 0 : free(v);
79 :
80 0 : if (n < 0 && errno != ERANGE)
81 0 : return -errno;
82 :
83 0 : n = fgetxattr(fd, name, NULL, 0);
84 0 : if (n < 0)
85 0 : return -errno;
86 : }
87 : }
88 :
89 4 : int 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 :
97 : char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
98 4 : _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 :
103 4 : if (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH))
104 0 : return -EINVAL;
105 :
106 4 : if (isempty(filename)) {
107 1 : if (!(flags & AT_EMPTY_PATH))
108 0 : return -EINVAL;
109 :
110 1 : xsprintf(fn, "/proc/self/fd/%i", dirfd);
111 : } else {
112 3 : fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
113 3 : if (fd < 0)
114 0 : return -errno;
115 :
116 3 : xsprintf(fn, "/proc/self/fd/%i", fd);
117 : }
118 :
119 4 : l = getxattr(fn, attribute, value, size);
120 4 : if (l < 0)
121 2 : return -errno;
122 :
123 2 : *ret_size = l;
124 2 : return 0;
125 : }
126 :
127 1 : static int parse_crtime(le64_t le, usec_t *usec) {
128 : uint64_t u;
129 :
130 1 : assert(usec);
131 :
132 1 : u = le64toh(le);
133 1 : if (IN_SET(u, 0, (uint64_t) -1))
134 0 : return -EIO;
135 :
136 1 : *usec = (usec_t) u;
137 1 : return 0;
138 : }
139 :
140 2 : int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) {
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 : ;
147 : usec_t a, b;
148 : le64_t le;
149 : size_t n;
150 : int r;
151 :
152 2 : assert(ret);
153 :
154 2 : if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
155 0 : return -EINVAL;
156 :
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 2 : if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
168 2 : (sx.stx_mask & STATX_BTIME) &&
169 1 : sx.stx_btime.tv_sec != 0)
170 2 : a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
171 1 : (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
172 : else
173 1 : a = USEC_INFINITY;
174 :
175 2 : r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
176 2 : if (r >= 0) {
177 1 : if (n != sizeof(le))
178 0 : r = -EIO;
179 : else
180 1 : r = parse_crtime(le, &b);
181 : }
182 2 : if (r < 0) {
183 1 : if (a != USEC_INFINITY) {
184 0 : *ret = a;
185 0 : return 0;
186 : }
187 :
188 1 : return r;
189 : }
190 :
191 1 : if (a != USEC_INFINITY)
192 1 : *ret = MIN(a, b);
193 : else
194 0 : *ret = b;
195 :
196 1 : return 0;
197 : }
198 :
199 1 : int fd_getcrtime(int fd, usec_t *ret) {
200 1 : return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
201 : }
202 :
203 0 : int path_getcrtime(const char *p, usec_t *ret) {
204 0 : return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
205 : }
206 :
207 26 : int fd_setcrtime(int fd, usec_t usec) {
208 : le64_t le;
209 :
210 26 : assert(fd >= 0);
211 :
212 26 : if (IN_SET(usec, 0, USEC_INFINITY))
213 25 : usec = now(CLOCK_REALTIME);
214 :
215 26 : le = htole64((uint64_t) usec);
216 26 : if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
217 1 : return -errno;
218 :
219 25 : return 0;
220 : }
|