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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <sched.h>
       4                 :            : #include <sys/prctl.h>
       5                 :            : #include <sys/stat.h>
       6                 :            : #include <unistd.h>
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "btrfs-util.h"
      10                 :            : #include "capability-util.h"
      11                 :            : #include "dirent-util.h"
      12                 :            : #include "fd-util.h"
      13                 :            : #include "fileio.h"
      14                 :            : #include "fs-util.h"
      15                 :            : #include "import-common.h"
      16                 :            : #include "os-util.h"
      17                 :            : #include "process-util.h"
      18                 :            : #include "selinux-util.h"
      19                 :            : #include "signal-util.h"
      20                 :            : #include "tmpfile-util.h"
      21                 :            : #include "util.h"
      22                 :            : 
      23                 :          0 : int import_make_read_only_fd(int fd) {
      24                 :            :         int r;
      25                 :            : 
      26         [ #  # ]:          0 :         assert(fd >= 0);
      27                 :            : 
      28                 :            :         /* First, let's make this a read-only subvolume if it refers
      29                 :            :          * to a subvolume */
      30                 :          0 :         r = btrfs_subvol_set_read_only_fd(fd, true);
      31   [ #  #  #  # ]:          0 :         if (IN_SET(r, -ENOTTY, -ENOTDIR, -EINVAL)) {
      32                 :            :                 struct stat st;
      33                 :            : 
      34                 :            :                 /* This doesn't refer to a subvolume, or the file
      35                 :            :                  * system isn't even btrfs. In that, case fall back to
      36                 :            :                  * chmod()ing */
      37                 :            : 
      38                 :          0 :                 r = fstat(fd, &st);
      39         [ #  # ]:          0 :                 if (r < 0)
      40         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to stat temporary image: %m");
      41                 :            : 
      42                 :            :                 /* Drop "w" flag */
      43         [ #  # ]:          0 :                 if (fchmod(fd, st.st_mode & 07555) < 0)
      44         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to chmod() final image: %m");
      45                 :            : 
      46                 :          0 :                 return 0;
      47                 :            : 
      48         [ #  # ]:          0 :         } else if (r < 0)
      49         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to make subvolume read-only: %m");
      50                 :            : 
      51                 :          0 :         return 0;
      52                 :            : }
      53                 :            : 
      54                 :          0 : int import_make_read_only(const char *path) {
      55                 :          0 :         _cleanup_close_ int fd = 1;
      56                 :            : 
      57                 :          0 :         fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
      58         [ #  # ]:          0 :         if (fd < 0)
      59         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to open %s: %m", path);
      60                 :            : 
      61                 :          0 :         return import_make_read_only_fd(fd);
      62                 :            : }
      63                 :            : 
      64                 :          0 : int import_fork_tar_x(const char *path, pid_t *ret) {
      65                 :          0 :         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
      66                 :            :         bool use_selinux;
      67                 :            :         pid_t pid;
      68                 :            :         int r;
      69                 :            : 
      70         [ #  # ]:          0 :         assert(path);
      71         [ #  # ]:          0 :         assert(ret);
      72                 :            : 
      73         [ #  # ]:          0 :         if (pipe2(pipefd, O_CLOEXEC) < 0)
      74         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
      75                 :            : 
      76                 :          0 :         use_selinux = mac_selinux_use();
      77                 :            : 
      78                 :          0 :         r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
      79         [ #  # ]:          0 :         if (r < 0)
      80                 :          0 :                 return r;
      81         [ #  # ]:          0 :         if (r == 0) {
      82                 :          0 :                 uint64_t retain =
      83                 :            :                         (1ULL << CAP_CHOWN) |
      84                 :            :                         (1ULL << CAP_FOWNER) |
      85                 :            :                         (1ULL << CAP_FSETID) |
      86                 :            :                         (1ULL << CAP_MKNOD) |
      87                 :            :                         (1ULL << CAP_SETFCAP) |
      88                 :            :                         (1ULL << CAP_DAC_OVERRIDE);
      89                 :            : 
      90                 :            :                 /* Child */
      91                 :            : 
      92                 :          0 :                 pipefd[1] = safe_close(pipefd[1]);
      93                 :            : 
      94                 :          0 :                 r = rearrange_stdio(pipefd[0], -1, STDERR_FILENO);
      95         [ #  # ]:          0 :                 if (r < 0) {
      96         [ #  # ]:          0 :                         log_error_errno(r, "Failed to rearrange stdin/stdout: %m");
      97                 :          0 :                         _exit(EXIT_FAILURE);
      98                 :            :                 }
      99                 :            : 
     100         [ #  # ]:          0 :                 if (unshare(CLONE_NEWNET) < 0)
     101         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
     102                 :            : 
     103                 :          0 :                 r = capability_bounding_set_drop(retain, true);
     104         [ #  # ]:          0 :                 if (r < 0)
     105         [ #  # ]:          0 :                         log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
     106                 :            : 
     107         [ #  # ]:          0 :                 execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*",
     108                 :            :                        use_selinux ? "--selinux" : "--no-selinux", NULL);
     109         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to execute tar: %m");
     110                 :          0 :                 _exit(EXIT_FAILURE);
     111                 :            :         }
     112                 :            : 
     113                 :          0 :         *ret = pid;
     114                 :            : 
     115                 :          0 :         return TAKE_FD(pipefd[1]);
     116                 :            : }
     117                 :            : 
     118                 :          0 : int import_fork_tar_c(const char *path, pid_t *ret) {
     119                 :          0 :         _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
     120                 :            :         bool use_selinux;
     121                 :            :         pid_t pid;
     122                 :            :         int r;
     123                 :            : 
     124         [ #  # ]:          0 :         assert(path);
     125         [ #  # ]:          0 :         assert(ret);
     126                 :            : 
     127         [ #  # ]:          0 :         if (pipe2(pipefd, O_CLOEXEC) < 0)
     128         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
     129                 :            : 
     130                 :          0 :         use_selinux = mac_selinux_use();
     131                 :            : 
     132                 :          0 :         r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
     133         [ #  # ]:          0 :         if (r < 0)
     134                 :          0 :                 return r;
     135         [ #  # ]:          0 :         if (r == 0) {
     136                 :          0 :                 uint64_t retain = (1ULL << CAP_DAC_OVERRIDE);
     137                 :            : 
     138                 :            :                 /* Child */
     139                 :            : 
     140                 :          0 :                 pipefd[0] = safe_close(pipefd[0]);
     141                 :            : 
     142                 :          0 :                 r = rearrange_stdio(-1, pipefd[1], STDERR_FILENO);
     143         [ #  # ]:          0 :                 if (r < 0) {
     144         [ #  # ]:          0 :                         log_error_errno(r, "Failed to rearrange stdin/stdout: %m");
     145                 :          0 :                         _exit(EXIT_FAILURE);
     146                 :            :                 }
     147                 :            : 
     148         [ #  # ]:          0 :                 if (unshare(CLONE_NEWNET) < 0)
     149         [ #  # ]:          0 :                         log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m");
     150                 :            : 
     151                 :          0 :                 r = capability_bounding_set_drop(retain, true);
     152         [ #  # ]:          0 :                 if (r < 0)
     153         [ #  # ]:          0 :                         log_error_errno(r, "Failed to drop capabilities, ignoring: %m");
     154                 :            : 
     155         [ #  # ]:          0 :                 execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*",
     156                 :            :                        use_selinux ? "--selinux" : "--no-selinux", ".", NULL);
     157         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to execute tar: %m");
     158                 :          0 :                 _exit(EXIT_FAILURE);
     159                 :            :         }
     160                 :            : 
     161                 :          0 :         *ret = pid;
     162                 :            : 
     163                 :          0 :         return TAKE_FD(pipefd[0]);
     164                 :            : }
     165                 :            : 
     166                 :          0 : int import_mangle_os_tree(const char *path) {
     167                 :          0 :         _cleanup_closedir_ DIR *d = NULL, *cd = NULL;
     168                 :          0 :         _cleanup_free_ char *child = NULL, *t = NULL;
     169                 :            :         const char *joined;
     170                 :            :         struct dirent *de;
     171                 :            :         int r;
     172                 :            : 
     173         [ #  # ]:          0 :         assert(path);
     174                 :            : 
     175                 :            :         /* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
     176                 :            :          * recognize this, and move the tree one level up. */
     177                 :            : 
     178                 :          0 :         r = path_is_os_tree(path);
     179         [ #  # ]:          0 :         if (r < 0)
     180         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
     181         [ #  # ]:          0 :         if (r > 0) {
     182         [ #  # ]:          0 :                 log_debug("Directory tree '%s' is a valid OS tree.", path);
     183                 :          0 :                 return 0;
     184                 :            :         }
     185                 :            : 
     186         [ #  # ]:          0 :         log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
     187                 :            : 
     188                 :          0 :         d = opendir(path);
     189         [ #  # ]:          0 :         if (!d)
     190         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to open directory '%s': %m", path);
     191                 :            : 
     192                 :          0 :         errno = 0;
     193                 :          0 :         de = readdir_no_dot(d);
     194         [ #  # ]:          0 :         if (!de) {
     195         [ #  # ]:          0 :                 if (errno != 0)
     196         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
     197                 :            : 
     198         [ #  # ]:          0 :                 log_debug("Directory '%s' is empty, leaving it as it is.", path);
     199                 :          0 :                 return 0;
     200                 :            :         }
     201                 :            : 
     202                 :          0 :         child = strdup(de->d_name);
     203         [ #  # ]:          0 :         if (!child)
     204                 :          0 :                 return log_oom();
     205                 :            : 
     206                 :          0 :         errno = 0;
     207                 :          0 :         de = readdir_no_dot(d);
     208         [ #  # ]:          0 :         if (de) {
     209         [ #  # ]:          0 :                 if (errno != 0)
     210         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
     211                 :            : 
     212         [ #  # ]:          0 :                 log_debug("Directory '%s' does not look like a directory tree, and has multiple children, leaving as it is.", path);
     213                 :          0 :                 return 0;
     214                 :            :         }
     215                 :            : 
     216   [ #  #  #  #  :          0 :         joined = prefix_roota(path, child);
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     217                 :          0 :         r = path_is_os_tree(joined);
     218         [ #  # ]:          0 :         if (r == -ENOTDIR) {
     219         [ #  # ]:          0 :                 log_debug("Directory '%s' does not look like a directory tree, and contains a single regular file only, leaving as it is.", path);
     220                 :          0 :                 return 0;
     221                 :            :         }
     222         [ #  # ]:          0 :         if (r < 0)
     223         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
     224         [ #  # ]:          0 :         if (r == 0) {
     225         [ #  # ]:          0 :                 log_debug("Neither '%s' nor '%s' is a valid OS tree, leaving them as they are.", path, joined);
     226                 :          0 :                 return 0;
     227                 :            :         }
     228                 :            : 
     229                 :            :         /* Nice, we have checked now:
     230                 :            :          *
     231                 :            :          * 1. The top-level directory does not qualify as OS tree
     232                 :            :          * 1. The top-level directory only contains one item
     233                 :            :          * 2. That item is a directory
     234                 :            :          * 3. And that directory qualifies as OS tree
     235                 :            :          *
     236                 :            :          * Let's now rearrange things, moving everything in the inner directory one level up */
     237                 :            : 
     238                 :          0 :         cd = xopendirat(dirfd(d), child, O_NOFOLLOW);
     239         [ #  # ]:          0 :         if (!cd)
     240         [ #  # ]:          0 :                 return log_error_errno(errno, "Can't open directory '%s': %m", joined);
     241                 :            : 
     242         [ #  # ]:          0 :         log_info("Rearranging '%s', moving OS tree one directory up.", joined);
     243                 :            : 
     244                 :            :         /* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
     245                 :            :          * safely moved up and won't collide with the name. */
     246                 :          0 :         r = tempfn_random(child, NULL, &t);
     247         [ #  # ]:          0 :         if (r < 0)
     248                 :          0 :                 return log_oom();
     249                 :          0 :         r = rename_noreplace(dirfd(d), child, dirfd(d), t);
     250         [ #  # ]:          0 :         if (r < 0)
     251         [ #  # ]:          0 :                 return log_error_errno(r, "Unable to rename '%s' to '%s/%s': %m", joined, path, t);
     252                 :            : 
     253   [ #  #  #  #  :          0 :         FOREACH_DIRENT_ALL(de, cd, return log_error_errno(errno, "Failed to iterate through directory '%s': %m", joined)) {
                   #  # ]
     254         [ #  # ]:          0 :                 if (dot_or_dot_dot(de->d_name))
     255                 :          0 :                         continue;
     256                 :            : 
     257                 :          0 :                 r = rename_noreplace(dirfd(cd), de->d_name, dirfd(d), de->d_name);
     258         [ #  # ]:          0 :                 if (r < 0)
     259         [ #  # ]:          0 :                         return log_error_errno(r, "Unable to move '%s/%s/%s' to '%s/%s': %m", path, t, de->d_name, path, de->d_name);
     260                 :            :         }
     261                 :            : 
     262         [ #  # ]:          0 :         if (unlinkat(dirfd(d), t, AT_REMOVEDIR) < 0)
     263         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to remove temporary directory '%s/%s': %m", path, t);
     264                 :            : 
     265         [ #  # ]:          0 :         log_info("Successfully rearranged OS tree.");
     266                 :            : 
     267                 :          0 :         return 0;
     268                 :            : }

Generated by: LCOV version 1.14