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