LCOV - code coverage report
Current view: top level - import - import-fs.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 21 148 14.2 %
Date: 2019-08-22 15:41:25 Functions: 3 10 30.0 %

          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           3 : static int help(int argc, char *argv[], void *userdata) {
     230             : 
     231           3 :         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           3 :         return 0;
     243             : }
     244             : 
     245           4 : 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           4 :         assert(argc >= 0);
     266           4 :         assert(argv);
     267             : 
     268           4 :         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
     269             : 
     270           4 :                 switch (c) {
     271             : 
     272           3 :                 case 'h':
     273           3 :                         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           1 :                 case '?':
     291           1 :                         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           4 : int main(int argc, char *argv[]) {
     312             :         int r;
     313             : 
     314           4 :         setlocale(LC_ALL, "");
     315           4 :         log_parse_environment();
     316           4 :         log_open();
     317             : 
     318           4 :         r = parse_argv(argc, argv);
     319           4 :         if (r <= 0)
     320           4 :                 goto finish;
     321             : 
     322           0 :         r = import_fs_main(argc, argv);
     323             : 
     324           4 : finish:
     325           4 :         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
     326             : }

Generated by: LCOV version 1.14