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

           Branch data     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