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