• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

87.82
/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(
1,922,273✔
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;
1,922,273✔
33
        int r;
1,922,273✔
34

35
        assert(wildcard_fd_is_valid(fd));
1,922,273✔
36
        assert(!isempty(path) || !follow);
1,922,273✔
37
        assert(verify_func);
1,922,273✔
38

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

44
        if (fstatat(fd, strempty(path), &st,
2,774,557✔
45
                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
2,992,262✔
46
                return -errno;
8,377✔
47

48
        r = verify_func(&st);
1,913,896✔
49
        return verify ? r : r >= 0;
1,913,896✔
50
}
51

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

56
        if (S_ISLNK(mode))
2,748,292✔
57
                return -ELOOP;
58

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

62
        return 0;
63
}
64

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

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

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

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

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

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

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

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

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

101
        return 0;
102
}
103

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

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

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

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

116
        return mode_verify_directory(stx->stx_mode);
72,125✔
117
}
118

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

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

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

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

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

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

141
        if (!S_ISLNK(st->st_mode))
43,514✔
142
                return -ENOLINK;
315✔
143

144
        return 0;
145
}
146

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

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

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

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

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

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

169
        return 0;
170
}
171

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

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

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

181
        return mode_verify_socket(stx->stx_mode);
93✔
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,824,103✔
197
        assert(st);
1,824,103✔
198

199
        if (st->st_nlink <= 0)
1,824,103✔
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) {
143✔
214
        assert(st);
143✔
215

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

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

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

225
        return 0;
226
}
227

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

232
        return verify_stat_at(fd, /* path= */ NULL, /* follow= */ false, stat_verify_block, /* verify= */ true);
2✔
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) {
18,824✔
251
        assert(st);
18,824✔
252

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

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

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

262
        return 0;
263
}
264

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

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

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

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

279
        if (!S_ISREG(st->st_mode) && !S_ISBLK(st->st_mode))
790✔
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) {
41,148✔
293
        _cleanup_close_ int fd = -EBADF;
41,148✔
294
        struct dirent *buf;
41,148✔
295
        size_t m;
41,148✔
296

297
        fd = xopenat(dir_fd, path, O_DIRECTORY|O_CLOEXEC);
41,148✔
298
        if (fd < 0)
41,148✔
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,999✔
306
        buf = alloca(m);
2,999✔
307

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

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

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

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

326
        return 1;
327
}
328

329
bool stat_may_be_dev_null(struct stat *st) {
822,257✔
330
        assert(st);
822,257✔
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);
822,257✔
336
}
337

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

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

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

348
        assert(fn);
642,662✔
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"))
1,285,324✔
355
                return true;
642,662✔
356

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

361
        return null_or_empty(&st);
642,329✔
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,
33,551,943✔
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. */
33,551,943✔
408
        int r;
33,551,943✔
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(wildcard_fd_is_valid(fd));
33,551,943✔
422
        assert((mandatory_mask & optional_mask) == 0);
33,551,943✔
423
        assert(!FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) || !((mandatory_mask|optional_mask) & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)));
33,551,943✔
424
        assert(ret);
33,551,943✔
425

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

431
        unsigned request_mask = mandatory_mask|optional_mask;
33,551,943✔
432
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST))
33,551,943✔
433
                request_mask |= STATX_MNT_ID|STATX_MNT_ID_UNIQUE;
32,709,992✔
434

435
        if (statx(fd,
33,551,943✔
436
                  strempty(path),
33,551,943✔
437
                  statx_flags|(isempty(path) ? AT_EMPTY_PATH : 0),
34,045,445✔
438
                  request_mask,
439
                  &sx) < 0)
440
                return negative_errno();
2,702✔
441

442
        if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
33,549,241✔
443
            !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
32,709,992✔
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)) {
33,549,241✔
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)) {
33,549,241✔
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;
33,549,241✔
465
        return FLAGS_SET(sx.stx_mask, optional_mask);
33,549,241✔
466
}
467

468
static int xfstatfs(int fd, struct statfs *ret) {
7,789,688✔
469
        assert(ret);
7,789,688✔
470

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

476
        assert(fd >= 0);
7,789,686✔
477
        return RET_NERRNO(fstatfs(fd, ret));
7,789,686✔
478
}
479

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

483
        assert(wildcard_fd_is_valid(dir_fd));
7,765,275✔
484
        assert(ret);
7,765,275✔
485

486
        if (!isempty(path)) {
7,765,275✔
487
                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC);
30,226✔
488
                if (fd < 0)
30,226✔
489
                        return fd;
490
                dir_fd = fd;
491
        }
492

493
        return xfstatfs(dir_fd, ret);
7,765,274✔
494
}
495

496
int fd_is_read_only_fs(int fd) {
2,075✔
497
        int r;
2,075✔
498

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

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

507
        if (is_network_fs(&st))
1,686✔
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,797✔
516
        _cleanup_close_ int fd = -EBADF;
1,797✔
517

518
        assert(path);
1,797✔
519

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

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

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

531
        assert(fda >= 0 || fda == AT_FDCWD);
22,604✔
532
        assert(fdb >= 0 || fdb == AT_FDCWD);
22,604✔
533
        assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
22,604✔
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))
43,179✔
537
                return -EINVAL;
22,604✔
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))
22,606✔
541
                return true;
542

543
        _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
45,207✔
544
        if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
22,603✔
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)) {
22,603✔
551
                        pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
20,575✔
552
                        if (pin_a < 0)
20,575✔
553
                                return -errno;
22,325✔
554

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

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

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

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

575
                r = name_to_handle_at_try_fid(
21,750✔
576
                                fda,
577
                                filea,
578
                                &ha,
579
                                &_mntida,
580
                                &mntida,
581
                                ntha_flags);
582
                if (r < 0) {
21,750✔
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;
21,750✔
589

590
                if (!have_unique_mntid)
21,750✔
591
                        mntida = _mntida;
×
592

593
                r = name_to_handle_at_try_fid(
21,750✔
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) {
21,750✔
601
                        if (is_name_to_handle_at_fatal_error(r))
×
602
                                return r;
603

604
                        goto fallback;
×
605
                }
606
                if (r == 0) {
21,750✔
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))
21,750✔
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)
20,318✔
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)
556✔
630
                return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
×
631

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

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

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

642
        return F_TYPE_EQUAL(s->f_type, magic_value);
7,774,348✔
643
}
644

645
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
7,765,274✔
646
        int r;
7,765,274✔
647

648
        struct statfs s;
7,765,274✔
649
        r = xstatfsat(dir_fd, path, &s);
7,765,274✔
650
        if (r < 0)
7,765,274✔
651
                return r;
7,765,274✔
652

653
        return is_fs_type(&s, magic_value);
7,765,273✔
654
}
655

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

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

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

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

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

675
int fd_is_network_fs(int fd) {
22,163✔
676
        int r;
22,163✔
677

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

683
        return is_network_fs(&s);
22,163✔
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) {
30,091✔
705
        /* This is typically used in error path. So, it is better to not overwrite the original errno. */
706
        PROTECT_ERRNO;
30,091✔
707
        int r;
30,091✔
708

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

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

715
        return r;
716
}
717

718
bool stat_inode_same(const struct stat *a, const struct stat *b) {
262,363✔
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) &&
522,642✔
724
                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
224,872✔
725
                a->st_dev == b->st_dev &&
449,207✔
726
                a->st_ino == b->st_ino;
186,844✔
727
}
728

729
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
109,139✔
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
        assert(a);
109,139✔
741
        assert(b);
109,139✔
742

743
        return stat_inode_same(a, b) &&
109,139✔
744
                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
65,869✔
745
                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
65,579✔
746
                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
174,681✔
747
                (!(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 */
65,542✔
748
}
749

750
bool statx_inode_same(const struct statx *a, const struct statx *b) {
1,050,648✔
751

752
        /* Same as stat_inode_same() but for struct statx */
753

754
        if (!statx_is_set(a) || !statx_is_set(b))
2,101,296✔
755
                return false;
756

757
        assert(FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO));
1,050,648✔
758
        assert(FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO));
1,050,648✔
759

760
        return
1,050,648✔
761
                ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
2,095,087✔
762
                a->stx_dev_major == b->stx_dev_major &&
1,044,439✔
763
                a->stx_dev_minor == b->stx_dev_minor &&
3,112,067✔
764
                a->stx_ino == b->stx_ino;
1,002,605✔
765
}
766

767
int statx_mount_same(const struct statx *a, const struct statx *b) {
328,317✔
768
        if (!statx_is_set(a) || !statx_is_set(b))
656,634✔
769
                return false;
770

771
        if ((FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID)) ||
328,317✔
772
            (FLAGS_SET(a->stx_mask, STATX_MNT_ID_UNIQUE) && FLAGS_SET(b->stx_mask, STATX_MNT_ID_UNIQUE)))
6✔
773
                return a->stx_mnt_id == b->stx_mnt_id;
328,317✔
774

775
        return -ENODATA;
776
}
777

778
usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
7,164✔
779
        assert(ts);
7,164✔
780
        return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
7,164✔
781
}
782
nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
820✔
783
        assert(ts);
820✔
784
        return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
820✔
785
}
786

787
void inode_hash_func(const struct stat *q, struct siphash *state) {
33,648✔
788
        assert(q);
33,648✔
789

790
        siphash24_compress_typesafe(q->st_dev, state);
33,648✔
791
        siphash24_compress_typesafe(q->st_ino, state);
33,648✔
792

793
        /* Also include inode type, to mirror stat_inode_same() */
794
        mode_t type = q->st_mode & S_IFMT;
33,648✔
795
        siphash24_compress_typesafe(type, state);
33,648✔
796
}
33,648✔
797

798
int inode_compare_func(const struct stat *a, const struct stat *b) {
27,461✔
799
        int r;
27,461✔
800

801
        assert(a);
27,461✔
802
        assert(b);
27,461✔
803

804
        r = CMP(a->st_dev, b->st_dev);
27,461✔
805
        if (r != 0)
22,791✔
806
                return r;
807

808
        r = CMP(a->st_ino, b->st_ino);
22,672✔
809
        if (r != 0)
7,834✔
810
                return r;
811

812
        return CMP(a->st_mode & S_IFMT, b->st_mode & S_IFMT);
93✔
813
}
814

815
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
1,073✔
816

817
void inode_unmodified_hash_func(const struct stat *q, struct siphash *state) {
7✔
818
        assert(q);
7✔
819

820
        inode_hash_func(q, state);
7✔
821

822
        siphash24_compress_typesafe(q->st_mtim.tv_sec, state);
7✔
823
        siphash24_compress_typesafe(q->st_mtim.tv_nsec, state);
7✔
824

825
        if (S_ISREG(q->st_mode))
7✔
826
                siphash24_compress_typesafe(q->st_size, state);
7✔
827
        else {
UNCOV
828
                uint64_t invalid = UINT64_MAX;
×
UNCOV
829
                siphash24_compress_typesafe(invalid, state);
×
830
        }
831

832
        if (S_ISCHR(q->st_mode) || S_ISBLK(q->st_mode))
7✔
UNCOV
833
                siphash24_compress_typesafe(q->st_rdev, state);
×
834
        else {
835
                dev_t invalid = (dev_t) -1;
7✔
836
                siphash24_compress_typesafe(invalid, state);
7✔
837
        }
838
}
7✔
839

840
int inode_unmodified_compare_func(const struct stat *a, const struct stat *b) {
12✔
841
        int r;
12✔
842

843
        assert(a);
12✔
844
        assert(b);
12✔
845

846
        r = inode_compare_func(a, b);
12✔
847
        if (r != 0)
12✔
848
                return r;
849

850
        r = CMP(a->st_mtim.tv_sec, b->st_mtim.tv_sec);
×
851
        if (r != 0)
×
852
                return r;
853

UNCOV
854
        r = CMP(a->st_mtim.tv_nsec, b->st_mtim.tv_nsec);
×
UNCOV
855
        if (r != 0)
×
856
                return r;
857

UNCOV
858
        if (S_ISREG(a->st_mode)) {
×
UNCOV
859
                r = CMP(a->st_size, b->st_size);
×
UNCOV
860
                if (r != 0)
×
861
                        return r;
862
        }
863

UNCOV
864
        if (S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) {
×
UNCOV
865
                r = CMP(a->st_rdev, b->st_rdev);
×
UNCOV
866
                if (r != 0)
×
UNCOV
867
                        return r;
×
868
        }
869

870
        return 0;
871
}
872

873
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_unmodified_hash_ops, struct stat, inode_unmodified_hash_func, inode_unmodified_compare_func, free);
4✔
874

875
const char* inode_type_to_string(mode_t m) {
20,496✔
876

877
        /* Returns a short string for the inode type. We use the same name as the underlying macros for each
878
         * inode type. */
879

880
        switch (m & S_IFMT) {
20,496✔
881
        case S_IFREG:
882
                return "reg";
883
        case S_IFDIR:
7,048✔
884
                return "dir";
7,048✔
885
        case S_IFLNK:
1✔
886
                return "lnk";
1✔
887
        case S_IFCHR:
1,116✔
888
                return "chr";
1,116✔
889
        case S_IFBLK:
516✔
890
                return "blk";
516✔
891
        case S_IFIFO:
516✔
892
                return "fifo";
516✔
893
        case S_IFSOCK:
661✔
894
                return "sock";
661✔
895
        }
896

897
        /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
898
         * return an .st_mode where we'll return NULL here! */
899
        return NULL;
4✔
900
}
901

902
mode_t inode_type_from_string(const char *s) {
13✔
903
        if (!s)
13✔
904
                return MODE_INVALID;
905

906
        if (streq(s, "reg"))
13✔
907
                return S_IFREG;
908
        if (streq(s, "dir"))
10✔
909
                return S_IFDIR;
910
        if (streq(s, "lnk"))
7✔
911
                return S_IFLNK;
912
        if (streq(s, "chr"))
6✔
913
                return S_IFCHR;
914
        if (streq(s, "blk"))
5✔
915
                return S_IFBLK;
916
        if (streq(s, "fifo"))
4✔
917
                return S_IFIFO;
918
        if (streq(s, "sock"))
2✔
919
                return S_IFSOCK;
2✔
920

921
        return MODE_INVALID;
922
}
923

924
int vfs_free_bytes(int fd, uint64_t *ret) {
22✔
925
        assert(fd >= 0);
22✔
926
        assert(ret);
22✔
927

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

931
        struct statvfs sv;
22✔
932
        if (fstatvfs(fd, &sv) < 0)
22✔
UNCOV
933
                return -errno;
×
934

935
        if (!MUL_SAFE(ret, (uint64_t) sv.f_frsize, (uint64_t) sv.f_bfree))
22✔
UNCOV
936
                return -ERANGE;
×
937

938
        return 0;
939
}
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