chiark / gitweb /
fs-util: add new unlinkat_deallocate() helper
[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_canonicalize(const char *p, const char *root, char **ret) {
237         char *t, *s;
238         int r;
239
240         assert(p);
241         assert(ret);
242
243         r = readlink_and_make_absolute(p, &t);
244         if (r < 0)
245                 return r;
246
247         r = chase_symlinks(t, root, 0, &s);
248         if (r < 0)
249                 /* If we can't follow up, then let's return the original string, slightly cleaned up. */
250                 *ret = path_kill_slashes(t);
251         else {
252                 *ret = s;
253                 free(t);
254         }
255
256         return 0;
257 }
258
259 int readlink_and_make_absolute_root(const char *root, const char *path, char **ret) {
260         _cleanup_free_ char *target = NULL, *t = NULL;
261         const char *full;
262         int r;
263
264         full = prefix_roota(root, path);
265         r = readlink_malloc(full, &target);
266         if (r < 0)
267                 return r;
268
269         t = file_in_same_dir(path, target);
270         if (!t)
271                 return -ENOMEM;
272
273         *ret = t;
274         t = NULL;
275
276         return 0;
277 }
278 #endif // 0
279
280 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
281         assert(path);
282
283         /* Under the assumption that we are running privileged we
284          * first change the access mode and only then hand out
285          * ownership to avoid a window where access is too open. */
286
287         if (mode != MODE_INVALID)
288                 if (chmod(path, mode) < 0)
289                         return -errno;
290
291         if (uid != UID_INVALID || gid != GID_INVALID)
292                 if (chown(path, uid, gid) < 0)
293                         return -errno;
294
295         return 0;
296 }
297
298 int fchmod_umask(int fd, mode_t m) {
299         mode_t u;
300         int r;
301
302         u = umask(0777);
303         r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
304         umask(u);
305
306         return r;
307 }
308
309 int fd_warn_permissions(const char *path, int fd) {
310         struct stat st;
311
312         if (fstat(fd, &st) < 0)
313                 return -errno;
314
315         if (st.st_mode & 0111)
316                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
317
318         if (st.st_mode & 0002)
319                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
320
321         if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
322                 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);
323
324         return 0;
325 }
326
327 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
328         char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
329         _cleanup_close_ int fd = -1;
330         int r, ret = 0;
331
332         assert(path);
333
334         /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
335          * itself which is updated, not its target
336          *
337          * Returns the first error we encounter, but tries to apply as much as possible. */
338
339         if (parents)
340                 (void) mkdir_parents(path, 0755);
341
342         /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
343          * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
344          * won't trigger any driver magic or so. */
345         fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
346         if (fd < 0) {
347                 if (errno != ENOENT)
348                         return -errno;
349
350                 /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
351                  * here, and nothing else */
352                 fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
353                 if (fd < 0)
354                         return -errno;
355         }
356
357         /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
358          * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object â€” which is
359          * something fchown(), fchmod(), futimensat() don't allow. */
360         xsprintf(fdpath, "/proc/self/fd/%i", fd);
361
362         if (mode != MODE_INVALID)
363                 if (chmod(fdpath, mode) < 0)
364                         ret = -errno;
365
366         if (uid_is_valid(uid) || gid_is_valid(gid))
367                 if (chown(fdpath, uid, gid) < 0 && ret >= 0)
368                         ret = -errno;
369
370         if (stamp != USEC_INFINITY) {
371                 struct timespec ts[2];
372
373                 timespec_store(&ts[0], stamp);
374                 ts[1] = ts[0];
375                 r = utimensat(AT_FDCWD, fdpath, ts, 0);
376         } else
377                 r = utimensat(AT_FDCWD, fdpath, NULL, 0);
378         if (r < 0 && ret >= 0)
379                 return -errno;
380
381         return ret;
382 }
383
384 int touch(const char *path) {
385         return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
386 }
387
388 #if 0 /// UNNEEDED by elogind
389 int symlink_idempotent(const char *from, const char *to) {
390         int r;
391
392         assert(from);
393         assert(to);
394
395         if (symlink(from, to) < 0) {
396                 _cleanup_free_ char *p = NULL;
397
398                 if (errno != EEXIST)
399                         return -errno;
400
401                 r = readlink_malloc(to, &p);
402                 if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
403                         return -EEXIST;
404                 if (r < 0) /* Any other error? In that case propagate it as is */
405                         return r;
406
407                 if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
408                         return -EEXIST;
409         }
410
411         return 0;
412 }
413
414 int symlink_atomic(const char *from, const char *to) {
415         _cleanup_free_ char *t = NULL;
416         int r;
417
418         assert(from);
419         assert(to);
420
421         r = tempfn_random(to, NULL, &t);
422         if (r < 0)
423                 return r;
424
425         if (symlink(from, t) < 0)
426                 return -errno;
427
428         if (rename(t, to) < 0) {
429                 unlink_noerrno(t);
430                 return -errno;
431         }
432
433         return 0;
434 }
435
436 int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
437         _cleanup_free_ char *t = NULL;
438         int r;
439
440         assert(path);
441
442         r = tempfn_random(path, NULL, &t);
443         if (r < 0)
444                 return r;
445
446         if (mknod(t, mode, dev) < 0)
447                 return -errno;
448
449         if (rename(t, path) < 0) {
450                 unlink_noerrno(t);
451                 return -errno;
452         }
453
454         return 0;
455 }
456
457 int mkfifo_atomic(const char *path, mode_t mode) {
458         _cleanup_free_ char *t = NULL;
459         int r;
460
461         assert(path);
462
463         r = tempfn_random(path, NULL, &t);
464         if (r < 0)
465                 return r;
466
467         if (mkfifo(t, mode) < 0)
468                 return -errno;
469
470         if (rename(t, path) < 0) {
471                 unlink_noerrno(t);
472                 return -errno;
473         }
474
475         return 0;
476 }
477 #endif // 0
478
479 int get_files_in_directory(const char *path, char ***list) {
480         _cleanup_closedir_ DIR *d = NULL;
481         struct dirent *de;
482         size_t bufsize = 0, n = 0;
483         _cleanup_strv_free_ char **l = NULL;
484
485         assert(path);
486
487         /* Returns all files in a directory in *list, and the number
488          * of files as return value. If list is NULL returns only the
489          * number. */
490
491         d = opendir(path);
492         if (!d)
493                 return -errno;
494
495         FOREACH_DIRENT_ALL(de, d, return -errno) {
496                 dirent_ensure_type(d, de);
497
498                 if (!dirent_is_file(de))
499                         continue;
500
501                 if (list) {
502                         /* one extra slot is needed for the terminating NULL */
503                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
504                                 return -ENOMEM;
505
506                         l[n] = strdup(de->d_name);
507                         if (!l[n])
508                                 return -ENOMEM;
509
510                         l[++n] = NULL;
511                 } else
512                         n++;
513         }
514
515         if (list) {
516                 *list = l;
517                 l = NULL; /* avoid freeing */
518         }
519
520         return n;
521 }
522
523 static int getenv_tmp_dir(const char **ret_path) {
524         const char *n;
525         int r, ret = 0;
526
527         assert(ret_path);
528
529         /* We use the same order of environment variables python uses in tempfile.gettempdir():
530          * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
531         FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
532                 const char *e;
533
534                 e = secure_getenv(n);
535                 if (!e)
536                         continue;
537                 if (!path_is_absolute(e)) {
538                         r = -ENOTDIR;
539                         goto next;
540                 }
541                 if (!path_is_normalized(e)) {
542                         r = -EPERM;
543                         goto next;
544                 }
545
546                 r = is_dir(e, true);
547                 if (r < 0)
548                         goto next;
549                 if (r == 0) {
550                         r = -ENOTDIR;
551                         goto next;
552                 }
553
554                 *ret_path = e;
555                 return 1;
556
557         next:
558                 /* Remember first error, to make this more debuggable */
559                 if (ret >= 0)
560                         ret = r;
561         }
562
563         if (ret < 0)
564                 return ret;
565
566         *ret_path = NULL;
567         return ret;
568 }
569
570 static int tmp_dir_internal(const char *def, const char **ret) {
571         const char *e;
572         int r, k;
573
574         assert(def);
575         assert(ret);
576
577         r = getenv_tmp_dir(&e);
578         if (r > 0) {
579                 *ret = e;
580                 return 0;
581         }
582
583         k = is_dir(def, true);
584         if (k == 0)
585                 k = -ENOTDIR;
586         if (k < 0)
587                 return r < 0 ? r : k;
588
589         *ret = def;
590         return 0;
591 }
592
593 #if 0 /// UNNEEDED by elogind
594 int var_tmp_dir(const char **ret) {
595
596         /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
597          * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
598          * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
599          * making it a variable that overrides all temporary file storage locations. */
600
601         return tmp_dir_internal("/var/tmp", ret);
602 }
603 #endif // 0
604
605 int tmp_dir(const char **ret) {
606
607         /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
608          * backed by an in-memory file system: /tmp. */
609
610         return tmp_dir_internal("/tmp", ret);
611 }
612
613 #if 0 /// UNNEEDED by elogind
614 int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
615         char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
616         int r;
617
618         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
619         xsprintf(path, "/proc/self/fd/%i", what);
620
621         r = inotify_add_watch(fd, path, mask);
622         if (r < 0)
623                 return -errno;
624
625         return r;
626 }
627 #endif // 0
628
629 static bool safe_transition(const struct stat *a, const struct stat *b) {
630         /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
631          * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
632          * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
633
634         if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
635                 return true;
636
637         return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
638 }
639
640 int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
641         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
642         _cleanup_close_ int fd = -1;
643         unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
644         struct stat previous_stat;
645         bool exists = true;
646         char *todo;
647         int r;
648
649         assert(path);
650
651         /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
652         if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
653                 return -EINVAL;
654
655         if (isempty(path))
656                 return -EINVAL;
657
658         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
659          * symlinks relative to a root directory, instead of the root of the host.
660          *
661          * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
662          * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
663          * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
664          * prefixed accordingly.
665          *
666          * Algorithmically this operates on two path buffers: "done" are the components of the path we already
667          * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
668          * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
669          * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
670          * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
671          * at a minimum.
672          *
673          * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
674          * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
675          * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
676          * specified path. */
677
678         /* A root directory of "/" or "" is identical to none */
679         if (isempty(original_root) || path_equal(original_root, "/"))
680                 original_root = NULL;
681
682         if (original_root) {
683                 r = path_make_absolute_cwd(original_root, &root);
684                 if (r < 0)
685                         return r;
686
687                 if (flags & CHASE_PREFIX_ROOT) {
688
689                         /* We don't support relative paths in combination with a root directory */
690                         if (!path_is_absolute(path))
691                                 return -EINVAL;
692
693                         path = prefix_roota(root, path);
694                 }
695         }
696
697         r = path_make_absolute_cwd(path, &buffer);
698         if (r < 0)
699                 return r;
700
701         fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
702         if (fd < 0)
703                 return -errno;
704
705         if (flags & CHASE_SAFE) {
706                 if (fstat(fd, &previous_stat) < 0)
707                         return -errno;
708         }
709
710         todo = buffer;
711         for (;;) {
712                 _cleanup_free_ char *first = NULL;
713                 _cleanup_close_ int child = -1;
714                 struct stat st;
715                 size_t n, m;
716
717                 /* Determine length of first component in the path */
718                 n = strspn(todo, "/");                  /* The slashes */
719                 m = n + strcspn(todo + n, "/");         /* The entire length of the component */
720
721                 /* Extract the first component. */
722                 first = strndup(todo, m);
723                 if (!first)
724                         return -ENOMEM;
725
726                 todo += m;
727
728                 /* Empty? Then we reached the end. */
729                 if (isempty(first))
730                         break;
731
732                 /* Just a single slash? Then we reached the end. */
733                 if (path_equal(first, "/")) {
734                         /* Preserve the trailing slash */
735                         if (!strextend(&done, "/", NULL))
736                                 return -ENOMEM;
737
738                         break;
739                 }
740
741                 /* Just a dot? Then let's eat this up. */
742                 if (path_equal(first, "/."))
743                         continue;
744
745                 /* Two dots? Then chop off the last bit of what we already found out. */
746                 if (path_equal(first, "/..")) {
747                         _cleanup_free_ char *parent = NULL;
748                         _cleanup_close_ int fd_parent = -1;
749
750                         /* If we already are at the top, then going up will not change anything. This is in-line with
751                          * how the kernel handles this. */
752                         if (isempty(done) || path_equal(done, "/"))
753                                 continue;
754
755                         parent = dirname_malloc(done);
756                         if (!parent)
757                                 return -ENOMEM;
758
759                         /* Don't allow this to leave the root dir.  */
760                         if (root &&
761                             path_startswith(done, root) &&
762                             !path_startswith(parent, root))
763                                 continue;
764
765                         free_and_replace(done, parent);
766
767                         fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
768                         if (fd_parent < 0)
769                                 return -errno;
770
771                         if (flags & CHASE_SAFE) {
772                                 if (fstat(fd_parent, &st) < 0)
773                                         return -errno;
774
775                                 if (!safe_transition(&previous_stat, &st))
776                                         return -EPERM;
777
778                                 previous_stat = st;
779                         }
780
781                         safe_close(fd);
782                         fd = fd_parent;
783                         fd_parent = -1;
784
785                         continue;
786                 }
787
788                 /* Otherwise let's see what this is. */
789                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
790                 if (child < 0) {
791
792                         if (errno == ENOENT &&
793                             (flags & CHASE_NONEXISTENT) &&
794                             (isempty(todo) || path_is_normalized(todo))) {
795
796                                 /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
797                                  * what we got so far. But don't allow this if the remaining path contains "../ or "./"
798                                  * or something else weird. */
799
800                                 /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
801                                 if (streq_ptr(done, "/"))
802                                         *done = '\0';
803
804                                 if (!strextend(&done, first, todo, NULL))
805                                         return -ENOMEM;
806
807                                 exists = false;
808                                 break;
809                         }
810
811                         return -errno;
812                 }
813
814                 if (fstat(child, &st) < 0)
815                         return -errno;
816                 if ((flags & CHASE_SAFE) &&
817                     !safe_transition(&previous_stat, &st))
818                         return -EPERM;
819
820                 previous_stat = st;
821
822                 if ((flags & CHASE_NO_AUTOFS) &&
823                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
824                         return -EREMOTE;
825
826                 if (S_ISLNK(st.st_mode)) {
827                         char *joined;
828
829                         _cleanup_free_ char *destination = NULL;
830
831                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
832                          * symlinks without bounds. */
833                         if (--max_follow <= 0)
834                                 return -ELOOP;
835
836                         r = readlinkat_malloc(fd, first + n, &destination);
837                         if (r < 0)
838                                 return r;
839                         if (isempty(destination))
840                                 return -EINVAL;
841
842                         if (path_is_absolute(destination)) {
843
844                                 /* An absolute destination. Start the loop from the beginning, but use the root
845                                  * directory as base. */
846
847                                 safe_close(fd);
848                                 fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
849                                 if (fd < 0)
850                                         return -errno;
851
852                                 if (flags & CHASE_SAFE) {
853                                         if (fstat(fd, &st) < 0)
854                                                 return -errno;
855
856                                         if (!safe_transition(&previous_stat, &st))
857                                                 return -EPERM;
858
859                                         previous_stat = st;
860                                 }
861
862                                 free(done);
863
864                                 /* Note that we do not revalidate the root, we take it as is. */
865                                 if (isempty(root))
866                                         done = NULL;
867                                 else {
868                                         done = strdup(root);
869                                         if (!done)
870                                                 return -ENOMEM;
871                                 }
872
873                                 /* Prefix what's left to do with what we just read, and start the loop again, but
874                                  * remain in the current directory. */
875                                 joined = strjoin(destination, todo);
876                         } else
877                                 joined = strjoin("/", destination, todo);
878                         if (!joined)
879                                 return -ENOMEM;
880
881                         free(buffer);
882                         todo = buffer = joined;
883
884                         continue;
885                 }
886
887                 /* If this is not a symlink, then let's just add the name we read to what we already verified. */
888                 if (!done) {
889                         done = first;
890                         first = NULL;
891                 } else {
892                         /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
893                         if (streq(done, "/"))
894                                 *done = '\0';
895
896                         if (!strextend(&done, first, NULL))
897                                 return -ENOMEM;
898                 }
899
900                 /* And iterate again, but go one directory further down. */
901                 safe_close(fd);
902                 fd = child;
903                 child = -1;
904         }
905
906         if (!done) {
907                 /* Special case, turn the empty string into "/", to indicate the root directory. */
908                 done = strdup("/");
909                 if (!done)
910                         return -ENOMEM;
911         }
912
913         if (ret) {
914                 *ret = done;
915                 done = NULL;
916         }
917
918         if (flags & CHASE_OPEN) {
919                 int q;
920
921                 /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
922                  * opening /proc/self/fd/xyz. */
923
924                 assert(fd >= 0);
925                 q = fd;
926                 fd = -1;
927
928                 return q;
929         }
930
931         return exists;
932 }
933
934 int access_fd(int fd, int mode) {
935         char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
936         int r;
937
938         /* Like access() but operates on an already open fd */
939
940         xsprintf(p, "/proc/self/fd/%i", fd);
941
942         r = access(p, mode);
943         if (r < 0)
944                 r = -errno;
945
946         return r;
947 }
948
949 int unlinkat_deallocate(int fd, const char *name, int flags) {
950         _cleanup_close_ int truncate_fd = -1;
951         struct stat st;
952         off_t l, bs;
953
954         /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
955          * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
956          * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
957          * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
958          * returned to the free pool.
959          *
960          * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
961          * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
962          * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
963          * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
964          * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
965          * truncation (đŸ”Ē), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
966          *
967          * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
968          * primary job â€“ to delete the file â€“ is accomplished. */
969
970         if ((flags & AT_REMOVEDIR) == 0) {
971                 truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
972                 if (truncate_fd < 0) {
973
974                         /* If this failed because the file doesn't exist propagate the error right-away. Also,
975                          * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
976                          * returned when this is a directory but we are not supposed to delete those, hence propagate
977                          * the error right-away too. */
978                         if (IN_SET(errno, ENOENT, EISDIR))
979                                 return -errno;
980
981                         if (errno != ELOOP) /* don't complain if this is a symlink */
982                                 log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
983                 }
984         }
985
986         if (unlinkat(fd, name, flags) < 0)
987                 return -errno;
988
989         if (truncate_fd < 0) /* Don't have a file handle, can't do more â˜šī¸ */
990                 return 0;
991
992         if (fstat(truncate_fd, &st) < 0) {
993                 log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring.", name);
994                 return 0;
995         }
996
997         if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
998                 return 0;
999
1000         /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
1001          * punch-hole/truncate this to release the disk space. */
1002
1003         bs = MAX(st.st_blksize, 512);
1004         l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
1005
1006         if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
1007                 return 0; /* Successfully punched a hole! đŸ˜Š */
1008
1009         /* Fall back to truncation */
1010         if (ftruncate(truncate_fd, 0) < 0) {
1011                 log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
1012                 return 0;
1013         }
1014
1015         return 0;
1016 }