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