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

systemd / systemd / 25196166722

30 Apr 2026 07:30PM UTC coverage: 72.134% (+0.3%) from 71.849%
25196166722

push

github

bluca
mkosi: update debian commit reference to 1302f123d

* 1302f123d9 Restrict wildcard for new files
* a6d0098d10 Install new files for upstream build
* ce07fd7616 d/t/boot-and-services: use coreutils tunable in apparmor test (LP: #2125614)

324804 of 450278 relevant lines covered (72.13%)

1187716.32 hits per line

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

83.49
/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,125,361✔
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,125,361✔
33
        int r;
2,125,361✔
34

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

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

44
        if (fstatat(fd, strempty(path), &st,
2,968,848✔
45
                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
3,407,235✔
46
                return -errno;
8,196✔
47

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

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

56
        if (S_ISLNK(mode))
2,785,882✔
57
                return -ELOOP;
58

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

62
        return 0;
63
}
64

65
int stat_verify_regular(const struct stat *st) {
2,785,916✔
66
        assert(st);
2,785,916✔
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,785,916✔
72
}
73

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

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

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

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

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

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

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

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

101
        return 0;
102
}
103

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

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

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

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

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

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

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

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

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

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

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

141
        if (!S_ISLNK(st->st_mode))
26,979✔
142
                return -ENOLINK;
281✔
143

144
        return 0;
145
}
146

147
int fd_verify_symlink(int fd) {
5,291✔
148
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
5,291✔
149
                return -EISDIR;
150

151
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_symlink, /* verify= */ true);
5,291✔
152
}
153

154
int is_symlink(const char *path) {
21,705✔
155
        assert(!isempty(path));
21,705✔
156
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
21,705✔
157
}
158

159
static int mode_verify_socket(mode_t mode) {
257✔
160
        if (S_ISDIR(mode))
257✔
161
                return -EISDIR;
162

163
        if (S_ISLNK(mode))
254✔
164
                return -ELOOP;
165

166
        if (!S_ISSOCK(mode))
254✔
167
                return -ENOTSOCK;
3✔
168

169
        return 0;
170
}
171

172
int stat_verify_socket(const struct stat *st) {
182✔
173
        assert(st);
182✔
174

175
        return mode_verify_socket(st->st_mode);
182✔
176
}
177

178
int statx_verify_socket(const struct statx *stx) {
75✔
179
        assert(stx);
75✔
180

181
        return mode_verify_socket(stx->stx_mode);
75✔
182
}
183

184
int fd_verify_socket(int fd) {
6✔
185
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
6✔
186
                return -EISDIR;
187

188
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_socket, /* verify= */ true);
6✔
189
}
190

191
int is_socket(const char *path) {
2✔
192
        assert(!isempty(path));
2✔
193
        return verify_stat_at(AT_FDCWD, path, /* follow= */ true, stat_verify_socket, /* verify= */ false);
2✔
194
}
195

196
int stat_verify_linked(const struct stat *st) {
1,881,761✔
197
        assert(st);
1,881,761✔
198

199
        if (st->st_nlink <= 0)
1,881,761✔
200
                return -EIDRM; /* recognizable error. */
2✔
201

202
        return 0;
203
}
204

205
int fd_verify_linked(int fd) {
50✔
206

207
        if (fd == XAT_FDROOT)
50✔
208
                return 0;
209

210
        return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
49✔
211
}
212

213
int stat_verify_block(const struct stat *st) {
181✔
214
        assert(st);
181✔
215

216
        if (S_ISDIR(st->st_mode))
181✔
217
                return -EISDIR;
218

219
        if (S_ISLNK(st->st_mode))
181✔
220
                return -ELOOP;
221

222
        if (!S_ISBLK(st->st_mode))
181✔
223
                return -ENOTBLK;
×
224

225
        return 0;
226
}
227

228
int fd_verify_block(int fd) {
×
229
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
×
230
                return -EISDIR;
231

232
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_block, /* verify= */ true);
×
233
}
234

235
int stat_verify_char(const struct stat *st) {
×
236
        assert(st);
×
237

238
        if (S_ISDIR(st->st_mode))
×
239
                return -EISDIR;
240

241
        if (S_ISLNK(st->st_mode))
×
242
                return -ELOOP;
243

244
        if (!S_ISCHR(st->st_mode))
×
245
                return -EBADFD;
×
246

247
        return 0;
248
}
249

250
int stat_verify_device_node(const struct stat *st) {
11,847✔
251
        assert(st);
11,847✔
252

253
        if (S_ISDIR(st->st_mode))
11,847✔
254
                return -EISDIR;
255

256
        if (S_ISLNK(st->st_mode))
11,724✔
257
                return -ELOOP;
258

259
        if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
11,724✔
260
                return -ENOTTY;
5✔
261

262
        return 0;
263
}
264

265
int is_device_node(const char *path) {
132✔
266
        assert(!isempty(path));
132✔
267
        return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
132✔
268
}
269

270
int stat_verify_regular_or_block(const struct stat *st) {
240✔
271
        assert(st);
240✔
272

273
        if (S_ISDIR(st->st_mode))
240✔
274
                return -EISDIR;
275

276
        if (S_ISLNK(st->st_mode))
240✔
277
                return -ELOOP;
278

279
        if (!S_ISREG(st->st_mode) && !S_ISBLK(st->st_mode))
240✔
280
                return -EBADFD;
×
281

282
        return 0;
283
}
284

285
int fd_verify_regular_or_block(int fd) {
5✔
286
        if (IN_SET(fd, AT_FDCWD, XAT_FDROOT))
5✔
287
                return -EISDIR;
288

289
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_regular_or_block, /* verify= */ true);
5✔
290
}
291

292
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
40,062✔
293
        _cleanup_close_ int fd = -EBADF;
40,062✔
294
        struct dirent *buf;
40,062✔
295
        size_t m;
40,062✔
296

297
        fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
40,062✔
298
        if (fd < 0)
40,062✔
299
                return fd;
300

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

308
        for (;;) {
3,724✔
309
                struct dirent *de;
3,724✔
310
                ssize_t n;
3,724✔
311

312
                n = getdents64(fd, buf, m);
3,724✔
313
                if (n < 0)
3,724✔
314
                        return -errno;
×
315
                if (n == 0)
3,724✔
316
                        break;
317

318
                assert((size_t) n <= m);
2,371✔
319
                msan_unpoison(buf, n);
2,371✔
320

321
                FOREACH_DIRENT_IN_BUFFER(de, buf, n)
6,343✔
322
                        if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
4,990✔
323
                                return 0;
324
        }
325

326
        return 1;
327
}
328

329
bool stat_may_be_dev_null(struct stat *st) {
524,748✔
330
        assert(st);
524,748✔
331

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

335
        return S_ISCHR(st->st_mode);
524,748✔
336
}
337

338
bool stat_is_empty(struct stat *st) {
507,228✔
339
        assert(st);
507,228✔
340

341
        return S_ISREG(st->st_mode) && st->st_size <= 0;
507,228✔
342
}
343

344
int null_or_empty_path_with_root(const char *fn, const char *root) {
384,887✔
345
        struct stat st;
384,887✔
346
        int r;
384,887✔
347

348
        assert(fn);
384,887✔
349

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

354
        if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
769,774✔
355
                return true;
384,887✔
356

357
        r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
384,725✔
358
        if (r < 0)
384,725✔
359
                return r;
360

361
        return null_or_empty(&st);
384,609✔
362
}
363

364
static const char* statx_mask_one_to_name(unsigned mask);
365
static const char* statx_attribute_to_name(uint64_t attr);
366

367
#include "statx-attribute-to-name.inc"
368
#include "statx-mask-to-name.inc"
369

370
#define DEFINE_STATX_BITS_TO_STRING(prefix, type, func, format_str)             \
371
        static char* prefix##_to_string(type v) {                               \
372
                if (v == 0)                                                     \
373
                        return strdup("");                                      \
374
                                                                                \
375
                _cleanup_free_ char *s = NULL;                                  \
376
                                                                                \
377
                BIT_FOREACH(i, v) {                                             \
378
                        type f = 1 << i;                                        \
379
                                                                                \
380
                        const char *n = func(f);                                \
381
                        if (!n)                                                 \
382
                                continue;                                       \
383
                                                                                \
384
                        if (!strextend_with_separator(&s, "|", n))              \
385
                                return NULL;                                    \
386
                        v &= ~f;                                                \
387
                }                                                               \
388
                                                                                \
389
                if (v != 0 && strextendf_with_separator(&s, "|", format_str, v) < 0) \
390
                        return NULL;                                            \
391
                                                                                \
392
                return TAKE_PTR(s);                                             \
393
        }
394

395
DEFINE_STATX_BITS_TO_STRING(statx_mask,       unsigned, statx_mask_one_to_name,  "0x%x");
×
396
DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, "0x%" PRIx64);
×
397

398
int xstatx_full(int fd,
24,333,246✔
399
                const char *path,
400
                int statx_flags,
401
                XStatXFlags xstatx_flags,
402
                unsigned mandatory_mask,
403
                unsigned optional_mask,
404
                uint64_t mandatory_attributes,
405
                struct statx *ret) {
406

407
        struct statx sx = {}; /* explicitly initialize the struct to make msan silent. */
24,333,246✔
408
        int r;
24,333,246✔
409

410
        /* Wrapper around statx(), with additional bells and whistles:
411
         *
412
         * 1. AT_EMPTY_PATH is implied on empty path
413
         * 2. Supports XAT_FDROOT
414
         * 3. Takes separate mandatory and optional mask params, plus mandatory attributes.
415
         *    Returns -EUNATCH if statx() does not return all masks specified as mandatory,
416
         *    > 0 if all optional masks are supported, 0 otherwise.
417
         * 4. Supports a new flag XSTATX_MNT_ID_BEST which acquires STATX_MNT_ID_UNIQUE if available and
418
         *    STATX_MNT_ID if not.
419
         */
420

421
        assert(fd >= 0 || IN_SET(fd, AT_FDCWD, XAT_FDROOT));
24,333,246✔
422
        assert((mandatory_mask & optional_mask) == 0);
24,333,246✔
423
        assert(!FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) || !((mandatory_mask|optional_mask) & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)));
24,333,246✔
424
        assert(ret);
24,333,246✔
425

426
        _cleanup_free_ char *p = NULL;
24,333,246✔
427
        r = resolve_xat_fdroot(&fd, &path, &p);
24,333,246✔
428
        if (r < 0)
24,333,246✔
429
                return r;
430

431
        unsigned request_mask = mandatory_mask|optional_mask;
24,333,246✔
432
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST))
24,333,246✔
433
                request_mask |= STATX_MNT_ID|STATX_MNT_ID_UNIQUE;
23,553,287✔
434

435
        if (statx(fd,
24,333,246✔
436
                  strempty(path),
24,333,246✔
437
                  statx_flags|(isempty(path) ? AT_EMPTY_PATH : 0),
24,791,132✔
438
                  request_mask,
439
                  &sx) < 0)
440
                return negative_errno();
2,550✔
441

442
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
24,330,696✔
443
            !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
23,553,287✔
444
                return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE.");
×
445

446
        if (!FLAGS_SET(sx.stx_mask, mandatory_mask)) {
24,330,696✔
447
                if (DEBUG_LOGGING) {
×
448
                        _cleanup_free_ char *mask_str = statx_mask_to_string(mandatory_mask & ~sx.stx_mask);
×
449
                        log_debug("statx() does not support '%s' mask (running on an old kernel?)", strnull(mask_str));
×
450
                }
451

452
                return -EUNATCH;
×
453
        }
454

455
        if (!FLAGS_SET(sx.stx_attributes_mask, mandatory_attributes)) {
24,330,696✔
456
                if (DEBUG_LOGGING) {
×
457
                        _cleanup_free_ char *attr_str = statx_attributes_to_string(mandatory_attributes & ~sx.stx_attributes_mask);
×
458
                        log_debug("statx() does not support '%s' attribute (running on an old kernel?)", strnull(attr_str));
×
459
                }
460

461
                return -EUNATCH;
×
462
        }
463

464
        *ret = sx;
24,330,696✔
465
        return FLAGS_SET(sx.stx_mask, optional_mask);
24,330,696✔
466
}
467

468
static int xfstatfs(int fd, struct statfs *ret) {
8,314,606✔
469
        assert(ret);
8,314,606✔
470

471
        if (fd == AT_FDCWD)
8,314,606✔
472
                return RET_NERRNO(statfs(".", ret));
×
473
        if (fd == XAT_FDROOT)
8,314,606✔
474
                return RET_NERRNO(statfs("/", ret));
2✔
475

476
        assert(fd >= 0);
8,314,604✔
477
        return RET_NERRNO(fstatfs(fd, ret));
8,314,604✔
478
}
479

480
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
8,290,728✔
481
        _cleanup_close_ int fd = -EBADF;
8,290,728✔
482

483
        assert(dir_fd >= 0 || IN_SET(dir_fd, AT_FDCWD, XAT_FDROOT));
8,290,728✔
484
        assert(ret);
8,290,728✔
485

486
        if (!isempty(path)) {
8,290,728✔
487
                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC);
31,166✔
488
                if (fd < 0)
31,166✔
489
                        return fd;
490
                dir_fd = fd;
491
        }
492

493
        return xfstatfs(dir_fd, ret);
8,290,727✔
494
}
495

496
int fd_is_read_only_fs(int fd) {
1,830✔
497
        int r;
1,830✔
498

499
        struct statfs st;
1,830✔
500
        r = xfstatfs(fd, &st);
1,830✔
501
        if (r < 0)
1,830✔
502
                return r;
1,830✔
503

504
        if (st.f_flags & ST_RDONLY)
1,830✔
505
                return true;
506

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

512
        return false;
513
}
514

515
int path_is_read_only_fs(const char *path) {
1,528✔
516
        _cleanup_close_ int fd = -EBADF;
1,528✔
517

518
        assert(path);
1,528✔
519

520
        fd = open(path, O_CLOEXEC | O_PATH);
1,528✔
521
        if (fd < 0)
1,528✔
522
                return -errno;
152✔
523

524
        return fd_is_read_only_fs(fd);
1,376✔
525
}
526

527
int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) {
17,493✔
528
        struct stat sta, stb;
17,493✔
529
        int r;
17,493✔
530

531
        assert(fda >= 0 || fda == AT_FDCWD);
17,493✔
532
        assert(fdb >= 0 || fdb == AT_FDCWD);
17,493✔
533
        assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
17,493✔
534

535
        /* Refuse an unset filea or fileb early unless AT_EMPTY_PATH is set */
536
        if ((isempty(filea) || isempty(fileb)) && !FLAGS_SET(flags, AT_EMPTY_PATH))
33,117✔
537
                return -EINVAL;
17,493✔
538

539
        /* Shortcut: comparing the same fd with itself means we can return true */
540
        if (fda >= 0 && fda == fdb && isempty(filea) && isempty(fileb) && FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW))
17,495✔
541
                return true;
542

543
        _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
34,985✔
544
        if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
17,492✔
545
                /* Let's try to use the name_to_handle_at() AT_HANDLE_FID API to identify identical
546
                 * inodes. We have to issue multiple calls on the same file for that (first, to acquire the
547
                 * FID, and then to check if .st_dev is actually the same). Hence let's pin the inode in
548
                 * between via O_PATH, unless we already have an fd for it. */
549

550
                if (!isempty(filea)) {
17,492✔
551
                        pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
15,624✔
552
                        if (pin_a < 0)
15,624✔
553
                                return -errno;
17,240✔
554

555
                        fda = pin_a;
15,158✔
556
                        filea = NULL;
15,158✔
557
                        flags |= AT_EMPTY_PATH;
15,158✔
558
                }
559

560
                if (!isempty(fileb)) {
17,026✔
561
                        pin_b = openat(fdb, fileb, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
15,167✔
562
                        if (pin_b < 0)
15,167✔
563
                                return -errno;
4✔
564

565
                        fdb = pin_b;
15,163✔
566
                        fileb = NULL;
15,163✔
567
                        flags |= AT_EMPTY_PATH;
15,163✔
568
                }
569

570
                int ntha_flags = at_flags_normalize_follow(flags) & (AT_EMPTY_PATH|AT_SYMLINK_FOLLOW);
17,022✔
571
                _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
17,022✔
572
                uint64_t mntida, mntidb;
17,022✔
573
                int _mntida, _mntidb;
17,022✔
574

575
                r = name_to_handle_at_try_fid(
17,022✔
576
                                fda,
577
                                filea,
578
                                &ha,
579
                                &_mntida,
580
                                &mntida,
581
                                ntha_flags);
582
                if (r < 0) {
17,022✔
583
                        if (is_name_to_handle_at_fatal_error(r))
×
584
                                return r;
585

586
                        goto fallback;
×
587
                }
588
                bool have_unique_mntid = r > 0;
17,022✔
589

590
                if (!have_unique_mntid)
17,022✔
591
                        mntida = _mntida;
×
592

593
                r = name_to_handle_at_try_fid(
17,022✔
594
                                fdb,
595
                                fileb,
596
                                &hb,
597
                                have_unique_mntid ? NULL : &_mntidb, /* if we managed to get unique mnt id for a, insist on that for b */
598
                                have_unique_mntid ? &mntidb : NULL,
599
                                ntha_flags);
600
                if (r < 0) {
17,022✔
601
                        if (is_name_to_handle_at_fatal_error(r))
×
602
                                return r;
603

604
                        goto fallback;
×
605
                }
606
                if (r == 0) {
17,022✔
607
                        assert(!have_unique_mntid); /* _mntidb was initialized by name_to_handle_at_try_fid() */
×
608
                        mntidb = _mntidb;
×
609
                }
610

611
                /* Now compare the two file handles */
612
                if (!file_handle_equal(ha, hb))
17,022✔
613
                        return false;
614

615
                /* If the file handles are the same and they come from the same mount ID? Great, then we are
616
                 * good, they are definitely the same */
617
                if (mntida == mntidb)
15,807✔
618
                        return true;
619

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

628
fallback:
×
629
        if (fstatat(fda, strempty(filea), &sta, flags) < 0)
504✔
630
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
×
631

632
        if (fstatat(fdb, strempty(fileb), &stb, flags) < 0)
504✔
633
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(fileb));
×
634

635
        return stat_inode_same(&sta, &stb);
252✔
636
}
637

638
bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
8,298,454✔
639
        assert(s);
8,298,454✔
640
        assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
8,298,454✔
641

642
        return F_TYPE_EQUAL(s->f_type, magic_value);
8,298,454✔
643
}
644

645
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
8,290,728✔
646
        int r;
8,290,728✔
647

648
        struct statfs s;
8,290,728✔
649
        r = xstatfsat(dir_fd, path, &s);
8,290,728✔
650
        if (r < 0)
8,290,728✔
651
                return r;
8,290,728✔
652

653
        return is_fs_type(&s, magic_value);
8,290,727✔
654
}
655

656
bool is_temporary_fs(const struct statfs *s) {
3,595✔
657
        return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
3,595✔
658
}
659

660
bool is_network_fs(const struct statfs *s) {
23,454✔
661
        return fs_in_group(s, FILESYSTEM_SET_NETWORK);
23,454✔
662
}
663

664
int fd_is_temporary_fs(int fd) {
152✔
665
        int r;
152✔
666

667
        struct statfs s;
152✔
668
        r = xfstatfs(fd, &s);
152✔
669
        if (r < 0)
152✔
670
                return r;
152✔
671

672
        return is_temporary_fs(&s);
152✔
673
}
674

675
int fd_is_network_fs(int fd) {
21,897✔
676
        int r;
21,897✔
677

678
        struct statfs s;
21,897✔
679
        r = xfstatfs(fd, &s);
21,897✔
680
        if (r < 0)
21,897✔
681
                return r;
21,897✔
682

683
        return is_network_fs(&s);
21,897✔
684
}
685

686
int path_is_temporary_fs(const char *path) {
11✔
687
        struct statfs s;
11✔
688

689
        if (statfs(path, &s) < 0)
11✔
690
                return -errno;
2✔
691

692
        return is_temporary_fs(&s);
9✔
693
}
694

695
int path_is_network_fs(const char *path) {
×
696
        struct statfs s;
×
697

698
        if (statfs(path, &s) < 0)
×
699
                return -errno;
×
700

701
        return is_network_fs(&s);
×
702
}
703

704
int proc_mounted(void) {
31,038✔
705
        /* This is typically used in error path. So, it is better to not overwrite the original errno. */
706
        PROTECT_ERRNO;
31,038✔
707
        int r;
31,038✔
708

709
        /* A quick check of procfs is properly mounted */
710

711
        r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
31,038✔
712
        if (r == -ENOENT) /* not mounted at all */
31,038✔
713
                return false;
×
714

715
        return r;
716
}
717

718
bool stat_inode_same(const struct stat *a, const struct stat *b) {
342,664✔
719

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

723
        return stat_is_set(a) && stat_is_set(b) &&
683,079✔
724
                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
286,981✔
725
                a->st_dev == b->st_dev &&
595,307✔
726
                a->st_ino == b->st_ino;
252,643✔
727
}
728

729
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
151,848✔
730

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

740
        return stat_inode_same(a, b) &&
151,848✔
741
                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
89,098✔
742
                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
88,763✔
743
                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
240,579✔
744
                (!(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 */
88,731✔
745
}
746

747
bool statx_inode_same(const struct statx *a, const struct statx *b) {
1,077,978✔
748

749
        /* Same as stat_inode_same() but for struct statx */
750

751
        if (!statx_is_set(a) || !statx_is_set(b))
2,155,956✔
752
                return false;
753

754
        assert(FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO));
1,077,978✔
755
        assert(FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO));
1,077,978✔
756

757
        return
1,077,978✔
758
                ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
2,150,190✔
759
                a->stx_dev_major == b->stx_dev_major &&
1,072,212✔
760
                a->stx_dev_minor == b->stx_dev_minor &&
3,196,296✔
761
                a->stx_ino == b->stx_ino;
1,032,399✔
762
}
763

764
int statx_mount_same(const struct statx *a, const struct statx *b) {
303,364✔
765
        if (!statx_is_set(a) || !statx_is_set(b))
606,728✔
766
                return false;
767

768
        if ((FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) ||
303,364✔
769
            (FLAGS_SET(a->stx_mask, STATX_MNT_ID_UNIQUE) && FLAGS_SET(b->stx_mask, STATX_MNT_ID_UNIQUE)))
6✔
770
                return a->stx_mnt_id == b->stx_mnt_id;
303,364✔
771

772
        return -ENODATA;
773
}
774

775
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
6,559✔
776
        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
6,559✔
777
}
778
nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
820✔
779
        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
820✔
780
}
781

782
void inode_hash_func(const struct stat *q, struct siphash *state) {
33,538✔
783
        siphash24_compress_typesafe(q->st_dev, state);
33,538✔
784
        siphash24_compress_typesafe(q->st_ino, state);
33,538✔
785

786
        /* Also include inode type, to mirror stat_inode_same() */
787
        mode_t type = q->st_mode & S_IFMT;
33,538✔
788
        siphash24_compress_typesafe(type, state);
33,538✔
789
}
33,538✔
790

791
int inode_compare_func(const struct stat *a, const struct stat *b) {
28,315✔
792
        int r;
28,315✔
793

794
        r = CMP(a->st_dev, b->st_dev);
28,315✔
795
        if (r != 0)
23,669✔
796
                return r;
4,837✔
797

798
        r = CMP(a->st_ino, b->st_ino);
23,478✔
799
        if (r != 0)
7,899✔
800
                return r;
23,387✔
801

802
        return CMP(a->st_mode & S_IFMT, b->st_mode & S_IFMT);
91✔
803
}
804

805
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
982✔
806

807
void inode_unmodified_hash_func(const struct stat *q, struct siphash *state) {
7✔
808
        inode_hash_func(q, state);
7✔
809

810
        siphash24_compress_typesafe(q->st_mtim.tv_sec, state);
7✔
811
        siphash24_compress_typesafe(q->st_mtim.tv_nsec, state);
7✔
812

813
        if (S_ISREG(q->st_mode))
7✔
814
                siphash24_compress_typesafe(q->st_size, state);
7✔
815
        else {
816
                uint64_t invalid = UINT64_MAX;
×
817
                siphash24_compress_typesafe(invalid, state);
×
818
        }
819

820
        if (S_ISCHR(q->st_mode) || S_ISBLK(q->st_mode))
7✔
821
                siphash24_compress_typesafe(q->st_rdev, state);
×
822
        else {
823
                dev_t invalid = (dev_t) -1;
7✔
824
                siphash24_compress_typesafe(invalid, state);
7✔
825
        }
826
}
7✔
827

828
int inode_unmodified_compare_func(const struct stat *a, const struct stat *b) {
×
829
        int r;
×
830

831
        r = inode_compare_func(a, b);
×
832
        if (r != 0)
×
833
                return r;
834

835
        r = CMP(a->st_mtim.tv_sec, b->st_mtim.tv_sec);
×
836
        if (r != 0)
×
837
                return r;
×
838

839
        r = CMP(a->st_mtim.tv_nsec, b->st_mtim.tv_nsec);
×
840
        if (r != 0)
×
841
                return r;
×
842

843
        if (S_ISREG(a->st_mode)) {
×
844
                r = CMP(a->st_size, b->st_size);
×
845
                if (r != 0)
×
846
                        return r;
×
847
        }
848

849
        if (S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) {
×
850
                r = CMP(a->st_rdev, b->st_rdev);
×
851
                if (r != 0)
×
852
                        return r;
×
853
        }
854

855
        return 0;
856
}
857

858
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_unmodified_hash_ops, struct stat, inode_unmodified_hash_func, inode_unmodified_compare_func, free);
4✔
859

860
const char* inode_type_to_string(mode_t m) {
19,756✔
861

862
        /* Returns a short string for the inode type. We use the same name as the underlying macros for each
863
         * inode type. */
864

865
        switch (m & S_IFMT) {
19,756✔
866
        case S_IFREG:
867
                return "reg";
868
        case S_IFDIR:
6,782✔
869
                return "dir";
6,782✔
870
        case S_IFLNK:
1✔
871
                return "lnk";
1✔
872
        case S_IFCHR:
1,019✔
873
                return "chr";
1,019✔
874
        case S_IFBLK:
470✔
875
                return "blk";
470✔
876
        case S_IFIFO:
470✔
877
                return "fifo";
470✔
878
        case S_IFSOCK:
614✔
879
                return "sock";
614✔
880
        }
881

882
        /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
883
         * return an .st_mode where we'll return NULL here! */
884
        return NULL;
4✔
885
}
886

887
mode_t inode_type_from_string(const char *s) {
13✔
888
        if (!s)
13✔
889
                return MODE_INVALID;
890

891
        if (streq(s, "reg"))
13✔
892
                return S_IFREG;
893
        if (streq(s, "dir"))
10✔
894
                return S_IFDIR;
895
        if (streq(s, "lnk"))
7✔
896
                return S_IFLNK;
897
        if (streq(s, "chr"))
6✔
898
                return S_IFCHR;
899
        if (streq(s, "blk"))
5✔
900
                return S_IFBLK;
901
        if (streq(s, "fifo"))
4✔
902
                return S_IFIFO;
903
        if (streq(s, "sock"))
2✔
904
                return S_IFSOCK;
2✔
905

906
        return MODE_INVALID;
907
}
908

909
int vfs_free_bytes(int fd, uint64_t *ret) {
×
910
        assert(fd >= 0);
×
911
        assert(ret);
×
912

913
        /* Safely returns the current available disk space (for root, i.e. including any space reserved for
914
         * root) of the disk referenced by the fd, converted to bytes. */
915

916
        struct statvfs sv;
×
917
        if (fstatvfs(fd, &sv) < 0)
×
918
                return -errno;
×
919

920
        if (!MUL_SAFE(ret, (uint64_t) sv.f_frsize, (uint64_t) sv.f_bfree))
×
921
                return -ERANGE;
×
922

923
        return 0;
924
}
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