]> git.proxmox.com Git - systemd.git/blame - src/basic/fs-util.c
New upstream version 236
[systemd.git] / src / basic / fs-util.c
CommitLineData
52ad194e 1/* SPDX-License-Identifier: LGPL-2.1+ */
db2df898
MP
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
4c89c718
MP
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>
f5e65279 27#include <linux/magic.h>
4c89c718
MP
28#include <time.h>
29#include <unistd.h>
30
db2df898
MP
31#include "alloc-util.h"
32#include "dirent-util.h"
33#include "fd-util.h"
34#include "fileio.h"
35#include "fs-util.h"
4c89c718
MP
36#include "log.h"
37#include "macro.h"
38#include "missing.h"
db2df898
MP
39#include "mkdir.h"
40#include "parse-util.h"
41#include "path-util.h"
5a920b42 42#include "stat-util.h"
aa27b158 43#include "stdio-util.h"
db2df898
MP
44#include "string-util.h"
45#include "strv.h"
4c89c718 46#include "time-util.h"
db2df898
MP
47#include "user-util.h"
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
db2df898
MP
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
2897b343 227int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
db2df898 228 char *t, *s;
2897b343 229 int r;
db2df898
MP
230
231 assert(p);
2897b343 232 assert(ret);
db2df898 233
2897b343
MP
234 r = readlink_and_make_absolute(p, &t);
235 if (r < 0)
236 return r;
db2df898 237
2897b343
MP
238 r = chase_symlinks(t, root, 0, &s);
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;
db2df898 244 free(t);
2897b343 245 }
db2df898
MP
246
247 return 0;
248}
249
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
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
db2df898
MP
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
f5e65279 311 if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
db2df898
MP
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
4c89c718 326 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
f5e65279 327 IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
db2df898
MP
328 if (fd < 0)
329 return -errno;
330
331 if (mode != MODE_INVALID) {
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) {
358 return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
359}
360
361int symlink_idempotent(const char *from, const char *to) {
db2df898
MP
362 int r;
363
364 assert(from);
365 assert(to);
366
367 if (symlink(from, to) < 0) {
f5e65279
MB
368 _cleanup_free_ char *p = NULL;
369
db2df898
MP
370 if (errno != EEXIST)
371 return -errno;
372
373 r = readlink_malloc(to, &p);
f5e65279
MB
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 */
db2df898
MP
377 return r;
378
f5e65279
MB
379 if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
380 return -EEXIST;
db2df898
MP
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;
2897b343 452 struct dirent *de;
db2df898
MP
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
2897b343 466 FOREACH_DIRENT_ALL(de, d, return -errno) {
db2df898
MP
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}
aa27b158 493
8a584da2
MP
494static int getenv_tmp_dir(const char **ret_path) {
495 const char *n;
496 int r, ret = 0;
5a920b42 497
8a584da2 498 assert(ret_path);
5a920b42 499
8a584da2
MP
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 }
52ad194e 512 if (!path_is_normalized(e)) {
8a584da2
MP
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;
5a920b42
MP
532 }
533
8a584da2
MP
534 if (ret < 0)
535 return ret;
5a920b42 536
8a584da2
MP
537 *ret_path = NULL;
538 return ret;
539}
540
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);
5a920b42 547
8a584da2
MP
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;
5a920b42
MP
561 return 0;
562}
563
8a584da2
MP
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
aa27b158 582int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
52ad194e 583 char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
aa27b158
MP
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}
8a584da2 595
2897b343 596int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
8a584da2
MP
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 */
2897b343 600 bool exists = true;
8a584da2
MP
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 *
2897b343
MP
609 * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
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.
8a584da2
MP
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
2897b343
MP
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
52ad194e 624 * specified path. */
8a584da2 625
2897b343
MP
626 if (original_root) {
627 r = path_make_absolute_cwd(original_root, &root);
8a584da2
MP
628 if (r < 0)
629 return r;
2897b343
MP
630
631 if (flags & CHASE_PREFIX_ROOT)
632 path = prefix_roota(root, path);
8a584da2
MP
633 }
634
2897b343
MP
635 r = path_make_absolute_cwd(path, &buffer);
636 if (r < 0)
637 return r;
638
8a584da2
MP
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
52ad194e
MB
661 /* Empty? Then we reached the end. */
662 if (isempty(first))
663 break;
664
8a584da2 665 /* Just a single slash? Then we reached the end. */
52ad194e
MB
666 if (path_equal(first, "/")) {
667 /* Preserve the trailing slash */
668 if (!strextend(&done, "/", NULL))
669 return -ENOMEM;
670
8a584da2 671 break;
52ad194e 672 }
8a584da2
MP
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
2897b343
MP
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. */
8a584da2 685 if (isempty(done) || path_equal(done, "/"))
2897b343 686 continue;
8a584da2
MP
687
688 parent = dirname_malloc(done);
689 if (!parent)
690 return -ENOMEM;
691
2897b343 692 /* Don't allow this to leave the root dir. */
8a584da2
MP
693 if (root &&
694 path_startswith(done, root) &&
695 !path_startswith(parent, root))
2897b343 696 continue;
8a584da2
MP
697
698 free_and_replace(done, parent);
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);
2897b343
MP
712 if (child < 0) {
713
714 if (errno == ENOENT &&
715 (flags & CHASE_NONEXISTENT) &&
52ad194e 716 (isempty(todo) || path_is_normalized(todo))) {
2897b343
MP
717
718 /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
719 * what we got so far. But don't allow this if the remaining path contains "../ or "./"
720 * or something else weird. */
721
52ad194e
MB
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
2897b343
MP
726 if (!strextend(&done, first, todo, NULL))
727 return -ENOMEM;
728
729 exists = false;
730 break;
731 }
732
8a584da2 733 return -errno;
2897b343 734 }
8a584da2
MP
735
736 if (fstat(child, &st) < 0)
737 return -errno;
f5e65279 738 if ((flags & CHASE_NO_AUTOFS) &&
52ad194e 739 fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
f5e65279 740 return -EREMOTE;
8a584da2
MP
741
742 if (S_ISLNK(st.st_mode)) {
2897b343
MP
743 char *joined;
744
8a584da2
MP
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
8a584da2
MP
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
52ad194e
MB
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);
2897b343
MP
784 if (!joined)
785 return -ENOMEM;
8a584da2 786
2897b343
MP
787 free(buffer);
788 todo = buffer = joined;
8a584da2
MP
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 {
52ad194e
MB
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
8a584da2
MP
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
2897b343
MP
819 if (ret) {
820 *ret = done;
821 done = NULL;
822 }
8a584da2 823
2897b343 824 return exists;
8a584da2 825}
52ad194e
MB
826
827int access_fd(int fd, int mode) {
828 char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
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}