LCOV - code coverage report
Current view: top level - import - import-fs.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 21 148 14.2 %
Date: 2019-08-23 13:36:53 Functions: 3 10 30.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 6 125 4.8 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <getopt.h>
       4                 :            : #include <locale.h>
       5                 :            : 
       6                 :            : #include "alloc-util.h"
       7                 :            : #include "btrfs-util.h"
       8                 :            : #include "fd-util.h"
       9                 :            : #include "format-util.h"
      10                 :            : #include "fs-util.h"
      11                 :            : #include "hostname-util.h"
      12                 :            : #include "import-common.h"
      13                 :            : #include "import-util.h"
      14                 :            : #include "machine-image.h"
      15                 :            : #include "mkdir.h"
      16                 :            : #include "ratelimit.h"
      17                 :            : #include "rm-rf.h"
      18                 :            : #include "string-util.h"
      19                 :            : #include "tmpfile-util.h"
      20                 :            : #include "verbs.h"
      21                 :            : 
      22                 :            : static bool arg_force = false;
      23                 :            : static bool arg_read_only = false;
      24                 :            : static const char *arg_image_root = "/var/lib/machines";
      25                 :            : 
      26                 :            : typedef struct ProgressInfo {
      27                 :            :         RateLimit limit;
      28                 :            :         char *path;
      29                 :            :         uint64_t size;
      30                 :            :         bool started;
      31                 :            :         bool logged_incomplete;
      32                 :            : } ProgressInfo;
      33                 :            : 
      34                 :            : static volatile sig_atomic_t cancelled = false;
      35                 :            : 
      36                 :          0 : static void sigterm_sigint(int sig) {
      37                 :          0 :         cancelled = true;
      38                 :          0 : }
      39                 :            : 
      40                 :          0 : static void progress_info_free(ProgressInfo *p) {
      41                 :          0 :         free(p->path);
      42                 :          0 : }
      43                 :            : 
      44                 :          0 : static void progress_show(ProgressInfo *p) {
      45         [ #  # ]:          0 :         assert(p);
      46                 :            : 
      47                 :            :         /* Show progress only every now and then. */
      48         [ #  # ]:          0 :         if (!ratelimit_below(&p->limit))
      49                 :          0 :                 return;
      50                 :            : 
      51                 :            :         /* Suppress the first message, start with the second one */
      52         [ #  # ]:          0 :         if (!p->started) {
      53                 :          0 :                 p->started = true;
      54                 :          0 :                 return;
      55                 :            :         }
      56                 :            : 
      57                 :            :         /* Mention the list is incomplete before showing first output. */
      58         [ #  # ]:          0 :         if (!p->logged_incomplete) {
      59         [ #  # ]:          0 :                 log_notice("(Note, file list shown below is incomplete, and is intended as sporadic progress report only.)");
      60                 :          0 :                 p->logged_incomplete = true;
      61                 :            :         }
      62                 :            : 
      63         [ #  # ]:          0 :         if (p->size == 0)
      64         [ #  # ]:          0 :                 log_info("Copying tree, currently at '%s'...", p->path);
      65                 :            :         else {
      66                 :            :                 char buffer[FORMAT_BYTES_MAX];
      67                 :            : 
      68         [ #  # ]:          0 :                 log_info("Copying tree, currently at '%s' (@%s)...", p->path, format_bytes(buffer, sizeof(buffer), p->size));
      69                 :            :         }
      70                 :            : }
      71                 :            : 
      72                 :          0 : static int progress_path(const char *path, const struct stat *st, void *userdata) {
      73                 :          0 :         ProgressInfo *p = userdata;
      74                 :            :         int r;
      75                 :            : 
      76         [ #  # ]:          0 :         assert(p);
      77                 :            : 
      78         [ #  # ]:          0 :         if (cancelled)
      79                 :          0 :                 return -EOWNERDEAD;
      80                 :            : 
      81                 :          0 :         r = free_and_strdup(&p->path, path);
      82         [ #  # ]:          0 :         if (r < 0)
      83                 :          0 :                 return r;
      84                 :            : 
      85                 :          0 :         p->size = 0;
      86                 :            : 
      87                 :          0 :         progress_show(p);
      88                 :          0 :         return 0;
      89                 :            : }
      90                 :            : 
      91                 :          0 : static int progress_bytes(uint64_t nbytes, void *userdata) {
      92                 :          0 :         ProgressInfo *p = userdata;
      93                 :            : 
      94         [ #  # ]:          0 :         assert(p);
      95         [ #  # ]:          0 :         assert(p->size != UINT64_MAX);
      96                 :            : 
      97         [ #  # ]:          0 :         if (cancelled)
      98                 :          0 :                 return -EOWNERDEAD;
      99                 :            : 
     100                 :          0 :         p->size += nbytes;
     101                 :            : 
     102                 :          0 :         progress_show(p);
     103                 :          0 :         return 0;
     104                 :            : }
     105                 :            : 
     106                 :          0 : static int import_fs(int argc, char *argv[], void *userdata) {
     107                 :          0 :         _cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL;
     108                 :          0 :         _cleanup_(progress_info_free) ProgressInfo progress = {};
     109                 :          0 :         const char *path = NULL, *local = NULL, *final_path;
     110                 :          0 :         _cleanup_close_ int open_fd = -1;
     111                 :            :         struct sigaction old_sigint_sa, old_sigterm_sa;
     112                 :            :         static const struct sigaction sa = {
     113                 :            :                 .sa_handler = sigterm_sigint,
     114                 :            :                 .sa_flags = SA_RESTART,
     115                 :            :         };
     116                 :            :         int r, fd;
     117                 :            : 
     118         [ #  # ]:          0 :         if (argc >= 2)
     119                 :          0 :                 path = argv[1];
     120                 :          0 :         path = empty_or_dash_to_null(path);
     121                 :            : 
     122         [ #  # ]:          0 :         if (argc >= 3)
     123                 :          0 :                 local = argv[2];
     124         [ #  # ]:          0 :         else if (path)
     125                 :          0 :                 local = basename(path);
     126                 :          0 :         local = empty_or_dash_to_null(local);
     127                 :            : 
     128         [ #  # ]:          0 :         if (local) {
     129         [ #  # ]:          0 :                 if (!machine_name_is_valid(local)) {
     130         [ #  # ]:          0 :                         log_error("Local image name '%s' is not valid.", local);
     131                 :          0 :                         return -EINVAL;
     132                 :            :                 }
     133                 :            : 
     134         [ #  # ]:          0 :                 if (!arg_force) {
     135                 :          0 :                         r = image_find(IMAGE_MACHINE, local, NULL);
     136         [ #  # ]:          0 :                         if (r < 0) {
     137         [ #  # ]:          0 :                                 if (r != -ENOENT)
     138         [ #  # ]:          0 :                                         return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
     139                 :            :                         } else {
     140         [ #  # ]:          0 :                                 log_error("Image '%s' already exists.", local);
     141                 :          0 :                                 return -EEXIST;
     142                 :            :                         }
     143                 :            :                 }
     144                 :            :         } else
     145                 :          0 :                 local = "imported";
     146                 :            : 
     147         [ #  # ]:          0 :         if (path) {
     148                 :          0 :                 open_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
     149         [ #  # ]:          0 :                 if (open_fd < 0)
     150         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to open directory to import: %m");
     151                 :            : 
     152                 :          0 :                 fd = open_fd;
     153                 :            : 
     154         [ #  # ]:          0 :                 log_info("Importing '%s', saving as '%s'.", path, local);
     155                 :            :         } else {
     156                 :          0 :                 _cleanup_free_ char *pretty = NULL;
     157                 :            : 
     158                 :          0 :                 fd = STDIN_FILENO;
     159                 :            : 
     160                 :          0 :                 (void) fd_get_path(fd, &pretty);
     161         [ #  # ]:          0 :                 log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
     162                 :            :         }
     163                 :            : 
     164   [ #  #  #  #  :          0 :         final_path = prefix_roota(arg_image_root, local);
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     165                 :            : 
     166                 :          0 :         r = tempfn_random(final_path, NULL, &temp_path);
     167         [ #  # ]:          0 :         if (r < 0)
     168                 :          0 :                 return log_oom();
     169                 :            : 
     170                 :          0 :         (void) mkdir_parents_label(temp_path, 0700);
     171                 :            : 
     172                 :          0 :         RATELIMIT_INIT(progress.limit, 200*USEC_PER_MSEC, 1);
     173                 :            : 
     174                 :            :         /* Hook into SIGINT/SIGTERM, so that we can cancel things then */
     175         [ #  # ]:          0 :         assert(sigaction(SIGINT, &sa, &old_sigint_sa) >= 0);
     176         [ #  # ]:          0 :         assert(sigaction(SIGTERM, &sa, &old_sigterm_sa) >= 0);
     177                 :            : 
     178                 :          0 :         r = btrfs_subvol_snapshot_fd_full(
     179                 :            :                         fd,
     180                 :            :                         temp_path,
     181                 :            :                         BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|BTRFS_SNAPSHOT_QUOTA,
     182                 :            :                         progress_path,
     183                 :            :                         progress_bytes,
     184                 :            :                         &progress);
     185         [ #  # ]:          0 :         if (r == -EOWNERDEAD) { /* SIGINT + SIGTERM cause this, see signal handler above */
     186         [ #  # ]:          0 :                 log_error("Copy cancelled.");
     187                 :          0 :                 goto finish;
     188                 :            :         }
     189         [ #  # ]:          0 :         if (r < 0) {
     190         [ #  # ]:          0 :                 log_error_errno(r, "Failed to copy directory: %m");
     191                 :          0 :                 goto finish;
     192                 :            :         }
     193                 :            : 
     194                 :          0 :         r = import_mangle_os_tree(temp_path);
     195         [ #  # ]:          0 :         if (r < 0)
     196                 :          0 :                 goto finish;
     197                 :            : 
     198                 :          0 :         (void) import_assign_pool_quota_and_warn(temp_path);
     199                 :            : 
     200         [ #  # ]:          0 :         if (arg_read_only) {
     201                 :          0 :                 r = import_make_read_only(temp_path);
     202         [ #  # ]:          0 :                 if (r < 0) {
     203         [ #  # ]:          0 :                         log_error_errno(r, "Failed to make directory read-only: %m");
     204                 :          0 :                         goto finish;
     205                 :            :                 }
     206                 :            :         }
     207                 :            : 
     208         [ #  # ]:          0 :         if (arg_force)
     209                 :          0 :                 (void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
     210                 :            : 
     211                 :          0 :         r = rename_noreplace(AT_FDCWD, temp_path, AT_FDCWD, final_path);
     212         [ #  # ]:          0 :         if (r < 0) {
     213         [ #  # ]:          0 :                 log_error_errno(r, "Failed to move image into place: %m");
     214                 :          0 :                 goto finish;
     215                 :            :         }
     216                 :            : 
     217                 :          0 :         temp_path = mfree(temp_path);
     218                 :            : 
     219         [ #  # ]:          0 :         log_info("Exiting.");
     220                 :            : 
     221                 :          0 : finish:
     222                 :            :         /* Put old signal handlers into place */
     223         [ #  # ]:          0 :         assert(sigaction(SIGINT, &old_sigint_sa, NULL) >= 0);
     224         [ #  # ]:          0 :         assert(sigaction(SIGTERM, &old_sigterm_sa, NULL) >= 0);
     225                 :            : 
     226                 :          0 :         return 0;
     227                 :            : }
     228                 :            : 
     229                 :         12 : static int help(int argc, char *argv[], void *userdata) {
     230                 :            : 
     231                 :         12 :         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
     232                 :            :                "Import container images from a file system.\n\n"
     233                 :            :                "  -h --help                   Show this help\n"
     234                 :            :                "     --version                Show package version\n"
     235                 :            :                "     --force                  Force creation of image\n"
     236                 :            :                "     --image-root=PATH        Image root directory\n"
     237                 :            :                "     --read-only              Create a read-only image\n\n"
     238                 :            :                "Commands:\n"
     239                 :            :                "  run DIRECTORY [NAME]             Import a directory\n",
     240                 :            :                program_invocation_short_name);
     241                 :            : 
     242                 :         12 :         return 0;
     243                 :            : }
     244                 :            : 
     245                 :         16 : static int parse_argv(int argc, char *argv[]) {
     246                 :            : 
     247                 :            :         enum {
     248                 :            :                 ARG_VERSION = 0x100,
     249                 :            :                 ARG_FORCE,
     250                 :            :                 ARG_IMAGE_ROOT,
     251                 :            :                 ARG_READ_ONLY,
     252                 :            :         };
     253                 :            : 
     254                 :            :         static const struct option options[] = {
     255                 :            :                 { "help",            no_argument,       NULL, 'h'                 },
     256                 :            :                 { "version",         no_argument,       NULL, ARG_VERSION         },
     257                 :            :                 { "force",           no_argument,       NULL, ARG_FORCE           },
     258                 :            :                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
     259                 :            :                 { "read-only",       no_argument,       NULL, ARG_READ_ONLY       },
     260                 :            :                 {}
     261                 :            :         };
     262                 :            : 
     263                 :            :         int c;
     264                 :            : 
     265         [ -  + ]:         16 :         assert(argc >= 0);
     266         [ -  + ]:         16 :         assert(argv);
     267                 :            : 
     268         [ +  - ]:         16 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     269                 :            : 
     270   [ +  -  -  -  :         16 :                 switch (c) {
                -  +  - ]
     271                 :            : 
     272                 :         12 :                 case 'h':
     273                 :         12 :                         return help(0, NULL, NULL);
     274                 :            : 
     275                 :          0 :                 case ARG_VERSION:
     276                 :          0 :                         return version();
     277                 :            : 
     278                 :          0 :                 case ARG_FORCE:
     279                 :          0 :                         arg_force = true;
     280                 :          0 :                         break;
     281                 :            : 
     282                 :          0 :                 case ARG_IMAGE_ROOT:
     283                 :          0 :                         arg_image_root = optarg;
     284                 :          0 :                         break;
     285                 :            : 
     286                 :          0 :                 case ARG_READ_ONLY:
     287                 :          0 :                         arg_read_only = true;
     288                 :          0 :                         break;
     289                 :            : 
     290                 :          4 :                 case '?':
     291                 :          4 :                         return -EINVAL;
     292                 :            : 
     293                 :          0 :                 default:
     294                 :          0 :                         assert_not_reached("Unhandled option");
     295                 :            :                 }
     296                 :            : 
     297                 :          0 :         return 1;
     298                 :            : }
     299                 :            : 
     300                 :          0 : static int import_fs_main(int argc, char *argv[]) {
     301                 :            : 
     302                 :            :         static const Verb verbs[] = {
     303                 :            :                 { "help", VERB_ANY, VERB_ANY, 0, help      },
     304                 :            :                 { "run",  2,        3,        0, import_fs },
     305                 :            :                 {}
     306                 :            :         };
     307                 :            : 
     308                 :          0 :         return dispatch_verb(argc, argv, verbs, NULL);
     309                 :            : }
     310                 :            : 
     311                 :         16 : int main(int argc, char *argv[]) {
     312                 :            :         int r;
     313                 :            : 
     314                 :         16 :         setlocale(LC_ALL, "");
     315                 :         16 :         log_parse_environment();
     316                 :         16 :         log_open();
     317                 :            : 
     318                 :         16 :         r = parse_argv(argc, argv);
     319         [ +  - ]:         16 :         if (r <= 0)
     320                 :         16 :                 goto finish;
     321                 :            : 
     322                 :          0 :         r = import_fs_main(argc, argv);
     323                 :            : 
     324                 :         16 : finish:
     325                 :         16 :         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
     326                 :            : }

Generated by: LCOV version 1.14