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

          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