1 /* SPDX-License-Identifier: LGPL-2.1+ */
6 #include <sys/statvfs.h>
10 #include "alloc-util.h"
11 #include "dirent-util.h"
16 #include "missing_fs.h"
17 #include "missing_magic.h"
18 #include "missing_syscall.h"
19 #include "parse-util.h"
20 #include "stat-util.h"
21 #include "string-util.h"
23 int is_symlink(const char *path
) {
28 if (lstat(path
, &info
) < 0)
31 return !!S_ISLNK(info
.st_mode
);
34 int is_dir(const char* path
, bool follow
) {
47 return !!S_ISDIR(st
.st_mode
);
50 int is_dir_fd(int fd
) {
53 if (fstat(fd
, &st
) < 0)
56 return !!S_ISDIR(st
.st_mode
);
59 int is_device_node(const char *path
) {
64 if (lstat(path
, &info
) < 0)
67 return !!(S_ISBLK(info
.st_mode
) || S_ISCHR(info
.st_mode
));
70 int dir_is_empty_at(int dir_fd
, const char *path
) {
71 _cleanup_close_
int fd
= -1;
72 _cleanup_closedir_
DIR *d
= NULL
;
76 fd
= openat(dir_fd
, path
, O_RDONLY
|O_DIRECTORY
|O_CLOEXEC
);
78 fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
82 d
= take_fdopendir(&fd
);
86 FOREACH_DIRENT(de
, d
, return -errno
)
92 bool null_or_empty(struct stat
*st
) {
95 if (S_ISREG(st
->st_mode
) && st
->st_size
<= 0)
98 /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character
99 * device node?" check. */
101 if (S_ISCHR(st
->st_mode
))
107 int null_or_empty_path(const char *fn
) {
112 /* If we have the path, let's do an easy text comparison first. */
113 if (path_equal(fn
, "/dev/null"))
116 if (stat(fn
, &st
) < 0)
119 return null_or_empty(&st
);
122 int null_or_empty_fd(int fd
) {
127 if (fstat(fd
, &st
) < 0)
130 return null_or_empty(&st
);
133 int path_is_read_only_fs(const char *path
) {
138 if (statvfs(path
, &st
) < 0)
141 if (st
.f_flag
& ST_RDONLY
)
144 /* On NFS, statvfs() might not reflect whether we can actually
145 * write to the remote share. Let's try again with
146 * access(W_OK) which is more reliable, at least sometimes. */
147 if (access(path
, W_OK
) < 0 && errno
== EROFS
)
153 int files_same(const char *filea
, const char *fileb
, int flags
) {
159 if (fstatat(AT_FDCWD
, filea
, &a
, flags
) < 0)
162 if (fstatat(AT_FDCWD
, fileb
, &b
, flags
) < 0)
165 return a
.st_dev
== b
.st_dev
&&
166 a
.st_ino
== b
.st_ino
;
169 bool is_fs_type(const struct statfs
*s
, statfs_f_type_t magic_value
) {
171 assert_cc(sizeof(statfs_f_type_t
) >= sizeof(s
->f_type
));
173 return F_TYPE_EQUAL(s
->f_type
, magic_value
);
176 int fd_is_fs_type(int fd
, statfs_f_type_t magic_value
) {
179 if (fstatfs(fd
, &s
) < 0)
182 return is_fs_type(&s
, magic_value
);
185 int path_is_fs_type(const char *path
, statfs_f_type_t magic_value
) {
188 if (statfs(path
, &s
) < 0)
191 return is_fs_type(&s
, magic_value
);
194 bool is_temporary_fs(const struct statfs
*s
) {
195 return is_fs_type(s
, TMPFS_MAGIC
) ||
196 is_fs_type(s
, RAMFS_MAGIC
);
199 bool is_network_fs(const struct statfs
*s
) {
200 return is_fs_type(s
, CIFS_MAGIC_NUMBER
) ||
201 is_fs_type(s
, CODA_SUPER_MAGIC
) ||
202 is_fs_type(s
, NCP_SUPER_MAGIC
) ||
203 is_fs_type(s
, NFS_SUPER_MAGIC
) ||
204 is_fs_type(s
, SMB_SUPER_MAGIC
) ||
205 is_fs_type(s
, V9FS_MAGIC
) ||
206 is_fs_type(s
, AFS_SUPER_MAGIC
) ||
207 is_fs_type(s
, OCFS2_SUPER_MAGIC
);
210 int fd_is_temporary_fs(int fd
) {
213 if (fstatfs(fd
, &s
) < 0)
216 return is_temporary_fs(&s
);
219 int fd_is_network_fs(int fd
) {
222 if (fstatfs(fd
, &s
) < 0)
225 return is_network_fs(&s
);
228 int path_is_temporary_fs(const char *path
) {
229 _cleanup_close_
int fd
= -1;
231 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_PATH
);
235 return fd_is_temporary_fs(fd
);
238 int stat_verify_regular(const struct stat
*st
) {
241 /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
244 if (S_ISDIR(st
->st_mode
))
247 if (S_ISLNK(st
->st_mode
))
250 if (!S_ISREG(st
->st_mode
))
256 int fd_verify_regular(int fd
) {
261 if (fstat(fd
, &st
) < 0)
264 return stat_verify_regular(&st
);
267 int stat_verify_directory(const struct stat
*st
) {
270 if (S_ISLNK(st
->st_mode
))
273 if (!S_ISDIR(st
->st_mode
))
279 int fd_verify_directory(int fd
) {
284 if (fstat(fd
, &st
) < 0)
287 return stat_verify_directory(&st
);
290 int device_path_make_major_minor(mode_t mode
, dev_t devno
, char **ret
) {
293 /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
297 else if (S_ISBLK(mode
))
302 if (asprintf(ret
, "/dev/%s/%u:%u", t
, major(devno
), minor(devno
)) < 0)
308 int device_path_make_canonical(mode_t mode
, dev_t devno
, char **ret
) {
309 _cleanup_free_
char *p
= NULL
;
312 /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
316 if (major(devno
) == 0 && minor(devno
) == 0) {
319 /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
320 * /dev/block/ and /dev/char/, hence we handle them specially here. */
323 s
= strdup("/run/systemd/inaccessible/chr");
324 else if (S_ISBLK(mode
))
325 s
= strdup("/run/systemd/inaccessible/blk");
336 r
= device_path_make_major_minor(mode
, devno
, &p
);
340 return chase_symlinks(p
, NULL
, 0, ret
, NULL
);
343 int device_path_parse_major_minor(const char *path
, mode_t
*ret_mode
, dev_t
*ret_devno
) {
348 /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
349 * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
350 * path cannot be parsed like this. */
352 if (path_equal(path
, "/run/systemd/inaccessible/chr")) {
354 devno
= makedev(0, 0);
355 } else if (path_equal(path
, "/run/systemd/inaccessible/blk")) {
357 devno
= makedev(0, 0);
361 w
= path_startswith(path
, "/dev/block/");
365 w
= path_startswith(path
, "/dev/char/");
372 r
= parse_dev(w
, &devno
);
385 int proc_mounted(void) {
388 /* A quick check of procfs is properly mounted */
390 r
= path_is_fs_type("/proc/", PROC_SUPER_MAGIC
);
391 if (r
== -ENOENT
) /* not mounted at all */
397 bool stat_inode_unmodified(const struct stat
*a
, const struct stat
*b
) {
399 /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
400 * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
401 * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
402 * size, backing device, inode type and if this refers to a device not the major/minor.
404 * Note that we don't care if file attributes such as ownership or access mode change, this here is
405 * about contents of the file. The purpose here is to detect file contents changes, and nothing
409 (a
->st_mode
& S_IFMT
) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
410 ((a
->st_mode
^ b
->st_mode
) & S_IFMT
) == 0 && /* same inode type */
411 a
->st_mtime
== b
->st_mtime
&&
412 (!S_ISREG(a
->st_mode
) || a
->st_size
== b
->st_size
) && /* if regular file, compare file size */
413 a
->st_dev
== b
->st_dev
&&
414 a
->st_ino
== b
->st_ino
&&
415 (!(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 */
418 int statx_fallback(int dfd
, const char *path
, int flags
, unsigned mask
, struct statx
*sx
) {
419 static bool avoid_statx
= false;
423 if (statx(dfd
, path
, flags
, mask
, sx
) < 0) {
424 if (!ERRNO_IS_NOT_SUPPORTED(errno
) && errno
!= EPERM
)
427 /* If statx() is not supported or if we see EPERM (which might indicate seccomp
428 * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
429 * since that is likely an indication of fs access issues, which we should
437 /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
439 if ((flags
& ~(AT_EMPTY_PATH
|AT_NO_AUTOMOUNT
|AT_SYMLINK_NOFOLLOW
|
440 AT_STATX_SYNC_AS_STAT
|AT_STATX_FORCE_SYNC
|AT_STATX_DONT_SYNC
)) != 0)
443 if (fstatat(dfd
, path
, &st
, flags
& (AT_EMPTY_PATH
|AT_NO_AUTOMOUNT
|AT_SYMLINK_NOFOLLOW
)) < 0)
446 *sx
= (struct statx
) {
447 .stx_mask
= STATX_TYPE
|STATX_MODE
|
448 STATX_NLINK
|STATX_UID
|STATX_GID
|
449 STATX_ATIME
|STATX_MTIME
|STATX_CTIME
|
450 STATX_INO
|STATX_SIZE
|STATX_BLOCKS
,
451 .stx_blksize
= st
.st_blksize
,
452 .stx_nlink
= st
.st_nlink
,
453 .stx_uid
= st
.st_uid
,
454 .stx_gid
= st
.st_gid
,
455 .stx_mode
= st
.st_mode
,
456 .stx_ino
= st
.st_ino
,
457 .stx_size
= st
.st_size
,
458 .stx_blocks
= st
.st_blocks
,
459 .stx_rdev_major
= major(st
.st_rdev
),
460 .stx_rdev_minor
= minor(st
.st_rdev
),
461 .stx_dev_major
= major(st
.st_dev
),
462 .stx_dev_minor
= minor(st
.st_dev
),
463 .stx_atime
.tv_sec
= st
.st_atim
.tv_sec
,
464 .stx_atime
.tv_nsec
= st
.st_atim
.tv_nsec
,
465 .stx_mtime
.tv_sec
= st
.st_mtim
.tv_sec
,
466 .stx_mtime
.tv_nsec
= st
.st_mtim
.tv_nsec
,
467 .stx_ctime
.tv_sec
= st
.st_ctim
.tv_sec
,
468 .stx_ctime
.tv_nsec
= st
.st_ctim
.tv_nsec
,