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