LCOV - code coverage report
Current view: top level - core - chown-recursive.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 62 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 3 0.0 %

          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