• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13935887515

18 Mar 2025 07:10PM UTC coverage: 71.913% (-0.03%) from 71.946%
13935887515

push

github

web-flow
Several fixes and cleanups around sd_listen_fds() (#36788)

15 of 24 new or added lines in 5 files covered. (62.5%)

993 existing lines in 54 files now uncovered.

296157 of 411825 relevant lines covered (71.91%)

710024.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.55
/src/basic/xattr-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <stdint.h>
6
#include <stdlib.h>
7
#include <sys/time.h>
8
#include <sys/xattr.h>
9
#include <threads.h>
10

11
#include "alloc-util.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "macro.h"
15
#include "missing_syscall.h"
16
#include "parse-util.h"
17
#include "sparse-endian.h"
18
#include "stat-util.h"
19
#include "stdio-util.h"
20
#include "string-util.h"
21
#include "time-util.h"
22
#include "xattr-util.h"
23

24
/* Use a single cache for all of *xattrat syscalls (added in kernel 6.13) */
25
static thread_local bool have_xattrat = true;
26

27
static int normalize_and_maybe_pin_inode(
607,765✔
28
                int *fd,
29
                const char **path,
30
                int *at_flags,
31
                int *ret_tfd,
32
                bool *ret_opath) {
33

34
        int r;
607,765✔
35

36
        assert(fd);
607,765✔
37
        assert(*fd >= 0 || *fd == AT_FDCWD);
607,765✔
38
        assert(path);
607,765✔
39
        assert(at_flags);
607,765✔
40
        assert(ret_tfd);
607,765✔
41
        assert(ret_opath);
607,765✔
42

43
        if (isempty(*path))
607,765✔
44
                *path = NULL; /* Normalize "" to NULL */
444,204✔
45

46
        if (*fd == AT_FDCWD) {
607,765✔
47
                if (!*path) /* Both unspecified? Then operate on current working directory */
20,843✔
48
                        *path = ".";
×
49

50
                *ret_tfd = -EBADF;
20,843✔
51
                *ret_opath = false;
20,843✔
52
                return 0;
20,843✔
53
        }
54

55
        *at_flags |= AT_EMPTY_PATH;
586,922✔
56

57
        if (!*path) {
586,922✔
58
                r = fd_is_opath(*fd);
444,204✔
59
                if (r < 0)
444,204✔
60
                        return r;
61
                *ret_opath = r;
444,204✔
62
                *ret_tfd = -EBADF;
444,204✔
63
                return 0;
444,204✔
64
        }
65

66
        /* If both have been specified, then we go via O_PATH */
67

68
        int tfd = openat(*fd, *path, O_PATH|O_CLOEXEC|(FLAGS_SET(*at_flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
285,121✔
69
        if (tfd < 0)
142,718✔
70
                return -errno;
1✔
71

72
        *fd = *ret_tfd = tfd;
142,717✔
73
        *path = NULL;
142,717✔
74
        *ret_opath = true;
142,717✔
75

76
        return 0;
142,717✔
77
}
78

79
static ssize_t getxattr_pinned_internal(
29,273✔
80
                int fd,
81
                const char *path,
82
                int at_flags,
83
                bool by_procfs,
84
                const char *name,
85
                char *buf,
86
                size_t size) {
87

88
        ssize_t n;
29,273✔
89

90
        assert(!path || !isempty(path));
29,273✔
91
        assert((fd >= 0) == !path);
29,273✔
92
        assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
29,273✔
93
        assert(name);
29,273✔
94
        assert(buf || size == 0);
29,273✔
95

96
        if (path)
29,273✔
97
                n = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? getxattr(path, name, buf, size)
325✔
98
                                                           : lgetxattr(path, name, buf, size);
20,837✔
99
        else
100
                n = by_procfs ? getxattr(FORMAT_PROC_FD_PATH(fd), name, buf, size)
8,436✔
101
                              : fgetxattr(fd, name, buf, size);
8,436✔
102
        if (n < 0)
29,273✔
103
                return -errno;
28,194✔
104

105
        assert((size_t) n <= size);
1,079✔
106
        return n;
107
}
108

109
int getxattr_at_malloc(
29,273✔
110
                int fd,
111
                const char *path,
112
                const char *name,
113
                int at_flags,
114
                char **ret,
115
                size_t *ret_size) {
116

117
        _cleanup_close_ int opened_fd = -EBADF;
29,273✔
118
        bool by_procfs;
29,273✔
119
        int r;
29,273✔
120

121
        assert(fd >= 0 || fd == AT_FDCWD);
29,273✔
122
        assert(name);
29,273✔
123
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
29,273✔
124
        assert(ret);
29,273✔
125

126
        /* So, this is single function that does what getxattr()/lgetxattr()/fgetxattr() does, but in one go,
127
         * and with additional bells and whistles. Specifically:
128
         *
129
         * 1. This works on O_PATH fds (via /proc/self/fd/, since getxattrat() syscall refuses them...)
130
         * 2. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is empty
131
         * 3. Does a malloc() loop, automatically sizing the allocation
132
         * 4. NUL-terminates the returned buffer (for safety)
133
         */
134

135
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
29,273✔
136
        if (r < 0)
29,273✔
137
                return r;
138

139
        size_t l = 100;
UNCOV
140
        for (unsigned n_attempts = 7;;) {
×
141
                _cleanup_free_ char *v = NULL;
29,273✔
142

143
                if (n_attempts == 0) /* If someone is racing against us, give up eventually */
29,273✔
144
                        return -EBUSY;
145
                n_attempts--;
29,273✔
146

147
                v = new(char, l+1);
29,273✔
148
                if (!v)
29,273✔
149
                        return -ENOMEM;
150

151
                l = MALLOC_ELEMENTSOF(v) - 1;
29,273✔
152

153
                ssize_t n;
29,273✔
154
                n = getxattr_pinned_internal(fd, path, at_flags, by_procfs, name, v, l);
29,273✔
155
                if (n >= 0) {
29,273✔
156
                        /* Refuse extended attributes with embedded NUL bytes if the caller isn't interested
157
                         * in the size. After all this must mean the caller assumes we return a NUL
158
                         * terminated strings, but if there's a NUL byte embedded they are definitely not
159
                         * regular strings */
160
                        if (!ret_size && n > 1 && memchr(v, 0, n - 1))
1,079✔
161
                                return -EBADMSG;
162

163
                        v[n] = 0; /* NUL terminate */
1,078✔
164
                        *ret = TAKE_PTR(v);
1,078✔
165
                        if (ret_size)
1,078✔
166
                                *ret_size = (size_t) n;
478✔
167

168
                        return 0;
1,078✔
169
                }
170
                if (n != -ERANGE)
28,194✔
171
                        return (int) n;
28,194✔
172

UNCOV
173
                n = getxattr_pinned_internal(fd, path, at_flags, by_procfs, name, NULL, 0);
×
UNCOV
174
                if (n < 0)
×
UNCOV
175
                        return (int) n;
×
176

UNCOV
177
                l = (size_t) n;
×
178
        }
179
}
180

181
int getxattr_at_bool(int fd, const char *path, const char *name, int at_flags) {
1,416✔
182
        _cleanup_free_ char *v = NULL;
1,416✔
183
        int r;
1,416✔
184

185
        r = getxattr_at_malloc(fd, path, name, at_flags, &v, /* ret_size= */ NULL);
1,416✔
186
        if (r < 0)
1,416✔
187
                return r;
188

189
        return parse_boolean(v);
559✔
190
}
191

192
static int listxattr_pinned_internal(
573,091✔
193
                int fd,
194
                const char *path,
195
                int at_flags,
196
                bool by_procfs,
197
                char *buf,
198
                size_t size) {
199

200
        ssize_t n;
573,091✔
201

202
        assert(!path || !isempty(path));
573,091✔
203
        assert((fd >= 0) == !path);
573,091✔
204
        assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
573,091✔
205
        assert(buf || size == 0);
573,091✔
206

207
        if (path)
573,091✔
UNCOV
208
                n = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? listxattr(path, buf, size)
×
209
                                                           : llistxattr(path, buf, size);
2✔
210
        else
211
                n = by_procfs ? listxattr(FORMAT_PROC_FD_PATH(fd), buf, size)
573,089✔
212
                              : flistxattr(fd, buf, size);
573,089✔
213
        if (n < 0)
573,091✔
214
                return -errno;
392✔
215

216
        assert((size_t) n <= size);
572,699✔
217

218
        if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
572,699✔
219
                return -E2BIG;
220

221
        return (int) n;
572,699✔
222
}
223

224
int listxattr_at_malloc(int fd, const char *path, int at_flags, char **ret) {
573,091✔
225
        _cleanup_close_ int opened_fd = -EBADF;
573,091✔
226
        bool by_procfs;
573,091✔
227
        int r;
573,091✔
228

229
        assert(fd >= 0 || fd == AT_FDCWD);
573,091✔
230
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
573,091✔
231
        assert(ret);
573,091✔
232

233
        /* This is to listxattr()/llistattr()/flistattr() what getxattr_at_malloc() is to getxattr()/… */
234

235
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
573,091✔
236
        if (r < 0)
573,091✔
237
                return r;
238

239
        size_t l = 100;
UNCOV
240
        for (unsigned n_attempts = 7;;) {
×
241
                _cleanup_free_ char *v = NULL;
573,091✔
242

243
                if (n_attempts == 0) /* If someone is racing against us, give up eventually */
573,091✔
244
                        return -EBUSY;
245
                n_attempts--;
573,091✔
246

247
                v = new(char, l+1);
573,091✔
248
                if (!v)
573,091✔
249
                        return -ENOMEM;
250

251
                l = MALLOC_ELEMENTSOF(v) - 1;
573,091✔
252

253
                r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, v, l);
573,091✔
254
                if (r >= 0) {
573,091✔
255
                        v[r] = 0; /* NUL terminate */
572,699✔
256
                        *ret = TAKE_PTR(v);
572,699✔
257
                        return r;
572,699✔
258
                }
259
                if (r != -ERANGE)
392✔
260
                        return r;
261

262
                r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, NULL, 0);
×
UNCOV
263
                if (r < 0)
×
264
                        return r;
265

UNCOV
266
                l = (size_t) r;
×
267
        }
268
}
269

270
int xsetxattr_full(
1,702✔
271
                int fd,
272
                const char *path,
273
                int at_flags,
274
                const char *name,
275
                const char *value,
276
                size_t size,
277
                int xattr_flags) {
278

279
        int r;
1,702✔
280

281
        assert(fd >= 0 || fd == AT_FDCWD);
1,702✔
282
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
1,702✔
283
        assert(name);
1,702✔
284
        assert(value);
1,702✔
285

286
        if (size == SIZE_MAX)
1,702✔
287
                size = strlen(value);
17✔
288

289
        if (have_xattrat && !isempty(path)) {
1,702✔
290
                struct xattr_args args = {
1✔
291
                        .value = PTR_TO_UINT64(value),
1✔
292
                        .size = size,
293
                        .flags = xattr_flags,
294
                };
295

296
                r = RET_NERRNO(setxattrat(fd, path,
1✔
297
                                          at_flags_normalize_nofollow(at_flags),
298
                                          name,
299
                                          &args, sizeof(args)));
300
                if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
1✔
301
                                     support xattr */
UNCOV
302
                        return r;
×
303

304
                have_xattrat = false;
1✔
305
        }
306

307
        _cleanup_close_ int opened_fd = -EBADF;
1,702✔
308
        bool by_procfs;
1,702✔
309

310
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
1,702✔
311
        if (r < 0)
1,702✔
312
                return r;
313

314
        if (path)
1,701✔
UNCOV
315
                r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, xattr_flags)
×
316
                                                           : lsetxattr(path, name, value, size, xattr_flags);
3✔
317
        else
318
                r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, xattr_flags)
1,698✔
319
                              : fsetxattr(fd, name, value, size, xattr_flags);
1,698✔
320
        if (r < 0)
1,701✔
321
                return -errno;
5✔
322

323
        return 0;
324
}
325

326
int xremovexattr(int fd, const char *path, int at_flags, const char *name) {
3,699✔
327
        int r;
3,699✔
328

329
        assert(fd >= 0 || fd == AT_FDCWD);
3,699✔
330
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
3,699✔
331
        assert(name);
3,699✔
332

333
        if (have_xattrat && !isempty(path)) {
3,699✔
UNCOV
334
                r = RET_NERRNO(removexattrat(fd, path,
×
335
                                             at_flags_normalize_nofollow(at_flags),
336
                                             name));
337
                if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
×
338
                                     support xattr */
UNCOV
339
                        return r;
×
340

UNCOV
341
                have_xattrat = false;
×
342
        }
343

344
        _cleanup_close_ int tfd = -EBADF;
3,699✔
345
        bool by_procfs;
3,699✔
346

347
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &tfd, &by_procfs);
3,699✔
348
        if (r < 0)
3,699✔
349
                return r;
350

351
        if (path)
3,699✔
UNCOV
352
                r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? removexattr(path, name)
×
353
                                                           : lremovexattr(path, name);
1✔
354
        else
355
                r = by_procfs ? removexattr(FORMAT_PROC_FD_PATH(fd), name)
3,698✔
356
                              : fremovexattr(fd, name);
3,698✔
357
        if (r < 0)
3,699✔
358
                return -errno;
110✔
359

360
        return 0;
361
}
362

363
static int parse_crtime(le64_t le, usec_t *ret) {
359✔
364
        usec_t u;
359✔
365

366
        assert(ret);
359✔
367

368
        assert_cc(sizeof(usec_t) == sizeof(uint64_t));
359✔
369
        assert_cc((usec_t) UINT64_MAX == USEC_INFINITY);
359✔
370

371
        u = (usec_t) le64toh(le);
359✔
372
        if (!timestamp_is_set(u))
359✔
373
                return -EIO;
374

375
        *ret = u;
359✔
376
        return 0;
359✔
377
}
378

379
int getcrtime_at(
7,308✔
380
                int fd,
381
                const char *path,
382
                int at_flags,
383
                usec_t *ret) {
384

385
        _cleanup_free_ le64_t *le = NULL;
7,308✔
386
        struct statx sx;
7,308✔
387
        usec_t a, b;
7,308✔
388
        int r;
7,308✔
389

390
        assert(fd >= 0 || fd == AT_FDCWD);
7,308✔
391
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
7,308✔
392

393
        if (isempty(path))
7,308✔
394
                at_flags |= AT_EMPTY_PATH;
6,994✔
395

396
        /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
397
         * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
398
         * implemented on various file systems on the lower level since a while, but never was accessible). However, we
399
         * needed a concept like that for vacuuming algorithms and such, hence we emulated it via a user xattr for a
400
         * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
401
         * time, where it is available. This function will read it, but it tries to keep some compatibility with older
402
         * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
403
         * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
404
         * most sense. */
405

406
        if (statx(fd, strempty(path),
14,302✔
407
                  at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC,
7,308✔
408
                  STATX_BTIME,
409
                  &sx) >= 0 &&
7,308✔
410
            FLAGS_SET(sx.stx_mask, STATX_BTIME) && sx.stx_btime.tv_sec != 0)
7,308✔
411
                a = statx_timestamp_load(&sx.stx_btime);
4,956✔
412
        else
413
                a = USEC_INFINITY;
414

415
        size_t le_size;
7,308✔
416
        r = getxattr_at_malloc(fd, path, "user.crtime_usec", at_flags, (char**) &le, &le_size);
7,308✔
417
        if (r >= 0) {
7,308✔
418
                if (le_size != sizeof(*le))
359✔
419
                        r = -EIO;
420
                else
421
                        r = parse_crtime(*le, &b);
359✔
422
        }
423
        if (r < 0) {
359✔
424
                if (a != USEC_INFINITY) {
6,949✔
425
                        if (ret)
4,597✔
426
                                *ret = a;
4,597✔
427
                        return 0;
4,597✔
428
                }
429

430
                return r;
431
        }
432

433
        if (ret)
359✔
434
                *ret = MIN(a, b);
359✔
435
        return 0;
436
}
437

438
int fd_setcrtime(int fd, usec_t usec) {
1,612✔
439
        le64_t le;
1,612✔
440

441
        assert(fd >= 0);
1,612✔
442

443
        if (!timestamp_is_set(usec))
1,612✔
444
                usec = now(CLOCK_REALTIME);
1,568✔
445

446
        le = htole64((uint64_t) usec);
1,612✔
447
        return xsetxattr_full(fd, /* path = */ NULL, AT_EMPTY_PATH,
1,612✔
448
                              "user.crtime_usec", (const char*) &le, sizeof(le),
449
                              /* xattr_flags = */ 0);
450
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc