chiark / gitweb /
fs-util: add new chase_symlinks() flag CHASE_OPEN
[elogind.git] / src / basic / fs-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 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 <errno.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <linux/magic.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include "alloc-util.h"
32 #include "dirent-util.h"
33 #include "fd-util.h"
34 #include "fileio.h"
35 #include "fs-util.h"
36 //#include "log.h"
37 //#include "macro.h"
38 //#include "missing.h"
39 #include "mkdir.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "stat-util.h"
43 #include "stdio-util.h"
44 #include "string-util.h"
45 #include "strv.h"
46 //#include "time-util.h"
47 #include "user-util.h"
48 #include "util.h"
49
50 /// Additional includes needed by elogind
51 #include "process-util.h"
52
53 int unlink_noerrno(const char *path) {
54         PROTECT_ERRNO;
55         int r;
56
57         r = unlink(path);
58         if (r < 0)
59                 return -errno;
60
61         return 0;
62 }
63
64 #if 0 /// UNNEEDED by elogind
65 int rmdir_parents(const char *path, const char *stop) {
66         size_t l;
67         int r = 0;
68
69         assert(path);
70         assert(stop);
71
72         l = strlen(path);
73
74         /* Skip trailing slashes */
75         while (l > 0 && path[l-1] == '/')
76                 l--;
77
78         while (l > 0) {
79                 char *t;
80
81                 /* Skip last component */
82                 while (l > 0 && path[l-1] != '/')
83                         l--;
84
85                 /* Skip trailing slashes */
86                 while (l > 0 && path[l-1] == '/')
87                         l--;
88
89                 if (l <= 0)
90                         break;
91
92                 t = strndup(path, l);
93                 if (!t)
94                         return -ENOMEM;
95
96                 if (path_startswith(stop, t)) {
97                         free(t);
98                         return 0;
99                 }
100
101                 r = rmdir(t);
102                 free(t);
103
104                 if (r < 0)
105                         if (errno != ENOENT)
106                                 return -errno;
107         }
108
109         return 0;
110 }
111
112 int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
113         struct stat buf;
114         int ret;
115
116         ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
117         if (ret >= 0)
118                 return 0;
119
120         /* renameat2() exists since Linux 3.15, btrfs added support for it later.
121          * If it is not implemented, fallback to another method. */
122         if (!IN_SET(errno, EINVAL, ENOSYS))
123                 return -errno;
124
125         /* The link()/unlink() fallback does not work on directories. But
126          * renameat() without RENAME_NOREPLACE gives the same semantics on
127          * directories, except when newpath is an *empty* directory. This is
128          * good enough. */
129         ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
130         if (ret >= 0 && S_ISDIR(buf.st_mode)) {
131                 ret = renameat(olddirfd, oldpath, newdirfd, newpath);
132                 return ret >= 0 ? 0 : -errno;
133         }
134
135         /* If it is not a directory, use the link()/unlink() fallback. */
136         ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
137         if (ret < 0)
138                 return -errno;
139
140         ret = unlinkat(olddirfd, oldpath, 0);
141         if (ret < 0) {
142                 /* backup errno before the following unlinkat() alters it */
143                 ret = errno;
144                 (void) unlinkat(newdirfd, newpath, 0);
145                 errno = ret;
146                 return -errno;
147         }
148
149         return 0;
150 }
151 #endif // 0
152
153 int readlinkat_malloc(int fd, const char *p, char **ret) {
154         size_t l = 100;
155         int r;
156
157         assert(p);
158         assert(ret);
159
160         for (;;) {
161                 char *c;
162                 ssize_t n;
163
164                 c = new(char, l);
165                 if (!c)
166                         return -ENOMEM;
167
168                 n = readlinkat(fd, p, c, l-1);
169                 if (n < 0) {
170                         r = -errno;
171                         free(c);
172                         return r;
173                 }
174
175                 if ((size_t) n < l-1) {
176                         c[n] = 0;
177                         *ret = c;
178                         return 0;
179                 }
180
181                 free(c);
182                 l *= 2;
183         }
184 }
185
186 int readlink_malloc(const char *p, char **ret) {
187         return readlinkat_malloc(AT_FDCWD, p, ret);
188 }
189
190 #if 0 /// UNNEEDED by elogind
191 int readlink_value(const char *p, char **ret) {
192         _cleanup_free_ char *link = NULL;
193         char *value;
194         int r;
195
196         r = readlink_malloc(p, &link);
197         if (r < 0)
198                 return r;
199
200         value = basename(link);
201         if (!value)
202                 return -ENOENT;
203
204         value = strdup(value);
205         if (!value)
206                 return -ENOMEM;
207
208         *ret = value;
209
210         return 0;
211 }
212 #endif // 0
213
214 int readlink_and_make_absolute(const char *p, char **r) {
215         _cleanup_free_ char *target = NULL;
216         char *k;
217         int j;
218
219         assert(p);
220         assert(r);
221
222         j = readlink_malloc(p, &target);
223         if (j < 0)
224                 return j;
225
226         k = file_in_same_dir(p, target);
227         if (!k)
228                 return -ENOMEM;
229
230         *r = k;
231         return 0;
232 }
233
234 #if 0 /// UNNEEDED by elogind
235 int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
236         char *t, *s;
237         int r;
238
239         assert(p);
240         assert(ret);
241
242         r = readlink_and_make_absolute(p, &t);
243         if (r < 0)
244                 return r;
245
246         r = chase_symlinks(t, root, 0, &s);
247         if (r < 0)
248                 /* If we can't follow up, then let's return the original string, slightly cleaned up. */
249                 *ret = path_kill_slashes(t);
250         else {
251                 *ret = s;
252                 free(t);
253         }
254
255         return 0;
256 }
257
258 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
259         _cleanup_free_ char *target = NULL, *t = NULL;
260         const char *full;
261         int r;
262
263         full = prefix_roota(root, path);
264         r = readlink_malloc(full, &target);
265         if (r < 0)
266                 return r;
267
268         t = file_in_same_dir(path, target);
269         if (!t)
270                 return -ENOMEM;
271
272         *ret = t;
273         t = NULL;
274
275         return 0;
276 }
277 #endif // 0
278
279 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
280         assert(path);
281
282         /* Under the assumption that we are running privileged we
283          * first change the access mode and only then hand out
284          * ownership to avoid a window where access is too open. */
285
286         if (mode != MODE_INVALID)
287                 if (chmod(path, mode) < 0)
288                         return -errno;
289
290         if (uid != UID_INVALID || gid != GID_INVALID)
291                 if (chown(path, uid, gid) < 0)
292                         return -errno;
293
294         return 0;
295 }
296
297 int fchmod_umask(int fd, mode_t m) {
298         mode_t u;
299         int r;
300
301         u = umask(0777);
302         r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
303         umask(u);
304
305         return r;
306 }
307
308 int fd_warn_permissions(const char *path, int fd) {
309         struct stat st;
310
311         if (fstat(fd, &st) < 0)
312                 return -errno;
313
314         if (st.st_mode & 0111)
315                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
316
317         if (st.st_mode & 0002)
318                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
319
320         if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
321                 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
322
323         return 0;
324 }
325
326 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
327         char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
328         _cleanup_close_ int fd = -1;
329         int r, ret = 0;
330
331         assert(path);
332
333         /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
334          * itself which is updated, not its target
335          *
336          * Returns the first error we encounter, but tries to apply as much as possible. */
337
338         if (parents)
339                 (void) mkdir_parents(path, 0755);
340
341         /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
342          * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
343          * won't trigger any driver magic or so. */
344         fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
345         if (fd < 0) {
346                 if (errno != ENOENT)
347                         return -errno;
348
349                 /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
350                  * here, and nothing else */
351                 fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
352                 if (fd < 0)
353                         return -errno;
354         }
355
356         /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
357          * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object â€” which is
358          * something fchown(), fchmod(), futimensat() don't allow. */
359         xsprintf(fdpath, "/proc/self/fd/%i", fd);
360
361         if (mode != MODE_INVALID)
362                 if (chmod(fdpath, mode) < 0)
363                         ret = -errno;
364
365         if (uid_is_valid(uid) || gid_is_valid(gid))
366                 if (chown(fdpath, uid, gid) < 0 && ret >= 0)
367                         ret = -errno;
368
369         if (stamp != USEC_INFINITY) {
370                 struct timespec ts[2];
371
372                 timespec_store(&ts[0], stamp);
373                 ts[1] = ts[0];
374                 r = utimensat(AT_FDCWD, fdpath, ts, 0);
375         } else
376                 r = utimensat(AT_FDCWD, fdpath, NULL, 0);
377         if (r < 0 && ret >= 0)
378                 return -errno;
379
380         return ret;
381 }
382
383 int touch(const char *path) {
384         return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
385 }
386
387 #if 0 /// UNNEEDED by elogind
388 int symlink_idempotent(const char *from, const char *to) {
389         int r;
390
391         assert(from);
392         assert(to);
393
394         if (symlink(from, to) < 0) {
395                 _cleanup_free_ char *p = NULL;
396
397                 if (errno != EEXIST)
398                         return -errno;
399
400                 r = readlink_malloc(to, &p);
401                 if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
402                         return -EEXIST;
403                 if (r < 0) /* Any other error? In that case propagate it as is */
404                         return r;
405
406                 if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
407                         return -EEXIST;
408         }
409
410         return 0;
411 }
412
413 int symlink_atomic(const char *from, const char *to) {
414         _cleanup_free_ char *t = NULL;
415         int r;
416
417         assert(from);
418         assert(to);
419
420         r = tempfn_random(to, NULL, &t);
421         if (r < 0)
422                 return r;
423
424         if (symlink(from, t) < 0)
425                 return -errno;
426
427         if (rename(t, to) < 0) {
428                 unlink_noerrno(t);
429                 return -errno;
430         }
431
432         return 0;
433 }
434
435 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
436         _cleanup_free_ char *t = NULL;
437         int r;
438
439         assert(path);
440
441         r = tempfn_random(path, NULL, &t);
442         if (r < 0)
443                 return r;
444
445         if (mknod(t, mode, dev) < 0)
446                 return -errno;
447
448         if (rename(t, path) < 0) {
449                 unlink_noerrno(t);
450                 return -errno;
451         }
452
453         return 0;
454 }
455
456 int mkfifo_atomic(const char *path, mode_t mode) {
457         _cleanup_free_ char *t = NULL;
458         int r;
459
460         assert(path);
461
462         r = tempfn_random(path, NULL, &t);
463         if (r < 0)
464                 return r;
465
466         if (mkfifo(t, mode) < 0)
467                 return -errno;
468
469         if (rename(t, path) < 0) {
470                 unlink_noerrno(t);
471                 return -errno;
472         }
473
474         return 0;
475 }
476 #endif // 0
477
478 int get_files_in_directory(const char *path, char ***list) {
479         _cleanup_closedir_ DIR *d = NULL;
480         struct dirent *de;
481         size_t bufsize = 0, n = 0;
482         _cleanup_strv_free_ char **l = NULL;
483
484         assert(path);
485
486         /* Returns all files in a directory in *list, and the number
487          * of files as return value. If list is NULL returns only the
488          * number. */
489
490         d = opendir(path);
491         if (!d)
492                 return -errno;
493
494         FOREACH_DIRENT_ALL(de, d, return -errno) {
495                 dirent_ensure_type(d, de);
496
497                 if (!dirent_is_file(de))
498                         continue;
499
500                 if (list) {
501                         /* one extra slot is needed for the terminating NULL */
502                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
503                                 return -ENOMEM;
504
505                         l[n] = strdup(de->d_name);
506                         if (!l[n])
507                                 return -ENOMEM;
508
509                         l[++n] = NULL;
510                 } else
511                         n++;
512         }
513
514         if (list) {
515                 *list = l;
516                 l = NULL; /* avoid freeing */
517         }
518
519         return n;
520 }
521
522 static int getenv_tmp_dir(const char **ret_path) {
523         const char *n;
524         int r, ret = 0;
525
526         assert(ret_path);
527
528         /* We use the same order of environment variables python uses in tempfile.gettempdir():
529          * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
530         FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
531                 const char *e;
532
533                 e = secure_getenv(n);
534                 if (!e)
535                         continue;
536                 if (!path_is_absolute(e)) {
537                         r = -ENOTDIR;
538                         goto next;
539                 }
540                 if (!path_is_normalized(e)) {
541                         r = -EPERM;
542                         goto next;
543                 }
544
545                 r = is_dir(e, true);
546                 if (r < 0)
547                         goto next;
548                 if (r == 0) {
549                         r = -ENOTDIR;
550                         goto next;
551                 }
552
553                 *ret_path = e;
554                 return 1;
555
556         next:
557                 /* Remember first error, to make this more debuggable */
558                 if (ret >= 0)
559                         ret = r;
560         }
561
562         if (ret < 0)
563                 return ret;
564
565         *ret_path = NULL;
566         return ret;
567 }
568
569 static int tmp_dir_internal(const char *def, const char **ret) {
570         const char *e;
571         int r, k;
572
573         assert(def);
574         assert(ret);
575
576         r = getenv_tmp_dir(&e);
577         if (r > 0) {
578                 *ret = e;
579                 return 0;
580         }
581
582         k = is_dir(def, true);
583         if (k == 0)
584                 k = -ENOTDIR;
585         if (k < 0)
586                 return r < 0 ? r : k;
587
588         *ret = def;
589         return 0;
590 }
591
592 #if 0 /// UNNEEDED by elogind
593 int var_tmp_dir(const char **ret) {
594
595         /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
596          * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
597          * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
598          * making it a variable that overrides all temporary file storage locations. */
599
600         return tmp_dir_internal("/var/tmp", ret);
601 }
602 #endif // 0
603
604 int tmp_dir(const char **ret) {
605
606         /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
607          * backed by an in-memory file system: /tmp. */
608
609         return tmp_dir_internal("/tmp", ret);
610 }
611
612 #if 0 /// UNNEEDED by elogind
613 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
614         char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
615         int r;
616
617         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
618         xsprintf(path, "/proc/self/fd/%i", what);
619
620         r = inotify_add_watch(fd, path, mask);
621         if (r < 0)
622                 return -errno;
623
624         return r;
625 }
626 #endif // 0
627
628 static bool safe_transition(const struct stat *a, const struct stat *b) {
629         /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
630          * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
631          * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
632
633         if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
634                 return true;
635
636         return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
637 }
638
639 int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
640         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
641         _cleanup_close_ int fd = -1;
642         unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
643         struct stat previous_stat;
644         bool exists = true;
645         char *todo;
646         int r;
647
648         assert(path);
649
650         /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
651         if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
652                 return -EINVAL;
653
654         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
655          * symlinks relative to a root directory, instead of the root of the host.
656          *
657          * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
658          * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
659          * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
660          * prefixed accordingly.
661          *
662          * Algorithmically this operates on two path buffers: "done" are the components of the path we already
663          * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
664          * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
665          * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
666          * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
667          * at a minimum.
668          *
669          * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
670          * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
671          * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
672          * specified path. */
673
674         if (original_root) {
675                 r = path_make_absolute_cwd(original_root, &root);
676                 if (r < 0)
677                         return r;
678
679                 if (flags & CHASE_PREFIX_ROOT)
680                         path = prefix_roota(root, path);
681         }
682
683         r = path_make_absolute_cwd(path, &buffer);
684         if (r < 0)
685                 return r;
686
687         fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
688         if (fd < 0)
689                 return -errno;
690
691         if (flags & CHASE_SAFE) {
692                 if (fstat(fd, &previous_stat) < 0)
693                         return -errno;
694         }
695
696         todo = buffer;
697         for (;;) {
698                 _cleanup_free_ char *first = NULL;
699                 _cleanup_close_ int child = -1;
700                 struct stat st;
701                 size_t n, m;
702
703                 /* Determine length of first component in the path */
704                 n = strspn(todo, "/");                  /* The slashes */
705                 m = n + strcspn(todo + n, "/");         /* The entire length of the component */
706
707                 /* Extract the first component. */
708                 first = strndup(todo, m);
709                 if (!first)
710                         return -ENOMEM;
711
712                 todo += m;
713
714                 /* Empty? Then we reached the end. */
715                 if (isempty(first))
716                         break;
717
718                 /* Just a single slash? Then we reached the end. */
719                 if (path_equal(first, "/")) {
720                         /* Preserve the trailing slash */
721                         if (!strextend(&done, "/", NULL))
722                                 return -ENOMEM;
723
724                         break;
725                 }
726
727                 /* Just a dot? Then let's eat this up. */
728                 if (path_equal(first, "/."))
729                         continue;
730
731                 /* Two dots? Then chop off the last bit of what we already found out. */
732                 if (path_equal(first, "/..")) {
733                         _cleanup_free_ char *parent = NULL;
734                         int fd_parent = -1;
735
736                         /* If we already are at the top, then going up will not change anything. This is in-line with
737                          * how the kernel handles this. */
738                         if (isempty(done) || path_equal(done, "/"))
739                                 continue;
740
741                         parent = dirname_malloc(done);
742                         if (!parent)
743                                 return -ENOMEM;
744
745                         /* Don't allow this to leave the root dir.  */
746                         if (root &&
747                             path_startswith(done, root) &&
748                             !path_startswith(parent, root))
749                                 continue;
750
751                         free_and_replace(done, parent);
752
753                         fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
754                         if (fd_parent < 0)
755                                 return -errno;
756
757                         if (flags & CHASE_SAFE) {
758                                 if (fstat(fd_parent, &st) < 0)
759                                         return -errno;
760
761                                 if (!safe_transition(&previous_stat, &st))
762                                         return -EPERM;
763
764                                 previous_stat = st;
765                         }
766
767                         safe_close(fd);
768                         fd = fd_parent;
769
770                         continue;
771                 }
772
773                 /* Otherwise let's see what this is. */
774                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
775                 if (child < 0) {
776
777                         if (errno == ENOENT &&
778                             (flags & CHASE_NONEXISTENT) &&
779                             (isempty(todo) || path_is_normalized(todo))) {
780
781                                 /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
782                                  * what we got so far. But don't allow this if the remaining path contains "../ or "./"
783                                  * or something else weird. */
784
785                                 /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
786                                 if (streq_ptr(done, "/"))
787                                         *done = '\0';
788
789                                 if (!strextend(&done, first, todo, NULL))
790                                         return -ENOMEM;
791
792                                 exists = false;
793                                 break;
794                         }
795
796                         return -errno;
797                 }
798
799                 if (fstat(child, &st) < 0)
800                         return -errno;
801                 if ((flags & CHASE_SAFE) &&
802                     !safe_transition(&previous_stat, &st))
803                         return -EPERM;
804
805                 previous_stat = st;
806
807                 if ((flags & CHASE_NO_AUTOFS) &&
808                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
809                         return -EREMOTE;
810
811                 if (S_ISLNK(st.st_mode)) {
812                         char *joined;
813
814                         _cleanup_free_ char *destination = NULL;
815
816                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
817                          * symlinks without bounds. */
818                         if (--max_follow <= 0)
819                                 return -ELOOP;
820
821                         r = readlinkat_malloc(fd, first + n, &destination);
822                         if (r < 0)
823                                 return r;
824                         if (isempty(destination))
825                                 return -EINVAL;
826
827                         if (path_is_absolute(destination)) {
828
829                                 /* An absolute destination. Start the loop from the beginning, but use the root
830                                  * directory as base. */
831
832                                 safe_close(fd);
833                                 fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
834                                 if (fd < 0)
835                                         return -errno;
836
837                                 free(done);
838
839                                 if (flags & CHASE_SAFE) {
840                                         if (fstat(fd, &st) < 0)
841                                                 return -errno;
842
843                                         if (!safe_transition(&previous_stat, &st))
844                                                 return -EPERM;
845
846                                         previous_stat = st;
847                                 }
848
849                                 /* Note that we do not revalidate the root, we take it as is. */
850                                 if (isempty(root))
851                                         done = NULL;
852                                 else {
853                                         done = strdup(root);
854                                         if (!done)
855                                                 return -ENOMEM;
856                                 }
857
858                                 /* Prefix what's left to do with what we just read, and start the loop again, but
859                                  * remain in the current directory. */
860                                 joined = strjoin(destination, todo);
861                         } else
862                                 joined = strjoin("/", destination, todo);
863                         if (!joined)
864                                 return -ENOMEM;
865
866                         free(buffer);
867                         todo = buffer = joined;
868
869                         continue;
870                 }
871
872                 /* If this is not a symlink, then let's just add the name we read to what we already verified. */
873                 if (!done) {
874                         done = first;
875                         first = NULL;
876                 } else {
877                         /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
878                         if (streq(done, "/"))
879                                 *done = '\0';
880
881                         if (!strextend(&done, first, NULL))
882                                 return -ENOMEM;
883                 }
884
885                 /* And iterate again, but go one directory further down. */
886                 safe_close(fd);
887                 fd = child;
888                 child = -1;
889         }
890
891         if (!done) {
892                 /* Special case, turn the empty string into "/", to indicate the root directory. */
893                 done = strdup("/");
894                 if (!done)
895                         return -ENOMEM;
896         }
897
898         if (ret) {
899                 *ret = done;
900                 done = NULL;
901         }
902
903         if (flags & CHASE_OPEN) {
904                 int q;
905
906                 /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
907                  * opening /proc/self/fd/xyz. */
908
909                 assert(fd >= 0);
910                 q = fd;
911                 fd = -1;
912
913                 return q;
914         }
915
916         return exists;
917 }
918
919 int access_fd(int fd, int mode) {
920         char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
921         int r;
922
923         /* Like access() but operates on an already open fd */
924
925         xsprintf(p, "/proc/self/fd/%i", fd);
926
927         r = access(p, mode);
928         if (r < 0)
929                 r = -errno;
930
931         return r;
932 }