LCOV - code coverage report
Current view: top level - import - export-tar.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 161 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 8 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "sd-daemon.h"
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "btrfs-util.h"
       7             : #include "export-tar.h"
       8             : #include "fd-util.h"
       9             : #include "import-common.h"
      10             : #include "process-util.h"
      11             : #include "ratelimit.h"
      12             : #include "string-util.h"
      13             : #include "tmpfile-util.h"
      14             : #include "util.h"
      15             : 
      16             : #define COPY_BUFFER_SIZE (16*1024)
      17             : 
      18             : struct TarExport {
      19             :         sd_event *event;
      20             : 
      21             :         TarExportFinished on_finished;
      22             :         void *userdata;
      23             : 
      24             :         char *path;
      25             :         char *temp_path;
      26             : 
      27             :         int output_fd;
      28             :         int tar_fd;
      29             : 
      30             :         ImportCompress compress;
      31             : 
      32             :         sd_event_source *output_event_source;
      33             : 
      34             :         void *buffer;
      35             :         size_t buffer_size;
      36             :         size_t buffer_allocated;
      37             : 
      38             :         uint64_t written_compressed;
      39             :         uint64_t written_uncompressed;
      40             : 
      41             :         pid_t tar_pid;
      42             : 
      43             :         struct stat st;
      44             :         uint64_t quota_referenced;
      45             : 
      46             :         unsigned last_percent;
      47             :         RateLimit progress_rate_limit;
      48             : 
      49             :         bool eof;
      50             :         bool tried_splice;
      51             : };
      52             : 
      53           0 : TarExport *tar_export_unref(TarExport *e) {
      54           0 :         if (!e)
      55           0 :                 return NULL;
      56             : 
      57           0 :         sd_event_source_unref(e->output_event_source);
      58             : 
      59           0 :         if (e->tar_pid > 1) {
      60           0 :                 (void) kill_and_sigcont(e->tar_pid, SIGKILL);
      61           0 :                 (void) wait_for_terminate(e->tar_pid, NULL);
      62             :         }
      63             : 
      64           0 :         if (e->temp_path) {
      65           0 :                 (void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA);
      66           0 :                 free(e->temp_path);
      67             :         }
      68             : 
      69           0 :         import_compress_free(&e->compress);
      70             : 
      71           0 :         sd_event_unref(e->event);
      72             : 
      73           0 :         safe_close(e->tar_fd);
      74             : 
      75           0 :         free(e->buffer);
      76           0 :         free(e->path);
      77           0 :         return mfree(e);
      78             : }
      79             : 
      80           0 : int tar_export_new(
      81             :                 TarExport **ret,
      82             :                 sd_event *event,
      83             :                 TarExportFinished on_finished,
      84             :                 void *userdata) {
      85             : 
      86           0 :         _cleanup_(tar_export_unrefp) TarExport *e = NULL;
      87             :         int r;
      88             : 
      89           0 :         assert(ret);
      90             : 
      91           0 :         e = new(TarExport, 1);
      92           0 :         if (!e)
      93           0 :                 return -ENOMEM;
      94             : 
      95           0 :         *e = (TarExport) {
      96             :                 .output_fd = -1,
      97             :                 .tar_fd = -1,
      98             :                 .on_finished = on_finished,
      99             :                 .userdata = userdata,
     100             :                 .quota_referenced = (uint64_t) -1,
     101             :                 .last_percent = (unsigned) -1,
     102             :         };
     103             : 
     104           0 :         RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1);
     105             : 
     106           0 :         if (event)
     107           0 :                 e->event = sd_event_ref(event);
     108             :         else {
     109           0 :                 r = sd_event_default(&e->event);
     110           0 :                 if (r < 0)
     111           0 :                         return r;
     112             :         }
     113             : 
     114           0 :         *ret = TAKE_PTR(e);
     115             : 
     116           0 :         return 0;
     117             : }
     118             : 
     119           0 : static void tar_export_report_progress(TarExport *e) {
     120             :         unsigned percent;
     121           0 :         assert(e);
     122             : 
     123             :         /* Do we have any quota info? If not, we don't know anything about the progress */
     124           0 :         if (e->quota_referenced == (uint64_t) -1)
     125           0 :                 return;
     126             : 
     127           0 :         if (e->written_uncompressed >= e->quota_referenced)
     128           0 :                 percent = 100;
     129             :         else
     130           0 :                 percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced);
     131             : 
     132           0 :         if (percent == e->last_percent)
     133           0 :                 return;
     134             : 
     135           0 :         if (!ratelimit_below(&e->progress_rate_limit))
     136           0 :                 return;
     137             : 
     138           0 :         sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
     139           0 :         log_info("Exported %u%%.", percent);
     140             : 
     141           0 :         e->last_percent = percent;
     142             : }
     143             : 
     144           0 : static int tar_export_finish(TarExport *e) {
     145             :         int r;
     146             : 
     147           0 :         assert(e);
     148           0 :         assert(e->tar_fd >= 0);
     149             : 
     150           0 :         if (e->tar_pid > 0) {
     151           0 :                 r = wait_for_terminate_and_check("tar", e->tar_pid, WAIT_LOG);
     152           0 :                 e->tar_pid = 0;
     153           0 :                 if (r < 0)
     154           0 :                         return r;
     155           0 :                 if (r != EXIT_SUCCESS)
     156           0 :                         return -EPROTO;
     157             :         }
     158             : 
     159           0 :         e->tar_fd = safe_close(e->tar_fd);
     160             : 
     161           0 :         return 0;
     162             : }
     163             : 
     164           0 : static int tar_export_process(TarExport *e) {
     165             :         ssize_t l;
     166             :         int r;
     167             : 
     168           0 :         assert(e);
     169             : 
     170           0 :         if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) {
     171             : 
     172           0 :                 l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0);
     173           0 :                 if (l < 0) {
     174           0 :                         if (errno == EAGAIN)
     175           0 :                                 return 0;
     176             : 
     177           0 :                         e->tried_splice = true;
     178           0 :                 } else if (l == 0) {
     179           0 :                         r = tar_export_finish(e);
     180           0 :                         goto finish;
     181             :                 } else {
     182           0 :                         e->written_uncompressed += l;
     183           0 :                         e->written_compressed += l;
     184             : 
     185           0 :                         tar_export_report_progress(e);
     186             : 
     187           0 :                         return 0;
     188             :                 }
     189             :         }
     190             : 
     191           0 :         while (e->buffer_size <= 0) {
     192             :                 uint8_t input[COPY_BUFFER_SIZE];
     193             : 
     194           0 :                 if (e->eof) {
     195           0 :                         r = tar_export_finish(e);
     196           0 :                         goto finish;
     197             :                 }
     198             : 
     199           0 :                 l = read(e->tar_fd, input, sizeof(input));
     200           0 :                 if (l < 0) {
     201           0 :                         r = log_error_errno(errno, "Failed to read tar file: %m");
     202           0 :                         goto finish;
     203             :                 }
     204             : 
     205           0 :                 if (l == 0) {
     206           0 :                         e->eof = true;
     207           0 :                         r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated);
     208             :                 } else {
     209           0 :                         e->written_uncompressed += l;
     210           0 :                         r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated);
     211             :                 }
     212           0 :                 if (r < 0) {
     213           0 :                         r = log_error_errno(r, "Failed to encode: %m");
     214           0 :                         goto finish;
     215             :                 }
     216             :         }
     217             : 
     218           0 :         l = write(e->output_fd, e->buffer, e->buffer_size);
     219           0 :         if (l < 0) {
     220           0 :                 if (errno == EAGAIN)
     221           0 :                         return 0;
     222             : 
     223           0 :                 r = log_error_errno(errno, "Failed to write output file: %m");
     224           0 :                 goto finish;
     225             :         }
     226             : 
     227           0 :         assert((size_t) l <= e->buffer_size);
     228           0 :         memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l);
     229           0 :         e->buffer_size -= l;
     230           0 :         e->written_compressed += l;
     231             : 
     232           0 :         tar_export_report_progress(e);
     233             : 
     234           0 :         return 0;
     235             : 
     236           0 : finish:
     237           0 :         if (e->on_finished)
     238           0 :                 e->on_finished(e, r, e->userdata);
     239             :         else
     240           0 :                 sd_event_exit(e->event, r);
     241             : 
     242           0 :         return 0;
     243             : }
     244             : 
     245           0 : static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     246           0 :         TarExport *i = userdata;
     247             : 
     248           0 :         return tar_export_process(i);
     249             : }
     250             : 
     251           0 : static int tar_export_on_defer(sd_event_source *s, void *userdata) {
     252           0 :         TarExport *i = userdata;
     253             : 
     254           0 :         return tar_export_process(i);
     255             : }
     256             : 
     257           0 : int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) {
     258           0 :         _cleanup_close_ int sfd = -1;
     259             :         int r;
     260             : 
     261           0 :         assert(e);
     262           0 :         assert(path);
     263           0 :         assert(fd >= 0);
     264           0 :         assert(compress < _IMPORT_COMPRESS_TYPE_MAX);
     265           0 :         assert(compress != IMPORT_COMPRESS_UNKNOWN);
     266             : 
     267           0 :         if (e->output_fd >= 0)
     268           0 :                 return -EBUSY;
     269             : 
     270           0 :         sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC);
     271           0 :         if (sfd < 0)
     272           0 :                 return -errno;
     273             : 
     274           0 :         if (fstat(sfd, &e->st) < 0)
     275           0 :                 return -errno;
     276             : 
     277           0 :         r = fd_nonblock(fd, true);
     278           0 :         if (r < 0)
     279           0 :                 return r;
     280             : 
     281           0 :         r = free_and_strdup(&e->path, path);
     282           0 :         if (r < 0)
     283           0 :                 return r;
     284             : 
     285           0 :         e->quota_referenced = (uint64_t) -1;
     286             : 
     287           0 :         if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */
     288             :                 BtrfsQuotaInfo q;
     289             : 
     290           0 :                 r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q);
     291           0 :                 if (r >= 0)
     292           0 :                         e->quota_referenced = q.referenced;
     293             : 
     294           0 :                 e->temp_path = mfree(e->temp_path);
     295             : 
     296           0 :                 r = tempfn_random(path, NULL, &e->temp_path);
     297           0 :                 if (r < 0)
     298           0 :                         return r;
     299             : 
     300             :                 /* Let's try to make a snapshot, if we can, so that the export is atomic */
     301           0 :                 r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE);
     302           0 :                 if (r < 0) {
     303           0 :                         log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path);
     304           0 :                         e->temp_path = mfree(e->temp_path);
     305             :                 }
     306             :         }
     307             : 
     308           0 :         r = import_compress_init(&e->compress, compress);
     309           0 :         if (r < 0)
     310           0 :                 return r;
     311             : 
     312           0 :         r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e);
     313           0 :         if (r == -EPERM) {
     314           0 :                 r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e);
     315           0 :                 if (r < 0)
     316           0 :                         return r;
     317             : 
     318           0 :                 r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON);
     319             :         }
     320           0 :         if (r < 0)
     321           0 :                 return r;
     322             : 
     323           0 :         e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid);
     324           0 :         if (e->tar_fd < 0) {
     325           0 :                 e->output_event_source = sd_event_source_unref(e->output_event_source);
     326           0 :                 return e->tar_fd;
     327             :         }
     328             : 
     329           0 :         e->output_fd = fd;
     330           0 :         return r;
     331             : }

Generated by: LCOV version 1.14