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

systemd / systemd / 18988181302

31 Oct 2025 09:30PM UTC coverage: 72.241% (+0.2%) from 72.046%
18988181302

push

github

web-flow
core: Add RootDirectoryFileDescriptor= (#39480)

RootDirectory= but via a open_tree() file descriptor. This allows
setting up the execution environment for a service by the client in a
mount namespace and then starting a transient unit in that execution
environment using the new property.

We also add --root-directory= and --same-root-dir= to systemd-run to
have it run services within the given root directory. As systemd-run
might be invoked from a different mount namespace than what systemd is
running in, systemd-run opens the given path with open_tree() and then
sends it to systemd using the new RootDirectoryFileDescriptor= property.

45 of 76 new or added lines in 8 files covered. (59.21%)

2101 existing lines in 44 files now uncovered.

305020 of 422226 relevant lines covered (72.24%)

1081585.12 hits per line

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

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

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

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

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

31
        struct stat st;
1,847,769✔
32
        int r;
1,847,769✔
33

34
        assert(fd >= 0 || fd == AT_FDCWD);
1,847,769✔
35
        assert(!isempty(path) || !follow);
1,847,769✔
36
        assert(verify_func);
1,847,769✔
37

38
        if (fstatat(fd, strempty(path), &st,
4,528,507✔
39
                    (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
1,847,769✔
40
                return -errno;
7,327✔
41

42
        r = verify_func(&st);
1,840,442✔
43
        return verify ? r : r >= 0;
1,840,442✔
44
}
45

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

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

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

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

58
        if (!S_ISREG(st->st_mode))
2,655,227✔
59
                return -EBADFD;
17✔
60

61
        return 0;
62
}
63

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

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

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

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

79
        if (!S_ISDIR(st->st_mode))
1,044,130✔
80
                return -ENOTDIR;
2✔
81

82
        return 0;
83
}
84

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

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

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

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

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

105
        if (!S_ISLNK(st->st_mode))
17,907✔
106
                return -ENOLINK;
268✔
107

108
        return 0;
109
}
110

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

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

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

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

126
        return 0;
127
}
128

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

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

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

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

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

146
        return 0;
147
}
148

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

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

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

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

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

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

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

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

188
        return 1;
189
}
190

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

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

197
        return S_ISCHR(st->st_mode);
408,724✔
198
}
199

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

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

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

210
        assert(fn);
294,476✔
211

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

216
        if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
588,952✔
217
                return true;
294,476✔
218

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

223
        return null_or_empty(&st);
294,199✔
224
}
225

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

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

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

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

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

244
        return false;
245
}
246

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

250
        assert(path);
1,542✔
251

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
316
                        goto fallback;
×
317
                }
318

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

UNCOV
329
                        goto fallback;
×
330
                }
331

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

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

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

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

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

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

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

363
        return F_TYPE_EQUAL(s->f_type, magic_value);
6,862,864✔
364
}
365

366
int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
6,857,900✔
367
        struct statfs s;
6,857,900✔
368
        int r;
6,857,900✔
369

370
        r = xstatfsat(dir_fd, path, &s);
6,857,900✔
371
        if (r < 0)
6,857,900✔
372
                return r;
6,857,900✔
373

374
        return is_fs_type(&s, magic_value);
6,857,899✔
375
}
376

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

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

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

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

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

394
int fd_is_network_fs(int fd) {
20,585✔
395
        struct statfs s;
20,585✔
396

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

400
        return is_network_fs(&s);
20,585✔
401
}
402

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

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

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

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

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

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

421
int proc_mounted(void) {
23,364✔
422
        int r;
23,364✔
423

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

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

430
        return r;
431
}
432

433
bool stat_inode_same(const struct stat *a, const struct stat *b) {
942,433✔
434

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

438
        return stat_is_set(a) && stat_is_set(b) &&
1,882,749✔
439
                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
906,475✔
440
                a->st_dev == b->st_dev &&
1,806,903✔
441
                a->st_ino == b->st_ino;
864,470✔
442
}
443

444
bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
121,321✔
445

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

455
        return stat_inode_same(a, b) &&
121,321✔
456
                a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
78,456✔
457
                a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
78,286✔
458
                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
199,580✔
459
                (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
78,259✔
460
}
461

462
bool statx_inode_same(const struct statx *a, const struct statx *b) {
3,735,483✔
463

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

466
        return statx_is_set(a) && statx_is_set(b) &&
7,470,966✔
467
                FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) &&
3,735,483✔
468
                ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
3,735,483✔
469
                a->stx_dev_major == b->stx_dev_major &&
3,735,483✔
470
                a->stx_dev_minor == b->stx_dev_minor &&
3,624,197✔
471
                a->stx_ino == b->stx_ino;
3,622,468✔
472
}
473

474
bool statx_mount_same(const struct statx *a, const struct statx *b) {
3,584,626✔
475
        if (!statx_is_set(a) || !statx_is_set(b))
7,169,252✔
476
                return false;
477

478
        /* if we have the mount ID, that's all we need */
479
        if (FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID))
3,584,626✔
480
                return a->stx_mnt_id == b->stx_mnt_id;
3,584,626✔
481

482
        /* Otherwise, major/minor of backing device must match */
483
        return a->stx_dev_major == b->stx_dev_major &&
×
484
                a->stx_dev_minor == b->stx_dev_minor;
×
485
}
486

487
int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
6,858,322✔
488
        _cleanup_close_ int fd = -EBADF;
6,858,322✔
489

490
        assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
6,858,322✔
491
        assert(ret);
6,858,322✔
492

493
        if (!isempty(path)) {
6,858,322✔
494
                fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
23,914✔
495
                if (fd < 0)
23,914✔
496
                        return fd;
497
                dir_fd = fd;
498
        }
499

500
        return RET_NERRNO(fstatfs(dir_fd, ret));
6,858,322✔
501
}
502

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

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

515
int inode_compare_func(const struct stat *a, const struct stat *b) {
31,411✔
516
        int r;
31,411✔
517

518
        r = CMP(a->st_dev, b->st_dev);
31,411✔
519
        if (r != 0)
26,368✔
520
                return r;
5,138✔
521

522
        return CMP(a->st_ino, b->st_ino);
26,273✔
523
}
524

525
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
840✔
526

527
const char* inode_type_to_string(mode_t m) {
7,560✔
528

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

532
        switch (m & S_IFMT) {
7,560✔
533
        case S_IFREG:
534
                return "reg";
535
        case S_IFDIR:
2,892✔
536
                return "dir";
2,892✔
537
        case S_IFLNK:
1✔
538
                return "lnk";
1✔
539
        case S_IFCHR:
993✔
540
                return "chr";
993✔
541
        case S_IFBLK:
464✔
542
                return "blk";
464✔
543
        case S_IFIFO:
464✔
544
                return "fifo";
464✔
545
        case S_IFSOCK:
596✔
546
                return "sock";
596✔
547
        }
548

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

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

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

573
        return MODE_INVALID;
574
}
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