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