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