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

systemd / systemd / 18607725477

17 Oct 2025 07:30PM UTC coverage: 72.189% (-0.2%) from 72.363%
18607725477

push

github

web-flow
Assorted coverity fixes (#39355)

5 of 6 new or added lines in 4 files covered. (83.33%)

858 existing lines in 51 files now uncovered.

303873 of 420941 relevant lines covered (72.19%)

1152650.64 hits per line

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

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

3
#include <fcntl.h>
4
#include <sys/xattr.h>
5
#include <threads.h>
6

7
#include "alloc-util.h"
8
#include "errno-util.h"
9
#include "fd-util.h"
10
#include "fs-util.h"
11
#include "nulstr-util.h"
12
#include "parse-util.h"
13
#include "sparse-endian.h"
14
#include "stat-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "time-util.h"
18
#include "xattr-util.h"
19

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

23
static int normalize_and_maybe_pin_inode(
605,020✔
24
                int *fd,
25
                const char **path,
26
                int *at_flags,
27
                int *ret_tfd,
28
                bool *ret_opath) {
29

30
        int r;
605,020✔
31

32
        assert(fd);
605,020✔
33
        assert(*fd >= 0 || *fd == AT_FDCWD);
605,020✔
34
        assert(path);
605,020✔
35
        assert(at_flags);
605,020✔
36
        assert(ret_tfd);
605,020✔
37
        assert(ret_opath);
605,020✔
38

39
        if (isempty(*path))
605,020✔
40
                *path = NULL; /* Normalize "" to NULL */
447,121✔
41

42
        if (*fd == AT_FDCWD) {
605,020✔
43
                if (!*path) /* Both unspecified? Then operate on current working directory */
16,133✔
44
                        *path = ".";
×
45

46
                *ret_tfd = -EBADF;
16,133✔
47
                *ret_opath = false;
16,133✔
48
                return 0;
16,133✔
49
        }
50

51
        *at_flags |= AT_EMPTY_PATH;
588,887✔
52

53
        if (!*path) {
588,887✔
54
                r = fd_is_opath(*fd);
447,121✔
55
                if (r < 0)
447,121✔
56
                        return r;
57
                *ret_opath = r;
447,121✔
58
                *ret_tfd = -EBADF;
447,121✔
59
                return 0;
447,121✔
60
        }
61

62
        /* If both have been specified, then we go via O_PATH */
63

64
        int tfd = openat(*fd, *path, O_PATH|O_CLOEXEC|(FLAGS_SET(*at_flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
283,195✔
65
        if (tfd < 0)
141,766✔
UNCOV
66
                return -errno;
×
67

68
        *fd = *ret_tfd = tfd;
141,766✔
69
        *path = NULL;
141,766✔
70
        *ret_opath = true;
141,766✔
71

72
        return 0;
141,766✔
73
}
74

75
static ssize_t getxattr_pinned_internal(
24,908✔
76
                int fd,
77
                const char *path,
78
                int at_flags,
79
                bool by_procfs,
80
                const char *name,
81
                char *buf,
82
                size_t size) {
83

84
        ssize_t n;
24,908✔
85

86
        assert(!path || !isempty(path));
24,908✔
87
        assert((fd >= 0) == !path);
24,908✔
88
        assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
24,908✔
89
        assert(name);
24,908✔
90
        assert(buf || size == 0);
24,908✔
91

92
        if (path)
24,908✔
93
                n = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? getxattr(path, name, buf, size)
331✔
94
                                                           : lgetxattr(path, name, buf, size);
16,131✔
95
        else
96
                n = by_procfs ? getxattr(FORMAT_PROC_FD_PATH(fd), name, buf, size)
8,339✔
97
                              : fgetxattr(fd, name, buf, size);
8,777✔
98
        if (n < 0)
24,908✔
99
                return -errno;
23,538✔
100

101
        assert(size == 0 || (size_t) n <= size);
1,370✔
102
        return n;
103
}
104

105
int getxattr_at_malloc(
24,794✔
106
                int fd,
107
                const char *path,
108
                const char *name,
109
                int at_flags,
110
                char **ret,
111
                size_t *ret_size) {
112

113
        _cleanup_close_ int opened_fd = -EBADF;
24,794✔
114
        bool by_procfs;
24,794✔
115
        int r;
24,794✔
116

117
        assert(fd >= 0 || fd == AT_FDCWD);
24,794✔
118
        assert(name);
24,794✔
119
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
24,794✔
120
        assert(ret);
24,794✔
121

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

131
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
24,794✔
132
        if (r < 0)
24,794✔
133
                return r;
134

135
        size_t l = 100;
136
        for (unsigned n_attempts = 7;;) {
57✔
137
                _cleanup_free_ char *v = NULL;
24,851✔
138

139
                if (n_attempts == 0) /* If someone is racing against us, give up eventually */
24,851✔
140
                        return -EBUSY;
141
                n_attempts--;
24,851✔
142

143
                v = new(char, l+1);
24,851✔
144
                if (!v)
24,851✔
145
                        return -ENOMEM;
146

147
                l = MALLOC_ELEMENTSOF(v) - 1;
24,851✔
148

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

159
                        v[n] = 0; /* NUL terminate */
1,312✔
160
                        *ret = TAKE_PTR(v);
1,312✔
161
                        if (ret_size)
1,312✔
162
                                *ret_size = (size_t) n;
727✔
163

164
                        return 0;
1,312✔
165
                }
166
                if (n != -ERANGE)
23,538✔
167
                        return (int) n;
23,481✔
168

169
                n = getxattr_pinned_internal(fd, path, at_flags, by_procfs, name, NULL, 0);
57✔
170
                if (n < 0)
57✔
171
                        return (int) n;
×
172

173
                l = (size_t) n;
57✔
174
        }
175
}
176

177
int getxattr_at_bool(int fd, const char *path, const char *name, int at_flags) {
1,130✔
178
        _cleanup_free_ char *v = NULL;
1,130✔
179
        int r;
1,130✔
180

181
        r = getxattr_at_malloc(fd, path, name, at_flags, &v, /* ret_size= */ NULL);
1,130✔
182
        if (r < 0)
1,130✔
183
                return r;
184

185
        return parse_boolean(v);
559✔
186
}
187

188
int getxattr_at_strv(int fd, const char *path, const char *name, int at_flags, char ***ret_strv) {
36✔
189
        _cleanup_free_ char *nulstr = NULL;
36✔
190
        size_t nulstr_size;
36✔
191
        int r;
36✔
192

193
        assert(ret_strv);
36✔
194

195
        r = getxattr_at_malloc(fd, path, name, at_flags, &nulstr, &nulstr_size);
36✔
196
        if (r < 0)
36✔
197
                return r;
198

199
        _cleanup_strv_free_ char **l = strv_parse_nulstr(nulstr, nulstr_size);
46✔
200
        if (!l)
23✔
201
                return -ENOMEM;
202

203
        *ret_strv = TAKE_PTR(l);
23✔
204
        return 0;
23✔
205
}
206

207
static int listxattr_pinned_internal(
574,955✔
208
                int fd,
209
                const char *path,
210
                int at_flags,
211
                bool by_procfs,
212
                char *buf,
213
                size_t size) {
214

215
        ssize_t n;
574,955✔
216

217
        assert(!path || !isempty(path));
574,955✔
218
        assert((fd >= 0) == !path);
574,955✔
219
        assert(path || FLAGS_SET(at_flags, AT_EMPTY_PATH));
574,955✔
220
        assert(buf || size == 0);
574,955✔
221

222
        if (path)
574,955✔
223
                n = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? listxattr(path, buf, size)
×
224
                                                           : llistxattr(path, buf, size);
2✔
225
        else
226
                n = by_procfs ? listxattr(FORMAT_PROC_FD_PATH(fd), buf, size)
141,450✔
227
                              : flistxattr(fd, buf, size);
574,953✔
228
        if (n < 0)
574,955✔
229
                return -errno;
392✔
230

231
        assert(size == 0 || (size_t) n <= size);
574,563✔
232

233
        if (n > INT_MAX) /* We couldn't return this as 'int' anymore */
574,563✔
234
                return -E2BIG;
235

236
        return (int) n;
574,563✔
237
}
238

239
int listxattr_at_malloc(int fd, const char *path, int at_flags, char **ret) {
574,955✔
240
        _cleanup_close_ int opened_fd = -EBADF;
574,955✔
241
        bool by_procfs;
574,955✔
242
        int r;
574,955✔
243

244
        assert(fd >= 0 || fd == AT_FDCWD);
574,955✔
245
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
574,955✔
246
        assert(ret);
574,955✔
247

248
        /* This is to listxattr()/llistattr()/flistattr() what getxattr_at_malloc() is to getxattr()/… */
249

250
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
574,955✔
251
        if (r < 0)
574,955✔
252
                return r;
253

254
        size_t l = 100;
255
        for (unsigned n_attempts = 7;;) {
×
256
                _cleanup_free_ char *v = NULL;
574,955✔
257

258
                if (n_attempts == 0) /* If someone is racing against us, give up eventually */
574,955✔
259
                        return -EBUSY;
260
                n_attempts--;
574,955✔
261

262
                v = new(char, l+1);
574,955✔
263
                if (!v)
574,955✔
264
                        return -ENOMEM;
265

266
                l = MALLOC_ELEMENTSOF(v) - 1;
574,955✔
267

268
                r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, v, l);
574,955✔
269
                if (r >= 0) {
574,955✔
270
                        v[r] = 0; /* NUL terminate */
574,563✔
271
                        *ret = TAKE_PTR(v);
574,563✔
272
                        return r;
574,563✔
273
                }
274
                if (r != -ERANGE)
392✔
275
                        return r;
276

277
                r = listxattr_pinned_internal(fd, path, at_flags, by_procfs, NULL, 0);
×
278
                if (r < 0)
×
279
                        return r;
280

281
                l = (size_t) r;
×
282
        }
283
}
284

285
int xsetxattr_full(
2,047✔
286
                int fd,
287
                const char *path,
288
                int at_flags,
289
                const char *name,
290
                const char *value,
291
                size_t size,
292
                int xattr_flags) {
293

294
        int r;
2,047✔
295

296
        assert(fd >= 0 || fd == AT_FDCWD);
2,047✔
297
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
2,047✔
298
        assert(name);
2,047✔
299
        assert(value);
2,047✔
300

301
        if (size == SIZE_MAX)
2,047✔
302
                size = strlen(value);
17✔
303

304
        if (have_xattrat && !isempty(path)) {
2,047✔
305
                struct xattr_args args = {
9✔
306
                        .value = PTR_TO_UINT64(value),
9✔
307
                        .size = size,
308
                        .flags = xattr_flags,
309
                };
310

311
                r = RET_NERRNO(setxattrat(fd, path,
9✔
312
                                          at_flags_normalize_nofollow(at_flags),
313
                                          name,
314
                                          &args, sizeof(args)));
315
                if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
4✔
316
                                     support xattr */
317
                        return r;
9✔
318

UNCOV
319
                have_xattrat = false;
×
320
        }
321

322
        _cleanup_close_ int opened_fd = -EBADF;
2,047✔
323
        bool by_procfs;
2,038✔
324

325
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &opened_fd, &by_procfs);
2,038✔
326
        if (r < 0)
2,038✔
327
                return r;
328

329
        if (path)
2,038✔
330
                r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, xattr_flags)
×
UNCOV
331
                                                           : lsetxattr(path, name, value, size, xattr_flags);
×
332
        else
333
                r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, xattr_flags)
6✔
334
                              : fsetxattr(fd, name, value, size, xattr_flags);
2,038✔
335
        if (r < 0)
2,038✔
336
                return -errno;
2✔
337

338
        return 0;
339
}
340

341
int xsetxattr_strv(int fd, const char *path, int at_flags, const char *name, char * const *l) {
160✔
342
        _cleanup_free_ char *nulstr = NULL;
160✔
343
        size_t size;
160✔
344
        int r;
160✔
345

346
        assert(name);
160✔
347

348
        r = strv_make_nulstr(l, &nulstr, &size);
160✔
349
        if (r < 0)
160✔
350
                return r;
351

352
        return xsetxattr_full(fd, path, at_flags, name, nulstr, size, /* xattr_flags= */ 0);
160✔
353
}
354

355
int xremovexattr(int fd, const char *path, int at_flags, const char *name) {
3,235✔
356
        int r;
3,235✔
357

358
        assert(fd >= 0 || fd == AT_FDCWD);
3,235✔
359
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
3,235✔
360
        assert(name);
3,235✔
361

362
        if (have_xattrat && !isempty(path)) {
3,235✔
363
                r = RET_NERRNO(removexattrat(fd, path,
2✔
364
                                             at_flags_normalize_nofollow(at_flags),
365
                                             name));
366
                if (r != -ENOSYS) /* No ERRNO_IS_NOT_SUPPORTED here, as EOPNOTSUPP denotes the fs doesn't
×
367
                                     support xattr */
368
                        return r;
2✔
369

370
                have_xattrat = false;
×
371
        }
372

373
        _cleanup_close_ int tfd = -EBADF;
3,235✔
374
        bool by_procfs;
3,233✔
375

376
        r = normalize_and_maybe_pin_inode(&fd, &path, &at_flags, &tfd, &by_procfs);
3,233✔
377
        if (r < 0)
3,233✔
378
                return r;
379

380
        if (path)
3,233✔
381
                r = FLAGS_SET(at_flags, AT_SYMLINK_FOLLOW) ? removexattr(path, name)
×
UNCOV
382
                                                           : lremovexattr(path, name);
×
383
        else
384
                r = by_procfs ? removexattr(FORMAT_PROC_FD_PATH(fd), name)
1,543✔
385
                              : fremovexattr(fd, name);
3,233✔
386
        if (r < 0)
3,233✔
387
                return -errno;
2✔
388

389
        return 0;
390
}
391

392
static int parse_crtime(le64_t le, usec_t *ret) {
382✔
393
        usec_t u;
382✔
394

395
        assert(ret);
382✔
396

397
        assert_cc(sizeof(usec_t) == sizeof(uint64_t));
382✔
398
        assert_cc((usec_t) UINT64_MAX == USEC_INFINITY);
382✔
399

400
        u = (usec_t) le64toh(le);
382✔
401
        if (!timestamp_is_set(u))
382✔
402
                return -EIO;
403

404
        *ret = u;
382✔
405
        return 0;
382✔
406
}
407

408
int getcrtime_at(
7,354✔
409
                int fd,
410
                const char *path,
411
                int at_flags,
412
                usec_t *ret) {
413

414
        _cleanup_free_ le64_t *le = NULL;
7,354✔
415
        struct statx sx;
7,354✔
416
        usec_t a, b;
7,354✔
417
        int r;
7,354✔
418

419
        assert(fd >= 0 || fd == AT_FDCWD);
7,354✔
420
        assert((at_flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
7,354✔
421

422
        if (isempty(path))
7,354✔
423
                at_flags |= AT_EMPTY_PATH;
7,017✔
424

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

435
        if (statx(fd, strempty(path),
14,371✔
436
                  at_flags_normalize_nofollow(at_flags)|AT_STATX_DONT_SYNC,
7,354✔
437
                  STATX_BTIME,
438
                  &sx) >= 0 &&
7,354✔
439
            FLAGS_SET(sx.stx_mask, STATX_BTIME) && sx.stx_btime.tv_sec != 0)
7,354✔
440
                a = statx_timestamp_load(&sx.stx_btime);
5,002✔
441
        else
442
                a = USEC_INFINITY;
443

444
        size_t le_size;
7,354✔
445
        r = getxattr_at_malloc(fd, path, "user.crtime_usec", at_flags, (char**) &le, &le_size);
7,354✔
446
        if (r >= 0) {
7,354✔
447
                if (le_size != sizeof(*le))
382✔
448
                        r = -EIO;
449
                else
450
                        r = parse_crtime(*le, &b);
382✔
451
        }
452
        if (r < 0) {
382✔
453
                if (a != USEC_INFINITY) {
6,972✔
454
                        if (ret)
4,620✔
455
                                *ret = a;
4,620✔
456
                        return 0;
4,620✔
457
                }
458

459
                return r;
460
        }
461

462
        if (ret)
382✔
463
                *ret = MIN(a, b);
382✔
464
        return 0;
465
}
466

467
int fd_setcrtime(int fd, usec_t usec) {
1,624✔
468
        le64_t le;
1,624✔
469

470
        assert(fd >= 0);
1,624✔
471

472
        if (!timestamp_is_set(usec))
1,624✔
473
                usec = now(CLOCK_REALTIME);
1,578✔
474

475
        le = htole64((uint64_t) usec);
1,624✔
476
        return xsetxattr_full(fd, /* path = */ NULL, AT_EMPTY_PATH,
1,624✔
477
                              "user.crtime_usec", (const char*) &le, sizeof(le),
478
                              /* xattr_flags = */ 0);
479
}
480

481
bool xattr_is_acl(const char *name) {
×
482
        return STR_IN_SET(
×
483
                        ASSERT_PTR(name),
484
                        "system.posix_acl_access",
485
                        "system.posix_acl_default");
486
}
487

488
bool xattr_is_selinux(const char *name) {
×
489
        return streq(ASSERT_PTR(name), "security.selinux");
×
490
}
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