Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <dirent.h>
4 : : #include <errno.h>
5 : : #include <fcntl.h>
6 : : #include <linux/magic.h>
7 : : #include <sched.h>
8 : : #include <sys/stat.h>
9 : : #include <sys/statvfs.h>
10 : : #include <sys/types.h>
11 : : #include <unistd.h>
12 : :
13 : : #include "alloc-util.h"
14 : : #include "dirent-util.h"
15 : : #include "fd-util.h"
16 : : #include "fs-util.h"
17 : : #include "macro.h"
18 : : #include "missing.h"
19 : : #include "parse-util.h"
20 : : #include "stat-util.h"
21 : : #include "string-util.h"
22 : :
23 : 112 : int is_symlink(const char *path) {
24 : : struct stat info;
25 : :
26 [ - + ]: 112 : assert(path);
27 : :
28 [ + + ]: 112 : if (lstat(path, &info) < 0)
29 : 92 : return -errno;
30 : :
31 : 20 : return !!S_ISLNK(info.st_mode);
32 : : }
33 : :
34 : 6628 : int is_dir(const char* path, bool follow) {
35 : : struct stat st;
36 : : int r;
37 : :
38 [ - + ]: 6628 : assert(path);
39 : :
40 [ + + ]: 6628 : if (follow)
41 : 1852 : r = stat(path, &st);
42 : : else
43 : 4776 : r = lstat(path, &st);
44 [ + + ]: 6628 : if (r < 0)
45 : 224 : return -errno;
46 : :
47 : 6404 : return !!S_ISDIR(st.st_mode);
48 : : }
49 : :
50 : 0 : int is_dir_fd(int fd) {
51 : : struct stat st;
52 : :
53 [ # # ]: 0 : if (fstat(fd, &st) < 0)
54 : 0 : return -errno;
55 : :
56 : 0 : return !!S_ISDIR(st.st_mode);
57 : : }
58 : :
59 : 4 : int is_device_node(const char *path) {
60 : : struct stat info;
61 : :
62 [ - + ]: 4 : assert(path);
63 : :
64 [ + - ]: 4 : if (lstat(path, &info) < 0)
65 : 4 : return -errno;
66 : :
67 [ # # # # ]: 0 : return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
68 : : }
69 : :
70 : 80 : int dir_is_empty_at(int dir_fd, const char *path) {
71 : 80 : _cleanup_close_ int fd = -1;
72 : 80 : _cleanup_closedir_ DIR *d = NULL;
73 : : struct dirent *de;
74 : :
75 [ + - ]: 80 : if (path)
76 : 80 : fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
77 : : else
78 : 0 : fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
79 [ + + ]: 80 : if (fd < 0)
80 : 8 : return -errno;
81 : :
82 : 72 : d = fdopendir(fd);
83 [ - + ]: 72 : if (!d)
84 : 0 : return -errno;
85 : 72 : fd = -1;
86 : :
87 [ + + - + : 104 : FOREACH_DIRENT(de, d, return -errno)
+ + ]
88 : 64 : return 0;
89 : :
90 : 8 : return 1;
91 : : }
92 : :
93 : 7308 : bool null_or_empty(struct stat *st) {
94 [ - + ]: 7308 : assert(st);
95 : :
96 [ + + + + ]: 7308 : if (S_ISREG(st->st_mode) && st->st_size <= 0)
97 : 24 : return true;
98 : :
99 : : /* We don't want to hardcode the major/minor of /dev/null,
100 : : * hence we do a simpler "is this a device node?" check. */
101 : :
102 [ + + - + ]: 7284 : if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
103 : 36 : return true;
104 : :
105 : 7248 : return false;
106 : : }
107 : :
108 : 3388 : int null_or_empty_path(const char *fn) {
109 : : struct stat st;
110 : :
111 [ - + ]: 3388 : assert(fn);
112 : :
113 [ - + ]: 3388 : if (stat(fn, &st) < 0)
114 : 0 : return -errno;
115 : :
116 : 3388 : return null_or_empty(&st);
117 : : }
118 : :
119 : 12 : int null_or_empty_fd(int fd) {
120 : : struct stat st;
121 : :
122 [ - + ]: 12 : assert(fd >= 0);
123 : :
124 [ - + ]: 12 : if (fstat(fd, &st) < 0)
125 : 0 : return -errno;
126 : :
127 : 12 : return null_or_empty(&st);
128 : : }
129 : :
130 : 32 : int path_is_read_only_fs(const char *path) {
131 : : struct statvfs st;
132 : :
133 [ - + ]: 32 : assert(path);
134 : :
135 [ - + ]: 32 : if (statvfs(path, &st) < 0)
136 : 0 : return -errno;
137 : :
138 [ - + ]: 32 : if (st.f_flag & ST_RDONLY)
139 : 0 : return true;
140 : :
141 : : /* On NFS, statvfs() might not reflect whether we can actually
142 : : * write to the remote share. Let's try again with
143 : : * access(W_OK) which is more reliable, at least sometimes. */
144 [ + + - + ]: 32 : if (access(path, W_OK) < 0 && errno == EROFS)
145 : 0 : return true;
146 : :
147 : 32 : return false;
148 : : }
149 : :
150 : 724 : int files_same(const char *filea, const char *fileb, int flags) {
151 : : struct stat a, b;
152 : :
153 [ - + ]: 724 : assert(filea);
154 [ - + ]: 724 : assert(fileb);
155 : :
156 [ + + ]: 724 : if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
157 : 360 : return -errno;
158 : :
159 [ + + ]: 364 : if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
160 : 16 : return -errno;
161 : :
162 [ + + ]: 472 : return a.st_dev == b.st_dev &&
163 [ + + ]: 124 : a.st_ino == b.st_ino;
164 : : }
165 : :
166 : 325252 : bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
167 [ - + ]: 325252 : assert(s);
168 : : assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
169 : :
170 : 325252 : return F_TYPE_EQUAL(s->f_type, magic_value);
171 : : }
172 : :
173 : 232 : int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
174 : : struct statfs s;
175 : :
176 [ - + ]: 232 : if (fstatfs(fd, &s) < 0)
177 : 0 : return -errno;
178 : :
179 : 232 : return is_fs_type(&s, magic_value);
180 : : }
181 : :
182 : 20 : int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
183 : 20 : _cleanup_close_ int fd = -1;
184 : :
185 : 20 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
186 [ + + ]: 20 : if (fd < 0)
187 : 4 : return -errno;
188 : :
189 : 16 : return fd_is_fs_type(fd, magic_value);
190 : : }
191 : :
192 : 16 : bool is_temporary_fs(const struct statfs *s) {
193 [ + + ]: 20 : return is_fs_type(s, TMPFS_MAGIC) ||
194 [ - + ]: 4 : is_fs_type(s, RAMFS_MAGIC);
195 : : }
196 : :
197 : 40620 : bool is_network_fs(const struct statfs *s) {
198 : 81240 : return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
199 [ + - ]: 40620 : is_fs_type(s, CODA_SUPER_MAGIC) ||
200 [ + - ]: 40620 : is_fs_type(s, NCP_SUPER_MAGIC) ||
201 [ + - ]: 40620 : is_fs_type(s, NFS_SUPER_MAGIC) ||
202 [ + - ]: 40620 : is_fs_type(s, SMB_SUPER_MAGIC) ||
203 [ + - ]: 40620 : is_fs_type(s, V9FS_MAGIC) ||
204 [ + - + - ]: 121860 : is_fs_type(s, AFS_SUPER_MAGIC) ||
205 [ - + ]: 40620 : is_fs_type(s, OCFS2_SUPER_MAGIC);
206 : : }
207 : :
208 : 12 : int fd_is_temporary_fs(int fd) {
209 : : struct statfs s;
210 : :
211 [ - + ]: 12 : if (fstatfs(fd, &s) < 0)
212 : 0 : return -errno;
213 : :
214 : 12 : return is_temporary_fs(&s);
215 : : }
216 : :
217 : 40620 : int fd_is_network_fs(int fd) {
218 : : struct statfs s;
219 : :
220 [ - + ]: 40620 : if (fstatfs(fd, &s) < 0)
221 : 0 : return -errno;
222 : :
223 : 40620 : return is_network_fs(&s);
224 : : }
225 : :
226 : 12 : int path_is_temporary_fs(const char *path) {
227 : 12 : _cleanup_close_ int fd = -1;
228 : :
229 : 12 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
230 [ + + ]: 12 : if (fd < 0)
231 : 4 : return -errno;
232 : :
233 : 8 : return fd_is_temporary_fs(fd);
234 : : }
235 : :
236 : 84810 : int stat_verify_regular(const struct stat *st) {
237 [ - + ]: 84810 : assert(st);
238 : :
239 : : /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
240 : : * code. */
241 : :
242 [ - + ]: 84810 : if (S_ISDIR(st->st_mode))
243 : 0 : return -EISDIR;
244 : :
245 [ - + ]: 84810 : if (S_ISLNK(st->st_mode))
246 : 0 : return -ELOOP;
247 : :
248 [ - + ]: 84810 : if (!S_ISREG(st->st_mode))
249 : 0 : return -EBADFD;
250 : :
251 : 84810 : return 0;
252 : : }
253 : :
254 : 3014 : int fd_verify_regular(int fd) {
255 : : struct stat st;
256 : :
257 [ - + ]: 3014 : assert(fd >= 0);
258 : :
259 [ - + ]: 3014 : if (fstat(fd, &st) < 0)
260 : 0 : return -errno;
261 : :
262 : 3014 : return stat_verify_regular(&st);
263 : : }
264 : :
265 : 0 : int stat_verify_directory(const struct stat *st) {
266 [ # # ]: 0 : assert(st);
267 : :
268 [ # # ]: 0 : if (S_ISLNK(st->st_mode))
269 : 0 : return -ELOOP;
270 : :
271 [ # # ]: 0 : if (!S_ISDIR(st->st_mode))
272 : 0 : return -ENOTDIR;
273 : :
274 : 0 : return 0;
275 : : }
276 : :
277 : 0 : int fd_verify_directory(int fd) {
278 : : struct stat st;
279 : :
280 [ # # ]: 0 : assert(fd >= 0);
281 : :
282 [ # # ]: 0 : if (fstat(fd, &st) < 0)
283 : 0 : return -errno;
284 : :
285 : 0 : return stat_verify_directory(&st);
286 : : }
287 : :
288 : 48 : int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret) {
289 : : const char *t;
290 : :
291 : : /* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
292 : :
293 [ + - ]: 48 : if (S_ISCHR(mode))
294 : 48 : t = "char";
295 [ # # ]: 0 : else if (S_ISBLK(mode))
296 : 0 : t = "block";
297 : : else
298 : 0 : return -ENODEV;
299 : :
300 [ - + ]: 48 : if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
301 : 0 : return -ENOMEM;
302 : :
303 : 48 : return 0;
304 : : }
305 : :
306 : 24 : int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
307 : 24 : _cleanup_free_ char *p = NULL;
308 : : int r;
309 : :
310 : : /* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
311 : :
312 [ - + ]: 24 : assert(ret);
313 : :
314 [ - + # # ]: 24 : if (major(devno) == 0 && minor(devno) == 0) {
315 : : char *s;
316 : :
317 : : /* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
318 : : * /dev/block/ and /dev/char/, hence we handle them specially here. */
319 : :
320 [ # # ]: 0 : if (S_ISCHR(mode))
321 : 0 : s = strdup("/run/systemd/inaccessible/chr");
322 [ # # ]: 0 : else if (S_ISBLK(mode))
323 : 0 : s = strdup("/run/systemd/inaccessible/blk");
324 : : else
325 : 0 : return -ENODEV;
326 : :
327 [ # # ]: 0 : if (!s)
328 : 0 : return -ENOMEM;
329 : :
330 : 0 : *ret = s;
331 : 0 : return 0;
332 : : }
333 : :
334 : 24 : r = device_path_make_major_minor(mode, devno, &p);
335 [ - + ]: 24 : if (r < 0)
336 : 0 : return r;
337 : :
338 : 24 : return chase_symlinks(p, NULL, 0, ret);
339 : : }
340 : :
341 : 24 : int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno) {
342 : : mode_t mode;
343 : : dev_t devno;
344 : : int r;
345 : :
346 : : /* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
347 : : * paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
348 : : * path cannot be parsed like this. */
349 : :
350 [ - + ]: 24 : if (path_equal(path, "/run/systemd/inaccessible/chr")) {
351 : 0 : mode = S_IFCHR;
352 : 0 : devno = makedev(0, 0);
353 [ - + ]: 24 : } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
354 : 0 : mode = S_IFBLK;
355 : 0 : devno = makedev(0, 0);
356 : : } else {
357 : : const char *w;
358 : :
359 : 24 : w = path_startswith(path, "/dev/block/");
360 [ - + ]: 24 : if (w)
361 : 0 : mode = S_IFBLK;
362 : : else {
363 : 24 : w = path_startswith(path, "/dev/char/");
364 [ - + ]: 24 : if (!w)
365 : 0 : return -ENODEV;
366 : :
367 : 24 : mode = S_IFCHR;
368 : : }
369 : :
370 : 24 : r = parse_dev(w, &devno);
371 [ - + ]: 24 : if (r < 0)
372 : 0 : return r;
373 : : }
374 : :
375 [ + - ]: 24 : if (ret_mode)
376 : 24 : *ret_mode = mode;
377 [ + - ]: 24 : if (ret_devno)
378 : 24 : *ret_devno = devno;
379 : :
380 : 24 : return 0;
381 : : }
|