1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <fcntl.h>
4 #include <sys/stat.h>
5
6 #include "fd-util.h"
7 #include "fs-util.h"
8 #include "path-util.h"
9 #include "sync-util.h"
10
fsync_directory_of_file(int fd)11 int fsync_directory_of_file(int fd) {
12 _cleanup_close_ int dfd = -1;
13 struct stat st;
14 int r;
15
16 assert(fd >= 0);
17
18 /* We only reasonably can do this for regular files and directories, or for O_PATH fds, hence check
19 * for the inode type first */
20 if (fstat(fd, &st) < 0)
21 return -errno;
22
23 if (S_ISDIR(st.st_mode)) {
24 dfd = openat(fd, "..", O_RDONLY|O_DIRECTORY|O_CLOEXEC, 0);
25 if (dfd < 0)
26 return -errno;
27
28 } else if (!S_ISREG(st.st_mode)) { /* Regular files are OK regardless if O_PATH or not, for all other
29 * types check O_PATH flag */
30 int flags;
31
32 flags = fcntl(fd, F_GETFL);
33 if (flags < 0)
34 return -errno;
35
36 if (!FLAGS_SET(flags, O_PATH)) /* If O_PATH this refers to the inode in the fs, in which case
37 * we can sensibly do what is requested. Otherwise this refers
38 * to a socket, fifo or device node, where the concept of a
39 * containing directory doesn't make too much sense. */
40 return -ENOTTY;
41 }
42
43 if (dfd < 0) {
44 _cleanup_free_ char *path = NULL;
45
46 r = fd_get_path(fd, &path);
47 if (r < 0) {
48 log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
49 fd,
50 r == -ENOSYS ? ", ignoring" : "");
51
52 if (r == -ENOSYS)
53 /* If /proc is not available, we're most likely running in some
54 * chroot environment, and syncing the directory is not very
55 * important in that case. Let's just silently do nothing. */
56 return 0;
57
58 return r;
59 }
60
61 if (!path_is_absolute(path))
62 return -EINVAL;
63
64 dfd = open_parent(path, O_CLOEXEC|O_NOFOLLOW, 0);
65 if (dfd < 0)
66 return dfd;
67 }
68
69 return RET_NERRNO(fsync(dfd));
70 }
71
fsync_full(int fd)72 int fsync_full(int fd) {
73 int r, q;
74
75 /* Sync both the file and the directory */
76
77 r = RET_NERRNO(fsync(fd));
78
79 q = fsync_directory_of_file(fd);
80 if (r < 0) /* Return earlier error */
81 return r;
82 if (q == -ENOTTY) /* Ignore if the 'fd' refers to a block device or so which doesn't really have a
83 * parent dir */
84 return 0;
85 return q;
86 }
87
fsync_path_at(int at_fd,const char * path)88 int fsync_path_at(int at_fd, const char *path) {
89 _cleanup_close_ int opened_fd = -1;
90 int fd;
91
92 if (isempty(path)) {
93 if (at_fd == AT_FDCWD) {
94 opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
95 if (opened_fd < 0)
96 return -errno;
97
98 fd = opened_fd;
99 } else
100 fd = at_fd;
101 } else {
102 opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
103 if (opened_fd < 0)
104 return -errno;
105
106 fd = opened_fd;
107 }
108
109 return RET_NERRNO(fsync(fd));
110 }
111
fsync_parent_at(int at_fd,const char * path)112 int fsync_parent_at(int at_fd, const char *path) {
113 _cleanup_close_ int opened_fd = -1;
114
115 if (isempty(path)) {
116 if (at_fd != AT_FDCWD)
117 return fsync_directory_of_file(at_fd);
118
119 opened_fd = open("..", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
120 if (opened_fd < 0)
121 return -errno;
122
123 return RET_NERRNO(fsync(opened_fd));
124 }
125
126 opened_fd = openat(at_fd, path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
127 if (opened_fd < 0)
128 return -errno;
129
130 return fsync_directory_of_file(opened_fd);
131 }
132
fsync_path_and_parent_at(int at_fd,const char * path)133 int fsync_path_and_parent_at(int at_fd, const char *path) {
134 _cleanup_close_ int opened_fd = -1;
135
136 if (isempty(path)) {
137 if (at_fd != AT_FDCWD)
138 return fsync_full(at_fd);
139
140 opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
141 } else
142 opened_fd = openat(at_fd, path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC);
143 if (opened_fd < 0)
144 return -errno;
145
146 return fsync_full(opened_fd);
147 }
148
syncfs_path(int at_fd,const char * path)149 int syncfs_path(int at_fd, const char *path) {
150 _cleanup_close_ int fd = -1;
151
152 if (isempty(path)) {
153 if (at_fd != AT_FDCWD)
154 return RET_NERRNO(syncfs(at_fd));
155
156 fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
157 } else
158 fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
159 if (fd < 0)
160 return -errno;
161
162 return RET_NERRNO(syncfs(fd));
163 }
164