chiark / gitweb /
tree-wide: drop license boilerplate
[elogind.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 #if 0 /// UNNEEDED by elogind
27 int is_symlink(const char *path) {
28         struct stat info;
29
30         assert(path);
31
32         if (lstat(path, &info) < 0)
33                 return -errno;
34
35         return !!S_ISLNK(info.st_mode);
36 }
37 #endif // 0
38
39 int is_dir(const char* path, bool follow) {
40         struct stat st;
41         int r;
42
43         assert(path);
44
45         if (follow)
46                 r = stat(path, &st);
47         else
48                 r = lstat(path, &st);
49         if (r < 0)
50                 return -errno;
51
52         return !!S_ISDIR(st.st_mode);
53 }
54
55 #if 0 /// UNNEEDED by elogind
56 int is_device_node(const char *path) {
57         struct stat info;
58
59         assert(path);
60
61         if (lstat(path, &info) < 0)
62                 return -errno;
63
64         return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
65 }
66
67 int dir_is_empty(const char *path) {
68         _cleanup_closedir_ DIR *d;
69         struct dirent *de;
70
71         d = opendir(path);
72         if (!d)
73                 return -errno;
74
75         FOREACH_DIRENT(de, d, return -errno)
76                 return 0;
77
78         return 1;
79 }
80 #endif // 0
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 #if 0 /// UNNEEDED by elogind
109 int null_or_empty_fd(int fd) {
110         struct stat st;
111
112         assert(fd >= 0);
113
114         if (fstat(fd, &st) < 0)
115                 return -errno;
116
117         return null_or_empty(&st);
118 }
119 #endif // 0
120
121 int 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
141 #if 0 /// UNNEEDED by elogind
142 int path_is_os_tree(const char *path) {
143         int r;
144
145         assert(path);
146
147         /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
148          * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
149          * the case where just the os-release file is missing. */
150         if (laccess(path, F_OK) < 0)
151                 return -errno;
152
153         /* We use /usr/lib/os-release as flag file if something is an OS */
154         r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
155         if (r == -ENOENT) {
156
157                 /* Also check for the old location in /etc, just in case. */
158                 r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
159                 if (r == -ENOENT)
160                         return 0; /* We got nothing */
161         }
162         if (r < 0)
163                 return r;
164
165         return 1;
166 }
167 #endif // 0
168
169 int files_same(const char *filea, const char *fileb, int flags) {
170         struct stat a, b;
171
172         assert(filea);
173         assert(fileb);
174
175         if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
176                 return -errno;
177
178         if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
179                 return -errno;
180
181         return a.st_dev == b.st_dev &&
182                a.st_ino == b.st_ino;
183 }
184
185 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
186         assert(s);
187         assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
188
189         return F_TYPE_EQUAL(s->f_type, magic_value);
190 }
191
192 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
193         struct statfs s;
194
195         if (fstatfs(fd, &s) < 0)
196                 return -errno;
197
198         return is_fs_type(&s, magic_value);
199 }
200
201 #if 0 /// UNNEEDED by elogind
202 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
203         _cleanup_close_ int fd = -1;
204
205         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
206         if (fd < 0)
207                 return -errno;
208
209         return fd_is_fs_type(fd, magic_value);
210 }
211 #endif // 0
212
213 bool is_temporary_fs(const struct statfs *s) {
214         return is_fs_type(s, TMPFS_MAGIC) ||
215                 is_fs_type(s, RAMFS_MAGIC);
216 }
217
218 bool is_network_fs(const struct statfs *s) {
219         return is_fs_type(s, CIFS_MAGIC_NUMBER) ||
220                 is_fs_type(s, CODA_SUPER_MAGIC) ||
221                 is_fs_type(s, NCP_SUPER_MAGIC) ||
222                 is_fs_type(s, NFS_SUPER_MAGIC) ||
223                 is_fs_type(s, SMB_SUPER_MAGIC) ||
224                 is_fs_type(s, V9FS_MAGIC) ||
225                 is_fs_type(s, AFS_SUPER_MAGIC) ||
226                 is_fs_type(s, OCFS2_SUPER_MAGIC);
227 }
228
229 #if 0 /// UNNEEDED by elogind
230 int fd_is_temporary_fs(int fd) {
231         struct statfs s;
232
233         if (fstatfs(fd, &s) < 0)
234                 return -errno;
235
236         return is_temporary_fs(&s);
237 }
238
239 int fd_is_network_fs(int fd) {
240         struct statfs s;
241
242         if (fstatfs(fd, &s) < 0)
243                 return -errno;
244
245         return is_network_fs(&s);
246 }
247
248 int fd_is_network_ns(int fd) {
249         int r;
250
251         r = fd_is_fs_type(fd, NSFS_MAGIC);
252         if (r <= 0)
253                 return r;
254
255         r = ioctl(fd, NS_GET_NSTYPE);
256         if (r < 0)
257                 return -errno;
258
259         return r == CLONE_NEWNET;
260 }
261
262 int path_is_temporary_fs(const char *path) {
263         _cleanup_close_ int fd = -1;
264
265         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
266         if (fd < 0)
267                 return -errno;
268
269         return fd_is_temporary_fs(fd);
270 }
271 #endif // 0
272
273 int 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
291 int 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 }