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

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <fcntl.h>
       4                 :            : #include <sys/stat.h>
       5                 :            : #include <sys/xattr.h>
       6                 :            : 
       7                 :            : #include "alloc-util.h"
       8                 :            : #include "fd-util.h"
       9                 :            : #include "format-util.h"
      10                 :            : #include "gcrypt-util.h"
      11                 :            : #include "hexdecoct.h"
      12                 :            : #include "import-util.h"
      13                 :            : #include "io-util.h"
      14                 :            : #include "machine-pool.h"
      15                 :            : #include "parse-util.h"
      16                 :            : #include "pull-common.h"
      17                 :            : #include "pull-job.h"
      18                 :            : #include "string-util.h"
      19                 :            : #include "strv.h"
      20                 :            : #include "xattr-util.h"
      21                 :            : 
      22                 :          0 : PullJob* pull_job_unref(PullJob *j) {
      23         [ #  # ]:          0 :         if (!j)
      24                 :          0 :                 return NULL;
      25                 :            : 
      26                 :          0 :         curl_glue_remove_and_free(j->glue, j->curl);
      27                 :          0 :         curl_slist_free_all(j->request_header);
      28                 :            : 
      29                 :          0 :         safe_close(j->disk_fd);
      30                 :            : 
      31                 :          0 :         import_compress_free(&j->compress);
      32                 :            : 
      33         [ #  # ]:          0 :         if (j->checksum_context)
      34                 :          0 :                 gcry_md_close(j->checksum_context);
      35                 :            : 
      36                 :          0 :         free(j->url);
      37                 :          0 :         free(j->etag);
      38                 :          0 :         strv_free(j->old_etags);
      39                 :          0 :         free(j->payload);
      40                 :          0 :         free(j->checksum);
      41                 :            : 
      42                 :          0 :         return mfree(j);
      43                 :            : }
      44                 :            : 
      45                 :          0 : static void pull_job_finish(PullJob *j, int ret) {
      46         [ #  # ]:          0 :         assert(j);
      47                 :            : 
      48   [ #  #  #  # ]:          0 :         if (IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED))
      49                 :          0 :                 return;
      50                 :            : 
      51         [ #  # ]:          0 :         if (ret == 0) {
      52                 :          0 :                 j->state = PULL_JOB_DONE;
      53                 :          0 :                 j->progress_percent = 100;
      54         [ #  # ]:          0 :                 log_info("Download of %s complete.", j->url);
      55                 :            :         } else {
      56                 :          0 :                 j->state = PULL_JOB_FAILED;
      57                 :          0 :                 j->error = ret;
      58                 :            :         }
      59                 :            : 
      60         [ #  # ]:          0 :         if (j->on_finished)
      61                 :          0 :                 j->on_finished(j);
      62                 :            : }
      63                 :            : 
      64                 :          0 : static int pull_job_restart(PullJob *j) {
      65                 :            :         int r;
      66                 :          0 :         char *chksum_url = NULL;
      67                 :            : 
      68                 :          0 :         r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url);
      69         [ #  # ]:          0 :         if (r < 0)
      70                 :          0 :                 return r;
      71                 :            : 
      72                 :          0 :         free(j->url);
      73                 :          0 :         j->url = chksum_url;
      74                 :          0 :         j->state = PULL_JOB_INIT;
      75                 :          0 :         j->payload = mfree(j->payload);
      76                 :          0 :         j->payload_size = 0;
      77                 :          0 :         j->payload_allocated = 0;
      78                 :          0 :         j->written_compressed = 0;
      79                 :          0 :         j->written_uncompressed = 0;
      80                 :            : 
      81                 :          0 :         r = pull_job_begin(j);
      82         [ #  # ]:          0 :         if (r < 0)
      83                 :          0 :                 return r;
      84                 :            : 
      85                 :          0 :         return 0;
      86                 :            : }
      87                 :            : 
      88                 :          0 : void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
      89                 :          0 :         PullJob *j = NULL;
      90                 :            :         CURLcode code;
      91                 :            :         long status;
      92                 :            :         int r;
      93                 :            : 
      94         [ #  # ]:          0 :         if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK)
      95                 :          0 :                 return;
      96                 :            : 
      97   [ #  #  #  #  :          0 :         if (!j || IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED))
                   #  # ]
      98                 :          0 :                 return;
      99                 :            : 
     100         [ #  # ]:          0 :         if (result != CURLE_OK) {
     101         [ #  # ]:          0 :                 log_error("Transfer failed: %s", curl_easy_strerror(result));
     102                 :          0 :                 r = -EIO;
     103                 :          0 :                 goto finish;
     104                 :            :         }
     105                 :            : 
     106                 :          0 :         code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
     107         [ #  # ]:          0 :         if (code != CURLE_OK) {
     108         [ #  # ]:          0 :                 log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
     109                 :          0 :                 r = -EIO;
     110                 :          0 :                 goto finish;
     111         [ #  # ]:          0 :         } else if (status == 304) {
     112         [ #  # ]:          0 :                 log_info("Image already downloaded. Skipping download.");
     113                 :          0 :                 j->etag_exists = true;
     114                 :          0 :                 r = 0;
     115                 :          0 :                 goto finish;
     116         [ #  # ]:          0 :         } else if (status >= 300) {
     117   [ #  #  #  # ]:          0 :                 if (status == 404 && j->style == VERIFICATION_PER_FILE) {
     118                 :            : 
     119                 :            :                         /* retry pull job with SHA256SUMS file */
     120                 :          0 :                         r = pull_job_restart(j);
     121         [ #  # ]:          0 :                         if (r < 0)
     122                 :          0 :                                 goto finish;
     123                 :            : 
     124                 :          0 :                         code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status);
     125         [ #  # ]:          0 :                         if (code != CURLE_OK) {
     126         [ #  # ]:          0 :                                 log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
     127                 :          0 :                                 r = -EIO;
     128                 :          0 :                                 goto finish;
     129                 :            :                         }
     130                 :            : 
     131         [ #  # ]:          0 :                         if (status == 0) {
     132                 :          0 :                                 j->style = VERIFICATION_PER_DIRECTORY;
     133                 :          0 :                                 return;
     134                 :            :                         }
     135                 :            :                 }
     136                 :            : 
     137         [ #  # ]:          0 :                 log_error("HTTP request to %s failed with code %li.", j->url, status);
     138                 :          0 :                 r = -EIO;
     139                 :          0 :                 goto finish;
     140         [ #  # ]:          0 :         } else if (status < 200) {
     141         [ #  # ]:          0 :                 log_error("HTTP request to %s finished with unexpected code %li.", j->url, status);
     142                 :          0 :                 r = -EIO;
     143                 :          0 :                 goto finish;
     144                 :            :         }
     145                 :            : 
     146         [ #  # ]:          0 :         if (j->state != PULL_JOB_RUNNING) {
     147         [ #  # ]:          0 :                 log_error("Premature connection termination.");
     148                 :          0 :                 r = -EIO;
     149                 :          0 :                 goto finish;
     150                 :            :         }
     151                 :            : 
     152         [ #  # ]:          0 :         if (j->content_length != (uint64_t) -1 &&
     153         [ #  # ]:          0 :             j->content_length != j->written_compressed) {
     154         [ #  # ]:          0 :                 log_error("Download truncated.");
     155                 :          0 :                 r = -EIO;
     156                 :          0 :                 goto finish;
     157                 :            :         }
     158                 :            : 
     159         [ #  # ]:          0 :         if (j->checksum_context) {
     160                 :            :                 uint8_t *k;
     161                 :            : 
     162                 :          0 :                 k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256);
     163         [ #  # ]:          0 :                 if (!k) {
     164         [ #  # ]:          0 :                         log_error("Failed to get checksum.");
     165                 :          0 :                         r = -EIO;
     166                 :          0 :                         goto finish;
     167                 :            :                 }
     168                 :            : 
     169                 :          0 :                 j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256));
     170         [ #  # ]:          0 :                 if (!j->checksum) {
     171                 :          0 :                         r = log_oom();
     172                 :          0 :                         goto finish;
     173                 :            :                 }
     174                 :            : 
     175         [ #  # ]:          0 :                 log_debug("SHA256 of %s is %s.", j->url, j->checksum);
     176                 :            :         }
     177                 :            : 
     178   [ #  #  #  # ]:          0 :         if (j->disk_fd >= 0 && j->allow_sparse) {
     179                 :            :                 /* Make sure the file size is right, in case the file was
     180                 :            :                  * sparse and we just seeked for the last part */
     181                 :            : 
     182         [ #  # ]:          0 :                 if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) {
     183         [ #  # ]:          0 :                         r = log_error_errno(errno, "Failed to truncate file: %m");
     184                 :          0 :                         goto finish;
     185                 :            :                 }
     186                 :            : 
     187         [ #  # ]:          0 :                 if (j->etag)
     188                 :          0 :                         (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0);
     189         [ #  # ]:          0 :                 if (j->url)
     190                 :          0 :                         (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0);
     191                 :            : 
     192         [ #  # ]:          0 :                 if (j->mtime != 0) {
     193                 :            :                         struct timespec ut[2];
     194                 :            : 
     195                 :          0 :                         timespec_store(&ut[0], j->mtime);
     196                 :          0 :                         ut[1] = ut[0];
     197                 :          0 :                         (void) futimens(j->disk_fd, ut);
     198                 :            : 
     199                 :          0 :                         (void) fd_setcrtime(j->disk_fd, j->mtime);
     200                 :            :                 }
     201                 :            :         }
     202                 :            : 
     203                 :          0 :         r = 0;
     204                 :            : 
     205                 :          0 : finish:
     206                 :          0 :         pull_job_finish(j, r);
     207                 :            : }
     208                 :            : 
     209                 :          0 : static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) {
     210                 :          0 :         PullJob *j = userdata;
     211                 :            :         ssize_t n;
     212                 :            : 
     213         [ #  # ]:          0 :         assert(j);
     214         [ #  # ]:          0 :         assert(p);
     215                 :            : 
     216         [ #  # ]:          0 :         if (sz <= 0)
     217                 :          0 :                 return 0;
     218                 :            : 
     219         [ #  # ]:          0 :         if (j->written_uncompressed + sz < j->written_uncompressed)
     220         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW),
     221                 :            :                                        "File too large, overflow");
     222                 :            : 
     223         [ #  # ]:          0 :         if (j->written_uncompressed + sz > j->uncompressed_max)
     224         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
     225                 :            :                                        "File overly large, refusing");
     226                 :            : 
     227         [ #  # ]:          0 :         if (j->disk_fd >= 0) {
     228                 :            : 
     229         [ #  # ]:          0 :                 if (j->allow_sparse)
     230                 :          0 :                         n = sparse_write(j->disk_fd, p, sz, 64);
     231                 :            :                 else {
     232                 :          0 :                         n = write(j->disk_fd, p, sz);
     233         [ #  # ]:          0 :                         if (n < 0)
     234                 :          0 :                                 n = -errno;
     235                 :            :                 }
     236         [ #  # ]:          0 :                 if (n < 0)
     237         [ #  # ]:          0 :                         return log_error_errno((int) n, "Failed to write file: %m");
     238         [ #  # ]:          0 :                 if ((size_t) n < sz)
     239         [ #  # ]:          0 :                         return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write");
     240                 :            :         } else {
     241                 :            : 
     242         [ #  # ]:          0 :                 if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz))
     243                 :          0 :                         return log_oom();
     244                 :            : 
     245                 :          0 :                 memcpy(j->payload + j->payload_size, p, sz);
     246                 :          0 :                 j->payload_size += sz;
     247                 :            :         }
     248                 :            : 
     249                 :          0 :         j->written_uncompressed += sz;
     250                 :            : 
     251                 :          0 :         return 0;
     252                 :            : }
     253                 :            : 
     254                 :          0 : static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) {
     255                 :            :         int r;
     256                 :            : 
     257         [ #  # ]:          0 :         assert(j);
     258         [ #  # ]:          0 :         assert(p);
     259                 :            : 
     260         [ #  # ]:          0 :         if (sz <= 0)
     261                 :          0 :                 return 0;
     262                 :            : 
     263         [ #  # ]:          0 :         if (j->written_compressed + sz < j->written_compressed)
     264         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "File too large, overflow");
     265                 :            : 
     266         [ #  # ]:          0 :         if (j->written_compressed + sz > j->compressed_max)
     267         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "File overly large, refusing.");
     268                 :            : 
     269         [ #  # ]:          0 :         if (j->content_length != (uint64_t) -1 &&
     270         [ #  # ]:          0 :             j->written_compressed + sz > j->content_length)
     271         [ #  # ]:          0 :                 return log_error_errno(SYNTHETIC_ERRNO(EFBIG),
     272                 :            :                                        "Content length incorrect.");
     273                 :            : 
     274         [ #  # ]:          0 :         if (j->checksum_context)
     275                 :          0 :                 gcry_md_write(j->checksum_context, p, sz);
     276                 :            : 
     277                 :          0 :         r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j);
     278         [ #  # ]:          0 :         if (r < 0)
     279                 :          0 :                 return r;
     280                 :            : 
     281                 :          0 :         j->written_compressed += sz;
     282                 :            : 
     283                 :          0 :         return 0;
     284                 :            : }
     285                 :            : 
     286                 :          0 : static int pull_job_open_disk(PullJob *j) {
     287                 :            :         int r;
     288                 :            : 
     289         [ #  # ]:          0 :         assert(j);
     290                 :            : 
     291         [ #  # ]:          0 :         if (j->on_open_disk) {
     292                 :          0 :                 r = j->on_open_disk(j);
     293         [ #  # ]:          0 :                 if (r < 0)
     294                 :          0 :                         return r;
     295                 :            :         }
     296                 :            : 
     297         [ #  # ]:          0 :         if (j->disk_fd >= 0) {
     298                 :            :                 /* Check if we can do sparse files */
     299                 :            : 
     300         [ #  # ]:          0 :                 if (lseek(j->disk_fd, SEEK_SET, 0) == 0)
     301                 :          0 :                         j->allow_sparse = true;
     302                 :            :                 else {
     303         [ #  # ]:          0 :                         if (errno != ESPIPE)
     304         [ #  # ]:          0 :                                 return log_error_errno(errno, "Failed to seek on file descriptor: %m");
     305                 :            : 
     306                 :          0 :                         j->allow_sparse = false;
     307                 :            :                 }
     308                 :            :         }
     309                 :            : 
     310         [ #  # ]:          0 :         if (j->calc_checksum) {
     311                 :          0 :                 initialize_libgcrypt(false);
     312                 :            : 
     313         [ #  # ]:          0 :                 if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0)
     314         [ #  # ]:          0 :                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
     315                 :            :                                                "Failed to initialize hash context.");
     316                 :            :         }
     317                 :            : 
     318                 :          0 :         return 0;
     319                 :            : }
     320                 :            : 
     321                 :          0 : static int pull_job_detect_compression(PullJob *j) {
     322                 :          0 :         _cleanup_free_ uint8_t *stub = NULL;
     323                 :            :         size_t stub_size;
     324                 :            : 
     325                 :            :         int r;
     326                 :            : 
     327         [ #  # ]:          0 :         assert(j);
     328                 :            : 
     329                 :          0 :         r = import_uncompress_detect(&j->compress, j->payload, j->payload_size);
     330         [ #  # ]:          0 :         if (r < 0)
     331         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to initialize compressor: %m");
     332         [ #  # ]:          0 :         if (r == 0)
     333                 :          0 :                 return 0;
     334                 :            : 
     335         [ #  # ]:          0 :         log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type));
     336                 :            : 
     337                 :          0 :         r = pull_job_open_disk(j);
     338         [ #  # ]:          0 :         if (r < 0)
     339                 :          0 :                 return r;
     340                 :            : 
     341                 :            :         /* Now, take the payload we read so far, and decompress it */
     342                 :          0 :         stub = j->payload;
     343                 :          0 :         stub_size = j->payload_size;
     344                 :            : 
     345                 :          0 :         j->payload = NULL;
     346                 :          0 :         j->payload_size = 0;
     347                 :          0 :         j->payload_allocated = 0;
     348                 :            : 
     349                 :          0 :         j->state = PULL_JOB_RUNNING;
     350                 :            : 
     351                 :          0 :         r = pull_job_write_compressed(j, stub, stub_size);
     352         [ #  # ]:          0 :         if (r < 0)
     353                 :          0 :                 return r;
     354                 :            : 
     355                 :          0 :         return 0;
     356                 :            : }
     357                 :            : 
     358                 :          0 : static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
     359                 :          0 :         PullJob *j = userdata;
     360                 :          0 :         size_t sz = size * nmemb;
     361                 :            :         int r;
     362                 :            : 
     363         [ #  # ]:          0 :         assert(contents);
     364         [ #  # ]:          0 :         assert(j);
     365                 :            : 
     366   [ #  #  #  # ]:          0 :         switch (j->state) {
     367                 :            : 
     368                 :          0 :         case PULL_JOB_ANALYZING:
     369                 :            :                 /* Let's first check what it actually is */
     370                 :            : 
     371         [ #  # ]:          0 :                 if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) {
     372                 :          0 :                         r = log_oom();
     373                 :          0 :                         goto fail;
     374                 :            :                 }
     375                 :            : 
     376                 :          0 :                 memcpy(j->payload + j->payload_size, contents, sz);
     377                 :          0 :                 j->payload_size += sz;
     378                 :            : 
     379                 :          0 :                 r = pull_job_detect_compression(j);
     380         [ #  # ]:          0 :                 if (r < 0)
     381                 :          0 :                         goto fail;
     382                 :            : 
     383                 :          0 :                 break;
     384                 :            : 
     385                 :          0 :         case PULL_JOB_RUNNING:
     386                 :            : 
     387                 :          0 :                 r = pull_job_write_compressed(j, contents, sz);
     388         [ #  # ]:          0 :                 if (r < 0)
     389                 :          0 :                         goto fail;
     390                 :            : 
     391                 :          0 :                 break;
     392                 :            : 
     393                 :          0 :         case PULL_JOB_DONE:
     394                 :            :         case PULL_JOB_FAILED:
     395                 :          0 :                 r = -ESTALE;
     396                 :          0 :                 goto fail;
     397                 :            : 
     398                 :          0 :         default:
     399                 :          0 :                 assert_not_reached("Impossible state.");
     400                 :            :         }
     401                 :            : 
     402                 :          0 :         return sz;
     403                 :            : 
     404                 :          0 : fail:
     405                 :          0 :         pull_job_finish(j, r);
     406                 :          0 :         return 0;
     407                 :            : }
     408                 :            : 
     409                 :          0 : static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
     410                 :          0 :         PullJob *j = userdata;
     411                 :          0 :         size_t sz = size * nmemb;
     412                 :          0 :         _cleanup_free_ char *length = NULL, *last_modified = NULL;
     413                 :            :         char *etag;
     414                 :            :         int r;
     415                 :            : 
     416         [ #  # ]:          0 :         assert(contents);
     417         [ #  # ]:          0 :         assert(j);
     418                 :            : 
     419   [ #  #  #  # ]:          0 :         if (IN_SET(j->state, PULL_JOB_DONE, PULL_JOB_FAILED)) {
     420                 :          0 :                 r = -ESTALE;
     421                 :          0 :                 goto fail;
     422                 :            :         }
     423                 :            : 
     424         [ #  # ]:          0 :         assert(j->state == PULL_JOB_ANALYZING);
     425                 :            : 
     426                 :          0 :         r = curl_header_strdup(contents, sz, "ETag:", &etag);
     427         [ #  # ]:          0 :         if (r < 0) {
     428                 :          0 :                 log_oom();
     429                 :          0 :                 goto fail;
     430                 :            :         }
     431         [ #  # ]:          0 :         if (r > 0) {
     432                 :          0 :                 free(j->etag);
     433                 :          0 :                 j->etag = etag;
     434                 :            : 
     435         [ #  # ]:          0 :                 if (strv_contains(j->old_etags, j->etag)) {
     436         [ #  # ]:          0 :                         log_info("Image already downloaded. Skipping download.");
     437                 :          0 :                         j->etag_exists = true;
     438                 :          0 :                         pull_job_finish(j, 0);
     439                 :          0 :                         return sz;
     440                 :            :                 }
     441                 :            : 
     442                 :          0 :                 return sz;
     443                 :            :         }
     444                 :            : 
     445                 :          0 :         r = curl_header_strdup(contents, sz, "Content-Length:", &length);
     446         [ #  # ]:          0 :         if (r < 0) {
     447                 :          0 :                 log_oom();
     448                 :          0 :                 goto fail;
     449                 :            :         }
     450         [ #  # ]:          0 :         if (r > 0) {
     451                 :          0 :                 (void) safe_atou64(length, &j->content_length);
     452                 :            : 
     453         [ #  # ]:          0 :                 if (j->content_length != (uint64_t) -1) {
     454                 :            :                         char bytes[FORMAT_BYTES_MAX];
     455                 :            : 
     456         [ #  # ]:          0 :                         if (j->content_length > j->compressed_max) {
     457         [ #  # ]:          0 :                                 log_error("Content too large.");
     458                 :          0 :                                 r = -EFBIG;
     459                 :          0 :                                 goto fail;
     460                 :            :                         }
     461                 :            : 
     462         [ #  # ]:          0 :                         log_info("Downloading %s for %s.", format_bytes(bytes, sizeof(bytes), j->content_length), j->url);
     463                 :            :                 }
     464                 :            : 
     465                 :          0 :                 return sz;
     466                 :            :         }
     467                 :            : 
     468                 :          0 :         r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified);
     469         [ #  # ]:          0 :         if (r < 0) {
     470                 :          0 :                 log_oom();
     471                 :          0 :                 goto fail;
     472                 :            :         }
     473         [ #  # ]:          0 :         if (r > 0) {
     474                 :          0 :                 (void) curl_parse_http_time(last_modified, &j->mtime);
     475                 :          0 :                 return sz;
     476                 :            :         }
     477                 :            : 
     478         [ #  # ]:          0 :         if (j->on_header) {
     479                 :          0 :                 r = j->on_header(j, contents, sz);
     480         [ #  # ]:          0 :                 if (r < 0)
     481                 :          0 :                         goto fail;
     482                 :            :         }
     483                 :            : 
     484                 :          0 :         return sz;
     485                 :            : 
     486                 :          0 : fail:
     487                 :          0 :         pull_job_finish(j, r);
     488                 :          0 :         return 0;
     489                 :            : }
     490                 :            : 
     491                 :          0 : static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
     492                 :          0 :         PullJob *j = userdata;
     493                 :            :         unsigned percent;
     494                 :            :         usec_t n;
     495                 :            : 
     496         [ #  # ]:          0 :         assert(j);
     497                 :            : 
     498         [ #  # ]:          0 :         if (dltotal <= 0)
     499                 :          0 :                 return 0;
     500                 :            : 
     501                 :          0 :         percent = ((100 * dlnow) / dltotal);
     502                 :          0 :         n = now(CLOCK_MONOTONIC);
     503                 :            : 
     504         [ #  # ]:          0 :         if (n > j->last_status_usec + USEC_PER_SEC &&
     505   [ #  #  #  # ]:          0 :             percent != j->progress_percent &&
     506                 :            :             dlnow < dltotal) {
     507                 :            :                 char buf[FORMAT_TIMESPAN_MAX];
     508                 :            : 
     509   [ #  #  #  # ]:          0 :                 if (n - j->start_usec > USEC_PER_SEC && dlnow > 0) {
     510                 :            :                         char y[FORMAT_BYTES_MAX];
     511                 :            :                         usec_t left, done;
     512                 :            : 
     513                 :          0 :                         done = n - j->start_usec;
     514                 :          0 :                         left = (usec_t) (((double) done * (double) dltotal) / dlnow) - done;
     515                 :            : 
     516         [ #  # ]:          0 :                         log_info("Got %u%% of %s. %s left at %s/s.",
     517                 :            :                                  percent,
     518                 :            :                                  j->url,
     519                 :            :                                  format_timespan(buf, sizeof(buf), left, USEC_PER_SEC),
     520                 :            :                                  format_bytes(y, sizeof(y), (uint64_t) ((double) dlnow / ((double) done / (double) USEC_PER_SEC))));
     521                 :            :                 } else
     522         [ #  # ]:          0 :                         log_info("Got %u%% of %s.", percent, j->url);
     523                 :            : 
     524                 :          0 :                 j->progress_percent = percent;
     525                 :          0 :                 j->last_status_usec = n;
     526                 :            : 
     527         [ #  # ]:          0 :                 if (j->on_progress)
     528                 :          0 :                         j->on_progress(j);
     529                 :            :         }
     530                 :            : 
     531                 :          0 :         return 0;
     532                 :            : }
     533                 :            : 
     534                 :          0 : int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) {
     535                 :          0 :         _cleanup_(pull_job_unrefp) PullJob *j = NULL;
     536                 :          0 :         _cleanup_free_ char *u = NULL;
     537                 :            : 
     538         [ #  # ]:          0 :         assert(url);
     539         [ #  # ]:          0 :         assert(glue);
     540         [ #  # ]:          0 :         assert(ret);
     541                 :            : 
     542                 :          0 :         u = strdup(url);
     543         [ #  # ]:          0 :         if (!u)
     544                 :          0 :                 return -ENOMEM;
     545                 :            : 
     546                 :          0 :         j = new(PullJob, 1);
     547         [ #  # ]:          0 :         if (!j)
     548                 :          0 :                 return -ENOMEM;
     549                 :            : 
     550                 :          0 :         *j = (PullJob) {
     551                 :            :                 .state = PULL_JOB_INIT,
     552                 :            :                 .disk_fd = -1,
     553                 :            :                 .userdata = userdata,
     554                 :            :                 .glue = glue,
     555                 :            :                 .content_length = (uint64_t) -1,
     556                 :          0 :                 .start_usec = now(CLOCK_MONOTONIC),
     557                 :            :                 .compressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
     558                 :            :                 .uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU, /* 64GB safety limit */
     559                 :            :                 .style = VERIFICATION_STYLE_UNSET,
     560                 :          0 :                 .url = TAKE_PTR(u),
     561                 :            :         };
     562                 :            : 
     563                 :          0 :         *ret = TAKE_PTR(j);
     564                 :            : 
     565                 :          0 :         return 0;
     566                 :            : }
     567                 :            : 
     568                 :          0 : int pull_job_begin(PullJob *j) {
     569                 :            :         int r;
     570                 :            : 
     571         [ #  # ]:          0 :         assert(j);
     572                 :            : 
     573         [ #  # ]:          0 :         if (j->state != PULL_JOB_INIT)
     574                 :          0 :                 return -EBUSY;
     575                 :            : 
     576                 :          0 :         r = curl_glue_make(&j->curl, j->url, j);
     577         [ #  # ]:          0 :         if (r < 0)
     578                 :          0 :                 return r;
     579                 :            : 
     580         [ #  # ]:          0 :         if (!strv_isempty(j->old_etags)) {
     581   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *cc = NULL, *hdr = NULL;
     582                 :            : 
     583                 :          0 :                 cc = strv_join(j->old_etags, ", ");
     584         [ #  # ]:          0 :                 if (!cc)
     585                 :          0 :                         return -ENOMEM;
     586                 :            : 
     587                 :          0 :                 hdr = strjoin("If-None-Match: ", cc);
     588         [ #  # ]:          0 :                 if (!hdr)
     589                 :          0 :                         return -ENOMEM;
     590                 :            : 
     591         [ #  # ]:          0 :                 if (!j->request_header) {
     592                 :          0 :                         j->request_header = curl_slist_new(hdr, NULL);
     593         [ #  # ]:          0 :                         if (!j->request_header)
     594                 :          0 :                                 return -ENOMEM;
     595                 :            :                 } else {
     596                 :            :                         struct curl_slist *l;
     597                 :            : 
     598                 :          0 :                         l = curl_slist_append(j->request_header, hdr);
     599         [ #  # ]:          0 :                         if (!l)
     600                 :          0 :                                 return -ENOMEM;
     601                 :            : 
     602                 :          0 :                         j->request_header = l;
     603                 :            :                 }
     604                 :            :         }
     605                 :            : 
     606         [ #  # ]:          0 :         if (j->request_header) {
     607         [ #  # ]:          0 :                 if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK)
     608                 :          0 :                         return -EIO;
     609                 :            :         }
     610                 :            : 
     611         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK)
     612                 :          0 :                 return -EIO;
     613                 :            : 
     614         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK)
     615                 :          0 :                 return -EIO;
     616                 :            : 
     617         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK)
     618                 :          0 :                 return -EIO;
     619                 :            : 
     620         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK)
     621                 :          0 :                 return -EIO;
     622                 :            : 
     623         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK)
     624                 :          0 :                 return -EIO;
     625                 :            : 
     626         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK)
     627                 :          0 :                 return -EIO;
     628                 :            : 
     629         [ #  # ]:          0 :         if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK)
     630                 :          0 :                 return -EIO;
     631                 :            : 
     632                 :          0 :         r = curl_glue_add(j->glue, j->curl);
     633         [ #  # ]:          0 :         if (r < 0)
     634                 :          0 :                 return r;
     635                 :            : 
     636                 :          0 :         j->state = PULL_JOB_ANALYZING;
     637                 :            : 
     638                 :          0 :         return 0;
     639                 :            : }

Generated by: LCOV version 1.14