LCOV - code coverage report
Current view: top level - core - chown-recursive.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 62 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 3 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 73 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <fcntl.h>
       4                 :            : #include <sys/stat.h>
       5                 :            : #include <sys/types.h>
       6                 :            : #include <sys/xattr.h>
       7                 :            : 
       8                 :            : #include "chown-recursive.h"
       9                 :            : #include "dirent-util.h"
      10                 :            : #include "fd-util.h"
      11                 :            : #include "fs-util.h"
      12                 :            : #include "macro.h"
      13                 :            : #include "stdio-util.h"
      14                 :            : #include "strv.h"
      15                 :            : #include "user-util.h"
      16                 :            : 
      17                 :          0 : static int chown_one(
      18                 :            :                 int fd,
      19                 :            :                 const struct stat *st,
      20                 :            :                 uid_t uid,
      21                 :            :                 gid_t gid,
      22                 :            :                 mode_t mask) {
      23                 :            : 
      24                 :            :         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
      25                 :            :         const char *n;
      26                 :            :         int r;
      27                 :            : 
      28         [ #  # ]:          0 :         assert(fd >= 0);
      29         [ #  # ]:          0 :         assert(st);
      30                 :            : 
      31                 :            :         /* We change ACLs through the /proc/self/fd/%i path, so that we have a stable reference that works
      32                 :            :          * with O_PATH. */
      33         [ #  # ]:          0 :         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
      34                 :            : 
      35                 :            :         /* Drop any ACL if there is one */
      36         [ #  # ]:          0 :         FOREACH_STRING(n, "system.posix_acl_access", "system.posix_acl_default")
      37         [ #  # ]:          0 :                 if (removexattr(procfs_path, n) < 0)
      38   [ #  #  #  # ]:          0 :                         if (!IN_SET(errno, ENODATA, EOPNOTSUPP, ENOSYS, ENOTTY))
      39                 :          0 :                                 return -errno;
      40                 :            : 
      41                 :          0 :         r = fchmod_and_chown(fd, st->st_mode & mask, uid, gid);
      42         [ #  # ]:          0 :         if (r < 0)
      43                 :          0 :                 return r;
      44                 :            : 
      45                 :          0 :         return 1;
      46                 :            : }
      47                 :            : 
      48                 :          0 : static int chown_recursive_internal(
      49                 :            :                 int fd,
      50                 :            :                 const struct stat *st,
      51                 :            :                 uid_t uid,
      52                 :            :                 gid_t gid,
      53                 :            :                 mode_t mask) {
      54                 :            : 
      55                 :          0 :         _cleanup_closedir_ DIR *d = NULL;
      56                 :          0 :         bool changed = false;
      57                 :            :         struct dirent *de;
      58                 :            :         int r;
      59                 :            : 
      60         [ #  # ]:          0 :         assert(fd >= 0);
      61         [ #  # ]:          0 :         assert(st);
      62                 :            : 
      63                 :          0 :         d = fdopendir(fd);
      64         [ #  # ]:          0 :         if (!d) {
      65                 :          0 :                 safe_close(fd);
      66                 :          0 :                 return -errno;
      67                 :            :         }
      68                 :            : 
      69   [ #  #  #  # ]:          0 :         FOREACH_DIRENT_ALL(de, d, return -errno) {
      70      [ #  #  # ]:          0 :                 _cleanup_close_ int path_fd = -1;
      71                 :            :                 struct stat fst;
      72                 :            : 
      73         [ #  # ]:          0 :                 if (dot_or_dot_dot(de->d_name))
      74                 :          0 :                         continue;
      75                 :            : 
      76                 :            :                 /* Let's pin the child inode we want to fix now with an O_PATH fd, so that it cannot be swapped out
      77                 :            :                  * while we manipulate it. */
      78                 :          0 :                 path_fd = openat(dirfd(d), de->d_name, O_PATH|O_CLOEXEC|O_NOFOLLOW);
      79         [ #  # ]:          0 :                 if (path_fd < 0)
      80                 :          0 :                         return -errno;
      81                 :            : 
      82         [ #  # ]:          0 :                 if (fstat(path_fd, &fst) < 0)
      83                 :          0 :                         return -errno;
      84                 :            : 
      85         [ #  # ]:          0 :                 if (S_ISDIR(fst.st_mode)) {
      86                 :            :                         int subdir_fd;
      87                 :            : 
      88                 :            :                         /* Convert it to a "real" (i.e. non-O_PATH) fd now */
      89                 :          0 :                         subdir_fd = fd_reopen(path_fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
      90         [ #  # ]:          0 :                         if (subdir_fd < 0)
      91                 :          0 :                                 return subdir_fd;
      92                 :            : 
      93                 :          0 :                         r = chown_recursive_internal(subdir_fd, &fst, uid, gid, mask); /* takes possession of subdir_fd even on failure */
      94         [ #  # ]:          0 :                         if (r < 0)
      95                 :          0 :                                 return r;
      96         [ #  # ]:          0 :                         if (r > 0)
      97                 :          0 :                                 changed = true;
      98                 :            :                 } else {
      99                 :          0 :                         r = chown_one(path_fd, &fst, uid, gid, mask);
     100         [ #  # ]:          0 :                         if (r < 0)
     101                 :          0 :                                 return r;
     102         [ #  # ]:          0 :                         if (r > 0)
     103                 :          0 :                                 changed = true;
     104                 :            :                 }
     105                 :            :         }
     106                 :            : 
     107                 :          0 :         r = chown_one(dirfd(d), st, uid, gid, mask);
     108         [ #  # ]:          0 :         if (r < 0)
     109                 :          0 :                 return r;
     110                 :            : 
     111   [ #  #  #  # ]:          0 :         return r > 0 || changed;
     112                 :            : }
     113                 :            : 
     114                 :          0 : int path_chown_recursive(
     115                 :            :                 const char *path,
     116                 :            :                 uid_t uid,
     117                 :            :                 gid_t gid,
     118                 :            :                 mode_t mask) {
     119                 :            : 
     120                 :          0 :         _cleanup_close_ int fd = -1;
     121                 :            :         struct stat st;
     122                 :            : 
     123                 :          0 :         fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
     124         [ #  # ]:          0 :         if (fd < 0)
     125                 :          0 :                 return -errno;
     126                 :            : 
     127   [ #  #  #  #  :          0 :         if (!uid_is_valid(uid) && !gid_is_valid(gid) && (mask & 07777) == 07777)
                   #  # ]
     128                 :          0 :                 return 0; /* nothing to do */
     129                 :            : 
     130         [ #  # ]:          0 :         if (fstat(fd, &st) < 0)
     131                 :          0 :                 return -errno;
     132                 :            : 
     133                 :            :         /* Let's take a shortcut: if the top-level directory is properly owned, we don't descend into the
     134                 :            :          * whole tree, under the assumption that all is OK anyway. */
     135   [ #  #  #  # ]:          0 :         if ((!uid_is_valid(uid) || st.st_uid == uid) &&
     136   [ #  #  #  # ]:          0 :             (!gid_is_valid(gid) || st.st_gid == gid) &&
     137         [ #  # ]:          0 :             ((st.st_mode & ~mask & 07777) == 0))
     138                 :          0 :                 return 0;
     139                 :            : 
     140                 :          0 :         return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid, mask); /* we donate the fd to the call, regardless if it succeeded or failed */
     141                 :            : }

Generated by: LCOV version 1.14