]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/stat-util.c
sd-ipv4acd: fix assertion triggered when an ARP received in STARTED state
[thirdparty/systemd.git] / src / basic / stat-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8fcde012 2
11c3a366 3#include <errno.h>
8fcde012 4#include <fcntl.h>
dccca82b 5#include <sched.h>
8fcde012 6#include <sys/statvfs.h>
dccca82b 7#include <sys/types.h>
8fcde012
LP
8#include <unistd.h>
9
846b3bd6 10#include "alloc-util.h"
f461a28d 11#include "chase.h"
8fcde012 12#include "dirent-util.h"
f4351959 13#include "errno-util.h"
8fcde012 14#include "fd-util.h"
9f81a592 15#include "fileio.h"
659d1924
ILG
16#include "filesystems.h"
17#include "fs-util.h"
ddfdf86f 18#include "hash-funcs.h"
8fcde012 19#include "macro.h"
f5947a5e
YW
20#include "missing_fs.h"
21#include "missing_magic.h"
ca194a2a 22#include "missing_syscall.h"
f663a15a 23#include "mountpoint-util.h"
659d1924 24#include "nulstr-util.h"
846b3bd6 25#include "parse-util.h"
8fcde012
LP
26#include "stat-util.h"
27#include "string-util.h"
28
2560dcbf
MY
29static int verify_stat_at(
30 int fd,
31 const char *path,
32 bool follow,
33 int (*verify_func)(const struct stat *st),
34 bool verify) {
8fcde012 35
2560dcbf
MY
36 struct stat st;
37 int r;
38
39 assert(fd >= 0 || fd == AT_FDCWD);
40 assert(!isempty(path) || !follow);
41 assert(verify_func);
8fcde012 42
2560dcbf
MY
43 if (fstatat(fd, strempty(path), &st,
44 (isempty(path) ? AT_EMPTY_PATH : 0) | (follow ? 0 : AT_SYMLINK_NOFOLLOW)) < 0)
8fcde012
LP
45 return -errno;
46
2560dcbf
MY
47 r = verify_func(&st);
48 return verify ? r : r >= 0;
8fcde012
LP
49}
50
2560dcbf
MY
51int stat_verify_regular(const struct stat *st) {
52 assert(st);
53
54 /* Checks whether the specified stat() structure refers to a regular file. If not returns an
55 * appropriate error code. */
8fcde012 56
2560dcbf
MY
57 if (S_ISDIR(st->st_mode))
58 return -EISDIR;
8fcde012 59
2560dcbf
MY
60 if (S_ISLNK(st->st_mode))
61 return -ELOOP;
62
63 if (!S_ISREG(st->st_mode))
64 return -EBADFD;
8fcde012 65
2560dcbf 66 return 0;
a12e4ade
FB
67}
68
2560dcbf
MY
69int verify_regular_at(int fd, const char *path, bool follow) {
70 return verify_stat_at(fd, path, follow, stat_verify_regular, true);
71}
8fcde012 72
2560dcbf
MY
73int fd_verify_regular(int fd) {
74 assert(fd >= 0);
75 return verify_regular_at(fd, NULL, false);
76}
8fcde012 77
2560dcbf
MY
78int stat_verify_directory(const struct stat *st) {
79 assert(st);
80
81 if (S_ISLNK(st->st_mode))
82 return -ELOOP;
83
84 if (!S_ISDIR(st->st_mode))
85 return -ENOTDIR;
86
87 return 0;
88}
89
90int fd_verify_directory(int fd) {
91 assert(fd >= 0);
92 return verify_stat_at(fd, NULL, false, stat_verify_directory, true);
93}
94
95int is_dir_at(int fd, const char *path, bool follow) {
96 return verify_stat_at(fd, path, follow, stat_verify_directory, false);
97}
98
99int is_dir(const char *path, bool follow) {
100 assert(!isempty(path));
101 return is_dir_at(AT_FDCWD, path, follow);
102}
103
104int stat_verify_symlink(const struct stat *st) {
105 assert(st);
106
107 if (S_ISDIR(st->st_mode))
108 return -EISDIR;
109
110 if (!S_ISLNK(st->st_mode))
111 return -ENOLINK;
112
113 return 0;
114}
115
116int is_symlink(const char *path) {
117 assert(!isempty(path));
118 return verify_stat_at(AT_FDCWD, path, false, stat_verify_symlink, false);
119}
120
121int stat_verify_linked(const struct stat *st) {
122 assert(st);
123
124 if (st->st_nlink <= 0)
125 return -EIDRM; /* recognizable error. */
126
127 return 0;
128}
129
130int fd_verify_linked(int fd) {
131 assert(fd >= 0);
132 return verify_stat_at(fd, NULL, false, stat_verify_linked, true);
133}
134
135int stat_verify_device_node(const struct stat *st) {
136 assert(st);
137
138 if (S_ISLNK(st->st_mode))
139 return -ELOOP;
140
141 if (S_ISDIR(st->st_mode))
142 return -EISDIR;
143
144 if (!S_ISBLK(st->st_mode) && !S_ISCHR(st->st_mode))
145 return -ENOTTY;
146
147 return 0;
148}
8fcde012 149
2560dcbf
MY
150int is_device_node(const char *path) {
151 assert(!isempty(path));
152 return verify_stat_at(AT_FDCWD, path, false, stat_verify_device_node, false);
8fcde012
LP
153}
154
db55bbf2 155int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
254d1313 156 _cleanup_close_ int fd = -EBADF;
db55bbf2
LP
157 struct dirent *buf;
158 size_t m;
8fcde012 159
b8f762f2 160 if (path) {
d96f21ee
LP
161 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
162
d7c1a15e 163 fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
b8f762f2
LB
164 if (fd < 0)
165 return -errno;
d96f21ee
LP
166 } else if (dir_fd == AT_FDCWD) {
167 fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
168 if (fd < 0)
169 return -errno;
b8f762f2 170 } else {
a068acea
LP
171 /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
172 * getedents64(). */
173 assert(dir_fd >= 0);
174
b9d06522 175 fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
b8f762f2
LB
176 if (fd < 0)
177 return fd;
178 }
d7c1a15e 179
db55bbf2
LP
180 /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
181 * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
182 * 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of
183 * entries that we end up ignoring. */
184 m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX;
185 buf = alloca(m);
186
187 for (;;) {
188 struct dirent *de;
189 ssize_t n;
190
191 n = getdents64(fd, buf, m);
192 if (n < 0)
193 return -errno;
194 if (n == 0)
195 break;
8fcde012 196
db55bbf2
LP
197 assert((size_t) n <= m);
198 msan_unpoison(buf, n);
0dbce03c 199
db55bbf2
LP
200 FOREACH_DIRENT_IN_BUFFER(de, buf, n)
201 if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
202 return 0;
203 }
8fcde012
LP
204
205 return 1;
206}
207
208bool null_or_empty(struct stat *st) {
209 assert(st);
210
211 if (S_ISREG(st->st_mode) && st->st_size <= 0)
212 return true;
213
29da4193
LP
214 /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character
215 * device node?" check. */
8fcde012 216
29da4193 217 if (S_ISCHR(st->st_mode))
8fcde012
LP
218 return true;
219
220 return false;
221}
222
48542eac 223int null_or_empty_path_with_root(const char *fn, const char *root) {
8fcde012 224 struct stat st;
48542eac 225 int r;
8fcde012
LP
226
227 assert(fn);
228
48542eac
ZJS
229 /* A symlink to /dev/null or an empty file?
230 * When looking under root_dir, we can't expect /dev/ to be mounted,
231 * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
232
1934242b 233 if (path_equal(path_startswith(fn, root ?: "/"), "dev/null"))
29da4193
LP
234 return true;
235
f461a28d 236 r = chase_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st);
48542eac
ZJS
237 if (r < 0)
238 return r;
8fcde012
LP
239
240 return null_or_empty(&st);
241}
242
d30d4488 243int fd_is_read_only_fs(int fd) {
8fcde012
LP
244 struct statvfs st;
245
dc1752ea 246 assert(fd >= 0);
8fcde012 247
dc1752ea 248 if (fstatvfs(fd, &st) < 0)
8fcde012
LP
249 return -errno;
250
251 if (st.f_flag & ST_RDONLY)
252 return true;
253
dc1752ea
YW
254 /* On NFS, fstatvfs() might not reflect whether we can actually write to the remote share. Let's try
255 * again with access(W_OK) which is more reliable, at least sometimes. */
256 if (access_fd(fd, W_OK) == -EROFS)
8fcde012
LP
257 return true;
258
259 return false;
260}
261
dc1752ea 262int path_is_read_only_fs(const char *path) {
a5937dcf 263 _cleanup_close_ int fd = -EBADF;
dc1752ea
YW
264
265 assert(path);
266
267 fd = open(path, O_CLOEXEC | O_PATH);
268 if (fd < 0)
269 return -errno;
270
271 return fd_is_read_only_fs(fd);
272}
273
563e6846 274int inode_same_at(int fda, const char *filea, int fdb, const char *fileb, int flags) {
f663a15a
LP
275 struct stat sta, stb;
276 int r;
8fcde012 277
676ade31 278 assert(fda >= 0 || fda == AT_FDCWD);
676ade31 279 assert(fdb >= 0 || fdb == AT_FDCWD);
f663a15a
LP
280 assert((flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT)) == 0);
281
282 /* Refuse an unset filea or fileb early unless AT_EMPTY_PATH is set */
283 if ((isempty(filea) || isempty(fileb)) && !FLAGS_SET(flags, AT_EMPTY_PATH))
284 return -EINVAL;
285
286 /* Shortcut: comparing the same fd with itself means we can return true */
287 if (fda >= 0 && fda == fdb && isempty(filea) && isempty(fileb) && FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW))
288 return true;
289
290 _cleanup_close_ int pin_a = -EBADF, pin_b = -EBADF;
291 if (!FLAGS_SET(flags, AT_NO_AUTOMOUNT)) {
292 /* Let's try to use the name_to_handle_at() AT_HANDLE_FID API to identify identical
293 * inodes. We have to issue multiple calls on the same file for that (first, to acquire the
294 * FID, and then to check if .st_dev is actually the same). Hence let's pin the inode in
295 * between via O_PATH, unless we already have an fd for it. */
296
297 if (!isempty(filea)) {
298 pin_a = openat(fda, filea, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
299 if (pin_a < 0)
300 return -errno;
301
302 fda = pin_a;
303 filea = NULL;
304 flags |= AT_EMPTY_PATH;
305 }
306
307 if (!isempty(fileb)) {
308 pin_b = openat(fdb, fileb, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0));
309 if (pin_b < 0)
310 return -errno;
311
312 fdb = pin_b;
313 fileb = NULL;
314 flags |= AT_EMPTY_PATH;
315 }
316
317 int ntha_flags = (flags & AT_EMPTY_PATH) | (FLAGS_SET(flags, AT_SYMLINK_NOFOLLOW) ? 0 : AT_SYMLINK_FOLLOW);
318 _cleanup_free_ struct file_handle *ha = NULL, *hb = NULL;
319 int mntida = -1, mntidb = -1;
320
321 r = name_to_handle_at_try_fid(
322 fda,
323 filea,
324 &ha,
325 &mntida,
326 ntha_flags);
327 if (r < 0) {
328 if (is_name_to_handle_at_fatal_error(r))
329 return r;
330
331 goto fallback;
332 }
333
334 r = name_to_handle_at_try_fid(
335 fdb,
336 fileb,
337 &hb,
338 &mntidb,
339 ntha_flags);
340 if (r < 0) {
341 if (is_name_to_handle_at_fatal_error(r))
342 return r;
343
344 goto fallback;
345 }
346
347 /* Now compare the two file handles */
348 if (!file_handle_equal(ha, hb))
349 return false;
350
351 /* If the file handles are the same and they come from the same mount ID? Great, then we are
352 * good, they are definitely the same */
353 if (mntida == mntidb)
354 return true;
355
356 /* File handles are the same, they are not on the same mount id. This might either be because
357 * they are on two entirely different file systems, that just happen to have the same FIDs
358 * (because they originally where created off the same disk images), or it could be because
359 * they are located on two distinct bind mounts of the same fs. To check that, let's look at
360 * .st_rdev of the inode. We simply reuse the fallback codepath for that, since it checks
361 * exactly that (it checks slightly more, but we don't care.) */
362 }
8fcde012 363
f663a15a
LP
364fallback:
365 if (fstatat(fda, strempty(filea), &sta, flags) < 0)
366 return log_debug_errno(errno, "Cannot stat %s: %m", strna(filea));
8fcde012 367
f663a15a
LP
368 if (fstatat(fdb, strempty(fileb), &stb, flags) < 0)
369 return log_debug_errno(errno, "Cannot stat %s: %m", strna(fileb));
8fcde012 370
f663a15a 371 return stat_inode_same(&sta, &stb);
8fcde012
LP
372}
373
374bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
375 assert(s);
376 assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
377
378 return F_TYPE_EQUAL(s->f_type, magic_value);
379}
380
b196e17e 381int is_fs_type_at(int dir_fd, const char *path, statfs_f_type_t magic_value) {
6d965610 382 struct statfs s;
b196e17e 383 int r;
8fcde012 384
b196e17e
DDM
385 r = xstatfsat(dir_fd, path, &s);
386 if (r < 0)
387 return r;
8fcde012 388
6d965610 389 return is_fs_type(&s, magic_value);
8fcde012
LP
390}
391
392bool is_temporary_fs(const struct statfs *s) {
659d1924 393 return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
77f9fa3b
LP
394}
395
396bool is_network_fs(const struct statfs *s) {
659d1924 397 return fs_in_group(s, FILESYSTEM_SET_NETWORK);
8fcde012
LP
398}
399
400int fd_is_temporary_fs(int fd) {
401 struct statfs s;
402
403 if (fstatfs(fd, &s) < 0)
404 return -errno;
405
406 return is_temporary_fs(&s);
407}
ffeb8285 408
77f9fa3b
LP
409int fd_is_network_fs(int fd) {
410 struct statfs s;
411
412 if (fstatfs(fd, &s) < 0)
413 return -errno;
414
415 return is_network_fs(&s);
416}
417
ffeb8285 418int path_is_temporary_fs(const char *path) {
15308e50 419 struct statfs s;
ffeb8285 420
15308e50 421 if (statfs(path, &s) < 0)
ffeb8285
LP
422 return -errno;
423
15308e50 424 return is_temporary_fs(&s);
ffeb8285 425}
3cc44114 426
4e247216
YW
427int path_is_network_fs(const char *path) {
428 struct statfs s;
429
430 if (statfs(path, &s) < 0)
431 return -errno;
432
433 return is_network_fs(&s);
434}
435
883fff25
LP
436int proc_mounted(void) {
437 int r;
438
439 /* A quick check of procfs is properly mounted */
440
441 r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
442 if (r == -ENOENT) /* not mounted at all */
443 return false;
444
445 return r;
446}
fee5c52a 447
38db6211
LP
448bool stat_inode_same(const struct stat *a, const struct stat *b) {
449
450 /* Returns if the specified stat structure references the same (though possibly modified) inode. Does
451 * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
452
1c248d7f 453 return stat_is_set(a) && stat_is_set(b) &&
38db6211
LP
454 ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */
455 a->st_dev == b->st_dev &&
456 a->st_ino == b->st_ino;
457}
458
fee5c52a
LP
459bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
460
461 /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
462 * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
463 * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
464 * size, backing device, inode type and if this refers to a device not the major/minor.
465 *
466 * Note that we don't care if file attributes such as ownership or access mode change, this here is
467 * about contents of the file. The purpose here is to detect file contents changes, and nothing
468 * else. */
469
38db6211 470 return stat_inode_same(a, b) &&
a59b0a9f
MS
471 a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
472 a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
fee5c52a 473 (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
fee5c52a
LP
474 (!(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 */
475}
ca194a2a 476
2bd315fb
LP
477bool statx_inode_same(const struct statx *a, const struct statx *b) {
478
479 /* Same as stat_inode_same() but for struct statx */
480
1c248d7f 481 return statx_is_set(a) && statx_is_set(b) &&
2bd315fb 482 FLAGS_SET(a->stx_mask, STATX_TYPE|STATX_INO) && FLAGS_SET(b->stx_mask, STATX_TYPE|STATX_INO) &&
2bd315fb
LP
483 ((a->stx_mode ^ b->stx_mode) & S_IFMT) == 0 &&
484 a->stx_dev_major == b->stx_dev_major &&
485 a->stx_dev_minor == b->stx_dev_minor &&
486 a->stx_ino == b->stx_ino;
487}
488
52f19d96 489bool statx_mount_same(const struct new_statx *a, const struct new_statx *b) {
1c248d7f 490 if (!new_statx_is_set(a) || !new_statx_is_set(b))
52f19d96
LP
491 return false;
492
493 /* if we have the mount ID, that's all we need */
494 if (FLAGS_SET(a->stx_mask, STATX_MNT_ID) && FLAGS_SET(b->stx_mask, STATX_MNT_ID))
495 return a->stx_mnt_id == b->stx_mnt_id;
496
497 /* Otherwise, major/minor of backing device must match */
498 return a->stx_dev_major == b->stx_dev_major &&
499 a->stx_dev_minor == b->stx_dev_minor;
500}
501
d35ff4b6
YW
502static bool is_statx_fatal_error(int err, int flags) {
503 assert(err < 0);
504
505 /* If statx() is not supported or if we see EPERM (which might indicate seccomp filtering or so),
506 * let's do a fallback. Note that on EACCES we'll not fall back, since that is likely an indication of
507 * fs access issues, which we should propagate. */
508 if (ERRNO_IS_NOT_SUPPORTED(err) || err == -EPERM)
509 return false;
510
511 /* When unsupported flags are specified, glibc's fallback function returns -EINVAL.
512 * See statx_generic() in glibc. */
513 if (err != -EINVAL)
514 return true;
515
516 if ((flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW | AT_STATX_SYNC_AS_STAT)) != 0)
517 return false; /* Unsupported flags are specified. Let's try to use our implementation. */
518
519 return true;
520}
521
ca194a2a
LP
522int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
523 static bool avoid_statx = false;
524 struct stat st;
d35ff4b6 525 int r;
ca194a2a
LP
526
527 if (!avoid_statx) {
d35ff4b6
YW
528 r = RET_NERRNO(statx(dfd, path, flags, mask, sx));
529 if (r >= 0 || is_statx_fatal_error(r, flags))
530 return r;
ca194a2a
LP
531
532 avoid_statx = true;
533 }
534
535 /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
536 * OK to ignore */
537 if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
538 AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
539 return -EOPNOTSUPP;
540
541 if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
542 return -errno;
543
544 *sx = (struct statx) {
545 .stx_mask = STATX_TYPE|STATX_MODE|
546 STATX_NLINK|STATX_UID|STATX_GID|
547 STATX_ATIME|STATX_MTIME|STATX_CTIME|
548 STATX_INO|STATX_SIZE|STATX_BLOCKS,
549 .stx_blksize = st.st_blksize,
550 .stx_nlink = st.st_nlink,
551 .stx_uid = st.st_uid,
552 .stx_gid = st.st_gid,
553 .stx_mode = st.st_mode,
554 .stx_ino = st.st_ino,
555 .stx_size = st.st_size,
556 .stx_blocks = st.st_blocks,
557 .stx_rdev_major = major(st.st_rdev),
558 .stx_rdev_minor = minor(st.st_rdev),
559 .stx_dev_major = major(st.st_dev),
560 .stx_dev_minor = minor(st.st_dev),
561 .stx_atime.tv_sec = st.st_atim.tv_sec,
562 .stx_atime.tv_nsec = st.st_atim.tv_nsec,
563 .stx_mtime.tv_sec = st.st_mtim.tv_sec,
564 .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
565 .stx_ctime.tv_sec = st.st_ctim.tv_sec,
566 .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
567 };
568
569 return 0;
570}
ddfdf86f 571
6eec59f9
DDM
572int xstatfsat(int dir_fd, const char *path, struct statfs *ret) {
573 _cleanup_close_ int fd = -EBADF;
574
575 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
6eec59f9
DDM
576 assert(ret);
577
e40b11be 578 fd = xopenat(dir_fd, path, O_PATH|O_CLOEXEC|O_NOCTTY);
6eec59f9
DDM
579 if (fd < 0)
580 return fd;
581
582 return RET_NERRNO(fstatfs(fd, ret));
583}
584
ddfdf86f 585void inode_hash_func(const struct stat *q, struct siphash *state) {
c01a5c05
YW
586 siphash24_compress_typesafe(q->st_dev, state);
587 siphash24_compress_typesafe(q->st_ino, state);
ddfdf86f
DDM
588}
589
590int inode_compare_func(const struct stat *a, const struct stat *b) {
591 int r;
592
593 r = CMP(a->st_dev, b->st_dev);
594 if (r != 0)
595 return r;
596
597 return CMP(a->st_ino, b->st_ino);
598}
599
600DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free);
d83ce136
LP
601
602const char* inode_type_to_string(mode_t m) {
603
604 /* Returns a short string for the inode type. We use the same name as the underlying macros for each
605 * inode type. */
606
607 switch (m & S_IFMT) {
608 case S_IFREG:
609 return "reg";
610 case S_IFDIR:
611 return "dir";
e83fbf4b
LP
612 case S_IFLNK:
613 return "lnk";
d83ce136
LP
614 case S_IFCHR:
615 return "chr";
616 case S_IFBLK:
617 return "blk";
618 case S_IFIFO:
619 return "fifo";
620 case S_IFSOCK:
621 return "sock";
622 }
623
f03caa0d 624 /* Note anonymous inodes in the kernel will have a zero type. Hence fstat() of an eventfd() will
7cff2b79 625 * return an .st_mode where we'll return NULL here! */
d83ce136
LP
626 return NULL;
627}
cc037880
LP
628
629mode_t inode_type_from_string(const char *s) {
630 if (!s)
631 return MODE_INVALID;
632
633 if (streq(s, "reg"))
634 return S_IFREG;
635 if (streq(s, "dir"))
636 return S_IFDIR;
637 if (streq(s, "lnk"))
638 return S_IFLNK;
639 if (streq(s, "chr"))
640 return S_IFCHR;
641 if (streq(s, "blk"))
642 return S_IFBLK;
643 if (streq(s, "fifo"))
644 return S_IFIFO;
645 if (streq(s, "sock"))
646 return S_IFSOCK;
647
648 return MODE_INVALID;
649}