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

systemd / systemd / 22648815823

03 Mar 2026 11:47PM UTC coverage: 72.603% (+0.3%) from 72.342%
22648815823

push

github

yuwata
udevadm: fix --help text for udevadm test-builtin

316021 of 435275 relevant lines covered (72.6%)

1244520.48 hits per line

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

92.26
/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 "bitfield.h"
10
#include "chase.h"
11
#include "dirent-util.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "filesystems.h"
15
#include "fs-util.h"
16
#include "hash-funcs.h"
17
#include "log.h"
18
#include "mountpoint-util.h"
19
#include "path-util.h"
20
#include "siphash24.h"
21
#include "stat-util.h"
22
#include "string-util.h"
23
#include "time-util.h"
24

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

32
        struct stat st;
2,208,470✔
33
        int r;
2,208,470✔
34

35
        assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
2,208,470✔
36
        assert(!isempty(path) || !follow);
2,208,470✔
37
        assert(verify_func);
2,208,470✔
38

39
        _cleanup_free_ char *p = NULL;
2,208,470✔
40
        r = resolve_xat_fdroot(&fd, &path, &p);
2,208,470✔
41
        if (r < 0)
2,208,470✔
42
                return r;
43

44
        if (fstatat(fd, strempty(path), &st,
3,141,626✔
45
                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
3,483,784✔
46
                return -errno;
8,310✔
47

48
        r = verify_func(&st);
2,200,160✔
49
        return verify ? r : r >= 0;
2,200,160✔
50
}
51

52
static int mode_verify_regular(mode_t mode) {
2,907,612✔
53
        if (S_ISDIR(mode))
2,907,612✔
54
                return -EISDIR;
55

56
        if (S_ISLNK(mode))
2,907,537✔
57
                return -ELOOP;
58

59
        if (!S_ISREG(mode))
2,907,500✔
60
                return -EBADFD;
20✔
61

62
        return 0;
63
}
64

65
int stat_verify_regular(const struct stat *st) {
2,907,554✔
66
        assert(st);
2,907,554✔
67

68
        /* Checks whether the specified stat() structure refers to a regular file. If not returns an
69
         * appropriate error code. */
70

71
        return mode_verify_regular(st->st_mode);
2,907,554✔
72
}
73

74
int statx_verify_regular(const struct statx *stx) {
58✔
75
        assert(stx);
58✔
76

77
        if (!FLAGS_SET(stx->stx_mask, STATX_TYPE))
58✔
78
                return -ENODATA;
79

80
        return mode_verify_regular(stx->stx_mode);
58✔
81
}
82

83
int verify_regular_at(int fd, const char *path, bool follow) {
927,376✔
84
        return verify_stat_at(fd, path, follow, stat_verify_regular, true);
927,376✔
85
}
86

87
int fd_verify_regular(int fd) {
927,093✔
88
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
927,093✔
89
                return -EISDIR;
90

91
        return verify_regular_at(fd, /* path= */ NULL, /* follow= */ false);
927,093✔
92
}
93

94
static int mode_verify_directory(mode_t mode) {
1,311,150✔
95
        if (S_ISLNK(mode))
1,311,150✔
96
                return -ELOOP;
97

98
        if (!S_ISDIR(mode))
1,311,140✔
99
                return -ENOTDIR;
7✔
100

101
        return 0;
102
}
103

104
int stat_verify_directory(const struct stat *st) {
1,250,365✔
105
        assert(st);
1,250,365✔
106

107
        return mode_verify_directory(st->st_mode);
1,250,365✔
108
}
109

110
int statx_verify_directory(const struct statx *stx) {
60,785✔
111
        assert(stx);
60,785✔
112

113
        if (!FLAGS_SET(stx->stx_mask, STATX_TYPE))
60,785✔
114
                return -ENODATA;
115

116
        return mode_verify_directory(stx->stx_mode);
60,785✔
117
}
118

119
int fd_verify_directory(int fd) {
166✔
120
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
166✔
121
                return 0;
122

123
        return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
165✔
124
}
125

126
int is_dir_at(int fd, const char *path, bool follow) {
1,255,934✔
127
        return verify_stat_at(fd, path, follow, stat_verify_directory, false);
1,255,934✔
128
}
129

130
int is_dir(const char *path, bool follow) {
448,863✔
131
        assert(!isempty(path));
448,863✔
132
        return is_dir_at(AT_FDCWD, path, follow);
448,863✔
133
}
134

135
int stat_verify_symlink(const struct stat *st) {
24,816✔
136
        assert(st);
24,816✔
137

138
        if (S_ISDIR(st->st_mode))
24,816✔
139
                return -EISDIR;
140

141
        if (!S_ISLNK(st->st_mode))
24,796✔
142
                return -ENOLINK;
287✔
143

144
        return 0;
145
}
146

147
int fd_verify_symlink(int fd) {
5,851✔
148
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_symlink, /* verify= */ true);
5,851✔
149
}
150

151
int is_symlink(const char *path) {
18,965✔
152
        assert(!isempty(path));
18,965✔
153
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
18,965✔
154
}
155

156
static mode_t mode_verify_socket(mode_t mode) {
74✔
157
        if (S_ISLNK(mode))
74✔
158
                return -ELOOP;
159

160
        if (S_ISDIR(mode))
74✔
161
                return -EISDIR;
162

163
        if (!S_ISSOCK(mode))
73✔
164
                return -ENOTSOCK;
×
165

166
        return 0;
167
}
168

169
int stat_verify_socket(const struct stat *st) {
2✔
170
        assert(st);
2✔
171

172
        return mode_verify_socket(st->st_mode);
2✔
173
}
174

175
int statx_verify_socket(const struct statx *stx) {
72✔
176
        assert(stx);
72✔
177

178
        return mode_verify_socket(stx->stx_mode);
72✔
179
}
180

181
int is_socket(const char *path) {
2✔
182
        assert(!isempty(path));
2✔
183
        return verify_stat_at(AT_FDCWD, path, /* follow= */ true, stat_verify_socket, /* verify= */ false);
2✔
184
}
185

186
int stat_verify_linked(const struct stat *st) {
1,916,487✔
187
        assert(st);
1,916,487✔
188

189
        if (st->st_nlink <= 0)
1,916,487✔
190
                return -EIDRM; /* recognizable error. */
2✔
191

192
        return 0;
193
}
194

195
int fd_verify_linked(int fd) {
48✔
196

197
        if (fd == XAT_FDROOT)
48✔
198
                return 0;
199

200
        return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
47✔
201
}
202

203
int stat_verify_device_node(const struct stat *st) {
3,574✔
204
        assert(st);
3,574✔
205

206
        if (S_ISLNK(st->st_mode))
3,574✔
207
                return -ELOOP;
208

209
        if (S_ISDIR(st->st_mode))
3,574✔
210
                return -EISDIR;
211

212
        if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
3,453✔
213
                return -ENOTTY;
5✔
214

215
        return 0;
216
}
217

218
int is_device_node(const char *path) {
130✔
219
        assert(!isempty(path));
130✔
220
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
130✔
221
}
222

223
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
40,978✔
224
        _cleanup_close_ int fd = -EBADF;
40,978✔
225
        struct dirent *buf;
40,978✔
226
        size_t m;
40,978✔
227

228
        fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
40,978✔
229
        if (fd < 0)
40,978✔
230
                return fd;
231

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

239
        for (;;) {
3,664✔
240
                struct dirent *de;
3,664✔
241
                ssize_t n;
3,664✔
242

243
                n = getdents64(fd, buf, m);
3,664✔
244
                if (n < 0)
3,664✔
245
                        return -errno;
×
246
                if (n == 0)
3,664✔
247
                        break;
248

249
                assert((size_t) n <= m);
2,348✔
250
                msan_unpoison(buf, n);
2,348✔
251

252
                FOREACH_DIRENT_IN_BUFFER(de, buf, n)
6,910✔
253
                        if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
5,594✔
254
                                return 0;
255
        }
256

257
        return 1;
258
}
259

260
bool stat_may_be_dev_null(struct stat *st) {
458,530✔
261
        assert(st);
458,530✔
262

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

266
        return S_ISCHR(st->st_mode);
458,530✔
267
}
268

269
bool stat_is_empty(struct stat *st) {
441,599✔
270
        assert(st);
441,599✔
271

272
        return S_ISREG(st->st_mode) && st->st_size <= 0;
441,599✔
273
}
274

275
int null_or_empty_path_with_root(const char *fn, const char *root) {
330,167✔
276
        struct stat st;
330,167✔
277
        int r;
330,167✔
278

279
        assert(fn);
330,167✔
280

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

285
        if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
660,334✔
286
                return true;
330,167✔
287

288
        r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
329,989✔
289
        if (r < 0)
329,989✔
290
                return r;
291

292
        return null_or_empty(&st);
329,873✔
293
}
294

295
static const char* statx_mask_one_to_name(unsigned mask);
296
static const char* statx_attribute_to_name(uint64_t attr);
297

298
#include "statx-attribute-to-name.inc"
299
#include "statx-mask-to-name.inc"
300

301
#define DEFINE_STATX_BITS_TO_STRING(prefix, type, func, format_str)             \
302
        static char* prefix##_to_string(type v) {                               \
303
                if (v == 0)                                                     \
304
                        return strdup("");                                      \
305
                                                                                \
306
                _cleanup_free_ char *s = NULL;                                  \
307
                                                                                \
308
                BIT_FOREACH(i, v) {                                             \
309
                        type f = 1 << i;                                        \
310
                                                                                \
311
                        const char *n = func(f);                                \
312
                        if (!n)                                                 \
313
                                continue;                                       \
314
                                                                                \
315
                        if (!strextend_with_separator(&s, "|", n))              \
316
                                return NULL;                                    \
317
                        v &= ~f;                                                \
318
                }                                                               \
319
                                                                                \
320
                if (v != 0 && strextendf_with_separator(&s, "|", format_str, v) < 0) \
321
                        return NULL;                                            \
322
                                                                                \
323
                return TAKE_PTR(s);                                             \
324
        }
325

326
DEFINE_STATX_BITS_TO_STRING(statx_mask,       unsigned, statx_mask_one_to_name,  "0x%x");
×
327
DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, "0x%" PRIx64);
×
328

329
int xstatx_full(int fd,
30,893,617✔
330
                const char *path,
331
                int statx_flags,
332
                XStatXFlags xstatx_flags,
333
                unsigned mandatory_mask,
334
                unsigned optional_mask,
335
                uint64_t mandatory_attributes,
336
                struct statx *ret) {
337

338
        struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */
30,893,617✔
339
        int r;
30,893,617✔
340

341
        /* Wrapper around statx(), with additional bells and whistles:
342
         *
343
         * 1. AT_EMPTY_PATH is implied on empty path
344
         * 2. Supports XAT_FDROOT
345
         * 3. Takes separate mandatory and optional mask params, plus mandatory attributes.
346
         *    Returns -EUNATCH if statx() does not return all masks specified as mandatory,
347
         *    > 0 if all optional masks are supported, 0 otherwise.
348
         * 4. Supports a new flag XSTATX_MNT_ID_BEST which acquires STATX_MNT_ID_UNIQUE if available and
349
         *    STATX_MNT_ID if not.
350
         */
351

352
        assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
30,893,617✔
353
        assert((mandatory_mask & optional_mask) == 0);
30,893,617✔
354
        assert(!FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) || !((mandatory_mask|optional_mask) & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)));
30,893,617✔
355
        assert(ret);
30,893,617✔
356

357
        _cleanup_free_ char *p = NULL;
30,893,617✔
358
        r = resolve_xat_fdroot(&fd, &path, &p);
30,893,617✔
359
        if (r < 0)
30,893,617✔
360
                return r;
361

362
        unsigned request_mask = mandatory_mask|optional_mask;
30,893,617✔
363
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST))
30,893,617✔
364
                request_mask |= STATX_MNT_ID|STATX_MNT_ID_UNIQUE;
23,198,417✔
365

366
        if (statx(fd,
30,893,617✔
367
                  strempty(path),
30,893,617✔
368
                  statx_flags|(isempty(path) ? AT_EMPTY_PATH : 0),
34,818,330✔
369
                  request_mask,
370
                  &sx) < 0)
371
                return negative_errno();
2,517✔
372

373
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
30,891,100✔
374
            !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
23,198,417✔
375
                return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE.");
×
376

377
        if (!FLAGS_SET(sx.stx_mask, mandatory_mask)) {
30,891,100✔
378
                if (DEBUG_LOGGING) {
×
379
                        _cleanup_free_ char *mask_str = statx_mask_to_string(mandatory_mask & ~sx.stx_mask);
×
380
                        log_debug("statx() does not support '%s' mask (running on an old kernel?)", strnull(mask_str));
×
381
                }
382

383
                return -EUNATCH;
×
384
        }
385

386
        if (!FLAGS_SET(sx.stx_attributes_mask, mandatory_attributes)) {
30,891,100✔
387
                if (DEBUG_LOGGING) {
×
388
                        _cleanup_free_ char *attr_str = statx_attributes_to_string(mandatory_attributes & ~sx.stx_attributes_mask);
×
389
                        log_debug("statx() does not support '%s' attribute (running on an old kernel?)", strnull(attr_str));
×
390
                }
391

392
                return -EUNATCH;
×
393
        }
394

395
        *ret = sx;
30,891,100✔
396
        return FLAGS_SET(sx.stx_mask, optional_mask);
30,891,100✔
397
}
398

399
static int xfstatfs(int fd, struct statfs *ret) {
8,423,470✔
400
        assert(ret);
8,423,470✔
401

402
        if (fd == AT_FDCWD)
8,423,470✔
403
                return RET_NERRNO(statfs(".", ret));
×
404
        if (fd == XAT_FDROOT)
8,423,470✔
405
                return RET_NERRNO(statfs("/", ret));
2✔
406

407
        assert(fd >= 0);
8,423,468✔
408
        return RET_NERRNO(fstatfs(fd, ret));
8,423,468✔
409
}
410

411
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
8,399,875✔
412
        _cleanup_close_ int fd = -EBADF;
8,399,875✔
413

414
        assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
8,399,875✔
415
        assert(ret);
8,399,875✔
416

417
        if (!isempty(path)) {
8,399,875✔
418
                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC);
33,036✔
419
                if (fd < 0)
33,036✔
420
                        return fd;
421
                dir_fd = fd;
422
        }
423

424
        return xfstatfs(dir_fd, ret);
8,399,863✔
425
}
426

427
int fd_is_read_only_fs(int fd) {
1,874✔
428
        int r;
1,874✔
429

430
        struct statfs st;
1,874✔
431
        r = xfstatfs(fd, &st);
1,874✔
432
        if (r < 0)
1,874✔
433
                return r;
1,874✔
434

435
        if (st.f_flags & ST_RDONLY)
1,874✔
436
                return true;
437

438
        if (is_network_fs(&st))
1,500✔
439
                /* On NFS, fstatfs() might not reflect whether we can actually write to the remote share.
440
                 * Let's try again with access(W_OK) which is more reliable, at least sometimes. */
441
                return access_fd(fd, W_OK) == -EROFS;
×
442

443
        return false;
444
}
445

446
int path_is_read_only_fs(const char *path) {
1,594✔
447
        _cleanup_close_ int fd = -EBADF;
1,594✔
448

449
        assert(path);
1,594✔
450

451
        fd = open(path, O_CLOEXEC | O_PATH);
1,594✔
452
        if (fd < 0)
1,594✔
453
                return -errno;
170✔
454

455
        return fd_is_read_only_fs(fd);
1,424✔
456
}
457

458
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) {
16,226✔
459
        struct stat sta, stb;
16,226✔
460
        int r;
16,226✔
461

462
        assert(fda >= 0 || fda == AT_FDCWD);
16,226✔
463
        assert(fdb >= 0 || fdb == AT_FDCWD);
16,226✔
464
        assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
16,226✔
465

466
        /* Refuse an unset filea or fileb early unless AT_EMPTY_PATH is set */
467
        if ((isempty(filea) || isempty(fileb)) && !FLAGS_SET(flags, AT_EMPTY_PATH))
30,576✔
468
                return -EINVAL;
16,226✔
469

470
        /* Shortcut: comparing the same fd with itself means we can return true */
471
        if (fda >= 0 && fda == fdb && isempty(filea) && isempty(fileb) && FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW))
16,228✔
472
                return true;
473

474
        _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
32,451✔
475
        if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
16,225✔
476
                /* Let's try to use the name_to_handle_at() AT_HANDLE_FID API to identify identical
477
                 * inodes. We have to issue multiple calls on the same file for that (first, to acquire the
478
                 * FID, and then to check if .st_dev is actually the same). Hence let's pin the inode in
479
                 * between via O_PATH, unless we already have an fd for it. */
480

481
                if (!isempty(filea)) {
16,225✔
482
                        pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
14,350✔
483
                        if (pin_a < 0)
14,350✔
484
                                return -errno;
15,845✔
485

486
                        fda = pin_a;
13,935✔
487
                        filea = NULL;
13,935✔
488
                        flags |= AT_EMPTY_PATH;
13,935✔
489
                }
490

491
                if (!isempty(fileb)) {
15,810✔
492
                        pin_b = openat(fdb, fileb, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
13,933✔
493
                        if (pin_b < 0)
13,933✔
494
                                return -errno;
4✔
495

496
                        fdb = pin_b;
13,929✔
497
                        fileb = NULL;
13,929✔
498
                        flags |= AT_EMPTY_PATH;
13,929✔
499
                }
500

501
                int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
15,806✔
502
                _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
15,806✔
503
                uint64_t mntida, mntidb;
15,806✔
504
                int _mntida, _mntidb;
15,806✔
505

506
                r = name_to_handle_at_try_fid(
15,806✔
507
                                fda,
508
                                filea,
509
                                &ha,
510
                                &_mntida,
511
                                &mntida,
512
                                ntha_flags);
513
                if (r < 0) {
15,806✔
514
                        if (is_name_to_handle_at_fatal_error(r))
120✔
515
                                return r;
516

517
                        goto fallback;
120✔
518
                }
519
                if (r == 0)
15,686✔
520
                        mntida = _mntida;
×
521

522
                r = name_to_handle_at_try_fid(
15,686✔
523
                                fdb,
524
                                fileb,
525
                                &hb,
526
                                r > 0 ? NULL : &_mntidb, /* if we managed to get unique mnt id for a, insist on that for b */
527
                                r > 0 ? &mntidb : NULL,
528
                                ntha_flags);
529
                if (r < 0) {
15,686✔
530
                        if (is_name_to_handle_at_fatal_error(r))
16✔
531
                                return r;
532

533
                        goto fallback;
16✔
534
                }
535
                if (r == 0)
15,670✔
536
                        mntidb = _mntidb;
×
537

538
                /* Now compare the two file handles */
539
                if (!file_handle_equal(ha, hb))
15,670✔
540
                        return false;
541

542
                /* If the file handles are the same and they come from the same mount ID? Great, then we are
543
                 * good, they are definitely the same */
544
                if (mntida == mntidb)
14,540✔
545
                        return true;
546

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

555
fallback:
×
556
        if (fstatat(fda, strempty(filea), &sta, flags) < 0)
760✔
557
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
×
558

559
        if (fstatat(fdb, strempty(fileb), &stb, flags) < 0)
760✔
560
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(fileb));
×
561

562
        return stat_inode_same(&sta, &stb);
380✔
563
}
564

565
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
8,404,669✔
566
        assert(s);
8,404,669✔
567
        assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
8,404,669✔
568

569
        return F_TYPE_EQUAL(s->f_type, magic_value);
8,404,669✔
570
}
571

572
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
8,399,361✔
573
        int r;
8,399,361✔
574

575
        struct statfs s;
8,399,361✔
576
        r = xstatfsat(dir_fd, path, &s);
8,399,361✔
577
        if (r < 0)
8,399,361✔
578
                return r;
8,399,361✔
579

580
        return is_fs_type(&s, magic_value);
8,399,360✔
581
}
582

583
bool is_temporary_fs(const struct statfs *s) {
10,817✔
584
        return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
10,817✔
585
}
586

587
bool is_network_fs(const struct statfs *s) {
23,185✔
588
        return fs_in_group(s, FILESYSTEM_SET_NETWORK);
23,185✔
589
}
590

591
int fd_is_temporary_fs(int fd) {
157✔
592
        int r;
157✔
593

594
        struct statfs s;
157✔
595
        r = xfstatfs(fd, &s);
157✔
596
        if (r < 0)
157✔
597
                return r;
157✔
598

599
        return is_temporary_fs(&s);
157✔
600
}
601

602
int fd_is_network_fs(int fd) {
21,576✔
603
        int r;
21,576✔
604

605
        struct statfs s;
21,576✔
606
        r = xfstatfs(fd, &s);
21,576✔
607
        if (r < 0)
21,576✔
608
                return r;
21,576✔
609

610
        return is_network_fs(&s);
21,576✔
611
}
612

613
int path_is_temporary_fs(const char *path) {
11✔
614
        struct statfs s;
11✔
615

616
        if (statfs(path, &s) < 0)
11✔
617
                return -errno;
2✔
618

619
        return is_temporary_fs(&s);
9✔
620
}
621

622
int path_is_network_fs(const char *path) {
×
623
        struct statfs s;
×
624

625
        if (statfs(path, &s) < 0)
×
626
                return -errno;
×
627

628
        return is_network_fs(&s);
×
629
}
630

631
int proc_mounted(void) {
32,396✔
632
        /* This is typically used in error path. So, it is better to not overwrite the original errno. */
633
        PROTECT_ERRNO;
32,396✔
634
        int r;
32,396✔
635

636
        /* A quick check of procfs is properly mounted */
637

638
        r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
32,396✔
639
        if (r == -ENOENT) /* not mounted at all */
32,396✔
640
                return false;
×
641

642
        return r;
643
}
644

645
bool stat_inode_same(const struct stat *a, const struct stat *b) {
288,287✔
646

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

650
        return stat_is_set(a) && stat_is_set(b) &&
574,384✔
651
                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
247,468✔
652
                a->st_dev == b->st_dev &&
502,311✔
653
                a->st_ino == b->st_ino;
214,024✔
654
}
655

656
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
128,954✔
657

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

667
        return stat_inode_same(a, b) &&
128,954✔
668
                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
81,137✔
669
                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
80,826✔
670
                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
209,760✔
671
                (!(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 */
80,806✔
672
}
673

674
bool statx_inode_same(const struct statx *a, const struct statx *b) {
4,536,668✔
675

676
        /* Same as stat_inode_same() but for struct statx */
677

678
        if (!statx_is_set(a) || !statx_is_set(b))
9,073,336✔
679
                return false;
680

681
        assert(FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO));
4,536,668✔
682
        assert(FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO));
4,536,668✔
683

684
        return
4,536,668✔
685
                ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
9,067,588✔
686
                a->stx_dev_major == b->stx_dev_major &&
4,530,920✔
687
                a->stx_dev_minor == b->stx_dev_minor &&
13,565,366✔
688
                a->stx_ino == b->stx_ino;
4,484,096✔
689
}
690

691
int statx_mount_same(const struct statx *a, const struct statx *b) {
3,752,973✔
692
        if (!statx_is_set(a) || !statx_is_set(b))
7,505,946✔
693
                return false;
694

695
        if ((FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) ||
3,752,973✔
696
            (FLAGS_SET(a->stx_mask, STATX_MNT_ID_UNIQUE) && FLAGS_SET(b->stx_mask, STATX_MNT_ID_UNIQUE)))
×
697
                return a->stx_mnt_id == b->stx_mnt_id;
3,752,973✔
698

699
        return -ENODATA;
700
}
701

702
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
6,054✔
703
        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
6,054✔
704
}
705
nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
820✔
706
        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
820✔
707
}
708

709
void inode_hash_func(const struct stat *q, struct siphash *state) {
37,925✔
710
        siphash24_compress_typesafe(q->st_dev, state);
37,925✔
711
        siphash24_compress_typesafe(q->st_ino, state);
37,925✔
712
}
37,925✔
713

714
int inode_compare_func(const struct stat *a, const struct stat *b) {
31,261✔
715
        int r;
31,261✔
716

717
        r = CMP(a->st_dev, b->st_dev);
31,261✔
718
        if (r != 0)
26,589✔
719
                return r;
4,775✔
720

721
        return CMP(a->st_ino, b->st_ino);
26,486✔
722
}
723

724
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
883✔
725

726
const char* inode_type_to_string(mode_t m) {
18,651✔
727

728
        /* Returns a short string for the inode type. We use the same name as the underlying macros for each
729
         * inode type. */
730

731
        switch (m & S_IFMT) {
18,651✔
732
        case S_IFREG:
733
                return "reg";
734
        case S_IFDIR:
6,537✔
735
                return "dir";
6,537✔
736
        case S_IFLNK:
1✔
737
                return "lnk";
1✔
738
        case S_IFCHR:
1,021✔
739
                return "chr";
1,021✔
740
        case S_IFBLK:
473✔
741
                return "blk";
473✔
742
        case S_IFIFO:
473✔
743
                return "fifo";
473✔
744
        case S_IFSOCK:
617✔
745
                return "sock";
617✔
746
        }
747

748
        /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
749
         * return an .st_mode where we'll return NULL here! */
750
        return NULL;
4✔
751
}
752

753
mode_t inode_type_from_string(const char *s) {
13✔
754
        if (!s)
13✔
755
                return MODE_INVALID;
756

757
        if (streq(s, "reg"))
13✔
758
                return S_IFREG;
759
        if (streq(s, "dir"))
10✔
760
                return S_IFDIR;
761
        if (streq(s, "lnk"))
7✔
762
                return S_IFLNK;
763
        if (streq(s, "chr"))
6✔
764
                return S_IFCHR;
765
        if (streq(s, "blk"))
5✔
766
                return S_IFBLK;
767
        if (streq(s, "fifo"))
4✔
768
                return S_IFIFO;
769
        if (streq(s, "sock"))
2✔
770
                return S_IFSOCK;
2✔
771

772
        return MODE_INVALID;
773
}
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