| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| |
| #include "fd-util.h" |
| #include "fs-util.h" |
| #include "path-util.h" |
| #include "sync-util.h" |
| |
| int fsync_directory_of_file(int fd) { |
| _cleanup_close_ int dfd = -EBADF; |
| struct stat st; |
| int r; |
| |
| assert(fd >= 0); |
| |
| /* 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_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) { |
| log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m", |
| fd, |
| r == -ENOSYS ? ", ignoring" : ""); |
| |
| if (r == -ENOSYS) |
| /* If /proc is not available, we're most likely running in some |
| * chroot environment, and syncing the directory is not very |
| * important in that case. Let's just silently do nothing. */ |
| return 0; |
| |
| return r; |
| } |
| |
| if (!path_is_absolute(path)) |
| return -EINVAL; |
| |
| dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0); |
| if (dfd < 0) |
| return dfd; |
| } |
| |
| return RET_NERRNO(fsync(dfd)); |
| } |
| |
| int fsync_full(int fd) { |
| int r, q; |
| |
| /* Sync both the file and the directory */ |
| |
| r = RET_NERRNO(fsync(fd)); |
| |
| q = fsync_directory_of_file(fd); |
| if (r < 0) /* Return earlier error */ |
| return r; |
| if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a |
| * parent dir */ |
| return 0; |
| return q; |
| } |
| |
| int fsync_path_at(int at_fd, const char *path) { |
| _cleanup_close_ int opened_fd = -EBADF; |
| int fd; |
| |
| if (isempty(path)) { |
| if (at_fd == AT_FDCWD) { |
| opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); |
| if (opened_fd < 0) |
| return -errno; |
| |
| fd = opened_fd; |
| } else |
| fd = at_fd; |
| } else { |
| opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); |
| if (opened_fd < 0) |
| return -errno; |
| |
| fd = opened_fd; |
| } |
| |
| return RET_NERRNO(fsync(fd)); |
| } |
| |
| int fsync_parent_at(int at_fd, const char *path) { |
| _cleanup_close_ int opened_fd = -EBADF; |
| |
| 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; |
| |
| return RET_NERRNO(fsync(opened_fd)); |
| } |
| |
| 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 = -EBADF; |
| |
| 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 at_fd, const char *path) { |
| _cleanup_close_ int fd = -EBADF; |
| |
| if (isempty(path)) { |
| if (at_fd != AT_FDCWD) |
| return RET_NERRNO(syncfs(at_fd)); |
| |
| fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC); |
| } else |
| fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); |
| if (fd < 0) |
| return -errno; |
| |
| return RET_NERRNO(syncfs(fd)); |
| } |