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