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