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