]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/stat-util.c
core/namespace: rework the return semantics of clone_device_node yet again
[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 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <linux/magic.h>
25 #include <sched.h>
26 #include <sys/stat.h>
27 #include <sys/statvfs.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include "dirent-util.h"
32 #include "fd-util.h"
33 #include "fs-util.h"
34 #include "macro.h"
35 #include "missing.h"
36 #include "stat-util.h"
37 #include "string-util.h"
38
39 int is_symlink(const char *path) {
40 struct stat info;
41
42 assert(path);
43
44 if (lstat(path, &info) < 0)
45 return -errno;
46
47 return !!S_ISLNK(info.st_mode);
48 }
49
50 int is_dir(const char* path, bool follow) {
51 struct stat st;
52 int r;
53
54 assert(path);
55
56 if (follow)
57 r = stat(path, &st);
58 else
59 r = lstat(path, &st);
60 if (r < 0)
61 return -errno;
62
63 return !!S_ISDIR(st.st_mode);
64 }
65
66 int is_device_node(const char *path) {
67 struct stat info;
68
69 assert(path);
70
71 if (lstat(path, &info) < 0)
72 return -errno;
73
74 return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
75 }
76
77 int dir_is_empty(const char *path) {
78 _cleanup_closedir_ DIR *d;
79 struct dirent *de;
80
81 d = opendir(path);
82 if (!d)
83 return -errno;
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 path_is_os_tree(const char *path) {
149 int r;
150
151 assert(path);
152
153 /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
154 * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
155 * the case where just the os-release file is missing. */
156 if (laccess(path, F_OK) < 0)
157 return -errno;
158
159 /* We use /usr/lib/os-release as flag file if something is an OS */
160 r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
161 if (r == -ENOENT) {
162
163 /* Also check for the old location in /etc, just in case. */
164 r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
165 if (r == -ENOENT)
166 return 0; /* We got nothing */
167 }
168 if (r < 0)
169 return r;
170
171 return 1;
172 }
173
174 int files_same(const char *filea, const char *fileb, int flags) {
175 struct stat a, b;
176
177 assert(filea);
178 assert(fileb);
179
180 if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
181 return -errno;
182
183 if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
184 return -errno;
185
186 return a.st_dev == b.st_dev &&
187 a.st_ino == b.st_ino;
188 }
189
190 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
191 assert(s);
192 assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
193
194 return F_TYPE_EQUAL(s->f_type, magic_value);
195 }
196
197 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
198 struct statfs s;
199
200 if (fstatfs(fd, &s) < 0)
201 return -errno;
202
203 return is_fs_type(&s, magic_value);
204 }
205
206 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
207 _cleanup_close_ int fd = -1;
208
209 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
210 if (fd < 0)
211 return -errno;
212
213 return fd_is_fs_type(fd, magic_value);
214 }
215
216 bool is_temporary_fs(const struct statfs *s) {
217 return is_fs_type(s, TMPFS_MAGIC) ||
218 is_fs_type(s, RAMFS_MAGIC);
219 }
220
221 bool is_network_fs(const struct statfs *s) {
222 return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
223 is_fs_type(s, CODA_SUPER_MAGIC) ||
224 is_fs_type(s, NCP_SUPER_MAGIC) ||
225 is_fs_type(s, NFS_SUPER_MAGIC) ||
226 is_fs_type(s, SMB_SUPER_MAGIC) ||
227 is_fs_type(s, V9FS_MAGIC) ||
228 is_fs_type(s, AFS_SUPER_MAGIC) ||
229 is_fs_type(s, OCFS2_SUPER_MAGIC);
230 }
231
232 int fd_is_temporary_fs(int fd) {
233 struct statfs s;
234
235 if (fstatfs(fd, &s) < 0)
236 return -errno;
237
238 return is_temporary_fs(&s);
239 }
240
241 int fd_is_network_fs(int fd) {
242 struct statfs s;
243
244 if (fstatfs(fd, &s) < 0)
245 return -errno;
246
247 return is_network_fs(&s);
248 }
249
250 int fd_is_network_ns(int fd) {
251 int r;
252
253 r = fd_is_fs_type(fd, NSFS_MAGIC);
254 if (r <= 0)
255 return r;
256
257 r = ioctl(fd, NS_GET_NSTYPE);
258 if (r < 0)
259 return -errno;
260
261 return r == CLONE_NEWNET;
262 }
263
264 int path_is_temporary_fs(const char *path) {
265 _cleanup_close_ int fd = -1;
266
267 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
268 if (fd < 0)
269 return -errno;
270
271 return fd_is_temporary_fs(fd);
272 }
273
274 int stat_verify_regular(const struct stat *st) {
275 assert(st);
276
277 /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
278 * code. */
279
280 if (S_ISDIR(st->st_mode))
281 return -EISDIR;
282
283 if (S_ISLNK(st->st_mode))
284 return -ELOOP;
285
286 if (!S_ISREG(st->st_mode))
287 return -EBADFD;
288
289 return 0;
290 }
291
292 int fd_verify_regular(int fd) {
293 struct stat st;
294
295 assert(fd >= 0);
296
297 if (fstat(fd, &st) < 0)
298 return -errno;
299
300 return stat_verify_regular(&st);
301 }