]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/stat-util.c
Merge pull request #7042 from vcaputo/iteratedcache
[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 int fd_is_temporary_fs(int fd) {
222 struct statfs s;
223
224 if (fstatfs(fd, &s) < 0)
225 return -errno;
226
227 return is_temporary_fs(&s);
228 }
229
230 int fd_is_network_ns(int fd) {
231 int r;
232
233 r = fd_is_fs_type(fd, NSFS_MAGIC);
234 if (r <= 0)
235 return r;
236 r = ioctl(fd, NS_GET_NSTYPE);
237 if (r < 0)
238 return -errno;
239 return r == CLONE_NEWNET;
240 }
241
242 int path_is_temporary_fs(const char *path) {
243 _cleanup_close_ int fd = -1;
244
245 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
246 if (fd < 0)
247 return -errno;
248
249 return fd_is_temporary_fs(fd);
250 }