]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/stat-util.c
Merge pull request #12679 from yuwata/journal-issue-12400
[thirdparty/systemd.git] / src / basic / stat-util.c
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 int 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
34 int 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
50 int is_dir_fd(int fd) {
51 struct stat st;
52
53 if (fstat(fd, &st) < 0)
54 return -errno;
55
56 return !!S_ISDIR(st.st_mode);
57 }
58
59 int 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
70 int dir_is_empty_at(int dir_fd, const char *path) {
71 _cleanup_close_ int fd = -1;
72 _cleanup_closedir_ DIR *d = NULL;
73 struct dirent *de;
74
75 if (path)
76 fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
77 else
78 fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
79 if (fd < 0)
80 return -errno;
81
82 d = fdopendir(fd);
83 if (!d)
84 return -errno;
85 fd = -1;
86
87 FOREACH_DIRENT(de, d, return -errno)
88 return 0;
89
90 return 1;
91 }
92
93 bool null_or_empty(struct stat *st) {
94 assert(st);
95
96 if (S_ISREG(st->st_mode) && st->st_size <= 0)
97 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 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
103 return true;
104
105 return false;
106 }
107
108 int null_or_empty_path(const char *fn) {
109 struct stat st;
110
111 assert(fn);
112
113 if (stat(fn, &st) < 0)
114 return -errno;
115
116 return null_or_empty(&st);
117 }
118
119 int null_or_empty_fd(int fd) {
120 struct stat st;
121
122 assert(fd >= 0);
123
124 if (fstat(fd, &st) < 0)
125 return -errno;
126
127 return null_or_empty(&st);
128 }
129
130 int path_is_read_only_fs(const char *path) {
131 struct statvfs st;
132
133 assert(path);
134
135 if (statvfs(path, &st) < 0)
136 return -errno;
137
138 if (st.f_flag & ST_RDONLY)
139 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 if (access(path, W_OK) < 0 && errno == EROFS)
145 return true;
146
147 return false;
148 }
149
150 int files_same(const char *filea, const char *fileb, int flags) {
151 struct stat a, b;
152
153 assert(filea);
154 assert(fileb);
155
156 if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
157 return -errno;
158
159 if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
160 return -errno;
161
162 return a.st_dev == b.st_dev &&
163 a.st_ino == b.st_ino;
164 }
165
166 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
167 assert(s);
168 assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
169
170 return F_TYPE_EQUAL(s->f_type, magic_value);
171 }
172
173 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
174 struct statfs s;
175
176 if (fstatfs(fd, &s) < 0)
177 return -errno;
178
179 return is_fs_type(&s, magic_value);
180 }
181
182 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
183 _cleanup_close_ int fd = -1;
184
185 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
186 if (fd < 0)
187 return -errno;
188
189 return fd_is_fs_type(fd, magic_value);
190 }
191
192 bool is_temporary_fs(const struct statfs *s) {
193 return is_fs_type(s, TMPFS_MAGIC) ||
194 is_fs_type(s, RAMFS_MAGIC);
195 }
196
197 bool is_network_fs(const struct statfs *s) {
198 return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
199 is_fs_type(s, CODA_SUPER_MAGIC) ||
200 is_fs_type(s, NCP_SUPER_MAGIC) ||
201 is_fs_type(s, NFS_SUPER_MAGIC) ||
202 is_fs_type(s, SMB_SUPER_MAGIC) ||
203 is_fs_type(s, V9FS_MAGIC) ||
204 is_fs_type(s, AFS_SUPER_MAGIC) ||
205 is_fs_type(s, OCFS2_SUPER_MAGIC);
206 }
207
208 int fd_is_temporary_fs(int fd) {
209 struct statfs s;
210
211 if (fstatfs(fd, &s) < 0)
212 return -errno;
213
214 return is_temporary_fs(&s);
215 }
216
217 int fd_is_network_fs(int fd) {
218 struct statfs s;
219
220 if (fstatfs(fd, &s) < 0)
221 return -errno;
222
223 return is_network_fs(&s);
224 }
225
226 int path_is_temporary_fs(const char *path) {
227 _cleanup_close_ int fd = -1;
228
229 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
230 if (fd < 0)
231 return -errno;
232
233 return fd_is_temporary_fs(fd);
234 }
235
236 int stat_verify_regular(const struct stat *st) {
237 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 if (S_ISDIR(st->st_mode))
243 return -EISDIR;
244
245 if (S_ISLNK(st->st_mode))
246 return -ELOOP;
247
248 if (!S_ISREG(st->st_mode))
249 return -EBADFD;
250
251 return 0;
252 }
253
254 int fd_verify_regular(int fd) {
255 struct stat st;
256
257 assert(fd >= 0);
258
259 if (fstat(fd, &st) < 0)
260 return -errno;
261
262 return stat_verify_regular(&st);
263 }
264
265 int stat_verify_directory(const struct stat *st) {
266 assert(st);
267
268 if (S_ISLNK(st->st_mode))
269 return -ELOOP;
270
271 if (!S_ISDIR(st->st_mode))
272 return -ENOTDIR;
273
274 return 0;
275 }
276
277 int fd_verify_directory(int fd) {
278 struct stat st;
279
280 assert(fd >= 0);
281
282 if (fstat(fd, &st) < 0)
283 return -errno;
284
285 return stat_verify_directory(&st);
286 }
287
288 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 if (S_ISCHR(mode))
294 t = "char";
295 else if (S_ISBLK(mode))
296 t = "block";
297 else
298 return -ENODEV;
299
300 if (asprintf(ret, "/dev/%s/%u:%u", t, major(devno), minor(devno)) < 0)
301 return -ENOMEM;
302
303 return 0;
304 }
305
306 int device_path_make_canonical(mode_t mode, dev_t devno, char **ret) {
307 _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 assert(ret);
313
314 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 if (S_ISCHR(mode))
321 s = strdup("/run/systemd/inaccessible/chr");
322 else if (S_ISBLK(mode))
323 s = strdup("/run/systemd/inaccessible/blk");
324 else
325 return -ENODEV;
326
327 if (!s)
328 return -ENOMEM;
329
330 *ret = s;
331 return 0;
332 }
333
334 r = device_path_make_major_minor(mode, devno, &p);
335 if (r < 0)
336 return r;
337
338 return chase_symlinks(p, NULL, 0, ret);
339 }
340
341 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 if (path_equal(path, "/run/systemd/inaccessible/chr")) {
351 mode = S_IFCHR;
352 devno = makedev(0, 0);
353 } else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
354 mode = S_IFBLK;
355 devno = makedev(0, 0);
356 } else {
357 const char *w;
358
359 w = path_startswith(path, "/dev/block/");
360 if (w)
361 mode = S_IFBLK;
362 else {
363 w = path_startswith(path, "/dev/char/");
364 if (!w)
365 return -ENODEV;
366
367 mode = S_IFCHR;
368 }
369
370 r = parse_dev(w, &devno);
371 if (r < 0)
372 return r;
373 }
374
375 if (ret_mode)
376 *ret_mode = mode;
377 if (ret_devno)
378 *ret_devno = devno;
379
380 return 0;
381 }