]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/stat-util.c
pkgconfig: define variables relative to ${prefix}/${rootprefix}/${sysconfdir}
[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 "dirent-util.h"
14 #include "fd-util.h"
15 #include "fs-util.h"
16 #include "macro.h"
17 #include "missing.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(const char *path) {
69 _cleanup_closedir_ DIR *d;
70 struct dirent *de;
71
72 d = opendir(path);
73 if (!d)
74 return -errno;
75
76 FOREACH_DIRENT(de, d, return -errno)
77 return 0;
78
79 return 1;
80 }
81
82 bool null_or_empty(struct stat *st) {
83 assert(st);
84
85 if (S_ISREG(st->st_mode) && st->st_size <= 0)
86 return true;
87
88 /* We don't want to hardcode the major/minor of /dev/null,
89 * hence we do a simpler "is this a device node?" check. */
90
91 if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
92 return true;
93
94 return false;
95 }
96
97 int null_or_empty_path(const char *fn) {
98 struct stat st;
99
100 assert(fn);
101
102 if (stat(fn, &st) < 0)
103 return -errno;
104
105 return null_or_empty(&st);
106 }
107
108 int null_or_empty_fd(int fd) {
109 struct stat st;
110
111 assert(fd >= 0);
112
113 if (fstat(fd, &st) < 0)
114 return -errno;
115
116 return null_or_empty(&st);
117 }
118
119 int path_is_read_only_fs(const char *path) {
120 struct statvfs st;
121
122 assert(path);
123
124 if (statvfs(path, &st) < 0)
125 return -errno;
126
127 if (st.f_flag & ST_RDONLY)
128 return true;
129
130 /* On NFS, statvfs() might not reflect whether we can actually
131 * write to the remote share. Let's try again with
132 * access(W_OK) which is more reliable, at least sometimes. */
133 if (access(path, W_OK) < 0 && errno == EROFS)
134 return true;
135
136 return false;
137 }
138
139 int files_same(const char *filea, const char *fileb, int flags) {
140 struct stat a, b;
141
142 assert(filea);
143 assert(fileb);
144
145 if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
146 return -errno;
147
148 if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
149 return -errno;
150
151 return a.st_dev == b.st_dev &&
152 a.st_ino == b.st_ino;
153 }
154
155 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
156 assert(s);
157 assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
158
159 return F_TYPE_EQUAL(s->f_type, magic_value);
160 }
161
162 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
163 struct statfs s;
164
165 if (fstatfs(fd, &s) < 0)
166 return -errno;
167
168 return is_fs_type(&s, magic_value);
169 }
170
171 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
172 _cleanup_close_ int fd = -1;
173
174 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
175 if (fd < 0)
176 return -errno;
177
178 return fd_is_fs_type(fd, magic_value);
179 }
180
181 bool is_temporary_fs(const struct statfs *s) {
182 return is_fs_type(s, TMPFS_MAGIC) ||
183 is_fs_type(s, RAMFS_MAGIC);
184 }
185
186 bool is_network_fs(const struct statfs *s) {
187 return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
188 is_fs_type(s, CODA_SUPER_MAGIC) ||
189 is_fs_type(s, NCP_SUPER_MAGIC) ||
190 is_fs_type(s, NFS_SUPER_MAGIC) ||
191 is_fs_type(s, SMB_SUPER_MAGIC) ||
192 is_fs_type(s, V9FS_MAGIC) ||
193 is_fs_type(s, AFS_SUPER_MAGIC) ||
194 is_fs_type(s, OCFS2_SUPER_MAGIC);
195 }
196
197 int fd_is_temporary_fs(int fd) {
198 struct statfs s;
199
200 if (fstatfs(fd, &s) < 0)
201 return -errno;
202
203 return is_temporary_fs(&s);
204 }
205
206 int fd_is_network_fs(int fd) {
207 struct statfs s;
208
209 if (fstatfs(fd, &s) < 0)
210 return -errno;
211
212 return is_network_fs(&s);
213 }
214
215 int fd_is_network_ns(int fd) {
216 struct statfs s;
217 int r;
218
219 /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice
220 * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle
221 * this somewhat nicely.
222 *
223 * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not
224 * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */
225
226 if (fstatfs(fd, &s) < 0)
227 return -errno;
228
229 if (!is_fs_type(&s, NSFS_MAGIC)) {
230 /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs
231 * instead. Handle that in a somewhat smart way. */
232
233 if (is_fs_type(&s, PROC_SUPER_MAGIC)) {
234 struct statfs t;
235
236 /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the
237 * passed fd might refer to a network namespace, but we can't know for sure. In that case,
238 * return a recognizable error. */
239
240 if (statfs("/proc/self/ns/net", &t) < 0)
241 return -errno;
242
243 if (s.f_type == t.f_type)
244 return -EUCLEAN; /* It's possible, we simply don't know */
245 }
246
247 return 0; /* No! */
248 }
249
250 r = ioctl(fd, NS_GET_NSTYPE);
251 if (r < 0) {
252 if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */
253 return -EUCLEAN;
254
255 return -errno;
256 }
257
258 return r == CLONE_NEWNET;
259 }
260
261 int path_is_temporary_fs(const char *path) {
262 _cleanup_close_ int fd = -1;
263
264 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
265 if (fd < 0)
266 return -errno;
267
268 return fd_is_temporary_fs(fd);
269 }
270
271 int stat_verify_regular(const struct stat *st) {
272 assert(st);
273
274 /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
275 * code. */
276
277 if (S_ISDIR(st->st_mode))
278 return -EISDIR;
279
280 if (S_ISLNK(st->st_mode))
281 return -ELOOP;
282
283 if (!S_ISREG(st->st_mode))
284 return -EBADFD;
285
286 return 0;
287 }
288
289 int fd_verify_regular(int fd) {
290 struct stat st;
291
292 assert(fd >= 0);
293
294 if (fstat(fd, &st) < 0)
295 return -errno;
296
297 return stat_verify_regular(&st);
298 }