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