1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <sched.h>
6 #include <sys/statvfs.h>
7 #include <sys/types.h>
8 #include <unistd.h>
9 
10 #include "alloc-util.h"
11 #include "chase-symlinks.h"
12 #include "dirent-util.h"
13 #include "errno-util.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "filesystems.h"
17 #include "fs-util.h"
18 #include "macro.h"
19 #include "missing_fs.h"
20 #include "missing_magic.h"
21 #include "missing_syscall.h"
22 #include "nulstr-util.h"
23 #include "parse-util.h"
24 #include "stat-util.h"
25 #include "string-util.h"
26 
is_symlink(const char * path)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 
is_dir(const char * path,bool follow)38 int is_dir(const char* path, bool follow) {
39         struct stat st;
40         int r;
41 
42         assert(path);
43 
44         if (follow)
45                 r = stat(path, &st);
46         else
47                 r = lstat(path, &st);
48         if (r < 0)
49                 return -errno;
50 
51         return !!S_ISDIR(st.st_mode);
52 }
53 
is_dir_fd(int fd)54 int is_dir_fd(int fd) {
55         struct stat st;
56 
57         if (fstat(fd, &st) < 0)
58                 return -errno;
59 
60         return !!S_ISDIR(st.st_mode);
61 }
62 
is_device_node(const char * path)63 int is_device_node(const char *path) {
64         struct stat info;
65 
66         assert(path);
67 
68         if (lstat(path, &info) < 0)
69                 return -errno;
70 
71         return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
72 }
73 
dir_is_empty_at(int dir_fd,const char * path,bool ignore_hidden_or_backup)74 int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
75         _cleanup_close_ int fd = -1;
76         struct dirent *buf;
77         size_t m;
78 
79         if (path) {
80                 assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
81 
82                 fd = openat(dir_fd, path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
83                 if (fd < 0)
84                         return -errno;
85         } else if (dir_fd == AT_FDCWD) {
86                 fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
87                 if (fd < 0)
88                         return -errno;
89         } else {
90                 /* Note that DUPing is not enough, as the internal pointer would still be shared and moved
91                  * getedents64(). */
92                 assert(dir_fd >= 0);
93 
94                 fd = fd_reopen(dir_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
95                 if (fd < 0)
96                         return fd;
97         }
98 
99         /* Allocate space for at least 3 full dirents, since every dir has at least two entries ("."  +
100          * ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
101          * 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of
102          * entries that we end up ignoring. */
103         m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX;
104         buf = alloca(m);
105 
106         for (;;) {
107                 struct dirent *de;
108                 ssize_t n;
109 
110                 n = getdents64(fd, buf, m);
111                 if (n < 0)
112                         return -errno;
113                 if (n == 0)
114                         break;
115 
116                 assert((size_t) n <= m);
117                 msan_unpoison(buf, n);
118 
119                 FOREACH_DIRENT_IN_BUFFER(de, buf, n)
120                         if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
121                                 return 0;
122         }
123 
124         return 1;
125 }
126 
null_or_empty(struct stat * st)127 bool null_or_empty(struct stat *st) {
128         assert(st);
129 
130         if (S_ISREG(st->st_mode) && st->st_size <= 0)
131                 return true;
132 
133         /* We don't want to hardcode the major/minor of /dev/null, hence we do a simpler "is this a character
134          * device node?" check. */
135 
136         if (S_ISCHR(st->st_mode))
137                 return true;
138 
139         return false;
140 }
141 
null_or_empty_path_with_root(const char * fn,const char * root)142 int null_or_empty_path_with_root(const char *fn, const char *root) {
143         struct stat st;
144         int r;
145 
146         assert(fn);
147 
148         /* A symlink to /dev/null or an empty file?
149          * When looking under root_dir, we can't expect /dev/ to be mounted,
150          * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
151 
152         if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
153                 return true;
154 
155         r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
156         if (r < 0)
157                 return r;
158 
159         return null_or_empty(&st);
160 }
161 
null_or_empty_fd(int fd)162 int null_or_empty_fd(int fd) {
163         struct stat st;
164 
165         assert(fd >= 0);
166 
167         if (fstat(fd, &st) < 0)
168                 return -errno;
169 
170         return null_or_empty(&st);
171 }
172 
path_is_read_only_fs(const char * path)173 int path_is_read_only_fs(const char *path) {
174         struct statvfs st;
175 
176         assert(path);
177 
178         if (statvfs(path, &st) < 0)
179                 return -errno;
180 
181         if (st.f_flag & ST_RDONLY)
182                 return true;
183 
184         /* On NFS, statvfs() might not reflect whether we can actually
185          * write to the remote share. Let's try again with
186          * access(W_OK) which is more reliable, at least sometimes. */
187         if (access(path, W_OK) < 0 && errno == EROFS)
188                 return true;
189 
190         return false;
191 }
192 
files_same(const char * filea,const char * fileb,int flags)193 int files_same(const char *filea, const char *fileb, int flags) {
194         struct stat a, b;
195 
196         assert(filea);
197         assert(fileb);
198 
199         if (fstatat(AT_FDCWD, filea, &a, flags) < 0)
200                 return -errno;
201 
202         if (fstatat(AT_FDCWD, fileb, &b, flags) < 0)
203                 return -errno;
204 
205         return stat_inode_same(&a, &b);
206 }
207 
is_fs_type(const struct statfs * s,statfs_f_type_t magic_value)208 bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) {
209         assert(s);
210         assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type));
211 
212         return F_TYPE_EQUAL(s->f_type, magic_value);
213 }
214 
fd_is_fs_type(int fd,statfs_f_type_t magic_value)215 int fd_is_fs_type(int fd, statfs_f_type_t magic_value) {
216         struct statfs s;
217 
218         if (fstatfs(fd, &s) < 0)
219                 return -errno;
220 
221         return is_fs_type(&s, magic_value);
222 }
223 
path_is_fs_type(const char * path,statfs_f_type_t magic_value)224 int path_is_fs_type(const char *path, statfs_f_type_t magic_value) {
225         struct statfs s;
226 
227         if (statfs(path, &s) < 0)
228                 return -errno;
229 
230         return is_fs_type(&s, magic_value);
231 }
232 
is_temporary_fs(const struct statfs * s)233 bool is_temporary_fs(const struct statfs *s) {
234         return fs_in_group(s, FILESYSTEM_SET_TEMPORARY);
235 }
236 
is_network_fs(const struct statfs * s)237 bool is_network_fs(const struct statfs *s) {
238         return fs_in_group(s, FILESYSTEM_SET_NETWORK);
239 }
240 
fd_is_temporary_fs(int fd)241 int fd_is_temporary_fs(int fd) {
242         struct statfs s;
243 
244         if (fstatfs(fd, &s) < 0)
245                 return -errno;
246 
247         return is_temporary_fs(&s);
248 }
249 
fd_is_network_fs(int fd)250 int fd_is_network_fs(int fd) {
251         struct statfs s;
252 
253         if (fstatfs(fd, &s) < 0)
254                 return -errno;
255 
256         return is_network_fs(&s);
257 }
258 
path_is_temporary_fs(const char * path)259 int path_is_temporary_fs(const char *path) {
260         struct statfs s;
261 
262         if (statfs(path, &s) < 0)
263                 return -errno;
264 
265         return is_temporary_fs(&s);
266 }
267 
path_is_network_fs(const char * path)268 int path_is_network_fs(const char *path) {
269         struct statfs s;
270 
271         if (statfs(path, &s) < 0)
272                 return -errno;
273 
274         return is_network_fs(&s);
275 }
276 
stat_verify_regular(const struct stat * st)277 int stat_verify_regular(const struct stat *st) {
278         assert(st);
279 
280         /* Checks whether the specified stat() structure refers to a regular file. If not returns an appropriate error
281          * code. */
282 
283         if (S_ISDIR(st->st_mode))
284                 return -EISDIR;
285 
286         if (S_ISLNK(st->st_mode))
287                 return -ELOOP;
288 
289         if (!S_ISREG(st->st_mode))
290                 return -EBADFD;
291 
292         return 0;
293 }
294 
fd_verify_regular(int fd)295 int fd_verify_regular(int fd) {
296         struct stat st;
297 
298         assert(fd >= 0);
299 
300         if (fstat(fd, &st) < 0)
301                 return -errno;
302 
303         return stat_verify_regular(&st);
304 }
305 
stat_verify_directory(const struct stat * st)306 int stat_verify_directory(const struct stat *st) {
307         assert(st);
308 
309         if (S_ISLNK(st->st_mode))
310                 return -ELOOP;
311 
312         if (!S_ISDIR(st->st_mode))
313                 return -ENOTDIR;
314 
315         return 0;
316 }
317 
fd_verify_directory(int fd)318 int fd_verify_directory(int fd) {
319         struct stat st;
320 
321         assert(fd >= 0);
322 
323         if (fstat(fd, &st) < 0)
324                 return -errno;
325 
326         return stat_verify_directory(&st);
327 }
328 
proc_mounted(void)329 int proc_mounted(void) {
330         int r;
331 
332         /* A quick check of procfs is properly mounted */
333 
334         r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
335         if (r == -ENOENT) /* not mounted at all */
336                 return false;
337 
338         return r;
339 }
340 
stat_inode_same(const struct stat * a,const struct stat * b)341 bool stat_inode_same(const struct stat *a, const struct stat *b) {
342 
343         /* Returns if the specified stat structure references the same (though possibly modified) inode. Does
344          * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */
345 
346         return a && b &&
347                 (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
348                 ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
349                 a->st_dev == b->st_dev &&
350                 a->st_ino == b->st_ino;
351 }
352 
stat_inode_unmodified(const struct stat * a,const struct stat * b)353 bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
354 
355         /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
356          * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
357          * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
358          * size, backing device, inode type and if this refers to a device not the major/minor.
359          *
360          * Note that we don't care if file attributes such as ownership or access mode change, this here is
361          * about contents of the file. The purpose here is to detect file contents changes, and nothing
362          * else. */
363 
364         return stat_inode_same(a, b) &&
365                 a->st_mtim.tv_sec == b->st_mtim.tv_sec &&
366                 a->st_mtim.tv_nsec == b->st_mtim.tv_nsec &&
367                 (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
368                 (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
369 }
370 
statx_fallback(int dfd,const char * path,int flags,unsigned mask,struct statx * sx)371 int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx) {
372         static bool avoid_statx = false;
373         struct stat st;
374 
375         if (!avoid_statx) {
376                 if (statx(dfd, path, flags, mask, sx) < 0) {
377                         if (!ERRNO_IS_NOT_SUPPORTED(errno) && errno != EPERM)
378                                 return -errno;
379 
380                         /* If statx() is not supported or if we see EPERM (which might indicate seccomp
381                          * filtering or so), let's do a fallback. Not that on EACCES we'll not fall back,
382                          * since that is likely an indication of fs access issues, which we should
383                          * propagate */
384                 } else
385                         return 0;
386 
387                 avoid_statx = true;
388         }
389 
390         /* Only do fallback if fstatat() supports the flag too, or if it's one of the sync flags, which are
391          * OK to ignore */
392         if ((flags & ~(AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW|
393                       AT_STATX_SYNC_AS_STAT|AT_STATX_FORCE_SYNC|AT_STATX_DONT_SYNC)) != 0)
394                 return -EOPNOTSUPP;
395 
396         if (fstatat(dfd, path, &st, flags & (AT_EMPTY_PATH|AT_NO_AUTOMOUNT|AT_SYMLINK_NOFOLLOW)) < 0)
397                 return -errno;
398 
399         *sx = (struct statx) {
400                 .stx_mask = STATX_TYPE|STATX_MODE|
401                 STATX_NLINK|STATX_UID|STATX_GID|
402                 STATX_ATIME|STATX_MTIME|STATX_CTIME|
403                 STATX_INO|STATX_SIZE|STATX_BLOCKS,
404                 .stx_blksize = st.st_blksize,
405                 .stx_nlink = st.st_nlink,
406                 .stx_uid = st.st_uid,
407                 .stx_gid = st.st_gid,
408                 .stx_mode = st.st_mode,
409                 .stx_ino = st.st_ino,
410                 .stx_size = st.st_size,
411                 .stx_blocks = st.st_blocks,
412                 .stx_rdev_major = major(st.st_rdev),
413                 .stx_rdev_minor = minor(st.st_rdev),
414                 .stx_dev_major = major(st.st_dev),
415                 .stx_dev_minor = minor(st.st_dev),
416                 .stx_atime.tv_sec = st.st_atim.tv_sec,
417                 .stx_atime.tv_nsec = st.st_atim.tv_nsec,
418                 .stx_mtime.tv_sec = st.st_mtim.tv_sec,
419                 .stx_mtime.tv_nsec = st.st_mtim.tv_nsec,
420                 .stx_ctime.tv_sec = st.st_ctim.tv_sec,
421                 .stx_ctime.tv_nsec = st.st_ctim.tv_nsec,
422         };
423 
424         return 0;
425 }
426