Bug Summary

File:build-scan/../src/import/pull-job.c
Warning:line 564, column 25
Potential leak of memory pointed to by 'j'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name pull-job.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I systemd-pull.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/import/pull-job.c
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
18PullJob* 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
41static 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
60static 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
85void 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
202finish:
203 pull_job_finish(j, r);
204}
205
206static 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
258static 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
295static 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
329static 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
366static 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
412fail:
413 pull_job_finish(j, r);
414 return 0;
415}
416
417static 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
494fail:
495 pull_job_finish(j, r);
496 return 0;
497}
498
499static 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
542int 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)
;
1
Assuming 'url' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
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)
;
4
Assuming 'glue' is non-null
5
Taking false branch
6
Loop condition is false. Exiting loop
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)
;
7
Assuming 'ret' is non-null
8
Taking false branch
9
Loop condition is false. Exiting loop
548
549 j = new0(PullJob, 1)((PullJob*) calloc((1), sizeof(PullJob)));
10
Memory is allocated
550 if (!j)
11
Assuming 'j' is non-null
12
Taking false branch
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)
13
Assuming field 'url' is null
14
Taking true branch
564 return -ENOMEM12;
15
Potential leak of memory pointed to by 'j'
565
566 *ret = TAKE_PTR(j)({ typeof(j) _ptr_ = (j); (j) = ((void*)0); _ptr_; });
567
568 return 0;
569}
570
571int 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}