| /* SPDX-License-Identifier: LGPL-2.1+ */ |
| /*** |
| This file is part of systemd. |
| |
| Copyright 2010-2012 Lennart Poettering |
| |
| systemd is free software; you can redistribute it and/or modify it |
| under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation; either version 2.1 of the License, or |
| (at your option) any later version. |
| |
| systemd is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with systemd; If not, see <http://www.gnu.org/licenses/>. |
| ***/ |
| |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <linux/magic.h> |
| #include <sys/statvfs.h> |
| #include <unistd.h> |
| |
| #include "dirent-util.h" |
| #include "fd-util.h" |
| #include "fs-util.h" |
| #include "macro.h" |
| #include "missing.h" |
| #include "stat-util.h" |
| #include "string-util.h" |
| |
| int is_symlink(const char *path) { |
| struct stat info; |
| |
| assert(path); |
| |
| if (lstat(path, &info) < 0) |
| return -errno; |
| |
| return !!S_ISLNK(info.st_mode); |
| } |
| |
| int is_dir(const char* path, bool follow) { |
| struct stat st; |
| int r; |
| |
| assert(path); |
| |
| if (follow) |
| r = stat(path, &st); |
| else |
| r = lstat(path, &st); |
| if (r < 0) |
| return -errno; |
| |
| return !!S_ISDIR(st.st_mode); |
| } |
| |
| int is_device_node(const char *path) { |
| struct stat info; |
| |
| assert(path); |
| |
| if (lstat(path, &info) < 0) |
| return -errno; |
| |
| return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); |
| } |
| |
| int dir_is_empty(const char *path) { |
| _cleanup_closedir_ DIR *d; |
| struct dirent *de; |
| |
| d = opendir(path); |
| if (!d) |
| return -errno; |
| |
| FOREACH_DIRENT(de, d, return -errno) |
| return 0; |
| |
| return 1; |
| } |
| |
| bool null_or_empty(struct stat *st) { |
| assert(st); |
| |
| if (S_ISREG(st->st_mode) && st->st_size <= 0) |
| return true; |
| |
| /* We don't want to hardcode the major/minor of /dev/null, |
| * hence we do a simpler "is this a device node?" check. */ |
| |
| if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) |
| return true; |
| |
| return false; |
| } |
| |
| int null_or_empty_path(const char *fn) { |
| struct stat st; |
| |
| assert(fn); |
| |
| if (stat(fn, &st) < 0) |
| return -errno; |
| |
| return null_or_empty(&st); |
| } |
| |
| int null_or_empty_fd(int fd) { |
| struct stat st; |
| |
| assert(fd >= 0); |
| |
| if (fstat(fd, &st) < 0) |
| return -errno; |
| |
| return null_or_empty(&st); |
| } |
| |
| int path_is_read_only_fs(const char *path) { |
| struct statvfs st; |
| |
| assert(path); |
| |
| if (statvfs(path, &st) < 0) |
| return -errno; |
| |
| if (st.f_flag & ST_RDONLY) |
| return true; |
| |
| /* On NFS, statvfs() might not reflect whether we can actually |
| * write to the remote share. Let's try again with |
| * access(W_OK) which is more reliable, at least sometimes. */ |
| if (access(path, W_OK) < 0 && errno == EROFS) |
| return true; |
| |
| return false; |
| } |
| |
| int path_is_os_tree(const char *path) { |
| int r; |
| |
| assert(path); |
| |
| /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir |
| * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from |
| * the case where just the os-release file is missing. */ |
| if (laccess(path, F_OK) < 0) |
| return -errno; |
| |
| /* We use /usr/lib/os-release as flag file if something is an OS */ |
| r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); |
| if (r == -ENOENT) { |
| |
| /* Also check for the old location in /etc, just in case. */ |
| r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); |
| if (r == -ENOENT) |
| return 0; /* We got nothing */ |
| } |
| if (r < 0) |
| return r; |
| |
| return 1; |
| } |
| |
| int files_same(const char *filea, const char *fileb, int flags) { |
| struct stat a, b; |
| |
| assert(filea); |
| assert(fileb); |
| |
| if (fstatat(AT_FDCWD, filea, &a, flags) < 0) |
| return -errno; |
| |
| if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) |
| return -errno; |
| |
| return a.st_dev == b.st_dev && |
| a.st_ino == b.st_ino; |
| } |
| |
| bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { |
| assert(s); |
| assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); |
| |
| return F_TYPE_EQUAL(s->f_type, magic_value); |
| } |
| |
| int fd_is_fs_type(int fd, statfs_f_type_t magic_value) { |
| struct statfs s; |
| |
| if (fstatfs(fd, &s) < 0) |
| return -errno; |
| |
| return is_fs_type(&s, magic_value); |
| } |
| |
| int path_is_fs_type(const char *path, statfs_f_type_t magic_value) { |
| _cleanup_close_ int fd = -1; |
| |
| fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); |
| if (fd < 0) |
| return -errno; |
| |
| return fd_is_fs_type(fd, magic_value); |
| } |
| |
| bool is_temporary_fs(const struct statfs *s) { |
| return is_fs_type(s, TMPFS_MAGIC) || |
| is_fs_type(s, RAMFS_MAGIC); |
| } |
| |
| int fd_is_temporary_fs(int fd) { |
| struct statfs s; |
| |
| if (fstatfs(fd, &s) < 0) |
| return -errno; |
| |
| return is_temporary_fs(&s); |
| } |
| |
| int fd_is_network_ns(int fd) { |
| int r; |
| |
| r = fd_is_fs_type(fd, NSFS_MAGIC); |
| if (r <= 0) |
| return r; |
| r = ioctl(fd, NS_GET_NSTYPE); |
| if (r < 0) |
| return -errno; |
| return r == CLONE_NEWNET; |
| } |
| |
| int path_is_temporary_fs(const char *path) { |
| _cleanup_close_ int fd = -1; |
| |
| fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); |
| if (fd < 0) |
| return -errno; |
| |
| return fd_is_temporary_fs(fd); |
| } |