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

systemd / systemd / 20977275812

13 Jan 2026 03:18AM UTC coverage: 72.685% (+0.3%) from 72.39%
20977275812

push

github

web-flow
Bump kernel requirements to >= 5.10, and recommend >= 5.14 (#38977)

Then, this drops several unnecessary code for older kernels.

83 of 98 new or added lines in 14 files covered. (84.69%)

267 existing lines in 44 files now uncovered.

310058 of 426578 relevant lines covered (72.68%)

1143071.97 hits per line

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

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

3
#include <fcntl.h>
4
#include <linux/magic.h>
5
#include <sys/statvfs.h>
6
#include <unistd.h>
7

8
#include "alloc-util.h"
9
#include "chase.h"
10
#include "dirent-util.h"
11
#include "errno-util.h"
12
#include "fd-util.h"
13
#include "filesystems.h"
14
#include "fs-util.h"
15
#include "hash-funcs.h"
16
#include "log.h"
17
#include "mountpoint-util.h"
18
#include "path-util.h"
19
#include "siphash24.h"
20
#include "stat-util.h"
21
#include "string-util.h"
22
#include "time-util.h"
23

24
static int verify_stat_at(
2,132,623✔
25
                int fd,
26
                const char *path,
27
                bool follow,
28
                int (*verify_func)(const struct stat *st),
29
                bool verify) {
30

31
        struct stat st;
2,132,623✔
32
        int r;
2,132,623✔
33

34
        assert(fd >= 0 || fd == AT_FDCWD);
2,132,623✔
35
        assert(!isempty(path) || !follow);
2,132,623✔
36
        assert(verify_func);
2,132,623✔
37

38
        if (fstatat(fd, strempty(path), &st,
5,124,591✔
39
                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
2,132,623✔
40
                return -errno;
7,296✔
41

42
        r = verify_func(&st);
2,125,327✔
43
        return verify ? r : r >= 0;
2,125,327✔
44
}
45

46
int stat_verify_regular(const struct stat *st) {
2,811,099✔
47
        assert(st);
2,811,099✔
48

49
        /* Checks whether the specified stat() structure refers to a regular file. If not returns an
50
         * appropriate error code. */
51

52
        if (S_ISDIR(st->st_mode))
2,811,099✔
53
                return -EISDIR;
54

55
        if (S_ISLNK(st->st_mode))
2,808,907✔
56
                return -ELOOP;
57

58
        if (!S_ISREG(st->st_mode))
2,808,872✔
59
                return -EBADFD;
19✔
60

61
        return 0;
62
}
63

64
int verify_regular_at(int fd, const char *path, bool follow) {
854,845✔
65
        return verify_stat_at(fd, path, follow, stat_verify_regular, true);
854,845✔
66
}
67

68
int fd_verify_regular(int fd) {
854,566✔
69
        assert(fd >= 0);
854,566✔
70
        return verify_regular_at(fd, NULL, false);
854,566✔
71
}
72

73
int stat_verify_directory(const struct stat *st) {
1,275,294✔
74
        assert(st);
1,275,294✔
75

76
        if (S_ISLNK(st->st_mode))
1,275,294✔
77
                return -ELOOP;
78

79
        if (!S_ISDIR(st->st_mode))
1,275,284✔
80
                return -ENOTDIR;
4✔
81

82
        return 0;
83
}
84

85
int fd_verify_directory(int fd) {
56✔
86
        assert(fd >= 0);
56✔
87
        return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
56✔
88
}
89

90
int is_dir_at(int fd, const char *path, bool follow) {
1,256,026✔
91
        return verify_stat_at(fd, path, follow, stat_verify_directory, false);
1,256,026✔
92
}
93

94
int is_dir(const char *path, bool follow) {
458,839✔
95
        assert(!isempty(path));
458,839✔
96
        return is_dir_at(AT_FDCWD, path, follow);
458,839✔
97
}
98

99
int stat_verify_symlink(const struct stat *st) {
21,520✔
100
        assert(st);
21,520✔
101

102
        if (S_ISDIR(st->st_mode))
21,520✔
103
                return -EISDIR;
104

105
        if (!S_ISLNK(st->st_mode))
21,503✔
106
                return -ENOLINK;
274✔
107

108
        return 0;
109
}
110

111
int fd_verify_symlink(int fd) {
4,677✔
112
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_symlink, /* verify= */ true);
4,677✔
113
}
114

115
int is_symlink(const char *path) {
16,843✔
116
        assert(!isempty(path));
16,843✔
117
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
16,843✔
118
}
119

120
int stat_verify_linked(const struct stat *st) {
1,923,423✔
121
        assert(st);
1,923,423✔
122

123
        if (st->st_nlink <= 0)
1,923,423✔
124
                return -EIDRM; /* recognizable error. */
2✔
125

126
        return 0;
127
}
128

129
int fd_verify_linked(int fd) {
46✔
130
        assert(fd >= 0);
46✔
131
        return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
46✔
132
}
133

134
int stat_verify_device_node(const struct stat *st) {
3,475✔
135
        assert(st);
3,475✔
136

137
        if (S_ISLNK(st->st_mode))
3,475✔
138
                return -ELOOP;
139

140
        if (S_ISDIR(st->st_mode))
3,475✔
141
                return -EISDIR;
142

143
        if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
3,354✔
144
                return -ENOTTY;
5✔
145

146
        return 0;
147
}
148

149
int is_device_node(const char *path) {
130✔
150
        assert(!isempty(path));
130✔
151
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
130✔
152
}
153

154
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
38,068✔
155
        _cleanup_close_ int fd = -EBADF;
38,068✔
156
        struct dirent *buf;
38,068✔
157
        size_t m;
38,068✔
158

159
        fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
38,068✔
160
        if (fd < 0)
38,068✔
161
                return fd;
162

163
        /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("."  +
164
         * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
165
         * 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of
166
         * entries that we end up ignoring. */
167
        m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX;
2,084✔
168
        buf = alloca(m);
2,084✔
169

170
        for (;;) {
3,372✔
171
                struct dirent *de;
3,372✔
172
                ssize_t n;
3,372✔
173

174
                n = getdents64(fd, buf, m);
3,372✔
175
                if (n < 0)
3,372✔
176
                        return -errno;
×
177
                if (n == 0)
3,372✔
178
                        break;
179

180
                assert((size_t) n <= m);
2,084✔
181
                msan_unpoison(buf, n);
2,084✔
182

183
                FOREACH_DIRENT_IN_BUFFER(de, buf, n)
5,707✔
184
                        if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
4,419✔
185
                                return 0;
186
        }
187

188
        return 1;
189
}
190

191
bool stat_may_be_dev_null(struct stat *st) {
422,305✔
192
        assert(st);
422,305✔
193

194
        /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character
195
         * device node?" check. */
196

197
        return S_ISCHR(st->st_mode);
422,305✔
198
}
199

200
bool stat_is_empty(struct stat *st) {
406,140✔
201
        assert(st);
406,140✔
202

203
        return S_ISREG(st->st_mode) && st->st_size <= 0;
406,140✔
204
}
205

206
int null_or_empty_path_with_root(const char *fn, const char *root) {
306,697✔
207
        struct stat st;
306,697✔
208
        int r;
306,697✔
209

210
        assert(fn);
306,697✔
211

212
        /* A symlink to /dev/null or an empty file?
213
         * When looking under root_dir, we can't expect /dev/ to be mounted,
214
         * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
215

216
        if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
613,394✔
217
                return true;
306,697✔
218

219
        r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
306,527✔
220
        if (r < 0)
306,527✔
221
                return r;
222

223
        return null_or_empty(&st);
306,410✔
224
}
225

226
int fd_is_read_only_fs(int fd) {
1,808✔
227
        struct statfs st;
1,808✔
228

229
        assert(fd >= 0);
1,808✔
230

231
        if (fstatfs(fd, &st) < 0)
1,808✔
232
                return -errno;
×
233

234
        if (st.f_flags & ST_RDONLY)
1,808✔
235
                return true;
236

237
        if (is_network_fs(&st)) {
1,435✔
238
                /* On NFS, fstatfs() might not reflect whether we can actually write to the remote share.
239
                 * Let's try again with access(W_OK) which is more reliable, at least sometimes. */
240
                if (access_fd(fd, W_OK) == -EROFS)
×
241
                        return true;
×
242
        }
243

244
        return false;
245
}
246

247
int path_is_read_only_fs(const char *path) {
1,514✔
248
        _cleanup_close_ int fd = -EBADF;
1,514✔
249

250
        assert(path);
1,514✔
251

252
        fd = open(path, O_CLOEXEC | O_PATH);
1,514✔
253
        if (fd < 0)
1,514✔
254
                return -errno;
152✔
255

256
        return fd_is_read_only_fs(fd);
1,362✔
257
}
258

259
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) {
13,975✔
260
        struct stat sta, stb;
13,975✔
261
        int r;
13,975✔
262

263
        assert(fda >= 0 || fda == AT_FDCWD);
13,975✔
264
        assert(fdb >= 0 || fdb == AT_FDCWD);
13,975✔
265
        assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
13,975✔
266

267
        /* Refuse an unset filea or fileb early unless AT_EMPTY_PATH is set */
268
        if ((isempty(filea) || isempty(fileb)) && !FLAGS_SET(flags, AT_EMPTY_PATH))
27,607✔
269
                return -EINVAL;
13,975✔
270

271
        /* Shortcut: comparing the same fd with itself means we can return true */
272
        if (fda >= 0 && fda == fdb && isempty(filea) && isempty(fileb) && FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW))
13,977✔
273
                return true;
274

275
        _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
27,949✔
276
        if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
13,974✔
277
                /* Let's try to use the name_to_handle_at() AT_HANDLE_FID API to identify identical
278
                 * inodes. We have to issue multiple calls on the same file for that (first, to acquire the
279
                 * FID, and then to check if .st_dev is actually the same). Hence let's pin the inode in
280
                 * between via O_PATH, unless we already have an fd for it. */
281

282
                if (!isempty(filea)) {
13,974✔
283
                        pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
13,632✔
284
                        if (pin_a < 0)
13,632✔
285
                                return -errno;
13,744✔
286

287
                        fda = pin_a;
13,261✔
288
                        filea = NULL;
13,261✔
289
                        flags |= AT_EMPTY_PATH;
13,261✔
290
                }
291

292
                if (!isempty(fileb)) {
13,603✔
293
                        pin_b = openat(fdb, fileb, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
13,259✔
294
                        if (pin_b < 0)
13,259✔
295
                                return -errno;
4✔
296

297
                        fdb = pin_b;
13,255✔
298
                        fileb = NULL;
13,255✔
299
                        flags |= AT_EMPTY_PATH;
13,255✔
300
                }
301

302
                int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
13,599✔
303
                _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
13,599✔
304
                int mntida = -1, mntidb = -1;
13,599✔
305

306
                r = name_to_handle_at_try_fid(
13,599✔
307
                                fda,
308
                                filea,
309
                                &ha,
310
                                &mntida,
311
                                ntha_flags);
312
                if (r < 0) {
13,599✔
313
                        if (is_name_to_handle_at_fatal_error(r))
×
314
                                return r;
315

316
                        goto fallback;
×
317
                }
318

319
                r = name_to_handle_at_try_fid(
13,599✔
320
                                fdb,
321
                                fileb,
322
                                &hb,
323
                                &mntidb,
324
                                ntha_flags);
325
                if (r < 0) {
13,599✔
326
                        if (is_name_to_handle_at_fatal_error(r))
×
327
                                return r;
328

329
                        goto fallback;
×
330
                }
331

332
                /* Now compare the two file handles */
333
                if (!file_handle_equal(ha, hb))
13,599✔
334
                        return false;
335

336
                /* If the file handles are the same and they come from the same mount ID? Great, then we are
337
                 * good, they are definitely the same */
338
                if (mntida == mntidb)
13,252✔
339
                        return true;
340

341
                /* File handles are the same, they are not on the same mount id. This might either be because
342
                 * they are on two entirely different file systems, that just happen to have the same FIDs
343
                 * (because they originally where created off the same disk images), or it could be because
344
                 * they are located on two distinct bind mounts of the same fs. To check that, let's look at
345
                 * .st_rdev of the inode. We simply reuse the fallback codepath for that, since it checks
346
                 * exactly that (it checks slightly more, but we don't care.) */
347
        }
348

349
fallback:
×
350
        if (fstatat(fda, strempty(filea), &sta, flags) < 0)
460✔
351
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
×
352

353
        if (fstatat(fdb, strempty(fileb), &stb, flags) < 0)
460✔
354
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(fileb));
×
355

356
        return stat_inode_same(&sta, &stb);
230✔
357
}
358

359
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
8,458,196✔
360
        assert(s);
8,458,196✔
361
        assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
8,458,196✔
362

363
        return F_TYPE_EQUAL(s->f_type, magic_value);
8,458,196✔
364
}
365

366
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
8,453,134✔
367
        struct statfs s;
8,453,134✔
368
        int r;
8,453,134✔
369

370
        r = xstatfsat(dir_fd, path, &s);
8,453,134✔
371
        if (r < 0)
8,453,134✔
372
                return r;
8,453,134✔
373

374
        return is_fs_type(&s, magic_value);
8,453,133✔
375
}
376

377
bool is_temporary_fs(const struct statfs *s) {
9,848✔
378
        return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
9,848✔
379
}
380

381
bool is_network_fs(const struct statfs *s) {
22,579✔
382
        return fs_in_group(s, FILESYSTEM_SET_NETWORK);
22,579✔
383
}
384

385
int fd_is_temporary_fs(int fd) {
153✔
386
        struct statfs s;
153✔
387

388
        if (fstatfs(fd, &s) < 0)
153✔
389
                return -errno;
×
390

391
        return is_temporary_fs(&s);
153✔
392
}
393

394
int fd_is_network_fs(int fd) {
21,037✔
395
        struct statfs s;
21,037✔
396

397
        if (fstatfs(fd, &s) < 0)
21,037✔
398
                return -errno;
×
399

400
        return is_network_fs(&s);
21,037✔
401
}
402

403
int path_is_temporary_fs(const char *path) {
11✔
404
        struct statfs s;
11✔
405

406
        if (statfs(path, &s) < 0)
11✔
407
                return -errno;
2✔
408

409
        return is_temporary_fs(&s);
9✔
410
}
411

412
int path_is_network_fs(const char *path) {
×
413
        struct statfs s;
×
414

415
        if (statfs(path, &s) < 0)
×
416
                return -errno;
×
417

418
        return is_network_fs(&s);
×
419
}
420

421
int proc_mounted(void) {
26,911✔
422
        int r;
26,911✔
423

424
        /* A quick check of procfs is properly mounted */
425

426
        r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
26,911✔
427
        if (r == -ENOENT) /* not mounted at all */
26,911✔
428
                return false;
×
429

430
        return r;
431
}
432

433
bool stat_inode_same(const struct stat *a, const struct stat *b) {
1,224,990✔
434

435
        /* Returns if the specified stat structure references the same (though possibly modified) inode. Does
436
         * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
437

438
        return stat_is_set(a) && stat_is_set(b) &&
2,447,821✔
439
                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
1,181,884✔
440
                a->st_dev == b->st_dev &&
2,364,610✔
441
                a->st_ino == b->st_ino;
1,139,620✔
442
}
443

444
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
130,314✔
445

446
        /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
447
         * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
448
         * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
449
         * size, backing device, inode type and if this refers to a device not the major/minor.
450
         *
451
         * Note that we don't care if file attributes such as ownership or access mode change, this here is
452
         * about contents of the file. The purpose here is to detect file contents changes, and nothing
453
         * else. */
454

455
        return stat_inode_same(a, b) &&
130,314✔
456
                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
80,176✔
457
                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
79,893✔
458
                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
210,166✔
459
                (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
79,852✔
460
}
461

462
bool statx_inode_same(const struct statx *a, const struct statx *b) {
4,309,521✔
463

464
        /* Same as stat_inode_same() but for struct statx */
465

466
        if (!statx_is_set(a) || !statx_is_set(b))
8,619,042✔
467
                return false;
468

469
        assert(FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO));
4,309,521✔
470
        assert(FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO));
4,309,521✔
471

472
        return
4,309,521✔
473
                ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
8,615,253✔
474
                a->stx_dev_major == b->stx_dev_major &&
4,305,732✔
475
                a->stx_dev_minor == b->stx_dev_minor &&
12,772,956✔
476
                a->stx_ino == b->stx_ino;
4,151,948✔
477
}
478

479
bool statx_mount_same(const struct statx *a, const struct statx *b) {
4,079,573✔
480
        if (!statx_is_set(a) || !statx_is_set(b))
8,159,146✔
481
                return false;
482

483
        assert(FLAGS_SET(a->stx_mask, STATX_MNT_ID));
4,079,573✔
484
        assert(FLAGS_SET(b->stx_mask, STATX_MNT_ID));
4,079,573✔
485

486
        return a->stx_mnt_id == b->stx_mnt_id;
4,079,573✔
487
}
488

489
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
8,453,588✔
490
        _cleanup_close_ int fd = -EBADF;
8,453,588✔
491

492
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
8,453,588✔
493
        assert(ret);
8,453,588✔
494

495
        if (!isempty(path)) {
8,453,588✔
496
                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
27,491✔
497
                if (fd < 0)
27,491✔
498
                        return fd;
499
                dir_fd = fd;
500
        }
501

502
        return RET_NERRNO(fstatfs(dir_fd, ret));
8,453,588✔
503
}
504

505
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
5,124✔
506
        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
5,124✔
507
}
508
nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
820✔
509
        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
820✔
510
}
511

512
void inode_hash_func(const struct stat *q, struct siphash *state) {
37,725✔
513
        siphash24_compress_typesafe(q->st_dev, state);
37,725✔
514
        siphash24_compress_typesafe(q->st_ino, state);
37,725✔
515
}
37,725✔
516

517
int inode_compare_func(const struct stat *a, const struct stat *b) {
31,081✔
518
        int r;
31,081✔
519

520
        r = CMP(a->st_dev, b->st_dev);
31,081✔
521
        if (r != 0)
26,393✔
522
                return r;
4,746✔
523

524
        return CMP(a->st_ino, b->st_ino);
26,335✔
525
}
526

527
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
861✔
528

529
const char* inode_type_to_string(mode_t m) {
7,613✔
530

531
        /* Returns a short string for the inode type. We use the same name as the underlying macros for each
532
         * inode type. */
533

534
        switch (m & S_IFMT) {
7,613✔
535
        case S_IFREG:
536
                return "reg";
537
        case S_IFDIR:
2,910✔
538
                return "dir";
2,910✔
539
        case S_IFLNK:
1✔
540
                return "lnk";
1✔
541
        case S_IFCHR:
1,000✔
542
                return "chr";
1,000✔
543
        case S_IFBLK:
461✔
544
                return "blk";
461✔
545
        case S_IFIFO:
461✔
546
                return "fifo";
461✔
547
        case S_IFSOCK:
605✔
548
                return "sock";
605✔
549
        }
550

551
        /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
552
         * return an .st_mode where we'll return NULL here! */
553
        return NULL;
4✔
554
}
555

556
mode_t inode_type_from_string(const char *s) {
13✔
557
        if (!s)
13✔
558
                return MODE_INVALID;
559

560
        if (streq(s, "reg"))
13✔
561
                return S_IFREG;
562
        if (streq(s, "dir"))
10✔
563
                return S_IFDIR;
564
        if (streq(s, "lnk"))
7✔
565
                return S_IFLNK;
566
        if (streq(s, "chr"))
6✔
567
                return S_IFCHR;
568
        if (streq(s, "blk"))
5✔
569
                return S_IFBLK;
570
        if (streq(s, "fifo"))
4✔
571
                return S_IFIFO;
572
        if (streq(s, "sock"))
2✔
573
                return S_IFSOCK;
2✔
574

575
        return MODE_INVALID;
576
}
577

578
int statx_warn_mount_root(const struct statx *sx, int log_level) {
81,710✔
579
        assert(sx);
81,710✔
580

581
        /* The STATX_ATTR_MOUNT_ROOT flag is supported since kernel v5.8. */
582
        if (!FLAGS_SET(sx->stx_attributes_mask, STATX_ATTR_MOUNT_ROOT))
81,710✔
NEW
583
                return log_full_errno(log_level, SYNTHETIC_ERRNO(ENOSYS),
×
584
                                      "statx() did not set STATX_ATTR_MOUNT_ROOT, running on an old kernel?");
585

586
        return 0;
587
}
588

589
int statx_warn_mount_id(const struct statx *sx, int log_level) {
6,914✔
590
        assert(sx);
6,914✔
591

592
        /* The STATX_MNT_ID flag is supported since kernel v5.10. */
593
        if (!FLAGS_SET(sx->stx_mask, STATX_MNT_ID))
6,914✔
NEW
594
                return log_full_errno(log_level, SYNTHETIC_ERRNO(ENOSYS),
×
595
                                      "statx() does not support STATX_MNT_ID, running on an old kernel?");
596

597
        return 0;
598
}
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