chiark / gitweb /
2b50ee352c6e7a77d7679cfdfb64c8a7c5280784
[elogind.git] / src / basic / xattr-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <fcntl.h>
10 //#include <linux/stat.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/time.h>
15 #include <sys/xattr.h>
16
17 #include "alloc-util.h"
18 #include "fd-util.h"
19 #include "macro.h"
20 //#include "missing.h"
21 #include "sparse-endian.h"
22 #include "stdio-util.h"
23 //#include "string-util.h"
24 #include "time-util.h"
25 #include "xattr-util.h"
26
27 int 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
65 int 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
97 #if 0 /// UNNEEDED by elogind
98 int fgetxattrat_fake(
99                 int dirfd,
100                 const char *filename,
101                 const char *attribute,
102                 void *value, size_t size,
103                 int flags,
104                 size_t *ret_size) {
105
106         char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
107         _cleanup_close_ int fd = -1;
108         ssize_t l;
109
110         /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
111
112         if (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH))
113                 return -EINVAL;
114
115         if (isempty(filename)) {
116                 if (!(flags & AT_EMPTY_PATH))
117                         return -EINVAL;
118
119                 xsprintf(fn, "/proc/self/fd/%i", dirfd);
120         } else {
121                 fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
122                 if (fd < 0)
123                         return -errno;
124
125                 xsprintf(fn, "/proc/self/fd/%i", fd);
126         }
127
128         l = getxattr(fn, attribute, value, size);
129         if (l < 0)
130                 return -errno;
131
132         *ret_size = l;
133         return 0;
134 }
135
136 static int parse_crtime(le64_t le, usec_t *usec) {
137         uint64_t u;
138
139         assert(usec);
140
141         u = le64toh(le);
142         if (IN_SET(u, 0, (uint64_t) -1))
143                 return -EIO;
144
145         *usec = (usec_t) u;
146         return 0;
147 }
148
149 int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) {
150         struct_statx sx;
151         usec_t a, b;
152         le64_t le;
153         size_t n;
154         int r;
155
156         assert(ret);
157
158         if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
159                 return -EINVAL;
160
161         /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
162          * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
163          * implemented on various file systems on the lower level since a while, but never was accessible). However, we
164          * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
165          * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
166          * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
167          * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
168          * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
169          * most sense. */
170
171         if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
172             (sx.stx_mask & STATX_BTIME) &&
173             sx.stx_btime.tv_sec != 0)
174                 a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
175                         (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
176         else
177                 a = USEC_INFINITY;
178
179         r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
180         if (r >= 0) {
181                 if (n != sizeof(le))
182                         r = -EIO;
183                 else
184                         r = parse_crtime(le, &b);
185         }
186         if (r < 0) {
187                 if (a != USEC_INFINITY) {
188                         *ret = a;
189                         return 0;
190                 }
191
192                 return r;
193         }
194
195         if (a != USEC_INFINITY)
196                 *ret = MIN(a, b);
197         else
198                 *ret = b;
199
200         return 0;
201 }
202
203 int fd_getcrtime(int fd, usec_t *ret) {
204         return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
205 }
206
207 int path_getcrtime(const char *p, usec_t *ret) {
208         return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
209 }
210
211 int fd_setcrtime(int fd, usec_t usec) {
212         le64_t le;
213
214         assert(fd >= 0);
215
216         if (IN_SET(usec, 0, USEC_INFINITY))
217                 usec = now(CLOCK_REALTIME);
218
219         le = htole64((uint64_t) usec);
220         if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
221                 return -errno;
222
223         return 0;
224 }
225 #endif // 0