LCOV - code coverage report
Current view: top level - basic - fs-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 397 682 58.2 %
Date: 2019-08-22 15:41:25 Functions: 29 41 70.7 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <stddef.h>
       5             : #include <stdio.h>
       6             : #include <stdlib.h>
       7             : #include <string.h>
       8             : #include <sys/stat.h>
       9             : #include <linux/falloc.h>
      10             : #include <linux/magic.h>
      11             : #include <time.h>
      12             : #include <unistd.h>
      13             : 
      14             : #include "alloc-util.h"
      15             : #include "dirent-util.h"
      16             : #include "fd-util.h"
      17             : #include "fs-util.h"
      18             : #include "locale-util.h"
      19             : #include "log.h"
      20             : #include "macro.h"
      21             : #include "missing.h"
      22             : #include "mkdir.h"
      23             : #include "parse-util.h"
      24             : #include "path-util.h"
      25             : #include "process-util.h"
      26             : #include "stat-util.h"
      27             : #include "stdio-util.h"
      28             : #include "string-util.h"
      29             : #include "strv.h"
      30             : #include "time-util.h"
      31             : #include "tmpfile-util.h"
      32             : #include "user-util.h"
      33             : #include "util.h"
      34             : 
      35          55 : int unlink_noerrno(const char *path) {
      36          55 :         PROTECT_ERRNO;
      37             :         int r;
      38             : 
      39          55 :         r = unlink(path);
      40          55 :         if (r < 0)
      41           2 :                 return -errno;
      42             : 
      43          53 :         return 0;
      44             : }
      45             : 
      46          20 : int rmdir_parents(const char *path, const char *stop) {
      47             :         size_t l;
      48          20 :         int r = 0;
      49             : 
      50          20 :         assert(path);
      51          20 :         assert(stop);
      52             : 
      53          20 :         l = strlen(path);
      54             : 
      55             :         /* Skip trailing slashes */
      56          20 :         while (l > 0 && path[l-1] == '/')
      57           0 :                 l--;
      58             : 
      59          25 :         while (l > 0) {
      60             :                 char *t;
      61             : 
      62             :                 /* Skip last component */
      63         490 :                 while (l > 0 && path[l-1] != '/')
      64         465 :                         l--;
      65             : 
      66             :                 /* Skip trailing slashes */
      67          50 :                 while (l > 0 && path[l-1] == '/')
      68          25 :                         l--;
      69             : 
      70          25 :                 if (l <= 0)
      71           0 :                         break;
      72             : 
      73          25 :                 t = strndup(path, l);
      74          25 :                 if (!t)
      75           0 :                         return -ENOMEM;
      76             : 
      77          25 :                 if (path_startswith(stop, t)) {
      78          10 :                         free(t);
      79          10 :                         return 0;
      80             :                 }
      81             : 
      82          15 :                 r = rmdir(t);
      83          15 :                 free(t);
      84             : 
      85          15 :                 if (r < 0)
      86          10 :                         if (errno != ENOENT)
      87          10 :                                 return -errno;
      88             :         }
      89             : 
      90           0 :         return 0;
      91             : }
      92             : 
      93           8 : int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
      94             :         int r;
      95             : 
      96             :         /* Try the ideal approach first */
      97           8 :         if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
      98           8 :                 return 0;
      99             : 
     100             :         /* renameat2() exists since Linux 3.15, btrfs and FAT added support for it later. If it is not implemented,
     101             :          * fall back to a different method. */
     102           0 :         if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY))
     103           0 :                 return -errno;
     104             : 
     105             :         /* Let's try to use linkat()+unlinkat() as fallback. This doesn't work on directories and on some file systems
     106             :          * that do not support hard links (such as FAT, most prominently), but for files it's pretty close to what we
     107             :          * want — though not atomic (i.e. for a short period both the new and the old filename will exist). */
     108           0 :         if (linkat(olddirfd, oldpath, newdirfd, newpath, 0) >= 0) {
     109             : 
     110           0 :                 if (unlinkat(olddirfd, oldpath, 0) < 0) {
     111           0 :                         r = -errno; /* Backup errno before the following unlinkat() alters it */
     112           0 :                         (void) unlinkat(newdirfd, newpath, 0);
     113           0 :                         return r;
     114             :                 }
     115             : 
     116           0 :                 return 0;
     117             :         }
     118             : 
     119           0 :         if (!IN_SET(errno, EINVAL, ENOSYS, ENOTTY, EPERM)) /* FAT returns EPERM on link()â€Ļ */
     120           0 :                 return -errno;
     121             : 
     122             :         /* OK, neither RENAME_NOREPLACE nor linkat()+unlinkat() worked. Let's then fallback to the racy TOCTOU
     123             :          * vulnerable accessat(F_OK) check followed by classic, replacing renameat(), we have nothing better. */
     124             : 
     125           0 :         if (faccessat(newdirfd, newpath, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
     126           0 :                 return -EEXIST;
     127           0 :         if (errno != ENOENT)
     128           0 :                 return -errno;
     129             : 
     130           0 :         if (renameat(olddirfd, oldpath, newdirfd, newpath) < 0)
     131           0 :                 return -errno;
     132             : 
     133           0 :         return 0;
     134             : }
     135             : 
     136      173706 : int readlinkat_malloc(int fd, const char *p, char **ret) {
     137      173706 :         size_t l = FILENAME_MAX+1;
     138             :         int r;
     139             : 
     140      173706 :         assert(p);
     141      173706 :         assert(ret);
     142             : 
     143           0 :         for (;;) {
     144             :                 char *c;
     145             :                 ssize_t n;
     146             : 
     147      173706 :                 c = new(char, l);
     148      173706 :                 if (!c)
     149           0 :                         return -ENOMEM;
     150             : 
     151      173706 :                 n = readlinkat(fd, p, c, l-1);
     152      173706 :                 if (n < 0) {
     153        2489 :                         r = -errno;
     154        2489 :                         free(c);
     155        2489 :                         return r;
     156             :                 }
     157             : 
     158      171217 :                 if ((size_t) n < l-1) {
     159      171217 :                         c[n] = 0;
     160      171217 :                         *ret = c;
     161      171217 :                         return 0;
     162             :                 }
     163             : 
     164           0 :                 free(c);
     165           0 :                 l *= 2;
     166             :         }
     167             : }
     168             : 
     169      168234 : int readlink_malloc(const char *p, char **ret) {
     170      168234 :         return readlinkat_malloc(AT_FDCWD, p, ret);
     171             : }
     172             : 
     173        8982 : int readlink_value(const char *p, char **ret) {
     174        8982 :         _cleanup_free_ char *link = NULL;
     175             :         char *value;
     176             :         int r;
     177             : 
     178        8982 :         r = readlink_malloc(p, &link);
     179        8982 :         if (r < 0)
     180        2210 :                 return r;
     181             : 
     182        6772 :         value = basename(link);
     183        6772 :         if (!value)
     184           0 :                 return -ENOENT;
     185             : 
     186        6772 :         value = strdup(value);
     187        6772 :         if (!value)
     188           0 :                 return -ENOMEM;
     189             : 
     190        6772 :         *ret = value;
     191             : 
     192        6772 :         return 0;
     193             : }
     194             : 
     195          11 : int readlink_and_make_absolute(const char *p, char **r) {
     196          11 :         _cleanup_free_ char *target = NULL;
     197             :         char *k;
     198             :         int j;
     199             : 
     200          11 :         assert(p);
     201          11 :         assert(r);
     202             : 
     203          11 :         j = readlink_malloc(p, &target);
     204          11 :         if (j < 0)
     205           0 :                 return j;
     206             : 
     207          11 :         k = file_in_same_dir(p, target);
     208          11 :         if (!k)
     209           0 :                 return -ENOMEM;
     210             : 
     211          11 :         *r = k;
     212          11 :         return 0;
     213             : }
     214             : 
     215           1 : int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
     216           1 :         _cleanup_close_ int fd = -1;
     217             : 
     218           1 :         assert(path);
     219             : 
     220           1 :         fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change
     221             :                                                        * mode/owner on the same file */
     222           1 :         if (fd < 0)
     223           0 :                 return -errno;
     224             : 
     225           1 :         return fchmod_and_chown(fd, mode, uid, gid);
     226             : }
     227             : 
     228       33139 : int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
     229             :         bool do_chown, do_chmod;
     230             :         struct stat st;
     231             : 
     232             :         /* Change ownership and access mode of the specified fd. Tries to do so safely, ensuring that at no
     233             :          * point in time the access mode is above the old access mode under the old ownership or the new
     234             :          * access mode under the new ownership. Note: this call tries hard to leave the access mode
     235             :          * unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
     236             :          * on chown().
     237             :          *
     238             :          * This call is happy with O_PATH fds. */
     239             : 
     240       33139 :         if (fstat(fd, &st) < 0)
     241           0 :                 return -errno;
     242             : 
     243       33139 :         do_chown =
     244       33145 :                 (uid != UID_INVALID && st.st_uid != uid) ||
     245           6 :                 (gid != GID_INVALID && st.st_gid != gid);
     246             : 
     247       33139 :         do_chmod =
     248       66277 :                 !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
     249       33138 :                 ((mode != MODE_INVALID && ((st.st_mode ^ mode) & 07777) != 0) ||
     250             :                  do_chown); /* If we change ownership, make sure we reset the mode afterwards, since chown()
     251             :                              * modifies the access mode too */
     252             : 
     253       33139 :         if (mode == MODE_INVALID)
     254       33133 :                 mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
     255           6 :         else if ((mode & S_IFMT) != 0 && ((mode ^ st.st_mode) & S_IFMT) != 0)
     256           0 :                 return -EINVAL; /* insist on the right file type if it was specified */
     257             : 
     258       33139 :         if (do_chown && do_chmod) {
     259           0 :                 mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
     260             : 
     261           0 :                 if (((minimal ^ st.st_mode) & 07777) != 0)
     262           0 :                         if (fchmod_opath(fd, minimal & 07777) < 0)
     263           0 :                                 return -errno;
     264             :         }
     265             : 
     266       33139 :         if (do_chown)
     267           0 :                 if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
     268           0 :                         return -errno;
     269             : 
     270       33139 :         if (do_chmod)
     271           3 :                 if (fchmod_opath(fd, mode & 07777) < 0)
     272           0 :                         return -errno;
     273             : 
     274       33139 :         return do_chown || do_chmod;
     275             : }
     276             : 
     277           2 : int fchmod_umask(int fd, mode_t m) {
     278             :         mode_t u;
     279             :         int r;
     280             : 
     281           2 :         u = umask(0777);
     282           2 :         r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
     283           2 :         umask(u);
     284             : 
     285           2 :         return r;
     286             : }
     287             : 
     288           3 : int fchmod_opath(int fd, mode_t m) {
     289             :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
     290             : 
     291             :         /* This function operates also on fd that might have been opened with
     292             :          * O_PATH. Indeed fchmodat() doesn't have the AT_EMPTY_PATH flag like
     293             :          * fchownat() does. */
     294             : 
     295           3 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
     296           3 :         if (chmod(procfs_path, m) < 0)
     297           0 :                 return -errno;
     298             : 
     299           3 :         return 0;
     300             : }
     301             : 
     302         849 : int fd_warn_permissions(const char *path, int fd) {
     303             :         struct stat st;
     304             : 
     305         849 :         if (fstat(fd, &st) < 0)
     306           0 :                 return -errno;
     307             : 
     308             :         /* Don't complain if we are reading something that is not a file, for example /dev/null */
     309         849 :         if (!S_ISREG(st.st_mode))
     310           0 :                 return 0;
     311             : 
     312         849 :         if (st.st_mode & 0111)
     313           0 :                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
     314             : 
     315         849 :         if (st.st_mode & 0002)
     316           0 :                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
     317             : 
     318         849 :         if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
     319           0 :                 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);
     320             : 
     321         849 :         return 0;
     322             : }
     323             : 
     324       33138 : int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
     325             :         char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
     326       33138 :         _cleanup_close_ int fd = -1;
     327       33138 :         int r, ret = 0;
     328             : 
     329       33138 :         assert(path);
     330             : 
     331             :         /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
     332             :          * itself which is updated, not its target
     333             :          *
     334             :          * Returns the first error we encounter, but tries to apply as much as possible. */
     335             : 
     336       33138 :         if (parents)
     337           0 :                 (void) mkdir_parents(path, 0755);
     338             : 
     339             :         /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
     340             :          * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
     341             :          * won't trigger any driver magic or so. */
     342       33138 :         fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
     343       33138 :         if (fd < 0) {
     344       33120 :                 if (errno != ENOENT)
     345           0 :                         return -errno;
     346             : 
     347             :                 /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
     348             :                  * here, and nothing else */
     349       33120 :                 fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
     350       33120 :                 if (fd < 0)
     351           0 :                         return -errno;
     352             :         }
     353             : 
     354             :         /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
     355             :          * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
     356             :          * something fchown(), fchmod(), futimensat() don't allow. */
     357       33138 :         xsprintf(fdpath, "/proc/self/fd/%i", fd);
     358             : 
     359       33138 :         ret = fchmod_and_chown(fd, mode, uid, gid);
     360             : 
     361       33138 :         if (stamp != USEC_INFINITY) {
     362             :                 struct timespec ts[2];
     363             : 
     364           5 :                 timespec_store(&ts[0], stamp);
     365           5 :                 ts[1] = ts[0];
     366           5 :                 r = utimensat(AT_FDCWD, fdpath, ts, 0);
     367             :         } else
     368       33133 :                 r = utimensat(AT_FDCWD, fdpath, NULL, 0);
     369       33138 :         if (r < 0 && ret >= 0)
     370           0 :                 return -errno;
     371             : 
     372       33138 :         return ret;
     373             : }
     374             : 
     375       33133 : int touch(const char *path) {
     376       33133 :         return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
     377             : }
     378             : 
     379           0 : int symlink_idempotent(const char *from, const char *to, bool make_relative) {
     380           0 :         _cleanup_free_ char *relpath = NULL;
     381             :         int r;
     382             : 
     383           0 :         assert(from);
     384           0 :         assert(to);
     385             : 
     386           0 :         if (make_relative) {
     387           0 :                 _cleanup_free_ char *parent = NULL;
     388             : 
     389           0 :                 parent = dirname_malloc(to);
     390           0 :                 if (!parent)
     391           0 :                         return -ENOMEM;
     392             : 
     393           0 :                 r = path_make_relative(parent, from, &relpath);
     394           0 :                 if (r < 0)
     395           0 :                         return r;
     396             : 
     397           0 :                 from = relpath;
     398             :         }
     399             : 
     400           0 :         if (symlink(from, to) < 0) {
     401           0 :                 _cleanup_free_ char *p = NULL;
     402             : 
     403           0 :                 if (errno != EEXIST)
     404           0 :                         return -errno;
     405             : 
     406           0 :                 r = readlink_malloc(to, &p);
     407           0 :                 if (r == -EINVAL) /* Not a symlink? In that case return the original error we encountered: -EEXIST */
     408           0 :                         return -EEXIST;
     409           0 :                 if (r < 0) /* Any other error? In that case propagate it as is */
     410           0 :                         return r;
     411             : 
     412           0 :                 if (!streq(p, from)) /* Not the symlink we want it to be? In that case, propagate the original -EEXIST */
     413           0 :                         return -EEXIST;
     414             :         }
     415             : 
     416           0 :         return 0;
     417             : }
     418             : 
     419           0 : int symlink_atomic(const char *from, const char *to) {
     420           0 :         _cleanup_free_ char *t = NULL;
     421             :         int r;
     422             : 
     423           0 :         assert(from);
     424           0 :         assert(to);
     425             : 
     426           0 :         r = tempfn_random(to, NULL, &t);
     427           0 :         if (r < 0)
     428           0 :                 return r;
     429             : 
     430           0 :         if (symlink(from, t) < 0)
     431           0 :                 return -errno;
     432             : 
     433           0 :         if (rename(t, to) < 0) {
     434           0 :                 unlink_noerrno(t);
     435           0 :                 return -errno;
     436             :         }
     437             : 
     438           0 :         return 0;
     439             : }
     440             : 
     441           0 : int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
     442           0 :         _cleanup_free_ char *t = NULL;
     443             :         int r;
     444             : 
     445           0 :         assert(path);
     446             : 
     447           0 :         r = tempfn_random(path, NULL, &t);
     448           0 :         if (r < 0)
     449           0 :                 return r;
     450             : 
     451           0 :         if (mknod(t, mode, dev) < 0)
     452           0 :                 return -errno;
     453             : 
     454           0 :         if (rename(t, path) < 0) {
     455           0 :                 unlink_noerrno(t);
     456           0 :                 return -errno;
     457             :         }
     458             : 
     459           0 :         return 0;
     460             : }
     461             : 
     462           0 : int mkfifo_atomic(const char *path, mode_t mode) {
     463           0 :         _cleanup_free_ char *t = NULL;
     464             :         int r;
     465             : 
     466           0 :         assert(path);
     467             : 
     468           0 :         r = tempfn_random(path, NULL, &t);
     469           0 :         if (r < 0)
     470           0 :                 return r;
     471             : 
     472           0 :         if (mkfifo(t, mode) < 0)
     473           0 :                 return -errno;
     474             : 
     475           0 :         if (rename(t, path) < 0) {
     476           0 :                 unlink_noerrno(t);
     477           0 :                 return -errno;
     478             :         }
     479             : 
     480           0 :         return 0;
     481             : }
     482             : 
     483           0 : int mkfifoat_atomic(int dirfd, const char *path, mode_t mode) {
     484           0 :         _cleanup_free_ char *t = NULL;
     485             :         int r;
     486             : 
     487           0 :         assert(path);
     488             : 
     489           0 :         if (path_is_absolute(path))
     490           0 :                 return mkfifo_atomic(path, mode);
     491             : 
     492             :         /* We're only interested in the (random) filename.  */
     493           0 :         r = tempfn_random_child("", NULL, &t);
     494           0 :         if (r < 0)
     495           0 :                 return r;
     496             : 
     497           0 :         if (mkfifoat(dirfd, t, mode) < 0)
     498           0 :                 return -errno;
     499             : 
     500           0 :         if (renameat(dirfd, t, dirfd, path) < 0) {
     501           0 :                 unlink_noerrno(t);
     502           0 :                 return -errno;
     503             :         }
     504             : 
     505           0 :         return 0;
     506             : }
     507             : 
     508          11 : int get_files_in_directory(const char *path, char ***list) {
     509          11 :         _cleanup_closedir_ DIR *d = NULL;
     510             :         struct dirent *de;
     511          11 :         size_t bufsize = 0, n = 0;
     512          11 :         _cleanup_strv_free_ char **l = NULL;
     513             : 
     514          11 :         assert(path);
     515             : 
     516             :         /* Returns all files in a directory in *list, and the number
     517             :          * of files as return value. If list is NULL returns only the
     518             :          * number. */
     519             : 
     520          11 :         d = opendir(path);
     521          11 :         if (!d)
     522           1 :                 return -errno;
     523             : 
     524        1625 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
     525        1615 :                 dirent_ensure_type(d, de);
     526             : 
     527        1615 :                 if (!dirent_is_file(de))
     528         811 :                         continue;
     529             : 
     530         804 :                 if (list) {
     531             :                         /* one extra slot is needed for the terminating NULL */
     532         429 :                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
     533           0 :                                 return -ENOMEM;
     534             : 
     535         429 :                         l[n] = strdup(de->d_name);
     536         429 :                         if (!l[n])
     537           0 :                                 return -ENOMEM;
     538             : 
     539         429 :                         l[++n] = NULL;
     540             :                 } else
     541         375 :                         n++;
     542             :         }
     543             : 
     544          10 :         if (list)
     545           7 :                 *list = TAKE_PTR(l);
     546             : 
     547          10 :         return n;
     548             : }
     549             : 
     550          12 : static int getenv_tmp_dir(const char **ret_path) {
     551             :         const char *n;
     552          12 :         int r, ret = 0;
     553             : 
     554          12 :         assert(ret_path);
     555             : 
     556             :         /* We use the same order of environment variables python uses in tempfile.gettempdir():
     557             :          * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
     558          45 :         FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
     559             :                 const char *e;
     560             : 
     561          34 :                 e = secure_getenv(n);
     562          34 :                 if (!e)
     563          29 :                         continue;
     564           5 :                 if (!path_is_absolute(e)) {
     565           0 :                         r = -ENOTDIR;
     566           0 :                         goto next;
     567             :                 }
     568           5 :                 if (!path_is_normalized(e)) {
     569           0 :                         r = -EPERM;
     570           0 :                         goto next;
     571             :                 }
     572             : 
     573           5 :                 r = is_dir(e, true);
     574           5 :                 if (r < 0)
     575           4 :                         goto next;
     576           1 :                 if (r == 0) {
     577           0 :                         r = -ENOTDIR;
     578           0 :                         goto next;
     579             :                 }
     580             : 
     581           1 :                 *ret_path = e;
     582           1 :                 return 1;
     583             : 
     584           4 :         next:
     585             :                 /* Remember first error, to make this more debuggable */
     586           4 :                 if (ret >= 0)
     587           4 :                         ret = r;
     588             :         }
     589             : 
     590          11 :         if (ret < 0)
     591           4 :                 return ret;
     592             : 
     593           7 :         *ret_path = NULL;
     594           7 :         return ret;
     595             : }
     596             : 
     597          12 : static int tmp_dir_internal(const char *def, const char **ret) {
     598             :         const char *e;
     599             :         int r, k;
     600             : 
     601          12 :         assert(def);
     602          12 :         assert(ret);
     603             : 
     604          12 :         r = getenv_tmp_dir(&e);
     605          12 :         if (r > 0) {
     606           1 :                 *ret = e;
     607           1 :                 return 0;
     608             :         }
     609             : 
     610          11 :         k = is_dir(def, true);
     611          11 :         if (k == 0)
     612           0 :                 k = -ENOTDIR;
     613          11 :         if (k < 0)
     614           0 :                 return r < 0 ? r : k;
     615             : 
     616          11 :         *ret = def;
     617          11 :         return 0;
     618             : }
     619             : 
     620           5 : int var_tmp_dir(const char **ret) {
     621             : 
     622             :         /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
     623             :          * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
     624             :          * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
     625             :          * making it a variable that overrides all temporary file storage locations. */
     626             : 
     627           5 :         return tmp_dir_internal("/var/tmp", ret);
     628             : }
     629             : 
     630           7 : int tmp_dir(const char **ret) {
     631             : 
     632             :         /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
     633             :          * backed by an in-memory file system: /tmp. */
     634             : 
     635           7 :         return tmp_dir_internal("/tmp", ret);
     636             : }
     637             : 
     638           0 : int unlink_or_warn(const char *filename) {
     639           0 :         if (unlink(filename) < 0 && errno != ENOENT)
     640             :                 /* If the file doesn't exist and the fs simply was read-only (in which
     641             :                  * case unlink() returns EROFS even if the file doesn't exist), don't
     642             :                  * complain */
     643           0 :                 if (errno != EROFS || access(filename, F_OK) >= 0)
     644           0 :                         return log_error_errno(errno, "Failed to remove \"%s\": %m", filename);
     645             : 
     646           0 :         return 0;
     647             : }
     648             : 
     649          10 : int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
     650             :         char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
     651             :         int r;
     652             : 
     653             :         /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */
     654          10 :         xsprintf(path, "/proc/self/fd/%i", what);
     655             : 
     656          10 :         r = inotify_add_watch(fd, path, mask);
     657          10 :         if (r < 0)
     658           0 :                 return -errno;
     659             : 
     660          10 :         return r;
     661             : }
     662             : 
     663          54 : static bool unsafe_transition(const struct stat *a, const struct stat *b) {
     664             :         /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
     665             :          * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
     666             :          * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
     667             : 
     668          54 :         if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
     669          54 :                 return false;
     670             : 
     671           0 :         return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
     672             : }
     673             : 
     674           0 : static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
     675           0 :         _cleanup_free_ char *n1 = NULL, *n2 = NULL;
     676             : 
     677           0 :         if (!FLAGS_SET(flags, CHASE_WARN))
     678           0 :                 return -ENOLINK;
     679             : 
     680           0 :         (void) fd_get_path(a, &n1);
     681           0 :         (void) fd_get_path(b, &n2);
     682             : 
     683           0 :         return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
     684             :                                  "Detected unsafe path transition %s %s %s during canonicalization of %s.",
     685             :                                  n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
     686             : }
     687             : 
     688           0 : static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
     689           0 :         _cleanup_free_ char *n1 = NULL;
     690             : 
     691           0 :         if (!FLAGS_SET(flags, CHASE_WARN))
     692           0 :                 return -EREMOTE;
     693             : 
     694           0 :         (void) fd_get_path(fd, &n1);
     695             : 
     696           0 :         return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
     697             :                                  "Detected autofs mount point %s during canonicalization of %s.",
     698             :                                  n1, path);
     699             : }
     700             : 
     701       20289 : int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
     702       20289 :         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
     703       20289 :         _cleanup_close_ int fd = -1;
     704       20289 :         unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
     705             :         struct stat previous_stat;
     706       20289 :         bool exists = true;
     707             :         char *todo;
     708             :         int r;
     709             : 
     710       20289 :         assert(path);
     711             : 
     712             :         /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
     713       20289 :         if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN))
     714           0 :                 return -EINVAL;
     715             : 
     716       20289 :         if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN))
     717           0 :                 return -EINVAL;
     718             : 
     719       20289 :         if (isempty(path))
     720           0 :                 return -EINVAL;
     721             : 
     722             :         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
     723             :          * symlinks relative to a root directory, instead of the root of the host.
     724             :          *
     725             :          * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
     726             :          * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
     727             :          * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
     728             :          * prefixed accordingly.
     729             :          *
     730             :          * Algorithmically this operates on two path buffers: "done" are the components of the path we already
     731             :          * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
     732             :          * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
     733             :          * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
     734             :          * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
     735             :          * to a minimum.
     736             :          *
     737             :          * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
     738             :          * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
     739             :          * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
     740             :          * specified path.
     741             :          *
     742             :          * There are three ways to invoke this function:
     743             :          *
     744             :          * 1. Without CHASE_STEP or CHASE_OPEN: in this case the path is resolved and the normalized path is returned
     745             :          *    in `ret`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0 is returned if the file
     746             :          *    doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is returned if the destination was
     747             :          *    found, -ENOENT if it wasn't.
     748             :          *
     749             :          * 2. With CHASE_OPEN: in this case the destination is opened after chasing it as O_PATH and this file
     750             :          *    descriptor is returned as return value. This is useful to open files relative to some root
     751             :          *    directory. Note that the returned O_PATH file descriptors must be converted into a regular one (using
     752             :          *    fd_reopen() or such) before it can be used for reading/writing. CHASE_OPEN may not be combined with
     753             :          *    CHASE_NONEXISTENT.
     754             :          *
     755             :          * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only the first
     756             :          *    symlink or ".." component of the path is resolved, and the resulting path is returned. This is useful if
     757             :          *    a caller wants to trace the a path through the file system verbosely. Returns < 0 on error, > 0 if the
     758             :          *    path is fully normalized, and == 0 for each normalization step. This may be combined with
     759             :          *    CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
     760             :          *
     761             :          * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
     762             :          *    unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
     763             :          *    CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
     764             :          *
     765             :          * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
     766             :          *    is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
     767             :          *    the mount point is emitted.
     768             :          *
     769             :          */
     770             : 
     771             :         /* A root directory of "/" or "" is identical to none */
     772       20289 :         if (empty_or_root(original_root))
     773       20212 :                 original_root = NULL;
     774             : 
     775       20289 :         if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN|CHASE_STEP)) == CHASE_OPEN) {
     776             :                 /* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
     777             :                  * and doesn't care about any of the other special features we provide either. */
     778           1 :                 r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
     779           1 :                 if (r < 0)
     780           0 :                         return -errno;
     781             : 
     782           1 :                 return r;
     783             :         }
     784             : 
     785       20288 :         if (original_root) {
     786          77 :                 r = path_make_absolute_cwd(original_root, &root);
     787          77 :                 if (r < 0)
     788           0 :                         return r;
     789             : 
     790          77 :                 if (flags & CHASE_PREFIX_ROOT) {
     791             : 
     792             :                         /* We don't support relative paths in combination with a root directory */
     793           2 :                         if (!path_is_absolute(path))
     794           0 :                                 return -EINVAL;
     795             : 
     796           2 :                         path = prefix_roota(root, path);
     797             :                 }
     798             :         }
     799             : 
     800       20288 :         r = path_make_absolute_cwd(path, &buffer);
     801       20288 :         if (r < 0)
     802           0 :                 return r;
     803             : 
     804       20288 :         fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     805       20288 :         if (fd < 0)
     806           0 :                 return -errno;
     807             : 
     808       20288 :         if (flags & CHASE_SAFE) {
     809          27 :                 if (fstat(fd, &previous_stat) < 0)
     810           0 :                         return -errno;
     811             :         }
     812             : 
     813       20288 :         todo = buffer;
     814      117559 :         for (;;) {
     815      137847 :                 _cleanup_free_ char *first = NULL;
     816      137847 :                 _cleanup_close_ int child = -1;
     817             :                 struct stat st;
     818             :                 size_t n, m;
     819             : 
     820             :                 /* Determine length of first component in the path */
     821      137847 :                 n = strspn(todo, "/");                  /* The slashes */
     822      137847 :                 m = n + strcspn(todo + n, "/");         /* The entire length of the component */
     823             : 
     824             :                 /* Extract the first component. */
     825      137847 :                 first = strndup(todo, m);
     826      137847 :                 if (!first)
     827           0 :                         return -ENOMEM;
     828             : 
     829      137847 :                 todo += m;
     830             : 
     831             :                 /* Empty? Then we reached the end. */
     832      137847 :                 if (isempty(first))
     833        8291 :                         break;
     834             : 
     835             :                 /* Just a single slash? Then we reached the end. */
     836      129556 :                 if (path_equal(first, "/")) {
     837             :                         /* Preserve the trailing slash */
     838             : 
     839          11 :                         if (flags & CHASE_TRAIL_SLASH)
     840           3 :                                 if (!strextend(&done, "/", NULL))
     841           0 :                                         return -ENOMEM;
     842             : 
     843          11 :                         break;
     844             :                 }
     845             : 
     846             :                 /* Just a dot? Then let's eat this up. */
     847      129545 :                 if (path_equal(first, "/."))
     848          18 :                         continue;
     849             : 
     850             :                 /* Two dots? Then chop off the last bit of what we already found out. */
     851      129527 :                 if (path_equal(first, "/..")) {
     852       12922 :                         _cleanup_free_ char *parent = NULL;
     853       12922 :                         _cleanup_close_ int fd_parent = -1;
     854             : 
     855             :                         /* If we already are at the top, then going up will not change anything. This is in-line with
     856             :                          * how the kernel handles this. */
     857       12922 :                         if (empty_or_root(done))
     858          16 :                                 continue;
     859             : 
     860       12906 :                         parent = dirname_malloc(done);
     861       12906 :                         if (!parent)
     862           0 :                                 return -ENOMEM;
     863             : 
     864             :                         /* Don't allow this to leave the root dir.  */
     865       12906 :                         if (root &&
     866          17 :                             path_startswith(done, root) &&
     867          17 :                             !path_startswith(parent, root))
     868          10 :                                 continue;
     869             : 
     870       12896 :                         free_and_replace(done, parent);
     871             : 
     872       12896 :                         if (flags & CHASE_STEP)
     873           1 :                                 goto chased_one;
     874             : 
     875       12895 :                         fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     876       12895 :                         if (fd_parent < 0)
     877           0 :                                 return -errno;
     878             : 
     879       12895 :                         if (flags & CHASE_SAFE) {
     880           0 :                                 if (fstat(fd_parent, &st) < 0)
     881           0 :                                         return -errno;
     882             : 
     883           0 :                                 if (unsafe_transition(&previous_stat, &st))
     884           0 :                                         return log_unsafe_transition(fd, fd_parent, path, flags);
     885             : 
     886           0 :                                 previous_stat = st;
     887             :                         }
     888             : 
     889       12895 :                         safe_close(fd);
     890       12895 :                         fd = TAKE_FD(fd_parent);
     891             : 
     892       12895 :                         continue;
     893             :                 }
     894             : 
     895             :                 /* Otherwise let's see what this is. */
     896      116605 :                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
     897      116605 :                 if (child < 0) {
     898             : 
     899       11979 :                         if (errno == ENOENT &&
     900       11985 :                             (flags & CHASE_NONEXISTENT) &&
     901          10 :                             (isempty(todo) || path_is_normalized(todo))) {
     902             : 
     903             :                                 /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
     904             :                                  * what we got so far. But don't allow this if the remaining path contains "../ or "./"
     905             :                                  * or something else weird. */
     906             : 
     907             :                                 /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
     908           6 :                                 if (streq_ptr(done, "/"))
     909           0 :                                         *done = '\0';
     910             : 
     911           6 :                                 if (!strextend(&done, first, todo, NULL))
     912           0 :                                         return -ENOMEM;
     913             : 
     914           6 :                                 exists = false;
     915           6 :                                 break;
     916             :                         }
     917             : 
     918       11973 :                         return -errno;
     919             :                 }
     920             : 
     921      104626 :                 if (fstat(child, &st) < 0)
     922           0 :                         return -errno;
     923      104680 :                 if ((flags & CHASE_SAFE) &&
     924         108 :                     (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) &&
     925          54 :                     unsafe_transition(&previous_stat, &st))
     926           0 :                         return log_unsafe_transition(fd, child, path, flags);
     927             : 
     928      104626 :                 previous_stat = st;
     929             : 
     930      104680 :                 if ((flags & CHASE_NO_AUTOFS) &&
     931          54 :                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
     932           0 :                         return log_autofs_mount_point(child, path, flags);
     933             : 
     934      104626 :                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
     935             :                         char *joined;
     936             : 
     937        5339 :                         _cleanup_free_ char *destination = NULL;
     938             : 
     939             :                         /* This is a symlink, in this case read the destination. But let's make sure we don't follow
     940             :                          * symlinks without bounds. */
     941        5339 :                         if (--max_follow <= 0)
     942           1 :                                 return -ELOOP;
     943             : 
     944        5338 :                         r = readlinkat_malloc(fd, first + n, &destination);
     945        5338 :                         if (r < 0)
     946           0 :                                 return r;
     947        5338 :                         if (isempty(destination))
     948           0 :                                 return -EINVAL;
     949             : 
     950        5338 :                         if (path_is_absolute(destination)) {
     951             : 
     952             :                                 /* An absolute destination. Start the loop from the beginning, but use the root
     953             :                                  * directory as base. */
     954             : 
     955          18 :                                 safe_close(fd);
     956          18 :                                 fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     957          18 :                                 if (fd < 0)
     958           0 :                                         return -errno;
     959             : 
     960          18 :                                 if (flags & CHASE_SAFE) {
     961           0 :                                         if (fstat(fd, &st) < 0)
     962           0 :                                                 return -errno;
     963             : 
     964           0 :                                         if (unsafe_transition(&previous_stat, &st))
     965           0 :                                                 return log_unsafe_transition(child, fd, path, flags);
     966             : 
     967           0 :                                         previous_stat = st;
     968             :                                 }
     969             : 
     970          18 :                                 free(done);
     971             : 
     972             :                                 /* Note that we do not revalidate the root, we take it as is. */
     973          18 :                                 if (isempty(root))
     974           8 :                                         done = NULL;
     975             :                                 else {
     976          10 :                                         done = strdup(root);
     977          10 :                                         if (!done)
     978           0 :                                                 return -ENOMEM;
     979             :                                 }
     980             : 
     981             :                                 /* Prefix what's left to do with what we just read, and start the loop again, but
     982             :                                  * remain in the current directory. */
     983          18 :                                 joined = path_join(destination, todo);
     984             :                         } else
     985        5320 :                                 joined = path_join("/", destination, todo);
     986        5338 :                         if (!joined)
     987           0 :                                 return -ENOMEM;
     988             : 
     989        5338 :                         free(buffer);
     990        5338 :                         todo = buffer = joined;
     991             : 
     992        5338 :                         if (flags & CHASE_STEP)
     993           5 :                                 goto chased_one;
     994             : 
     995        5333 :                         continue;
     996             :                 }
     997             : 
     998             :                 /* If this is not a symlink, then let's just add the name we read to what we already verified. */
     999       99287 :                 if (!done)
    1000       20292 :                         done = TAKE_PTR(first);
    1001             :                 else {
    1002             :                         /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
    1003       78995 :                         if (streq(done, "/"))
    1004           3 :                                 *done = '\0';
    1005             : 
    1006       78995 :                         if (!strextend(&done, first, NULL))
    1007           0 :                                 return -ENOMEM;
    1008             :                 }
    1009             : 
    1010             :                 /* And iterate again, but go one directory further down. */
    1011       99287 :                 safe_close(fd);
    1012       99287 :                 fd = TAKE_FD(child);
    1013             :         }
    1014             : 
    1015        8308 :         if (!done) {
    1016             :                 /* Special case, turn the empty string into "/", to indicate the root directory. */
    1017           1 :                 done = strdup("/");
    1018           1 :                 if (!done)
    1019           0 :                         return -ENOMEM;
    1020             :         }
    1021             : 
    1022        8308 :         if (ret)
    1023        8256 :                 *ret = TAKE_PTR(done);
    1024             : 
    1025        8308 :         if (flags & CHASE_OPEN) {
    1026             :                 /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
    1027             :                  * opening /proc/self/fd/xyz. */
    1028             : 
    1029          53 :                 assert(fd >= 0);
    1030          53 :                 return TAKE_FD(fd);
    1031             :         }
    1032             : 
    1033        8255 :         if (flags & CHASE_STEP)
    1034           1 :                 return 1;
    1035             : 
    1036        8254 :         return exists;
    1037             : 
    1038           6 : chased_one:
    1039           6 :         if (ret) {
    1040             :                 char *c;
    1041             : 
    1042           6 :                 c = strjoin(strempty(done), todo);
    1043           6 :                 if (!c)
    1044           0 :                         return -ENOMEM;
    1045             : 
    1046           6 :                 *ret = c;
    1047             :         }
    1048             : 
    1049           6 :         return 0;
    1050             : }
    1051             : 
    1052          30 : int chase_symlinks_and_open(
    1053             :                 const char *path,
    1054             :                 const char *root,
    1055             :                 unsigned chase_flags,
    1056             :                 int open_flags,
    1057             :                 char **ret_path) {
    1058             : 
    1059          30 :         _cleanup_close_ int path_fd = -1;
    1060          30 :         _cleanup_free_ char *p = NULL;
    1061             :         int r;
    1062             : 
    1063          30 :         if (chase_flags & CHASE_NONEXISTENT)
    1064           0 :                 return -EINVAL;
    1065             : 
    1066          30 :         if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
    1067             :                 /* Shortcut this call if none of the special features of this call are requested */
    1068           6 :                 r = open(path, open_flags);
    1069           6 :                 if (r < 0)
    1070           0 :                         return -errno;
    1071             : 
    1072           6 :                 return r;
    1073             :         }
    1074             : 
    1075          24 :         path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
    1076          24 :         if (path_fd < 0)
    1077           0 :                 return path_fd;
    1078             : 
    1079          24 :         r = fd_reopen(path_fd, open_flags);
    1080          24 :         if (r < 0)
    1081           0 :                 return r;
    1082             : 
    1083          24 :         if (ret_path)
    1084           0 :                 *ret_path = TAKE_PTR(p);
    1085             : 
    1086          24 :         return r;
    1087             : }
    1088             : 
    1089           0 : int chase_symlinks_and_opendir(
    1090             :                 const char *path,
    1091             :                 const char *root,
    1092             :                 unsigned chase_flags,
    1093             :                 char **ret_path,
    1094             :                 DIR **ret_dir) {
    1095             : 
    1096             :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
    1097           0 :         _cleanup_close_ int path_fd = -1;
    1098           0 :         _cleanup_free_ char *p = NULL;
    1099             :         DIR *d;
    1100             : 
    1101           0 :         if (!ret_dir)
    1102           0 :                 return -EINVAL;
    1103           0 :         if (chase_flags & CHASE_NONEXISTENT)
    1104           0 :                 return -EINVAL;
    1105             : 
    1106           0 :         if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
    1107             :                 /* Shortcut this call if none of the special features of this call are requested */
    1108           0 :                 d = opendir(path);
    1109           0 :                 if (!d)
    1110           0 :                         return -errno;
    1111             : 
    1112           0 :                 *ret_dir = d;
    1113           0 :                 return 0;
    1114             :         }
    1115             : 
    1116           0 :         path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
    1117           0 :         if (path_fd < 0)
    1118           0 :                 return path_fd;
    1119             : 
    1120           0 :         xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
    1121           0 :         d = opendir(procfs_path);
    1122           0 :         if (!d)
    1123           0 :                 return -errno;
    1124             : 
    1125           0 :         if (ret_path)
    1126           0 :                 *ret_path = TAKE_PTR(p);
    1127             : 
    1128           0 :         *ret_dir = d;
    1129           0 :         return 0;
    1130             : }
    1131             : 
    1132          71 : int chase_symlinks_and_stat(
    1133             :                 const char *path,
    1134             :                 const char *root,
    1135             :                 unsigned chase_flags,
    1136             :                 char **ret_path,
    1137             :                 struct stat *ret_stat) {
    1138             : 
    1139          71 :         _cleanup_close_ int path_fd = -1;
    1140          71 :         _cleanup_free_ char *p = NULL;
    1141             : 
    1142          71 :         assert(path);
    1143          71 :         assert(ret_stat);
    1144             : 
    1145          71 :         if (chase_flags & CHASE_NONEXISTENT)
    1146           0 :                 return -EINVAL;
    1147             : 
    1148          71 :         if (empty_or_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
    1149             :                 /* Shortcut this call if none of the special features of this call are requested */
    1150          71 :                 if (stat(path, ret_stat) < 0)
    1151          47 :                         return -errno;
    1152             : 
    1153          24 :                 return 1;
    1154             :         }
    1155             : 
    1156           0 :         path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
    1157           0 :         if (path_fd < 0)
    1158           0 :                 return path_fd;
    1159             : 
    1160           0 :         if (fstat(path_fd, ret_stat) < 0)
    1161           0 :                 return -errno;
    1162             : 
    1163           0 :         if (ret_path)
    1164           0 :                 *ret_path = TAKE_PTR(p);
    1165             : 
    1166           0 :         if (chase_flags & CHASE_OPEN)
    1167           0 :                 return TAKE_FD(path_fd);
    1168             : 
    1169           0 :         return 1;
    1170             : }
    1171             : 
    1172           6 : int access_fd(int fd, int mode) {
    1173             :         char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
    1174             :         int r;
    1175             : 
    1176             :         /* Like access() but operates on an already open fd */
    1177             : 
    1178           6 :         xsprintf(p, "/proc/self/fd/%i", fd);
    1179           6 :         r = access(p, mode);
    1180           6 :         if (r < 0)
    1181           2 :                 return -errno;
    1182             : 
    1183           4 :         return r;
    1184             : }
    1185             : 
    1186          53 : void unlink_tempfilep(char (*p)[]) {
    1187             :         /* If the file is created with mkstemp(), it will (almost always)
    1188             :          * change the suffix. Treat this as a sign that the file was
    1189             :          * successfully created. We ignore both the rare case where the
    1190             :          * original suffix is used and unlink failures. */
    1191          53 :         if (!endswith(*p, ".XXXXXX"))
    1192          53 :                 (void) unlink_noerrno(*p);
    1193          53 : }
    1194             : 
    1195           3 : int unlinkat_deallocate(int fd, const char *name, int flags) {
    1196           3 :         _cleanup_close_ int truncate_fd = -1;
    1197             :         struct stat st;
    1198             :         off_t l, bs;
    1199             : 
    1200             :         /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
    1201             :          * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
    1202             :          * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
    1203             :          * jobs ("vacuuming"), where we want to make sure the data is really gone and the disk space released and
    1204             :          * returned to the free pool.
    1205             :          *
    1206             :          * Deallocation is preferably done by FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE (👊) if supported, which means
    1207             :          * the file won't change size. That's a good thing since we shouldn't needlessly trigger SIGBUS in other
    1208             :          * programs that have mmap()ed the file. (The assumption here is that changing file contents to all zeroes
    1209             :          * underneath those programs is the better choice than simply triggering SIGBUS in them which truncation does.)
    1210             :          * However if hole punching is not implemented in the kernel or file system we'll fall back to normal file
    1211             :          * truncation (đŸ”Ē), as our goal of deallocating the data space trumps our goal of being nice to readers (💐).
    1212             :          *
    1213             :          * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
    1214             :          * primary job – to delete the file – is accomplished. */
    1215             : 
    1216           3 :         if ((flags & AT_REMOVEDIR) == 0) {
    1217           3 :                 truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
    1218           3 :                 if (truncate_fd < 0) {
    1219             : 
    1220             :                         /* If this failed because the file doesn't exist propagate the error right-away. Also,
    1221             :                          * AT_REMOVEDIR wasn't set, and we tried to open the file for writing, which means EISDIR is
    1222             :                          * returned when this is a directory but we are not supposed to delete those, hence propagate
    1223             :                          * the error right-away too. */
    1224           0 :                         if (IN_SET(errno, ENOENT, EISDIR))
    1225           0 :                                 return -errno;
    1226             : 
    1227           0 :                         if (errno != ELOOP) /* don't complain if this is a symlink */
    1228           0 :                                 log_debug_errno(errno, "Failed to open file '%s' for deallocation, ignoring: %m", name);
    1229             :                 }
    1230             :         }
    1231             : 
    1232           3 :         if (unlinkat(fd, name, flags) < 0)
    1233           0 :                 return -errno;
    1234             : 
    1235           3 :         if (truncate_fd < 0) /* Don't have a file handle, can't do more ☚ī¸ */
    1236           0 :                 return 0;
    1237             : 
    1238           3 :         if (fstat(truncate_fd, &st) < 0) {
    1239           0 :                 log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
    1240           0 :                 return 0;
    1241             :         }
    1242             : 
    1243           3 :         if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
    1244           0 :                 return 0;
    1245             : 
    1246             :         /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
    1247             :          * punch-hole/truncate this to release the disk space. */
    1248             : 
    1249           3 :         bs = MAX(st.st_blksize, 512);
    1250           3 :         l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
    1251             : 
    1252           3 :         if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
    1253           3 :                 return 0; /* Successfully punched a hole! 😊 */
    1254             : 
    1255             :         /* Fall back to truncation */
    1256           0 :         if (ftruncate(truncate_fd, 0) < 0) {
    1257           0 :                 log_debug_errno(errno, "Failed to truncate file to 0, ignoring: %m");
    1258           0 :                 return 0;
    1259             :         }
    1260             : 
    1261           0 :         return 0;
    1262             : }
    1263             : 
    1264          29 : int fsync_directory_of_file(int fd) {
    1265          29 :         _cleanup_free_ char *path = NULL;
    1266          29 :         _cleanup_close_ int dfd = -1;
    1267             :         int r;
    1268             : 
    1269          29 :         r = fd_verify_regular(fd);
    1270          29 :         if (r < 0)
    1271           0 :                 return r;
    1272             : 
    1273          29 :         r = fd_get_path(fd, &path);
    1274          29 :         if (r < 0) {
    1275           0 :                 log_debug_errno(r, "Failed to query /proc/self/fd/%d%s: %m",
    1276             :                                 fd,
    1277             :                                 r == -EOPNOTSUPP ? ", ignoring" : "");
    1278             : 
    1279           0 :                 if (r == -EOPNOTSUPP)
    1280             :                         /* If /proc is not available, we're most likely running in some
    1281             :                          * chroot environment, and syncing the directory is not very
    1282             :                          * important in that case. Let's just silently do nothing. */
    1283           0 :                         return 0;
    1284             : 
    1285           0 :                 return r;
    1286             :         }
    1287             : 
    1288          29 :         if (!path_is_absolute(path))
    1289           0 :                 return -EINVAL;
    1290             : 
    1291          29 :         dfd = open_parent(path, O_CLOEXEC, 0);
    1292          29 :         if (dfd < 0)
    1293           0 :                 return dfd;
    1294             : 
    1295          29 :         if (fsync(dfd) < 0)
    1296           0 :                 return -errno;
    1297             : 
    1298          29 :         return 0;
    1299             : }
    1300             : 
    1301           0 : int fsync_full(int fd) {
    1302             :         int r, q;
    1303             : 
    1304             :         /* Sync both the file and the directory */
    1305             : 
    1306           0 :         r = fsync(fd) < 0 ? -errno : 0;
    1307           0 :         q = fsync_directory_of_file(fd);
    1308             : 
    1309           0 :         return r < 0 ? r : q;
    1310             : }
    1311             : 
    1312           0 : int fsync_path_at(int at_fd, const char *path) {
    1313           0 :         _cleanup_close_ int opened_fd = -1;
    1314             :         int fd;
    1315             : 
    1316           0 :         if (isempty(path)) {
    1317           0 :                 if (at_fd == AT_FDCWD) {
    1318           0 :                         opened_fd = open(".", O_RDONLY|O_DIRECTORY|O_CLOEXEC);
    1319           0 :                         if (opened_fd < 0)
    1320           0 :                                 return -errno;
    1321             : 
    1322           0 :                         fd = opened_fd;
    1323             :                 } else
    1324           0 :                         fd = at_fd;
    1325             :         } else {
    1326             : 
    1327           0 :                 opened_fd = openat(at_fd, path, O_RDONLY|O_CLOEXEC);
    1328           0 :                 if (opened_fd < 0)
    1329           0 :                         return -errno;
    1330             : 
    1331           0 :                 fd = opened_fd;
    1332             :         }
    1333             : 
    1334           0 :         if (fsync(fd) < 0)
    1335           0 :                 return -errno;
    1336             : 
    1337           0 :         return 0;
    1338             : }
    1339             : 
    1340           0 : int syncfs_path(int atfd, const char *path) {
    1341           0 :         _cleanup_close_ int fd = -1;
    1342             : 
    1343           0 :         assert(path);
    1344             : 
    1345           0 :         fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK);
    1346           0 :         if (fd < 0)
    1347           0 :                 return -errno;
    1348             : 
    1349           0 :         if (syncfs(fd) < 0)
    1350           0 :                 return -errno;
    1351             : 
    1352           0 :         return 0;
    1353             : }
    1354             : 
    1355          60 : int open_parent(const char *path, int flags, mode_t mode) {
    1356          60 :         _cleanup_free_ char *parent = NULL;
    1357             :         int fd;
    1358             : 
    1359          60 :         if (isempty(path))
    1360           0 :                 return -EINVAL;
    1361          60 :         if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */
    1362           0 :                 return -EINVAL;
    1363             : 
    1364          60 :         parent = dirname_malloc(path);
    1365          60 :         if (!parent)
    1366           0 :                 return -ENOMEM;
    1367             : 
    1368             :         /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an
    1369             :          * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */
    1370             : 
    1371          60 :         if (FLAGS_SET(flags, O_PATH))
    1372          28 :                 flags |= O_DIRECTORY;
    1373          32 :         else if (!FLAGS_SET(flags, O_TMPFILE))
    1374          29 :                 flags |= O_DIRECTORY|O_RDONLY;
    1375             : 
    1376          60 :         fd = open(parent, flags, mode);
    1377          60 :         if (fd < 0)
    1378           0 :                 return -errno;
    1379             : 
    1380          60 :         return fd;
    1381             : }

Generated by: LCOV version 1.14