diff options
Diffstat (limited to 'src/libnm-systemd-shared/src/basic/fs-util.c')
-rw-r--r-- | src/libnm-systemd-shared/src/basic/fs-util.c | 183 |
1 files changed, 80 insertions, 103 deletions
diff --git a/src/libnm-systemd-shared/src/basic/fs-util.c b/src/libnm-systemd-shared/src/basic/fs-util.c index 5fe8fbab98..28c7247e05 100644 --- a/src/libnm-systemd-shared/src/basic/fs-util.c +++ b/src/libnm-systemd-shared/src/basic/fs-util.c @@ -8,7 +8,6 @@ #include <unistd.h> #include "alloc-util.h" -#include "blockdev-util.h" #include "dirent-util.h" #include "fd-util.h" #include "fileio.h" @@ -581,8 +580,6 @@ int get_files_in_directory(const char *path, char ***list) { return -errno; FOREACH_DIRENT_ALL(de, d, return -errno) { - dirent_ensure_type(d, de); - if (!dirent_is_file(de)) continue; @@ -745,7 +742,8 @@ bool unsafe_transition(const struct stat *a, const struct stat *b) { } static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) { - _cleanup_free_ char *n1 = NULL, *n2 = NULL; + _cleanup_free_ char *n1 = NULL, *n2 = NULL, *user_a = NULL, *user_b = NULL; + struct stat st; if (!FLAGS_SET(flags, CHASE_WARN)) return -ENOLINK; @@ -753,9 +751,14 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) (void) fd_get_path(a, &n1); (void) fd_get_path(b, &n2); + if (fstat(a, &st) == 0) + user_a = uid_to_name(st.st_uid); + if (fstat(b, &st) == 0) + user_b = uid_to_name(st.st_uid); + return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), - "Detected unsafe path transition %s %s %s during canonicalization of %s.", - strna(n1), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), path); + "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.", + strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path); } static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { @@ -1375,18 +1378,39 @@ int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) { } int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL; _cleanup_close_ int dfd = -1; struct stat st; int r; assert(fd >= 0); - /* We only reasonably can do this for regular files and directories, hence check for that */ + /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check + * for the inode type first */ if (fstat(fd, &st) < 0) return -errno; - if (S_ISREG(st.st_mode)) { + if (S_ISDIR(st.st_mode)) { + dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); + if (dfd < 0) + return -errno; + + } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other + * types check O_PATH flag */ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return -errno; + + if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case + * we can sensibly do what is requested. Otherwise this refers + * to a socket, fifo or device node, where the concept of a + * containing directory doesn't make too much sense. */ + return -ENOTTY; + } + + if (dfd < 0) { + _cleanup_free_ char *path = NULL; r = fd_get_path(fd, &path); if (r < 0) { @@ -1409,13 +1433,7 @@ int fsync_directory_of_file(int fd) { dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); if (dfd < 0) return dfd; - - } else if (S_ISDIR(st.st_mode)) { - dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0); - if (dfd < 0) - return -errno; - } else - return -ENOTTY; + } if (fsync(dfd) < 0) return -errno; @@ -1466,12 +1484,56 @@ int fsync_path_at(int at_fd, const char *path) { return 0; } +int fsync_parent_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + + if (isempty(path)) { + if (at_fd != AT_FDCWD) + return fsync_directory_of_file(at_fd); + + opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + if (fsync(opened_fd) < 0) + return -errno; + + return 0; + } + + opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (opened_fd < 0) + return -errno; + + return fsync_directory_of_file(opened_fd); +} + +int fsync_path_and_parent_at(int at_fd, const char *path) { + _cleanup_close_ int opened_fd = -1; + + if (isempty(path)) { + if (at_fd != AT_FDCWD) + return fsync_full(at_fd); + + opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + } else + opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC); + if (opened_fd < 0) + return -errno; + + return fsync_full(opened_fd); +} + int syncfs_path(int atfd, const char *path) { _cleanup_close_ int fd = -1; - assert(path); + if (isempty(path)) { + if (atfd != AT_FDCWD) + return syncfs(atfd) < 0 ? -errno : 0; - fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); + fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); + } else + fd = openat(atfd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (fd < 0) return -errno; @@ -1504,91 +1566,6 @@ int open_parent(const char *path, int flags, mode_t mode) { return fd; } -static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) { - _cleanup_free_ char *p = NULL, *uuids = NULL; - _cleanup_closedir_ DIR *d = NULL; - int r, found_encrypted = false; - - assert(sysfs_path); - - if (depth_left == 0) - return -EINVAL; - - p = path_join(sysfs_path, "dm/uuid"); - if (!p) - return -ENOMEM; - - r = read_one_line_file(p, &uuids); - if (r != -ENOENT) { - if (r < 0) - return r; - - /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */ - if (startswith(uuids, "CRYPT-")) - return true; - } - - /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/" - * subdir. */ - - p = mfree(p); - p = path_join(sysfs_path, "slaves"); - if (!p) - return -ENOMEM; - - d = opendir(p); - if (!d) { - if (errno == ENOENT) /* Doesn't have underlying devices */ - return false; - - return -errno; - } - - for (;;) { - _cleanup_free_ char *q = NULL; - struct dirent *de; - - errno = 0; - de = readdir_no_dot(d); - if (!de) { - if (errno != 0) - return -errno; - - break; /* No more underlying devices */ - } - - q = path_join(p, de->d_name); - if (!q) - return -ENOMEM; - - r = blockdev_is_encrypted(q, depth_left - 1); - if (r < 0) - return r; - if (r == 0) /* we found one that is not encrypted? then propagate that immediately */ - return false; - - found_encrypted = true; - } - - return found_encrypted; -} - -int path_is_encrypted(const char *path) { - char p[SYS_BLOCK_PATH_MAX(NULL)]; - dev_t devt; - int r; - - r = get_block_device(path, &devt); - if (r < 0) - return r; - if (r == 0) /* doesn't have a block device */ - return false; - - xsprintf_sys_block_path(p, NULL, devt); - - return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */); -} - int conservative_renameat( int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { |