]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/stat-util.c
Merge pull request #8575 from keszybz/non-absolute-paths
[thirdparty/systemd.git] / src / basic / stat-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010-2012 Lennart Poettering
6 ***/
7
8 #include <dirent.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <linux/magic.h>
12 #include <sched.h>
13 #include <sys/stat.h>
14 #include <sys/statvfs.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "dirent-util.h"
19 #include "fd-util.h"
20 #include "fs-util.h"
21 #include "macro.h"
22 #include "missing.h"
23 #include "stat-util.h"
24 #include "string-util.h"
25
26 int is_symlink(const char *path) {
27 struct stat info;
28
29 assert(path);
30
31 if (lstat(path, &info) < 0)
32 return -errno;
33
34 return !!S_ISLNK(info.st_mode);
35 }
36
37 int is_dir(const char* path, bool follow) {
38 struct stat st;
39 int r;
40
41 assert(path);
42
43 if (follow)
44 r = stat(path, &st);
45 else
46 r = lstat(path, &st);
47 if (r < 0)
48 return -errno;
49
50 return !!S_ISDIR(st.st_mode);
51 }
52
53 int is_device_node(const char *path) {
54 struct stat info;
55
56 assert(path);
57
58 if (lstat(path, &info) < 0)
59 return -errno;
60
61 return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
62 }
63
64 int dir_is_empty(const char *path) {
65 _cleanup_closedir_ DIR *d;
66 struct dirent *de;
67
68 d = opendir(path);
69 if (!d)
70 return -errno;
71
72 FOREACH_DIRENT(de, d, return -errno)
73 return 0;
74
75 return 1;
76 }
77
78 bool null_or_empty(struct stat *st) {
79 assert(st);
80
81 if (S_ISREG(st->st_mode) && st->st_size <= 0)
82 return true;
83
84 /* We don't want to hardcode the major/minor of /dev/null,
85 * hence we do a simpler "is this a device node?" check. */
86
87 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
88 return true;
89
90 return false;
91 }
92
93 int null_or_empty_path(const char *fn) {
94 struct stat st;
95
96 assert(fn);
97
98 if (stat(fn, &st) < 0)
99 return -errno;
100
101 return null_or_empty(&st);
102 }
103
104 int null_or_empty_fd(int fd) {
105 struct stat st;
106
107 assert(fd >= 0);
108
109 if (fstat(fd, &st) < 0)
110 return -errno;
111
112 return null_or_empty(&st);
113 }
114
115 int path_is_read_only_fs(const char *path) {
116 struct statvfs st;
117
118 assert(path);
119
120 if (statvfs(path, &st) < 0)
121 return -errno;
122
123 if (st.f_flag & ST_RDONLY)
124 return true;
125
126 /* On NFS, statvfs() might not reflect whether we can actually
127 * write to the remote share. Let's try again with
128 * access(W_OK) which is more reliable, at least sometimes. */
129 if (access(path, W_OK) < 0 && errno == EROFS)
130 return true;
131
132 return false;
133 }
134
135 int path_is_os_tree(const char *path) {
136 int r;
137
138 assert(path);
139
140 /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
141 * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
142 * the case where just the os-release file is missing. */
143 if (laccess(path, F_OK) < 0)
144 return -errno;
145
146 /* We use /usr/lib/os-release as flag file if something is an OS */
147 r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
148 if (r == -ENOENT) {
149
150 /* Also check for the old location in /etc, just in case. */
151 r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
152 if (r == -ENOENT)
153 return 0; /* We got nothing */
154 }
155 if (r < 0)
156 return r;
157
158 return 1;
159 }
160
161 int files_same(const char *filea, const char *fileb, int flags) {
162 struct stat a, b;
163
164 assert(filea);
165 assert(fileb);
166
167 if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
168 return -errno;
169
170 if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
171 return -errno;
172
173 return a.st_dev == b.st_dev &&
174 a.st_ino == b.st_ino;
175 }
176
177 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
178 assert(s);
179 assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
180
181 return F_TYPE_EQUAL(s->f_type, magic_value);
182 }
183
184 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
185 struct statfs s;
186
187 if (fstatfs(fd, &s) < 0)
188 return -errno;
189
190 return is_fs_type(&s, magic_value);
191 }
192
193 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
194 _cleanup_close_ int fd = -1;
195
196 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
197 if (fd < 0)
198 return -errno;
199
200 return fd_is_fs_type(fd, magic_value);
201 }
202
203 bool is_temporary_fs(const struct statfs *s) {
204 return is_fs_type(s, TMPFS_MAGIC) ||
205 is_fs_type(s, RAMFS_MAGIC);
206 }
207
208 bool is_network_fs(const struct statfs *s) {
209 return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
210 is_fs_type(s, CODA_SUPER_MAGIC) ||
211 is_fs_type(s, NCP_SUPER_MAGIC) ||
212 is_fs_type(s, NFS_SUPER_MAGIC) ||
213 is_fs_type(s, SMB_SUPER_MAGIC) ||
214 is_fs_type(s, V9FS_MAGIC) ||
215 is_fs_type(s, AFS_SUPER_MAGIC) ||
216 is_fs_type(s, OCFS2_SUPER_MAGIC);
217 }
218
219 int fd_is_temporary_fs(int fd) {
220 struct statfs s;
221
222 if (fstatfs(fd, &s) < 0)
223 return -errno;
224
225 return is_temporary_fs(&s);
226 }
227
228 int fd_is_network_fs(int fd) {
229 struct statfs s;
230
231 if (fstatfs(fd, &s) < 0)
232 return -errno;
233
234 return is_network_fs(&s);
235 }
236
237 int fd_is_network_ns(int fd) {
238 int r;
239
240 r = fd_is_fs_type(fd, NSFS_MAGIC);
241 if (r <= 0)
242 return r;
243
244 r = ioctl(fd, NS_GET_NSTYPE);
245 if (r < 0)
246 return -errno;
247
248 return r == CLONE_NEWNET;
249 }
250
251 int path_is_temporary_fs(const char *path) {
252 _cleanup_close_ int fd = -1;
253
254 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
255 if (fd < 0)
256 return -errno;
257
258 return fd_is_temporary_fs(fd);
259 }
260
261 int stat_verify_regular(const struct stat *st) {
262 assert(st);
263
264 /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
265 * code. */
266
267 if (S_ISDIR(st->st_mode))
268 return -EISDIR;
269
270 if (S_ISLNK(st->st_mode))
271 return -ELOOP;
272
273 if (!S_ISREG(st->st_mode))
274 return -EBADFD;
275
276 return 0;
277 }
278
279 int fd_verify_regular(int fd) {
280 struct stat st;
281
282 assert(fd >= 0);
283
284 if (fstat(fd, &st) < 0)
285 return -errno;
286
287 return stat_verify_regular(&st);
288 }