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 28 : int is_symlink(const char *path) {
24 : struct stat info;
25 :
26 28 : assert(path);
27 :
28 28 : if (lstat(path, &info) < 0)
29 23 : return -errno;
30 :
31 5 : return !!S_ISLNK(info.st_mode);
32 : }
33 :
34 1657 : int is_dir(const char* path, bool follow) {
35 : struct stat st;
36 : int r;
37 :
38 1657 : assert(path);
39 :
40 1657 : if (follow)
41 463 : r = stat(path, &st);
42 : else
43 1194 : r = lstat(path, &st);
44 1657 : if (r < 0)
45 56 : return -errno;
46 :
47 1601 : 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 1 : int is_device_node(const char *path) {
60 : struct stat info;
61 :
62 1 : assert(path);
63 :
64 1 : if (lstat(path, &info) < 0)
65 1 : return -errno;
66 :
67 0 : return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
68 : }
69 :
70 20 : int dir_is_empty_at(int dir_fd, const char *path) {
71 20 : _cleanup_close_ int fd = -1;
72 20 : _cleanup_closedir_ DIR *d = NULL;
73 : struct dirent *de;
74 :
75 20 : if (path)
76 20 : fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
77 : else
78 0 : fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
79 20 : if (fd < 0)
80 2 : return -errno;
81 :
82 18 : d = fdopendir(fd);
83 18 : if (!d)
84 0 : return -errno;
85 18 : fd = -1;
86 :
87 26 : FOREACH_DIRENT(de, d, return -errno)
88 16 : return 0;
89 :
90 2 : return 1;
91 : }
92 :
93 1827 : bool null_or_empty(struct stat *st) {
94 1827 : assert(st);
95 :
96 1827 : if (S_ISREG(st->st_mode) && st->st_size <= 0)
97 6 : 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 1821 : if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
103 9 : return true;
104 :
105 1812 : return false;
106 : }
107 :
108 847 : int null_or_empty_path(const char *fn) {
109 : struct stat st;
110 :
111 847 : assert(fn);
112 :
113 847 : if (stat(fn, &st) < 0)
114 0 : return -errno;
115 :
116 847 : return null_or_empty(&st);
117 : }
118 :
119 3 : int null_or_empty_fd(int fd) {
120 : struct stat st;
121 :
122 3 : assert(fd >= 0);
123 :
124 3 : if (fstat(fd, &st) < 0)
125 0 : return -errno;
126 :
127 3 : return null_or_empty(&st);
128 : }
129 :
130 8 : int path_is_read_only_fs(const char *path) {
131 : struct statvfs st;
132 :
133 8 : assert(path);
134 :
135 8 : if (statvfs(path, &st) < 0)
136 0 : return -errno;
137 :
138 8 : 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 8 : if (access(path, W_OK) < 0 && errno == EROFS)
145 0 : return true;
146 :
147 8 : return false;
148 : }
149 :
150 181 : int files_same(const char *filea, const char *fileb, int flags) {
151 : struct stat a, b;
152 :
153 181 : assert(filea);
154 181 : assert(fileb);
155 :
156 181 : if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
157 90 : return -errno;
158 :
159 91 : if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
160 4 : return -errno;
161 :
162 118 : return a.st_dev == b.st_dev &&
163 31 : a.st_ino == b.st_ino;
164 : }
165 :
166 81313 : bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
167 81313 : assert(s);
168 : assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
169 :
170 81313 : return F_TYPE_EQUAL(s->f_type, magic_value);
171 : }
172 :
173 58 : int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
174 : struct statfs s;
175 :
176 58 : if (fstatfs(fd, &s) < 0)
177 0 : return -errno;
178 :
179 58 : return is_fs_type(&s, magic_value);
180 : }
181 :
182 5 : int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
183 5 : _cleanup_close_ int fd = -1;
184 :
185 5 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
186 5 : if (fd < 0)
187 1 : return -errno;
188 :
189 4 : return fd_is_fs_type(fd, magic_value);
190 : }
191 :
192 4 : bool is_temporary_fs(const struct statfs *s) {
193 5 : return is_fs_type(s, TMPFS_MAGIC) ||
194 1 : is_fs_type(s, RAMFS_MAGIC);
195 : }
196 :
197 10155 : bool is_network_fs(const struct statfs *s) {
198 20310 : return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
199 10155 : is_fs_type(s, CODA_SUPER_MAGIC) ||
200 10155 : is_fs_type(s, NCP_SUPER_MAGIC) ||
201 10155 : is_fs_type(s, NFS_SUPER_MAGIC) ||
202 10155 : is_fs_type(s, SMB_SUPER_MAGIC) ||
203 10155 : is_fs_type(s, V9FS_MAGIC) ||
204 30465 : is_fs_type(s, AFS_SUPER_MAGIC) ||
205 10155 : is_fs_type(s, OCFS2_SUPER_MAGIC);
206 : }
207 :
208 3 : int fd_is_temporary_fs(int fd) {
209 : struct statfs s;
210 :
211 3 : if (fstatfs(fd, &s) < 0)
212 0 : return -errno;
213 :
214 3 : return is_temporary_fs(&s);
215 : }
216 :
217 10155 : int fd_is_network_fs(int fd) {
218 : struct statfs s;
219 :
220 10155 : if (fstatfs(fd, &s) < 0)
221 0 : return -errno;
222 :
223 10155 : return is_network_fs(&s);
224 : }
225 :
226 3 : int path_is_temporary_fs(const char *path) {
227 3 : _cleanup_close_ int fd = -1;
228 :
229 3 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
230 3 : if (fd < 0)
231 1 : return -errno;
232 :
233 2 : return fd_is_temporary_fs(fd);
234 : }
235 :
236 21169 : int stat_verify_regular(const struct stat *st) {
237 21169 : 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 21169 : if (S_ISDIR(st->st_mode))
243 0 : return -EISDIR;
244 :
245 21169 : if (S_ISLNK(st->st_mode))
246 0 : return -ELOOP;
247 :
248 21169 : if (!S_ISREG(st->st_mode))
249 0 : return -EBADFD;
250 :
251 21169 : return 0;
252 : }
253 :
254 720 : int fd_verify_regular(int fd) {
255 : struct stat st;
256 :
257 720 : assert(fd >= 0);
258 :
259 720 : if (fstat(fd, &st) < 0)
260 0 : return -errno;
261 :
262 720 : 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 12 : 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 12 : if (S_ISCHR(mode))
294 12 : t = "char";
295 0 : else if (S_ISBLK(mode))
296 0 : t = "block";
297 : else
298 0 : return -ENODEV;
299 :
300 12 : if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
301 0 : return -ENOMEM;
302 :
303 12 : return 0;
304 : }
305 :
306 6 : int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
307 6 : _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 6 : assert(ret);
313 :
314 6 : 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 6 : r = device_path_make_major_minor(mode, devno, &p);
335 6 : if (r < 0)
336 0 : return r;
337 :
338 6 : return chase_symlinks(p, NULL, 0, ret);
339 : }
340 :
341 6 : 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 6 : if (path_equal(path, "/run/systemd/inaccessible/chr")) {
351 0 : mode = S_IFCHR;
352 0 : devno = makedev(0, 0);
353 6 : } 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 6 : w = path_startswith(path, "/dev/block/");
360 6 : if (w)
361 0 : mode = S_IFBLK;
362 : else {
363 6 : w = path_startswith(path, "/dev/char/");
364 6 : if (!w)
365 0 : return -ENODEV;
366 :
367 6 : mode = S_IFCHR;
368 : }
369 :
370 6 : r = parse_dev(w, &devno);
371 6 : if (r < 0)
372 0 : return r;
373 : }
374 :
375 6 : if (ret_mode)
376 6 : *ret_mode = mode;
377 6 : if (ret_devno)
378 6 : *ret_devno = devno;
379 :
380 6 : return 0;
381 : }
|