LCOV - code coverage report
Current view: top level - basic - fs-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 397 682 58.2 %
Date: 2019-08-23 13:36:53 Functions: 29 41 70.7 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 296 635 46.6 %

           Branch data     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                 :        220 : int unlink_noerrno(const char *path) {
      36                 :        220 :         PROTECT_ERRNO;
      37                 :            :         int r;
      38                 :            : 
      39                 :        220 :         r = unlink(path);
      40         [ +  + ]:        220 :         if (r < 0)
      41                 :          8 :                 return -errno;
      42                 :            : 
      43                 :        212 :         return 0;
      44                 :            : }
      45                 :            : 
      46                 :         80 : int rmdir_parents(const char *path, const char *stop) {
      47                 :            :         size_t l;
      48                 :         80 :         int r = 0;
      49                 :            : 
      50         [ -  + ]:         80 :         assert(path);
      51         [ -  + ]:         80 :         assert(stop);
      52                 :            : 
      53                 :         80 :         l = strlen(path);
      54                 :            : 
      55                 :            :         /* Skip trailing slashes */
      56   [ +  -  -  + ]:         80 :         while (l > 0 && path[l-1] == '/')
      57                 :          0 :                 l--;
      58                 :            : 
      59         [ +  - ]:        100 :         while (l > 0) {
      60                 :            :                 char *t;
      61                 :            : 
      62                 :            :                 /* Skip last component */
      63   [ +  -  +  + ]:       1960 :                 while (l > 0 && path[l-1] != '/')
      64                 :       1860 :                         l--;
      65                 :            : 
      66                 :            :                 /* Skip trailing slashes */
      67   [ +  -  +  + ]:        200 :                 while (l > 0 && path[l-1] == '/')
      68                 :        100 :                         l--;
      69                 :            : 
      70         [ -  + ]:        100 :                 if (l <= 0)
      71                 :          0 :                         break;
      72                 :            : 
      73                 :        100 :                 t = strndup(path, l);
      74         [ -  + ]:        100 :                 if (!t)
      75                 :          0 :                         return -ENOMEM;
      76                 :            : 
      77         [ +  + ]:        100 :                 if (path_startswith(stop, t)) {
      78                 :         40 :                         free(t);
      79                 :         40 :                         return 0;
      80                 :            :                 }
      81                 :            : 
      82                 :         60 :                 r = rmdir(t);
      83                 :         60 :                 free(t);
      84                 :            : 
      85         [ +  + ]:         60 :                 if (r < 0)
      86         [ +  - ]:         40 :                         if (errno != ENOENT)
      87                 :         40 :                                 return -errno;
      88                 :            :         }
      89                 :            : 
      90                 :          0 :         return 0;
      91                 :            : }
      92                 :            : 
      93                 :         32 : int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
      94                 :            :         int r;
      95                 :            : 
      96                 :            :         /* Try the ideal approach first */
      97         [ +  - ]:         32 :         if (renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE) >= 0)
      98                 :         32 :                 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                 :     693912 : int readlinkat_malloc(int fd, const char *p, char **ret) {
     137                 :     693912 :         size_t l = FILENAME_MAX+1;
     138                 :            :         int r;
     139                 :            : 
     140         [ -  + ]:     693912 :         assert(p);
     141         [ -  + ]:     693912 :         assert(ret);
     142                 :            : 
     143                 :          0 :         for (;;) {
     144                 :            :                 char *c;
     145                 :            :                 ssize_t n;
     146                 :            : 
     147                 :     693912 :                 c = new(char, l);
     148         [ -  + ]:     693912 :                 if (!c)
     149                 :          0 :                         return -ENOMEM;
     150                 :            : 
     151                 :     693912 :                 n = readlinkat(fd, p, c, l-1);
     152         [ +  + ]:     693912 :                 if (n < 0) {
     153                 :       9948 :                         r = -errno;
     154                 :       9948 :                         free(c);
     155                 :       9948 :                         return r;
     156                 :            :                 }
     157                 :            : 
     158         [ +  - ]:     683964 :                 if ((size_t) n < l-1) {
     159                 :     683964 :                         c[n] = 0;
     160                 :     683964 :                         *ret = c;
     161                 :     683964 :                         return 0;
     162                 :            :                 }
     163                 :            : 
     164                 :          0 :                 free(c);
     165                 :          0 :                 l *= 2;
     166                 :            :         }
     167                 :            : }
     168                 :            : 
     169                 :     672396 : int readlink_malloc(const char *p, char **ret) {
     170                 :     672396 :         return readlinkat_malloc(AT_FDCWD, p, ret);
     171                 :            : }
     172                 :            : 
     173                 :      35368 : int readlink_value(const char *p, char **ret) {
     174                 :      35368 :         _cleanup_free_ char *link = NULL;
     175                 :            :         char *value;
     176                 :            :         int r;
     177                 :            : 
     178                 :      35368 :         r = readlink_malloc(p, &link);
     179         [ +  + ]:      35368 :         if (r < 0)
     180                 :       8812 :                 return r;
     181                 :            : 
     182                 :      26556 :         value = basename(link);
     183         [ -  + ]:      26556 :         if (!value)
     184                 :          0 :                 return -ENOENT;
     185                 :            : 
     186                 :      26556 :         value = strdup(value);
     187         [ -  + ]:      26556 :         if (!value)
     188                 :          0 :                 return -ENOMEM;
     189                 :            : 
     190                 :      26556 :         *ret = value;
     191                 :            : 
     192                 :      26556 :         return 0;
     193                 :            : }
     194                 :            : 
     195                 :         44 : int readlink_and_make_absolute(const char *p, char **r) {
     196                 :         44 :         _cleanup_free_ char *target = NULL;
     197                 :            :         char *k;
     198                 :            :         int j;
     199                 :            : 
     200         [ -  + ]:         44 :         assert(p);
     201         [ -  + ]:         44 :         assert(r);
     202                 :            : 
     203                 :         44 :         j = readlink_malloc(p, &target);
     204         [ -  + ]:         44 :         if (j < 0)
     205                 :          0 :                 return j;
     206                 :            : 
     207                 :         44 :         k = file_in_same_dir(p, target);
     208         [ -  + ]:         44 :         if (!k)
     209                 :          0 :                 return -ENOMEM;
     210                 :            : 
     211                 :         44 :         *r = k;
     212                 :         44 :         return 0;
     213                 :            : }
     214                 :            : 
     215                 :          4 : int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
     216                 :          4 :         _cleanup_close_ int fd = -1;
     217                 :            : 
     218         [ -  + ]:          4 :         assert(path);
     219                 :            : 
     220                 :          4 :         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         [ -  + ]:          4 :         if (fd < 0)
     223                 :          0 :                 return -errno;
     224                 :            : 
     225                 :          4 :         return fchmod_and_chown(fd, mode, uid, gid);
     226                 :            : }
     227                 :            : 
     228                 :     132556 : 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         [ -  + ]:     132556 :         if (fstat(fd, &st) < 0)
     241                 :          0 :                 return -errno;
     242                 :            : 
     243                 :     132556 :         do_chown =
     244   [ +  +  +  -  :     132580 :                 (uid != UID_INVALID && st.st_uid != uid) ||
                   +  + ]
     245         [ -  + ]:         24 :                 (gid != GID_INVALID && st.st_gid != gid);
     246                 :            : 
     247                 :     132556 :         do_chmod =
     248   [ +  +  +  + ]:     265108 :                 !S_ISLNK(st.st_mode) && /* chmod is not defined on symlinks */
     249   [ +  +  -  + ]:     132552 :                 ((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         [ +  + ]:     132556 :         if (mode == MODE_INVALID)
     254                 :     132532 :                 mode = st.st_mode; /* If we only shall do a chown(), save original mode, since chown() might break it. */
     255   [ -  +  #  # ]:         24 :         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   [ -  +  #  # ]:     132556 :         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         [ -  + ]:     132556 :         if (do_chown)
     267         [ #  # ]:          0 :                 if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
     268                 :          0 :                         return -errno;
     269                 :            : 
     270         [ +  + ]:     132556 :         if (do_chmod)
     271         [ -  + ]:         12 :                 if (fchmod_opath(fd, mode & 07777) < 0)
     272                 :          0 :                         return -errno;
     273                 :            : 
     274   [ +  -  +  + ]:     132556 :         return do_chown || do_chmod;
     275                 :            : }
     276                 :            : 
     277                 :          8 : int fchmod_umask(int fd, mode_t m) {
     278                 :            :         mode_t u;
     279                 :            :         int r;
     280                 :            : 
     281                 :          8 :         u = umask(0777);
     282         [ -  + ]:          8 :         r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
     283                 :          8 :         umask(u);
     284                 :            : 
     285                 :          8 :         return r;
     286                 :            : }
     287                 :            : 
     288                 :         12 : 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         [ -  + ]:         12 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
     296         [ -  + ]:         12 :         if (chmod(procfs_path, m) < 0)
     297                 :          0 :                 return -errno;
     298                 :            : 
     299                 :         12 :         return 0;
     300                 :            : }
     301                 :            : 
     302                 :       3396 : int fd_warn_permissions(const char *path, int fd) {
     303                 :            :         struct stat st;
     304                 :            : 
     305         [ -  + ]:       3396 :         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         [ -  + ]:       3396 :         if (!S_ISREG(st.st_mode))
     310                 :          0 :                 return 0;
     311                 :            : 
     312         [ -  + ]:       3396 :         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         [ -  + ]:       3396 :         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   [ -  +  #  # ]:       3396 :         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                 :       3396 :         return 0;
     322                 :            : }
     323                 :            : 
     324                 :     132552 : 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                 :     132552 :         _cleanup_close_ int fd = -1;
     327                 :     132552 :         int r, ret = 0;
     328                 :            : 
     329         [ -  + ]:     132552 :         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         [ -  + ]:     132552 :         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                 :     132552 :         fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
     343         [ +  + ]:     132552 :         if (fd < 0) {
     344         [ -  + ]:     132480 :                 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   [ +  +  +  + ]:     132480 :                 fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
     350         [ -  + ]:     132480 :                 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         [ -  + ]:     132552 :         xsprintf(fdpath, "/proc/self/fd/%i", fd);
     358                 :            : 
     359                 :     132552 :         ret = fchmod_and_chown(fd, mode, uid, gid);
     360                 :            : 
     361         [ +  + ]:     132552 :         if (stamp != USEC_INFINITY) {
     362                 :            :                 struct timespec ts[2];
     363                 :            : 
     364                 :         20 :                 timespec_store(&ts[0], stamp);
     365                 :         20 :                 ts[1] = ts[0];
     366                 :         20 :                 r = utimensat(AT_FDCWD, fdpath, ts, 0);
     367                 :            :         } else
     368                 :     132532 :                 r = utimensat(AT_FDCWD, fdpath, NULL, 0);
     369   [ -  +  #  # ]:     132552 :         if (r < 0 && ret >= 0)
     370                 :          0 :                 return -errno;
     371                 :            : 
     372                 :     132552 :         return ret;
     373                 :            : }
     374                 :            : 
     375                 :     132532 : int touch(const char *path) {
     376                 :     132532 :         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                 :         44 : int get_files_in_directory(const char *path, char ***list) {
     509                 :         44 :         _cleanup_closedir_ DIR *d = NULL;
     510                 :            :         struct dirent *de;
     511                 :         44 :         size_t bufsize = 0, n = 0;
     512                 :         44 :         _cleanup_strv_free_ char **l = NULL;
     513                 :            : 
     514         [ -  + ]:         44 :         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                 :         44 :         d = opendir(path);
     521         [ +  + ]:         44 :         if (!d)
     522                 :          4 :                 return -errno;
     523                 :            : 
     524   [ +  +  -  + ]:       6960 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
     525                 :       6920 :                 dirent_ensure_type(d, de);
     526                 :            : 
     527         [ +  + ]:       6920 :                 if (!dirent_is_file(de))
     528                 :       3297 :                         continue;
     529                 :            : 
     530         [ +  + ]:       3623 :                 if (list) {
     531                 :            :                         /* one extra slot is needed for the terminating NULL */
     532         [ -  + ]:       2023 :                         if (!GREEDY_REALLOC(l, bufsize, n + 2))
     533                 :          0 :                                 return -ENOMEM;
     534                 :            : 
     535                 :       2023 :                         l[n] = strdup(de->d_name);
     536         [ -  + ]:       2023 :                         if (!l[n])
     537                 :          0 :                                 return -ENOMEM;
     538                 :            : 
     539                 :       2023 :                         l[++n] = NULL;
     540                 :            :                 } else
     541                 :       1600 :                         n++;
     542                 :            :         }
     543                 :            : 
     544         [ +  + ]:         40 :         if (list)
     545                 :         28 :                 *list = TAKE_PTR(l);
     546                 :            : 
     547                 :         40 :         return n;
     548                 :            : }
     549                 :            : 
     550                 :         48 : static int getenv_tmp_dir(const char **ret_path) {
     551                 :            :         const char *n;
     552                 :         48 :         int r, ret = 0;
     553                 :            : 
     554         [ -  + ]:         48 :         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         [ +  + ]:        180 :         FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
     559                 :            :                 const char *e;
     560                 :            : 
     561                 :        136 :                 e = secure_getenv(n);
     562         [ +  + ]:        136 :                 if (!e)
     563                 :        116 :                         continue;
     564         [ -  + ]:         20 :                 if (!path_is_absolute(e)) {
     565                 :          0 :                         r = -ENOTDIR;
     566                 :          0 :                         goto next;
     567                 :            :                 }
     568         [ -  + ]:         20 :                 if (!path_is_normalized(e)) {
     569                 :          0 :                         r = -EPERM;
     570                 :          0 :                         goto next;
     571                 :            :                 }
     572                 :            : 
     573                 :         20 :                 r = is_dir(e, true);
     574         [ +  + ]:         20 :                 if (r < 0)
     575                 :         16 :                         goto next;
     576         [ -  + ]:          4 :                 if (r == 0) {
     577                 :          0 :                         r = -ENOTDIR;
     578                 :          0 :                         goto next;
     579                 :            :                 }
     580                 :            : 
     581                 :          4 :                 *ret_path = e;
     582                 :          4 :                 return 1;
     583                 :            : 
     584                 :         16 :         next:
     585                 :            :                 /* Remember first error, to make this more debuggable */
     586         [ +  - ]:         16 :                 if (ret >= 0)
     587                 :         16 :                         ret = r;
     588                 :            :         }
     589                 :            : 
     590         [ +  + ]:         44 :         if (ret < 0)
     591                 :         16 :                 return ret;
     592                 :            : 
     593                 :         28 :         *ret_path = NULL;
     594                 :         28 :         return ret;
     595                 :            : }
     596                 :            : 
     597                 :         48 : static int tmp_dir_internal(const char *def, const char **ret) {
     598                 :            :         const char *e;
     599                 :            :         int r, k;
     600                 :            : 
     601         [ -  + ]:         48 :         assert(def);
     602         [ -  + ]:         48 :         assert(ret);
     603                 :            : 
     604                 :         48 :         r = getenv_tmp_dir(&e);
     605         [ +  + ]:         48 :         if (r > 0) {
     606                 :          4 :                 *ret = e;
     607                 :          4 :                 return 0;
     608                 :            :         }
     609                 :            : 
     610                 :         44 :         k = is_dir(def, true);
     611         [ -  + ]:         44 :         if (k == 0)
     612                 :          0 :                 k = -ENOTDIR;
     613         [ -  + ]:         44 :         if (k < 0)
     614         [ #  # ]:          0 :                 return r < 0 ? r : k;
     615                 :            : 
     616                 :         44 :         *ret = def;
     617                 :         44 :         return 0;
     618                 :            : }
     619                 :            : 
     620                 :         20 : 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                 :         20 :         return tmp_dir_internal("/var/tmp", ret);
     628                 :            : }
     629                 :            : 
     630                 :         28 : 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                 :         28 :         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                 :         40 : 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         [ -  + ]:         40 :         xsprintf(path, "/proc/self/fd/%i", what);
     655                 :            : 
     656                 :         40 :         r = inotify_add_watch(fd, path, mask);
     657         [ -  + ]:         40 :         if (r < 0)
     658                 :          0 :                 return -errno;
     659                 :            : 
     660                 :         40 :         return r;
     661                 :            : }
     662                 :            : 
     663                 :        216 : 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         [ +  - ]:        216 :         if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
     669                 :        216 :                 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                 :      80660 : int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
     702                 :      80660 :         _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
     703                 :      80660 :         _cleanup_close_ int fd = -1;
     704                 :      80660 :         unsigned max_follow = CHASE_SYMLINKS_MAX; /* how many symlinks to follow before giving up and returning ELOOP */
     705                 :            :         struct stat previous_stat;
     706                 :      80660 :         bool exists = true;
     707                 :            :         char *todo;
     708                 :            :         int r;
     709                 :            : 
     710         [ -  + ]:      80660 :         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         [ -  + ]:      80660 :         if (FLAGS_SET(flags, CHASE_NONEXISTENT | CHASE_OPEN))
     714                 :          0 :                 return -EINVAL;
     715                 :            : 
     716         [ -  + ]:      80660 :         if (FLAGS_SET(flags, CHASE_STEP | CHASE_OPEN))
     717                 :          0 :                 return -EINVAL;
     718                 :            : 
     719         [ -  + ]:      80660 :         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         [ +  + ]:      80660 :         if (empty_or_root(original_root))
     773                 :      80352 :                 original_root = NULL;
     774                 :            : 
     775   [ +  +  +  +  :      80660 :         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                 :          4 :                 r = open(path, O_PATH|O_CLOEXEC|((flags & CHASE_NOFOLLOW) ? O_NOFOLLOW : 0));
     779         [ -  + ]:          4 :                 if (r < 0)
     780                 :          0 :                         return -errno;
     781                 :            : 
     782                 :          4 :                 return r;
     783                 :            :         }
     784                 :            : 
     785         [ +  + ]:      80656 :         if (original_root) {
     786                 :        308 :                 r = path_make_absolute_cwd(original_root, &root);
     787         [ -  + ]:        308 :                 if (r < 0)
     788                 :          0 :                         return r;
     789                 :            : 
     790         [ +  + ]:        308 :                 if (flags & CHASE_PREFIX_ROOT) {
     791                 :            : 
     792                 :            :                         /* We don't support relative paths in combination with a root directory */
     793         [ -  + ]:          8 :                         if (!path_is_absolute(path))
     794                 :          0 :                                 return -EINVAL;
     795                 :            : 
     796   [ +  -  -  +  :          8 :                         path = prefix_roota(root, path);
          -  +  -  +  -  
          +  +  -  -  +  
                   -  + ]
     797                 :            :                 }
     798                 :            :         }
     799                 :            : 
     800                 :      80656 :         r = path_make_absolute_cwd(path, &buffer);
     801         [ -  + ]:      80656 :         if (r < 0)
     802                 :          0 :                 return r;
     803                 :            : 
     804                 :      80656 :         fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     805         [ -  + ]:      80656 :         if (fd < 0)
     806                 :          0 :                 return -errno;
     807                 :            : 
     808         [ +  + ]:      80656 :         if (flags & CHASE_SAFE) {
     809         [ -  + ]:        108 :                 if (fstat(fd, &previous_stat) < 0)
     810                 :          0 :                         return -errno;
     811                 :            :         }
     812                 :            : 
     813                 :      80656 :         todo = buffer;
     814                 :     462600 :         for (;;) {
     815   [ +  +  +  +  :     543256 :                 _cleanup_free_ char *first = NULL;
                      + ]
     816   [ +  +  +  +  :     543256 :                 _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                 :     543256 :                 n = strspn(todo, "/");                  /* The slashes */
     822                 :     543256 :                 m = n + strcspn(todo + n, "/");         /* The entire length of the component */
     823                 :            : 
     824                 :            :                 /* Extract the first component. */
     825                 :     543256 :                 first = strndup(todo, m);
     826         [ -  + ]:     543256 :                 if (!first)
     827                 :          0 :                         return -ENOMEM;
     828                 :            : 
     829                 :     543256 :                 todo += m;
     830                 :            : 
     831                 :            :                 /* Empty? Then we reached the end. */
     832         [ +  + ]:     543256 :                 if (isempty(first))
     833                 :      32668 :                         break;
     834                 :            : 
     835                 :            :                 /* Just a single slash? Then we reached the end. */
     836         [ +  + ]:     510588 :                 if (path_equal(first, "/")) {
     837                 :            :                         /* Preserve the trailing slash */
     838                 :            : 
     839         [ +  + ]:         44 :                         if (flags & CHASE_TRAIL_SLASH)
     840         [ -  + ]:         12 :                                 if (!strextend(&done, "/", NULL))
     841                 :          0 :                                         return -ENOMEM;
     842                 :            : 
     843                 :         44 :                         break;
     844                 :            :                 }
     845                 :            : 
     846                 :            :                 /* Just a dot? Then let's eat this up. */
     847         [ +  + ]:     510544 :                 if (path_equal(first, "/."))
     848                 :         72 :                         continue;
     849                 :            : 
     850                 :            :                 /* Two dots? Then chop off the last bit of what we already found out. */
     851         [ +  + ]:     510472 :                 if (path_equal(first, "/..")) {
     852      [ +  -  + ]:      50704 :                         _cleanup_free_ char *parent = NULL;
     853      [ +  -  + ]:      50704 :                         _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         [ +  + ]:      50704 :                         if (empty_or_root(done))
     858                 :         64 :                                 continue;
     859                 :            : 
     860                 :      50640 :                         parent = dirname_malloc(done);
     861         [ -  + ]:      50640 :                         if (!parent)
     862                 :          0 :                                 return -ENOMEM;
     863                 :            : 
     864                 :            :                         /* Don't allow this to leave the root dir.  */
     865         [ +  + ]:      50640 :                         if (root &&
     866         [ +  - ]:         68 :                             path_startswith(done, root) &&
     867         [ +  + ]:         68 :                             !path_startswith(parent, root))
     868                 :         40 :                                 continue;
     869                 :            : 
     870                 :      50600 :                         free_and_replace(done, parent);
     871                 :            : 
     872         [ +  + ]:      50600 :                         if (flags & CHASE_STEP)
     873                 :          4 :                                 goto chased_one;
     874                 :            : 
     875                 :      50596 :                         fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     876         [ -  + ]:      50596 :                         if (fd_parent < 0)
     877                 :          0 :                                 return -errno;
     878                 :            : 
     879         [ -  + ]:      50596 :                         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                 :      50596 :                         safe_close(fd);
     890                 :      50596 :                         fd = TAKE_FD(fd_parent);
     891                 :            : 
     892                 :      50596 :                         continue;
     893                 :            :                 }
     894                 :            : 
     895                 :            :                 /* Otherwise let's see what this is. */
     896                 :     459768 :                 child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
     897         [ +  + ]:     459768 :                 if (child < 0) {
     898                 :            : 
     899         [ +  + ]:      47916 :                         if (errno == ENOENT &&
     900   [ +  +  +  + ]:      47940 :                             (flags & CHASE_NONEXISTENT) &&
     901         [ +  + ]:         40 :                             (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         [ -  + ]:         24 :                                 if (streq_ptr(done, "/"))
     909                 :          0 :                                         *done = '\0';
     910                 :            : 
     911         [ -  + ]:         24 :                                 if (!strextend(&done, first, todo, NULL))
     912                 :          0 :                                         return -ENOMEM;
     913                 :            : 
     914                 :         24 :                                 exists = false;
     915                 :         24 :                                 break;
     916                 :            :                         }
     917                 :            : 
     918                 :      47892 :                         return -errno;
     919                 :            :                 }
     920                 :            : 
     921         [ -  + ]:     411852 :                 if (fstat(child, &st) < 0)
     922                 :          0 :                         return -errno;
     923   [ +  +  -  + ]:     412068 :                 if ((flags & CHASE_SAFE) &&
     924   [ #  #  -  + ]:        432 :                     (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) &&
     925                 :        216 :                     unsafe_transition(&previous_stat, &st))
     926                 :          0 :                         return log_unsafe_transition(fd, child, path, flags);
     927                 :            : 
     928                 :     411852 :                 previous_stat = st;
     929                 :            : 
     930   [ +  +  -  + ]:     412068 :                 if ((flags & CHASE_NO_AUTOFS) &&
     931                 :        216 :                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
     932                 :          0 :                         return log_autofs_mount_point(child, path, flags);
     933                 :            : 
     934   [ +  +  +  +  :     411852 :                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                   +  + ]
     935                 :            :                         char *joined;
     936                 :            : 
     937      [ +  +  + ]:      20984 :                         _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         [ +  + ]:      20984 :                         if (--max_follow <= 0)
     942                 :          4 :                                 return -ELOOP;
     943                 :            : 
     944                 :      20980 :                         r = readlinkat_malloc(fd, first + n, &destination);
     945         [ -  + ]:      20980 :                         if (r < 0)
     946                 :          0 :                                 return r;
     947         [ -  + ]:      20980 :                         if (isempty(destination))
     948                 :          0 :                                 return -EINVAL;
     949                 :            : 
     950         [ +  + ]:      20980 :                         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                 :         72 :                                 safe_close(fd);
     956         [ +  + ]:         72 :                                 fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
     957         [ -  + ]:         72 :                                 if (fd < 0)
     958                 :          0 :                                         return -errno;
     959                 :            : 
     960         [ -  + ]:         72 :                                 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                 :         72 :                                 free(done);
     971                 :            : 
     972                 :            :                                 /* Note that we do not revalidate the root, we take it as is. */
     973         [ +  + ]:         72 :                                 if (isempty(root))
     974                 :         32 :                                         done = NULL;
     975                 :            :                                 else {
     976                 :         40 :                                         done = strdup(root);
     977         [ -  + ]:         40 :                                         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                 :         72 :                                 joined = path_join(destination, todo);
     984                 :            :                         } else
     985                 :      20908 :                                 joined = path_join("/", destination, todo);
     986         [ -  + ]:      20980 :                         if (!joined)
     987                 :          0 :                                 return -ENOMEM;
     988                 :            : 
     989                 :      20980 :                         free(buffer);
     990                 :      20980 :                         todo = buffer = joined;
     991                 :            : 
     992         [ +  + ]:      20980 :                         if (flags & CHASE_STEP)
     993                 :         20 :                                 goto chased_one;
     994                 :            : 
     995                 :      20960 :                         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         [ +  + ]:     390868 :                 if (!done)
    1000                 :      80672 :                         done = TAKE_PTR(first);
    1001                 :            :                 else {
    1002                 :            :                         /* If done is "/", as first also contains slash at the head, then remove this redundant slash. */
    1003         [ +  + ]:     310196 :                         if (streq(done, "/"))
    1004                 :         12 :                                 *done = '\0';
    1005                 :            : 
    1006         [ -  + ]:     310196 :                         if (!strextend(&done, first, NULL))
    1007                 :          0 :                                 return -ENOMEM;
    1008                 :            :                 }
    1009                 :            : 
    1010                 :            :                 /* And iterate again, but go one directory further down. */
    1011                 :     390868 :                 safe_close(fd);
    1012                 :     390868 :                 fd = TAKE_FD(child);
    1013                 :            :         }
    1014                 :            : 
    1015         [ +  + ]:      32736 :         if (!done) {
    1016                 :            :                 /* Special case, turn the empty string into "/", to indicate the root directory. */
    1017                 :          4 :                 done = strdup("/");
    1018         [ -  + ]:          4 :                 if (!done)
    1019                 :          0 :                         return -ENOMEM;
    1020                 :            :         }
    1021                 :            : 
    1022         [ +  + ]:      32736 :         if (ret)
    1023                 :      32528 :                 *ret = TAKE_PTR(done);
    1024                 :            : 
    1025         [ +  + ]:      32736 :         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         [ -  + ]:        212 :                 assert(fd >= 0);
    1030                 :        212 :                 return TAKE_FD(fd);
    1031                 :            :         }
    1032                 :            : 
    1033         [ +  + ]:      32524 :         if (flags & CHASE_STEP)
    1034                 :          4 :                 return 1;
    1035                 :            : 
    1036                 :      32520 :         return exists;
    1037                 :            : 
    1038                 :         24 : chased_one:
    1039         [ +  - ]:         24 :         if (ret) {
    1040                 :            :                 char *c;
    1041                 :            : 
    1042                 :         24 :                 c = strjoin(strempty(done), todo);
    1043         [ -  + ]:         24 :                 if (!c)
    1044                 :          0 :                         return -ENOMEM;
    1045                 :            : 
    1046                 :         24 :                 *ret = c;
    1047                 :            :         }
    1048                 :            : 
    1049                 :         24 :         return 0;
    1050                 :            : }
    1051                 :            : 
    1052                 :        120 : 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                 :        120 :         _cleanup_close_ int path_fd = -1;
    1060                 :        120 :         _cleanup_free_ char *p = NULL;
    1061                 :            :         int r;
    1062                 :            : 
    1063         [ -  + ]:        120 :         if (chase_flags & CHASE_NONEXISTENT)
    1064                 :          0 :                 return -EINVAL;
    1065                 :            : 
    1066   [ +  +  +  -  :        120 :         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                 :         24 :                 r = open(path, open_flags);
    1069         [ -  + ]:         24 :                 if (r < 0)
    1070                 :          0 :                         return -errno;
    1071                 :            : 
    1072                 :         24 :                 return r;
    1073                 :            :         }
    1074                 :            : 
    1075         [ -  + ]:         96 :         path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
    1076         [ -  + ]:         96 :         if (path_fd < 0)
    1077                 :          0 :                 return path_fd;
    1078                 :            : 
    1079                 :         96 :         r = fd_reopen(path_fd, open_flags);
    1080         [ -  + ]:         96 :         if (r < 0)
    1081                 :          0 :                 return r;
    1082                 :            : 
    1083         [ -  + ]:         96 :         if (ret_path)
    1084                 :          0 :                 *ret_path = TAKE_PTR(p);
    1085                 :            : 
    1086                 :         96 :         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                 :        284 : 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                 :        284 :         _cleanup_close_ int path_fd = -1;
    1140                 :        284 :         _cleanup_free_ char *p = NULL;
    1141                 :            : 
    1142         [ -  + ]:        284 :         assert(path);
    1143         [ -  + ]:        284 :         assert(ret_stat);
    1144                 :            : 
    1145         [ -  + ]:        284 :         if (chase_flags & CHASE_NONEXISTENT)
    1146                 :          0 :                 return -EINVAL;
    1147                 :            : 
    1148   [ +  -  +  -  :        284 :         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         [ +  + ]:        284 :                 if (stat(path, ret_stat) < 0)
    1151                 :        188 :                         return -errno;
    1152                 :            : 
    1153                 :         96 :                 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                 :         24 : 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         [ -  + ]:         24 :         xsprintf(p, "/proc/self/fd/%i", fd);
    1179                 :         24 :         r = access(p, mode);
    1180         [ +  + ]:         24 :         if (r < 0)
    1181                 :          8 :                 return -errno;
    1182                 :            : 
    1183                 :         16 :         return r;
    1184                 :            : }
    1185                 :            : 
    1186                 :        212 : 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         [ +  - ]:        212 :         if (!endswith(*p, ".XXXXXX"))
    1192                 :        212 :                 (void) unlink_noerrno(*p);
    1193                 :        212 : }
    1194                 :            : 
    1195                 :         12 : int unlinkat_deallocate(int fd, const char *name, int flags) {
    1196                 :         12 :         _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         [ +  - ]:         12 :         if ((flags & AT_REMOVEDIR) == 0) {
    1217                 :         12 :                 truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
    1218         [ -  + ]:         12 :                 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         [ -  + ]:         12 :         if (unlinkat(fd, name, flags) < 0)
    1233                 :          0 :                 return -errno;
    1234                 :            : 
    1235         [ -  + ]:         12 :         if (truncate_fd < 0) /* Don't have a file handle, can't do more ☚ī¸ */
    1236                 :          0 :                 return 0;
    1237                 :            : 
    1238         [ -  + ]:         12 :         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   [ +  -  +  -  :         12 :         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                 :         12 :         bs = MAX(st.st_blksize, 512);
    1250                 :         12 :         l = DIV_ROUND_UP(st.st_size, bs) * bs; /* Round up to next block size */
    1251                 :            : 
    1252         [ +  - ]:         12 :         if (fallocate(truncate_fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 0, l) >= 0)
    1253                 :         12 :                 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                 :        116 : int fsync_directory_of_file(int fd) {
    1265                 :        116 :         _cleanup_free_ char *path = NULL;
    1266                 :        116 :         _cleanup_close_ int dfd = -1;
    1267                 :            :         int r;
    1268                 :            : 
    1269                 :        116 :         r = fd_verify_regular(fd);
    1270         [ -  + ]:        116 :         if (r < 0)
    1271                 :          0 :                 return r;
    1272                 :            : 
    1273                 :        116 :         r = fd_get_path(fd, &path);
    1274         [ -  + ]:        116 :         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         [ -  + ]:        116 :         if (!path_is_absolute(path))
    1289                 :          0 :                 return -EINVAL;
    1290                 :            : 
    1291                 :        116 :         dfd = open_parent(path, O_CLOEXEC, 0);
    1292         [ -  + ]:        116 :         if (dfd < 0)
    1293                 :          0 :                 return dfd;
    1294                 :            : 
    1295         [ -  + ]:        116 :         if (fsync(dfd) < 0)
    1296                 :          0 :                 return -errno;
    1297                 :            : 
    1298                 :        116 :         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                 :        240 : int open_parent(const char *path, int flags, mode_t mode) {
    1356                 :        240 :         _cleanup_free_ char *parent = NULL;
    1357                 :            :         int fd;
    1358                 :            : 
    1359         [ -  + ]:        240 :         if (isempty(path))
    1360                 :          0 :                 return -EINVAL;
    1361         [ -  + ]:        240 :         if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */
    1362                 :          0 :                 return -EINVAL;
    1363                 :            : 
    1364                 :        240 :         parent = dirname_malloc(path);
    1365         [ -  + ]:        240 :         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         [ +  + ]:        240 :         if (FLAGS_SET(flags, O_PATH))
    1372                 :        112 :                 flags |= O_DIRECTORY;
    1373         [ +  + ]:        128 :         else if (!FLAGS_SET(flags, O_TMPFILE))
    1374                 :        116 :                 flags |= O_DIRECTORY|O_RDONLY;
    1375                 :            : 
    1376                 :        240 :         fd = open(parent, flags, mode);
    1377         [ -  + ]:        240 :         if (fd < 0)
    1378                 :          0 :                 return -errno;
    1379                 :            : 
    1380                 :        240 :         return fd;
    1381                 :            : }

Generated by: LCOV version 1.14