Branch data 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 : 16 : 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 : 16 : _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 [ - + ]: 16 : if (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH))
104 : 0 : return -EINVAL;
105 : :
106 [ + + ]: 16 : if (isempty(filename)) {
107 [ - + ]: 4 : if (!(flags & AT_EMPTY_PATH))
108 : 0 : return -EINVAL;
109 : :
110 [ - + ]: 4 : xsprintf(fn, "/proc/self/fd/%i", dirfd);
111 : : } else {
112 : 12 : fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
113 [ - + ]: 12 : if (fd < 0)
114 : 0 : return -errno;
115 : :
116 [ - + ]: 12 : xsprintf(fn, "/proc/self/fd/%i", fd);
117 : : }
118 : :
119 : 16 : l = getxattr(fn, attribute, value, size);
120 [ + + ]: 16 : if (l < 0)
121 : 8 : return -errno;
122 : :
123 : 8 : *ret_size = l;
124 : 8 : return 0;
125 : : }
126 : :
127 : 4 : static int parse_crtime(le64_t le, usec_t *usec) {
128 : : uint64_t u;
129 : :
130 [ - + ]: 4 : assert(usec);
131 : :
132 : 4 : u = le64toh(le);
133 [ - + - + ]: 4 : if (IN_SET(u, 0, (uint64_t) -1))
134 : 0 : return -EIO;
135 : :
136 : 4 : *usec = (usec_t) u;
137 : 4 : return 0;
138 : : }
139 : :
140 : 8 : 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 [ - + ]: 8 : assert(ret);
153 : :
154 [ - + ]: 8 : 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 [ + - ]: 8 : if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
168 [ + + ]: 8 : (sx.stx_mask & STATX_BTIME) &&
169 [ + - ]: 4 : sx.stx_btime.tv_sec != 0)
170 : 8 : a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
171 : 4 : (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
172 : : else
173 : 4 : a = USEC_INFINITY;
174 : :
175 : 8 : r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
176 [ + + ]: 8 : if (r >= 0) {
177 [ - + ]: 4 : if (n != sizeof(le))
178 : 0 : r = -EIO;
179 : : else
180 : 4 : r = parse_crtime(le, &b);
181 : : }
182 [ + + ]: 8 : if (r < 0) {
183 [ - + ]: 4 : if (a != USEC_INFINITY) {
184 : 0 : *ret = a;
185 : 0 : return 0;
186 : : }
187 : :
188 : 4 : return r;
189 : : }
190 : :
191 [ + - ]: 4 : if (a != USEC_INFINITY)
192 : 4 : *ret = MIN(a, b);
193 : : else
194 : 0 : *ret = b;
195 : :
196 : 4 : return 0;
197 : : }
198 : :
199 : 4 : int fd_getcrtime(int fd, usec_t *ret) {
200 : 4 : 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 : 104 : int fd_setcrtime(int fd, usec_t usec) {
208 : : le64_t le;
209 : :
210 [ - + ]: 104 : assert(fd >= 0);
211 : :
212 [ + + + + ]: 104 : if (IN_SET(usec, 0, USEC_INFINITY))
213 : 100 : usec = now(CLOCK_REALTIME);
214 : :
215 : 104 : le = htole64((uint64_t) usec);
216 [ + + ]: 104 : if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
217 : 4 : return -errno;
218 : :
219 : 100 : return 0;
220 : : }
|