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