]>
Commit | Line | Data |
---|---|---|
72648326 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2014 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
9 | under the terms of the GNU Lesser General Public License as published by | |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | Lesser General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU Lesser General Public License | |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
20 | ***/ | |
21 | ||
22 | #include <curl/curl.h> | |
23 | #include <sys/prctl.h> | |
24 | ||
7079cfef | 25 | #include "sd-daemon.h" |
72648326 LP |
26 | #include "json.h" |
27 | #include "strv.h" | |
72648326 | 28 | #include "btrfs-util.h" |
91f4347e | 29 | #include "utf8.h" |
ff2670ad | 30 | #include "mkdir.h" |
c6878637 | 31 | #include "rm-rf.h" |
26166c88 | 32 | #include "path-util.h" |
3d7415f4 | 33 | #include "import-util.h" |
ff2670ad LP |
34 | #include "curl-util.h" |
35 | #include "aufs-util.h" | |
dc2c282b LP |
36 | #include "pull-job.h" |
37 | #include "pull-common.h" | |
b6e676ce | 38 | #include "import-common.h" |
dc2c282b | 39 | #include "pull-dkr.h" |
0b452006 | 40 | #include "process-util.h" |
958b66ea | 41 | #include "hostname-util.h" |
72648326 | 42 | |
7079cfef LP |
43 | typedef enum DkrProgress { |
44 | DKR_SEARCHING, | |
45 | DKR_RESOLVING, | |
46 | DKR_METADATA, | |
47 | DKR_DOWNLOADING, | |
48 | DKR_COPYING, | |
49 | } DkrProgress; | |
50 | ||
dc2c282b | 51 | struct DkrPull { |
ff2670ad LP |
52 | sd_event *event; |
53 | CurlGlue *glue; | |
72648326 | 54 | |
2c4fb0ea PO |
55 | char *index_protocol; |
56 | char *index_address; | |
57 | ||
ff2670ad LP |
58 | char *index_url; |
59 | char *image_root; | |
72648326 | 60 | |
dc2c282b LP |
61 | PullJob *images_job; |
62 | PullJob *tags_job; | |
63 | PullJob *ancestry_job; | |
64 | PullJob *json_job; | |
65 | PullJob *layer_job; | |
72648326 LP |
66 | |
67 | char *name; | |
2c4fb0ea | 68 | char *reference; |
72648326 | 69 | char *id; |
72648326 | 70 | |
2c4fb0ea | 71 | char *response_digest; |
ff2670ad LP |
72 | char *response_token; |
73 | char **response_registries; | |
72648326 LP |
74 | |
75 | char **ancestry; | |
7079cfef | 76 | unsigned n_ancestry; |
72648326 LP |
77 | unsigned current_ancestry; |
78 | ||
dc2c282b | 79 | DkrPullFinished on_finished; |
ff2670ad | 80 | void *userdata; |
ea1ae8c3 | 81 | |
ff2670ad LP |
82 | char *local; |
83 | bool force_local; | |
26166c88 | 84 | bool grow_machine_directory; |
72648326 | 85 | |
ff2670ad LP |
86 | char *temp_path; |
87 | char *final_path; | |
14ed8b92 | 88 | |
ff2670ad | 89 | pid_t tar_pid; |
72648326 LP |
90 | }; |
91 | ||
92 | #define PROTOCOL_PREFIX "https://" | |
72648326 LP |
93 | |
94 | #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:" | |
2c4fb0ea PO |
95 | #define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:" |
96 | #define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:" | |
97 | #define LAYERS_MAX 127 | |
72648326 | 98 | |
dc2c282b | 99 | static void dkr_pull_job_on_finished(PullJob *j); |
72648326 | 100 | |
dc2c282b | 101 | DkrPull* dkr_pull_unref(DkrPull *i) { |
ff2670ad | 102 | if (!i) |
72648326 LP |
103 | return NULL; |
104 | ||
ff2670ad LP |
105 | if (i->tar_pid > 1) { |
106 | (void) kill_and_sigcont(i->tar_pid, SIGKILL); | |
107 | (void) wait_for_terminate(i->tar_pid, NULL); | |
72648326 LP |
108 | } |
109 | ||
dc2c282b LP |
110 | pull_job_unref(i->images_job); |
111 | pull_job_unref(i->tags_job); | |
112 | pull_job_unref(i->ancestry_job); | |
113 | pull_job_unref(i->json_job); | |
114 | pull_job_unref(i->layer_job); | |
72648326 | 115 | |
ff2670ad LP |
116 | curl_glue_unref(i->glue); |
117 | sd_event_unref(i->event); | |
72648326 | 118 | |
ff2670ad | 119 | if (i->temp_path) { |
d9e2daaf | 120 | (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); |
ff2670ad LP |
121 | free(i->temp_path); |
122 | } | |
72648326 | 123 | |
ff2670ad | 124 | free(i->name); |
2c4fb0ea | 125 | free(i->reference); |
ff2670ad LP |
126 | free(i->id); |
127 | free(i->response_token); | |
ff2670ad LP |
128 | strv_free(i->ancestry); |
129 | free(i->final_path); | |
2c4fb0ea PO |
130 | free(i->index_address); |
131 | free(i->index_protocol); | |
ff2670ad LP |
132 | free(i->index_url); |
133 | free(i->image_root); | |
134 | free(i->local); | |
135 | free(i); | |
72648326 LP |
136 | |
137 | return NULL; | |
138 | } | |
139 | ||
dc2c282b LP |
140 | int dkr_pull_new( |
141 | DkrPull **ret, | |
ff2670ad LP |
142 | sd_event *event, |
143 | const char *index_url, | |
144 | const char *image_root, | |
dc2c282b | 145 | DkrPullFinished on_finished, |
ff2670ad | 146 | void *userdata) { |
72648326 | 147 | |
dc2c282b | 148 | _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL; |
ff2670ad LP |
149 | char *e; |
150 | int r; | |
72648326 | 151 | |
ff2670ad LP |
152 | assert(ret); |
153 | assert(index_url); | |
72648326 | 154 | |
ff2670ad LP |
155 | if (!http_url_is_valid(index_url)) |
156 | return -EINVAL; | |
72648326 | 157 | |
dc2c282b | 158 | i = new0(DkrPull, 1); |
ff2670ad LP |
159 | if (!i) |
160 | return -ENOMEM; | |
72648326 | 161 | |
ff2670ad LP |
162 | i->on_finished = on_finished; |
163 | i->userdata = userdata; | |
72648326 | 164 | |
ff2670ad LP |
165 | i->image_root = strdup(image_root ?: "/var/lib/machines"); |
166 | if (!i->image_root) | |
167 | return -ENOMEM; | |
72648326 | 168 | |
26166c88 LP |
169 | i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); |
170 | ||
ff2670ad LP |
171 | i->index_url = strdup(index_url); |
172 | if (!i->index_url) | |
173 | return -ENOMEM; | |
72648326 | 174 | |
ff2670ad LP |
175 | e = endswith(i->index_url, "/"); |
176 | if (e) | |
177 | *e = 0; | |
72648326 | 178 | |
ff2670ad LP |
179 | if (event) |
180 | i->event = sd_event_ref(event); | |
181 | else { | |
182 | r = sd_event_default(&i->event); | |
183 | if (r < 0) | |
184 | return r; | |
185 | } | |
72648326 | 186 | |
ff2670ad LP |
187 | r = curl_glue_new(&i->glue, i->event); |
188 | if (r < 0) | |
189 | return r; | |
72648326 | 190 | |
dc2c282b | 191 | i->glue->on_finished = pull_job_curl_on_finished; |
ff2670ad | 192 | i->glue->userdata = i; |
14ed8b92 | 193 | |
ff2670ad LP |
194 | *ret = i; |
195 | i = NULL; | |
14ed8b92 | 196 | |
ff2670ad | 197 | return 0; |
72648326 LP |
198 | } |
199 | ||
dc2c282b | 200 | static void dkr_pull_report_progress(DkrPull *i, DkrProgress p) { |
7079cfef LP |
201 | unsigned percent; |
202 | ||
203 | assert(i); | |
204 | ||
205 | switch (p) { | |
206 | ||
207 | case DKR_SEARCHING: | |
208 | percent = 0; | |
209 | if (i->images_job) | |
210 | percent += i->images_job->progress_percent * 5 / 100; | |
211 | break; | |
212 | ||
213 | case DKR_RESOLVING: | |
214 | percent = 5; | |
215 | if (i->tags_job) | |
216 | percent += i->tags_job->progress_percent * 5 / 100; | |
217 | break; | |
218 | ||
219 | case DKR_METADATA: | |
220 | percent = 10; | |
221 | if (i->ancestry_job) | |
222 | percent += i->ancestry_job->progress_percent * 5 / 100; | |
223 | if (i->json_job) | |
224 | percent += i->json_job->progress_percent * 5 / 100; | |
225 | break; | |
226 | ||
227 | case DKR_DOWNLOADING: | |
228 | percent = 20; | |
229 | percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry); | |
230 | if (i->layer_job) | |
231 | percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100; | |
232 | ||
233 | break; | |
234 | ||
235 | case DKR_COPYING: | |
236 | percent = 95; | |
237 | break; | |
238 | ||
239 | default: | |
240 | assert_not_reached("Unknown progress state"); | |
241 | } | |
242 | ||
243 | sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); | |
244 | log_debug("Combined progress %u%%", percent); | |
245 | } | |
246 | ||
72648326 LP |
247 | static int parse_id(const void *payload, size_t size, char **ret) { |
248 | _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL; | |
249 | union json_value v = {}; | |
250 | void *json_state = NULL; | |
251 | const char *p; | |
252 | int t; | |
253 | ||
254 | assert(payload); | |
255 | assert(ret); | |
256 | ||
257 | if (size <= 0) | |
258 | return -EBADMSG; | |
259 | ||
260 | if (memchr(payload, 0, size)) | |
261 | return -EBADMSG; | |
262 | ||
263 | buf = strndup(payload, size); | |
264 | if (!buf) | |
265 | return -ENOMEM; | |
266 | ||
267 | p = buf; | |
268 | t = json_tokenize(&p, &id, &v, &json_state, NULL); | |
269 | if (t < 0) | |
270 | return t; | |
271 | if (t != JSON_STRING) | |
272 | return -EBADMSG; | |
273 | ||
274 | t = json_tokenize(&p, &other, &v, &json_state, NULL); | |
275 | if (t < 0) | |
276 | return t; | |
277 | if (t != JSON_END) | |
278 | return -EBADMSG; | |
279 | ||
91f4347e | 280 | if (!dkr_id_is_valid(id)) |
72648326 LP |
281 | return -EBADMSG; |
282 | ||
283 | *ret = id; | |
284 | id = NULL; | |
285 | ||
286 | return 0; | |
287 | } | |
288 | ||
289 | static int parse_ancestry(const void *payload, size_t size, char ***ret) { | |
290 | _cleanup_free_ char *buf = NULL; | |
291 | void *json_state = NULL; | |
292 | const char *p; | |
293 | enum { | |
294 | STATE_BEGIN, | |
295 | STATE_ITEM, | |
296 | STATE_COMMA, | |
297 | STATE_END, | |
298 | } state = STATE_BEGIN; | |
299 | _cleanup_strv_free_ char **l = NULL; | |
300 | size_t n = 0, allocated = 0; | |
301 | ||
302 | if (size <= 0) | |
303 | return -EBADMSG; | |
304 | ||
305 | if (memchr(payload, 0, size)) | |
306 | return -EBADMSG; | |
307 | ||
308 | buf = strndup(payload, size); | |
309 | if (!buf) | |
310 | return -ENOMEM; | |
311 | ||
312 | p = buf; | |
313 | for (;;) { | |
314 | _cleanup_free_ char *str; | |
315 | union json_value v = {}; | |
316 | int t; | |
317 | ||
318 | t = json_tokenize(&p, &str, &v, &json_state, NULL); | |
319 | if (t < 0) | |
320 | return t; | |
321 | ||
322 | switch (state) { | |
323 | ||
324 | case STATE_BEGIN: | |
325 | if (t == JSON_ARRAY_OPEN) | |
326 | state = STATE_ITEM; | |
327 | else | |
328 | return -EBADMSG; | |
329 | ||
330 | break; | |
331 | ||
332 | case STATE_ITEM: | |
333 | if (t == JSON_STRING) { | |
91f4347e | 334 | if (!dkr_id_is_valid(str)) |
72648326 LP |
335 | return -EBADMSG; |
336 | ||
337 | if (n+1 > LAYERS_MAX) | |
338 | return -EFBIG; | |
339 | ||
340 | if (!GREEDY_REALLOC(l, allocated, n + 2)) | |
341 | return -ENOMEM; | |
342 | ||
343 | l[n++] = str; | |
344 | str = NULL; | |
345 | l[n] = NULL; | |
346 | ||
347 | state = STATE_COMMA; | |
348 | ||
349 | } else if (t == JSON_ARRAY_CLOSE) | |
350 | state = STATE_END; | |
351 | else | |
352 | return -EBADMSG; | |
353 | ||
354 | break; | |
355 | ||
356 | case STATE_COMMA: | |
357 | if (t == JSON_COMMA) | |
358 | state = STATE_ITEM; | |
359 | else if (t == JSON_ARRAY_CLOSE) | |
360 | state = STATE_END; | |
361 | else | |
362 | return -EBADMSG; | |
363 | break; | |
364 | ||
365 | case STATE_END: | |
366 | if (t == JSON_END) { | |
367 | ||
368 | if (strv_isempty(l)) | |
369 | return -EBADMSG; | |
370 | ||
371 | if (!strv_is_uniq(l)) | |
372 | return -EBADMSG; | |
373 | ||
374 | l = strv_reverse(l); | |
375 | ||
376 | *ret = l; | |
377 | l = NULL; | |
378 | return 0; | |
379 | } else | |
380 | return -EBADMSG; | |
381 | } | |
382 | ||
383 | } | |
384 | } | |
385 | ||
dc2c282b | 386 | static const char *dkr_pull_current_layer(DkrPull *i) { |
ff2670ad | 387 | assert(i); |
72648326 | 388 | |
ff2670ad | 389 | if (strv_isempty(i->ancestry)) |
72648326 LP |
390 | return NULL; |
391 | ||
ff2670ad | 392 | return i->ancestry[i->current_ancestry]; |
72648326 LP |
393 | } |
394 | ||
dc2c282b | 395 | static const char *dkr_pull_current_base_layer(DkrPull *i) { |
ff2670ad | 396 | assert(i); |
72648326 | 397 | |
ff2670ad | 398 | if (strv_isempty(i->ancestry)) |
72648326 LP |
399 | return NULL; |
400 | ||
ff2670ad | 401 | if (i->current_ancestry <= 0) |
72648326 LP |
402 | return NULL; |
403 | ||
ff2670ad | 404 | return i->ancestry[i->current_ancestry-1]; |
72648326 LP |
405 | } |
406 | ||
dc2c282b | 407 | static int dkr_pull_add_token(DkrPull *i, PullJob *j) { |
ff2670ad | 408 | const char *t; |
72648326 | 409 | |
ff2670ad LP |
410 | assert(i); |
411 | assert(j); | |
72648326 | 412 | |
ff2670ad | 413 | if (i->response_token) |
63c372cb | 414 | t = strjoina("Authorization: Token ", i->response_token); |
ff2670ad LP |
415 | else |
416 | t = HEADER_TOKEN " true"; | |
72648326 | 417 | |
ff2670ad LP |
418 | j->request_header = curl_slist_new("Accept: application/json", t, NULL); |
419 | if (!j->request_header) | |
420 | return -ENOMEM; | |
72648326 | 421 | |
ff2670ad | 422 | return 0; |
72648326 LP |
423 | } |
424 | ||
2c4fb0ea PO |
425 | static int dkr_pull_add_bearer_token(DkrPull *i, PullJob *j) { |
426 | const char *t = NULL; | |
427 | ||
428 | assert(i); | |
429 | assert(j); | |
430 | ||
431 | if (i->response_token) | |
432 | t = strjoina("Authorization: Bearer ", i->response_token); | |
433 | else | |
434 | return -EINVAL; | |
435 | ||
436 | j->request_header = curl_slist_new("Accept: application/json", t, NULL); | |
437 | if (!j->request_header) | |
438 | return -ENOMEM; | |
439 | ||
440 | return 0; | |
441 | } | |
442 | ||
dc2c282b | 443 | static bool dkr_pull_is_done(DkrPull *i) { |
ff2670ad LP |
444 | assert(i); |
445 | assert(i->images_job); | |
dc2c282b | 446 | if (i->images_job->state != PULL_JOB_DONE) |
ff2670ad | 447 | return false; |
72648326 | 448 | |
dc2c282b | 449 | if (!i->tags_job || i->tags_job->state != PULL_JOB_DONE) |
ff2670ad | 450 | return false; |
72648326 | 451 | |
dc2c282b | 452 | if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE) |
ff2670ad | 453 | return false; |
72648326 | 454 | |
2c4fb0ea | 455 | if (i->json_job && i->json_job->state != PULL_JOB_DONE) |
ff2670ad | 456 | return false; |
72648326 | 457 | |
dc2c282b | 458 | if (i->layer_job && i->layer_job->state != PULL_JOB_DONE) |
ff2670ad | 459 | return false; |
72648326 | 460 | |
dc2c282b | 461 | if (dkr_pull_current_layer(i)) |
ff2670ad | 462 | return false; |
72648326 | 463 | |
ff2670ad LP |
464 | return true; |
465 | } | |
72648326 | 466 | |
2c4fb0ea | 467 | static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) { |
ff2670ad | 468 | int r; |
2c4fb0ea | 469 | _cleanup_free_ char *p = NULL; |
72648326 | 470 | |
ff2670ad | 471 | assert(i); |
72648326 | 472 | |
ff2670ad LP |
473 | if (!i->local) |
474 | return 0; | |
72648326 | 475 | |
ff2670ad LP |
476 | if (!i->final_path) { |
477 | i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL); | |
478 | if (!i->final_path) | |
479 | return log_oom(); | |
72648326 LP |
480 | } |
481 | ||
2c4fb0ea PO |
482 | if (version == DKR_PULL_V2) { |
483 | r = path_get_parent(i->image_root, &p); | |
484 | if (r < 0) | |
485 | return r; | |
486 | } | |
487 | ||
488 | r = pull_make_local_copy(i->final_path, p ?: i->image_root, i->local, i->force_local); | |
ff2670ad LP |
489 | if (r < 0) |
490 | return r; | |
491 | ||
2c4fb0ea | 492 | if (version == DKR_PULL_V2) { |
5bcd08db LP |
493 | char **k; |
494 | ||
2c4fb0ea | 495 | STRV_FOREACH(k, i->ancestry) { |
5bcd08db LP |
496 | _cleanup_free_ char *d; |
497 | ||
498 | d = strjoin(i->image_root, "/.dkr-", *k, NULL); | |
499 | if (!d) | |
500 | return -ENOMEM; | |
501 | ||
502 | r = btrfs_subvol_remove(d, BTRFS_REMOVE_QUOTA); | |
2c4fb0ea PO |
503 | if (r < 0) |
504 | return r; | |
505 | } | |
506 | ||
507 | r = rmdir(i->image_root); | |
508 | if (r < 0) | |
509 | return r; | |
510 | } | |
511 | ||
ff2670ad | 512 | return 0; |
72648326 LP |
513 | } |
514 | ||
dc2c282b | 515 | static int dkr_pull_job_on_open_disk(PullJob *j) { |
ff2670ad | 516 | const char *base; |
dc2c282b | 517 | DkrPull *i; |
ff2670ad | 518 | int r; |
72648326 | 519 | |
ff2670ad LP |
520 | assert(j); |
521 | assert(j->userdata); | |
72648326 | 522 | |
ff2670ad LP |
523 | i = j->userdata; |
524 | assert(i->layer_job == j); | |
525 | assert(i->final_path); | |
526 | assert(!i->temp_path); | |
527 | assert(i->tar_pid <= 0); | |
72648326 | 528 | |
14bcf25c | 529 | r = tempfn_random(i->final_path, NULL, &i->temp_path); |
ff2670ad LP |
530 | if (r < 0) |
531 | return log_oom(); | |
72648326 | 532 | |
ff2670ad | 533 | mkdir_parents_label(i->temp_path, 0700); |
72648326 | 534 | |
dc2c282b | 535 | base = dkr_pull_current_base_layer(i); |
ff2670ad LP |
536 | if (base) { |
537 | const char *base_path; | |
72648326 | 538 | |
63c372cb | 539 | base_path = strjoina(i->image_root, "/.dkr-", base); |
5bcd08db | 540 | r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_QUOTA); |
ff2670ad LP |
541 | } else |
542 | r = btrfs_subvol_make(i->temp_path); | |
543 | if (r < 0) | |
544 | return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path); | |
72648326 | 545 | |
8c9cfc28 LP |
546 | (void) import_assign_pool_quota_and_warn(i->temp_path); |
547 | ||
587fec42 | 548 | j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); |
2c140ded LP |
549 | if (j->disk_fd < 0) |
550 | return j->disk_fd; | |
72648326 | 551 | |
72648326 LP |
552 | return 0; |
553 | } | |
554 | ||
dc2c282b LP |
555 | static void dkr_pull_job_on_progress(PullJob *j) { |
556 | DkrPull *i; | |
7079cfef LP |
557 | |
558 | assert(j); | |
559 | assert(j->userdata); | |
560 | ||
561 | i = j->userdata; | |
562 | ||
dc2c282b | 563 | dkr_pull_report_progress( |
7079cfef LP |
564 | i, |
565 | j == i->images_job ? DKR_SEARCHING : | |
566 | j == i->tags_job ? DKR_RESOLVING : | |
567 | j == i->ancestry_job || j == i->json_job ? DKR_METADATA : | |
568 | DKR_DOWNLOADING); | |
569 | } | |
570 | ||
2c4fb0ea PO |
571 | static void dkr_pull_job_on_finished_v2(PullJob *j); |
572 | ||
573 | static int dkr_pull_pull_layer_v2(DkrPull *i) { | |
574 | _cleanup_free_ char *path = NULL; | |
575 | const char *url, *layer = NULL; | |
576 | int r; | |
577 | ||
578 | assert(i); | |
579 | assert(!i->layer_job); | |
580 | assert(!i->temp_path); | |
581 | assert(!i->final_path); | |
582 | ||
583 | for (;;) { | |
584 | layer = dkr_pull_current_layer(i); | |
585 | if (!layer) | |
586 | return 0; /* no more layers */ | |
587 | ||
588 | path = strjoin(i->image_root, "/.dkr-", layer, NULL); | |
589 | if (!path) | |
590 | return log_oom(); | |
591 | ||
592 | if (laccess(path, F_OK) < 0) { | |
593 | if (errno == ENOENT) | |
594 | break; | |
595 | ||
596 | return log_error_errno(errno, "Failed to check for container: %m"); | |
597 | } | |
598 | ||
599 | log_info("Layer %s already exists, skipping.", layer); | |
600 | ||
601 | i->current_ancestry++; | |
602 | ||
97b11eed | 603 | path = mfree(path); |
2c4fb0ea PO |
604 | } |
605 | ||
606 | log_info("Pulling layer %s...", layer); | |
607 | ||
608 | i->final_path = path; | |
609 | path = NULL; | |
610 | ||
611 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/blobs/", layer); | |
612 | r = pull_job_new(&i->layer_job, url, i->glue, i); | |
613 | if (r < 0) | |
614 | return log_error_errno(r, "Failed to allocate layer job: %m"); | |
615 | ||
616 | r = dkr_pull_add_bearer_token(i, i->layer_job); | |
617 | if (r < 0) | |
618 | return log_oom(); | |
619 | ||
620 | i->layer_job->on_finished = dkr_pull_job_on_finished_v2; | |
621 | i->layer_job->on_open_disk = dkr_pull_job_on_open_disk; | |
622 | i->layer_job->on_progress = dkr_pull_job_on_progress; | |
623 | i->layer_job->grow_machine_directory = i->grow_machine_directory; | |
624 | ||
625 | r = pull_job_begin(i->layer_job); | |
626 | if (r < 0) | |
627 | return log_error_errno(r, "Failed to start layer job: %m"); | |
628 | ||
629 | return 0; | |
630 | } | |
631 | ||
dc2c282b | 632 | static int dkr_pull_pull_layer(DkrPull *i) { |
ff2670ad LP |
633 | _cleanup_free_ char *path = NULL; |
634 | const char *url, *layer = NULL; | |
72648326 LP |
635 | int r; |
636 | ||
ff2670ad LP |
637 | assert(i); |
638 | assert(!i->layer_job); | |
639 | assert(!i->temp_path); | |
640 | assert(!i->final_path); | |
72648326 LP |
641 | |
642 | for (;;) { | |
dc2c282b | 643 | layer = dkr_pull_current_layer(i); |
ff2670ad LP |
644 | if (!layer) |
645 | return 0; /* no more layers */ | |
72648326 | 646 | |
ff2670ad | 647 | path = strjoin(i->image_root, "/.dkr-", layer, NULL); |
72648326 LP |
648 | if (!path) |
649 | return log_oom(); | |
650 | ||
651 | if (laccess(path, F_OK) < 0) { | |
652 | if (errno == ENOENT) | |
653 | break; | |
654 | ||
655 | return log_error_errno(errno, "Failed to check for container: %m"); | |
656 | } | |
657 | ||
658 | log_info("Layer %s already exists, skipping.", layer); | |
659 | ||
ff2670ad | 660 | i->current_ancestry++; |
72648326 | 661 | |
97b11eed | 662 | path = mfree(path); |
72648326 LP |
663 | } |
664 | ||
ff2670ad | 665 | log_info("Pulling layer %s...", layer); |
72648326 | 666 | |
ff2670ad LP |
667 | i->final_path = path; |
668 | path = NULL; | |
72648326 | 669 | |
63c372cb | 670 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer"); |
dc2c282b | 671 | r = pull_job_new(&i->layer_job, url, i->glue, i); |
ff2670ad LP |
672 | if (r < 0) |
673 | return log_error_errno(r, "Failed to allocate layer job: %m"); | |
72648326 | 674 | |
dc2c282b | 675 | r = dkr_pull_add_token(i, i->layer_job); |
72648326 LP |
676 | if (r < 0) |
677 | return log_oom(); | |
678 | ||
dc2c282b LP |
679 | i->layer_job->on_finished = dkr_pull_job_on_finished; |
680 | i->layer_job->on_open_disk = dkr_pull_job_on_open_disk; | |
681 | i->layer_job->on_progress = dkr_pull_job_on_progress; | |
26166c88 | 682 | i->layer_job->grow_machine_directory = i->grow_machine_directory; |
72648326 | 683 | |
dc2c282b | 684 | r = pull_job_begin(i->layer_job); |
72648326 | 685 | if (r < 0) |
ff2670ad | 686 | return log_error_errno(r, "Failed to start layer job: %m"); |
72648326 LP |
687 | |
688 | return 0; | |
689 | } | |
690 | ||
2c4fb0ea PO |
691 | static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz) { |
692 | _cleanup_free_ char *registry = NULL; | |
693 | char *token, *digest; | |
694 | DkrPull *i; | |
695 | int r; | |
696 | ||
697 | assert(j); | |
698 | assert(j->userdata); | |
699 | ||
700 | i = j->userdata; | |
701 | r = curl_header_strdup(header, sz, HEADER_TOKEN, &token); | |
702 | if (r < 0) | |
703 | return log_oom(); | |
704 | if (r > 0) { | |
705 | free(i->response_token); | |
706 | i->response_token = token; | |
707 | return 0; | |
708 | } | |
709 | ||
710 | r = curl_header_strdup(header, sz, HEADER_DIGEST, &digest); | |
711 | if (r < 0) | |
712 | return log_oom(); | |
713 | if (r > 0) { | |
714 | free(i->response_digest); | |
715 | i->response_digest = digest; | |
716 | return 0; | |
717 | } | |
718 | ||
719 | r = curl_header_strdup(header, sz, HEADER_REGISTRY, ®istry); | |
720 | if (r < 0) | |
721 | return log_oom(); | |
722 | if (r > 0) { | |
723 | char **l, **k; | |
724 | ||
725 | l = strv_split(registry, ","); | |
726 | if (!l) | |
727 | return log_oom(); | |
728 | ||
729 | STRV_FOREACH(k, l) { | |
8fb49443 | 730 | if (!hostname_is_valid(*k, false)) { |
2c4fb0ea PO |
731 | log_error("Registry hostname is not valid."); |
732 | strv_free(l); | |
733 | return -EBADMSG; | |
734 | } | |
735 | } | |
736 | ||
737 | strv_free(i->response_registries); | |
738 | i->response_registries = l; | |
739 | } | |
740 | ||
741 | return 0; | |
742 | } | |
743 | ||
744 | static void dkr_pull_job_on_finished_v2(PullJob *j) { | |
745 | DkrPull *i; | |
746 | int r; | |
747 | ||
748 | assert(j); | |
749 | assert(j->userdata); | |
750 | ||
751 | i = j->userdata; | |
752 | if (j->error != 0) { | |
753 | if (j == i->images_job) | |
754 | log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)"); | |
755 | else if (j == i->ancestry_job) | |
756 | log_error_errno(j->error, "Failed to retrieve manifest."); | |
757 | else if (j == i->json_job) | |
758 | log_error_errno(j->error, "Failed to retrieve json data."); | |
759 | else | |
760 | log_error_errno(j->error, "Failed to retrieve layer data."); | |
761 | ||
762 | r = j->error; | |
763 | goto finish; | |
764 | } | |
765 | ||
766 | if (i->images_job == j) { | |
767 | const char *url; | |
768 | ||
769 | assert(!i->tags_job); | |
770 | assert(!i->ancestry_job); | |
771 | assert(!i->json_job); | |
772 | assert(!i->layer_job); | |
773 | ||
774 | if (strv_isempty(i->response_registries)) { | |
775 | r = -EBADMSG; | |
776 | log_error("Didn't get registry information."); | |
777 | goto finish; | |
778 | } | |
779 | ||
780 | log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]); | |
781 | dkr_pull_report_progress(i, DKR_RESOLVING); | |
782 | ||
783 | url = strjoina(i->index_protocol, "auth.", i->index_address, "/v2/token/?scope=repository:", | |
784 | i->name, ":pull&service=registry.", i->index_address); | |
785 | r = pull_job_new(&i->tags_job, url, i->glue, i); | |
786 | if (r < 0) { | |
787 | log_error_errno(r, "Failed to allocate tags job: %m"); | |
788 | goto finish; | |
789 | } | |
790 | ||
791 | i->tags_job->on_finished = dkr_pull_job_on_finished_v2; | |
792 | i->tags_job->on_progress = dkr_pull_job_on_progress; | |
793 | ||
794 | r = pull_job_begin(i->tags_job); | |
795 | if (r < 0) { | |
796 | log_error_errno(r, "Failed to start tags job: %m"); | |
797 | goto finish; | |
798 | } | |
799 | ||
800 | } else if (i->tags_job == j) { | |
801 | const char *url; | |
45d9a304 | 802 | _cleanup_free_ char *buf; |
dde8bb32 | 803 | _cleanup_json_variant_unref_ JsonVariant *doc = NULL; |
2c4fb0ea PO |
804 | JsonVariant *e = NULL; |
805 | ||
806 | assert(!i->ancestry_job); | |
807 | assert(!i->json_job); | |
808 | assert(!i->layer_job); | |
809 | ||
810 | buf = strndup((const char *)j->payload, j->payload_size); | |
811 | if (!buf) { | |
812 | r = -ENOMEM; | |
813 | log_oom(); | |
814 | goto finish; | |
815 | } | |
816 | ||
817 | r = json_parse(buf, &doc); | |
818 | if (r < 0) { | |
819 | log_error("Unable to parse bearer token\n%s", j->payload); | |
820 | goto finish; | |
821 | } | |
822 | ||
823 | e = json_variant_value(doc, "token"); | |
824 | if (!e || e->type != JSON_VARIANT_STRING) { | |
825 | r = -EBADMSG; | |
826 | log_error("Invalid JSON format for Bearer token"); | |
827 | goto finish; | |
828 | } | |
829 | ||
830 | r = free_and_strdup(&i->response_token, json_variant_string(e)); | |
831 | if (r < 0) { | |
832 | log_oom(); | |
833 | goto finish; | |
834 | } | |
835 | ||
836 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/manifests/", i->reference); | |
837 | r = pull_job_new(&i->ancestry_job, url, i->glue, i); | |
838 | if (r < 0) { | |
839 | log_error_errno(r, "Failed to allocate ancestry job: %m"); | |
840 | goto finish; | |
841 | } | |
842 | ||
843 | r = dkr_pull_add_bearer_token(i, i->ancestry_job); | |
844 | if (r < 0) | |
845 | goto finish; | |
846 | ||
847 | i->ancestry_job->on_finished = dkr_pull_job_on_finished_v2; | |
848 | i->ancestry_job->on_progress = dkr_pull_job_on_progress; | |
849 | i->ancestry_job->on_header = dkr_pull_job_on_header; | |
850 | ||
851 | ||
852 | r = pull_job_begin(i->ancestry_job); | |
853 | if (r < 0) { | |
854 | log_error_errno(r, "Failed to start ancestry job: %m"); | |
855 | goto finish; | |
856 | } | |
857 | ||
858 | } else if (i->ancestry_job == j) { | |
859 | ||
dde8bb32 | 860 | _cleanup_json_variant_unref_ JsonVariant *doc = NULL, *compat = NULL; |
2c4fb0ea PO |
861 | JsonVariant *e = NULL; |
862 | _cleanup_strv_free_ char **ancestry = NULL; | |
863 | size_t allocated = 0, size = 0; | |
864 | char *path = NULL, **k = NULL; | |
865 | ||
866 | r = json_parse((const char *)j->payload, &doc); | |
867 | if (r < 0) { | |
868 | log_error("Invalid JSON Manifest"); | |
869 | goto finish; | |
870 | } | |
871 | ||
872 | e = json_variant_value(doc, "fsLayers"); | |
37591152 | 873 | if (!e || e->type != JSON_VARIANT_ARRAY || e->size == 0) { |
2c4fb0ea PO |
874 | r = -EBADMSG; |
875 | goto finish; | |
876 | } | |
877 | ||
878 | log_info("JSON manifest with schema v%"PRIi64" for %s parsed!", | |
879 | json_variant_integer(json_variant_value(doc, "schemaVersion")), | |
880 | json_variant_string(json_variant_value(doc, "name"))); | |
881 | ||
882 | for (unsigned z = 0; z < e->size; z++) { | |
883 | JsonVariant *f = json_variant_element(e, z), *g = NULL; | |
884 | const char *layer; | |
885 | if (f->type != JSON_VARIANT_OBJECT) { | |
886 | r = -EBADMSG; | |
887 | goto finish; | |
888 | } | |
889 | ||
890 | g = json_variant_value(f, "blobSum"); | |
891 | ||
892 | layer = json_variant_string(g); | |
893 | if (!dkr_digest_is_valid(layer)) { | |
894 | r = -EBADMSG; | |
895 | goto finish; | |
896 | } | |
897 | ||
898 | if (!GREEDY_REALLOC(ancestry, allocated, size + 2)) { | |
899 | r = -ENOMEM; | |
900 | log_oom(); | |
901 | goto finish; | |
902 | } | |
903 | ||
904 | ancestry[size] = strdup(layer); | |
905 | if (!ancestry[size]) { | |
906 | r = -ENOMEM; | |
907 | log_oom(); | |
908 | goto finish; | |
909 | } | |
910 | ||
911 | ancestry[size+1] = NULL; | |
912 | size += 1; | |
913 | } | |
914 | ||
915 | e = json_variant_value(doc, "history"); | |
916 | if (!e || e->type != JSON_VARIANT_ARRAY) { | |
917 | r = -EBADMSG; | |
918 | goto finish; | |
919 | } | |
920 | ||
921 | e = json_variant_element(e, 0); | |
922 | e = json_variant_value(e, "v1Compatibility"); | |
923 | r = json_parse(json_variant_string(e), &compat); | |
924 | if (r < 0) { | |
925 | log_error("Invalid v1Compatibility JSON"); | |
926 | goto finish; | |
927 | } | |
928 | ||
929 | e = json_variant_value(compat, "id"); | |
930 | ||
931 | strv_free(i->ancestry); | |
932 | i->ancestry = strv_reverse(strv_uniq(ancestry)); | |
933 | i->n_ancestry = strv_length(i->ancestry); | |
934 | i->current_ancestry = 0; | |
935 | i->id = strdup(i->ancestry[i->n_ancestry - 1]); | |
936 | if (!i->id) { | |
937 | r = -ENOMEM; | |
938 | log_oom(); | |
939 | goto finish; | |
940 | } | |
941 | path = strjoin(i->image_root, "/.dkr-", json_variant_string(e), NULL); | |
942 | if (!path) { | |
943 | r = -ENOMEM; | |
944 | log_oom(); | |
945 | goto finish; | |
946 | } | |
947 | free(i->image_root); | |
948 | i->image_root = path; | |
949 | ancestry = NULL; | |
950 | ||
951 | log_info("Required layers:\n"); | |
952 | STRV_FOREACH(k, i->ancestry) | |
953 | log_info("\t%s", *k); | |
954 | log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e), i->response_digest); | |
955 | ||
956 | dkr_pull_report_progress(i, DKR_DOWNLOADING); | |
957 | ||
958 | r = dkr_pull_pull_layer_v2(i); | |
959 | if (r < 0) | |
960 | goto finish; | |
961 | ||
962 | } else if (i->layer_job == j) { | |
963 | assert(i->temp_path); | |
964 | assert(i->final_path); | |
965 | ||
966 | j->disk_fd = safe_close(j->disk_fd); | |
967 | ||
968 | if (i->tar_pid > 0) { | |
969 | r = wait_for_terminate_and_warn("tar", i->tar_pid, true); | |
970 | i->tar_pid = 0; | |
971 | if (r < 0) | |
972 | goto finish; | |
973 | } | |
974 | ||
975 | r = aufs_resolve(i->temp_path); | |
976 | if (r < 0) { | |
977 | log_error_errno(r, "Failed to resolve aufs whiteouts: %m"); | |
978 | goto finish; | |
979 | } | |
980 | ||
981 | r = btrfs_subvol_set_read_only(i->temp_path, true); | |
982 | if (r < 0) { | |
983 | log_error_errno(r, "Failed to mark snapshot read-only: %m"); | |
984 | goto finish; | |
985 | } | |
986 | ||
987 | if (rename(i->temp_path, i->final_path) < 0) { | |
988 | log_error_errno(errno, "Failed to rename snaphsot: %m"); | |
989 | goto finish; | |
990 | } | |
991 | ||
992 | log_info("Completed writing to layer %s.", i->final_path); | |
993 | ||
994 | i->layer_job = pull_job_unref(i->layer_job); | |
995 | free(i->temp_path); | |
996 | i->temp_path = NULL; | |
997 | free(i->final_path); | |
998 | i->final_path = NULL; | |
999 | ||
1000 | i->current_ancestry ++; | |
1001 | r = dkr_pull_pull_layer_v2(i); | |
1002 | if (r < 0) | |
1003 | goto finish; | |
1004 | ||
1005 | } else if (i->json_job != j) | |
1006 | assert_not_reached("Got finished event for unknown curl object"); | |
1007 | ||
1008 | if (!dkr_pull_is_done(i)) | |
1009 | return; | |
1010 | ||
1011 | dkr_pull_report_progress(i, DKR_COPYING); | |
1012 | ||
1013 | r = dkr_pull_make_local_copy(i, DKR_PULL_V2); | |
1014 | if (r < 0) | |
1015 | goto finish; | |
1016 | ||
1017 | r = 0; | |
1018 | ||
1019 | finish: | |
1020 | if (i->on_finished) | |
1021 | i->on_finished(i, r, i->userdata); | |
1022 | else | |
1023 | sd_event_exit(i->event, r); | |
1024 | ||
1025 | } | |
1026 | ||
dc2c282b LP |
1027 | static void dkr_pull_job_on_finished(PullJob *j) { |
1028 | DkrPull *i; | |
72648326 LP |
1029 | int r; |
1030 | ||
ff2670ad LP |
1031 | assert(j); |
1032 | assert(j->userdata); | |
1033 | ||
1034 | i = j->userdata; | |
1035 | if (j->error != 0) { | |
1036 | if (j == i->images_job) | |
1037 | log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)"); | |
1038 | else if (j == i->tags_job) | |
1039 | log_error_errno(j->error, "Failed to retrieve tags list."); | |
1040 | else if (j == i->ancestry_job) | |
1041 | log_error_errno(j->error, "Failed to retrieve ancestry list."); | |
1042 | else if (j == i->json_job) | |
1043 | log_error_errno(j->error, "Failed to retrieve json data."); | |
1044 | else | |
1045 | log_error_errno(j->error, "Failed to retrieve layer data."); | |
1046 | ||
1047 | r = j->error; | |
1048 | goto finish; | |
1049 | } | |
72648326 | 1050 | |
ff2670ad | 1051 | if (i->images_job == j) { |
72648326 | 1052 | const char *url; |
72648326 | 1053 | |
ff2670ad LP |
1054 | assert(!i->tags_job); |
1055 | assert(!i->ancestry_job); | |
1056 | assert(!i->json_job); | |
1057 | assert(!i->layer_job); | |
72648326 | 1058 | |
ff2670ad | 1059 | if (strv_isempty(i->response_registries)) { |
72648326 | 1060 | r = -EBADMSG; |
ff2670ad LP |
1061 | log_error("Didn't get registry information."); |
1062 | goto finish; | |
72648326 LP |
1063 | } |
1064 | ||
ff2670ad | 1065 | log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]); |
dc2c282b | 1066 | dkr_pull_report_progress(i, DKR_RESOLVING); |
72648326 | 1067 | |
2c4fb0ea | 1068 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->reference); |
dc2c282b | 1069 | r = pull_job_new(&i->tags_job, url, i->glue, i); |
ff2670ad LP |
1070 | if (r < 0) { |
1071 | log_error_errno(r, "Failed to allocate tags job: %m"); | |
1072 | goto finish; | |
1073 | } | |
1074 | ||
dc2c282b | 1075 | r = dkr_pull_add_token(i, i->tags_job); |
ff2670ad LP |
1076 | if (r < 0) { |
1077 | log_oom(); | |
1078 | goto finish; | |
1079 | } | |
1080 | ||
dc2c282b LP |
1081 | i->tags_job->on_finished = dkr_pull_job_on_finished; |
1082 | i->tags_job->on_progress = dkr_pull_job_on_progress; | |
72648326 | 1083 | |
dc2c282b | 1084 | r = pull_job_begin(i->tags_job); |
72648326 | 1085 | if (r < 0) { |
ff2670ad LP |
1086 | log_error_errno(r, "Failed to start tags job: %m"); |
1087 | goto finish; | |
72648326 LP |
1088 | } |
1089 | ||
ff2670ad | 1090 | } else if (i->tags_job == j) { |
72648326 | 1091 | const char *url; |
ff2670ad | 1092 | char *id = NULL; |
72648326 | 1093 | |
ff2670ad LP |
1094 | assert(!i->ancestry_job); |
1095 | assert(!i->json_job); | |
1096 | assert(!i->layer_job); | |
72648326 | 1097 | |
ff2670ad | 1098 | r = parse_id(j->payload, j->payload_size, &id); |
72648326 LP |
1099 | if (r < 0) { |
1100 | log_error_errno(r, "Failed to parse JSON id."); | |
ff2670ad | 1101 | goto finish; |
72648326 LP |
1102 | } |
1103 | ||
ff2670ad LP |
1104 | free(i->id); |
1105 | i->id = id; | |
72648326 | 1106 | |
ff2670ad | 1107 | log_info("Tag lookup succeeded, resolved to layer %s.", i->id); |
dc2c282b | 1108 | dkr_pull_report_progress(i, DKR_METADATA); |
ff2670ad | 1109 | |
63c372cb | 1110 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry"); |
dc2c282b | 1111 | r = pull_job_new(&i->ancestry_job, url, i->glue, i); |
ff2670ad LP |
1112 | if (r < 0) { |
1113 | log_error_errno(r, "Failed to allocate ancestry job: %m"); | |
1114 | goto finish; | |
1115 | } | |
1116 | ||
dc2c282b | 1117 | r = dkr_pull_add_token(i, i->ancestry_job); |
ff2670ad LP |
1118 | if (r < 0) { |
1119 | log_oom(); | |
1120 | goto finish; | |
1121 | } | |
72648326 | 1122 | |
dc2c282b LP |
1123 | i->ancestry_job->on_finished = dkr_pull_job_on_finished; |
1124 | i->ancestry_job->on_progress = dkr_pull_job_on_progress; | |
72648326 | 1125 | |
63c372cb | 1126 | url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json"); |
dc2c282b | 1127 | r = pull_job_new(&i->json_job, url, i->glue, i); |
72648326 | 1128 | if (r < 0) { |
ff2670ad LP |
1129 | log_error_errno(r, "Failed to allocate json job: %m"); |
1130 | goto finish; | |
72648326 LP |
1131 | } |
1132 | ||
dc2c282b | 1133 | r = dkr_pull_add_token(i, i->json_job); |
72648326 | 1134 | if (r < 0) { |
ff2670ad LP |
1135 | log_oom(); |
1136 | goto finish; | |
72648326 LP |
1137 | } |
1138 | ||
dc2c282b LP |
1139 | i->json_job->on_finished = dkr_pull_job_on_finished; |
1140 | i->json_job->on_progress = dkr_pull_job_on_progress; | |
ff2670ad | 1141 | |
dc2c282b | 1142 | r = pull_job_begin(i->ancestry_job); |
ff2670ad LP |
1143 | if (r < 0) { |
1144 | log_error_errno(r, "Failed to start ancestry job: %m"); | |
1145 | goto finish; | |
1146 | } | |
1147 | ||
dc2c282b | 1148 | r = pull_job_begin(i->json_job); |
ff2670ad LP |
1149 | if (r < 0) { |
1150 | log_error_errno(r, "Failed to start json job: %m"); | |
1151 | goto finish; | |
1152 | } | |
1153 | ||
1154 | } else if (i->ancestry_job == j) { | |
1155 | char **ancestry = NULL, **k; | |
72648326 LP |
1156 | unsigned n; |
1157 | ||
ff2670ad LP |
1158 | assert(!i->layer_job); |
1159 | ||
1160 | r = parse_ancestry(j->payload, j->payload_size, &ancestry); | |
72648326 LP |
1161 | if (r < 0) { |
1162 | log_error_errno(r, "Failed to parse JSON id."); | |
ff2670ad | 1163 | goto finish; |
72648326 LP |
1164 | } |
1165 | ||
1166 | n = strv_length(ancestry); | |
ff2670ad | 1167 | if (n <= 0 || !streq(ancestry[n-1], i->id)) { |
72648326 | 1168 | log_error("Ancestry doesn't end in main layer."); |
ff2670ad | 1169 | strv_free(ancestry); |
72648326 | 1170 | r = -EBADMSG; |
ff2670ad | 1171 | goto finish; |
72648326 LP |
1172 | } |
1173 | ||
1174 | log_info("Ancestor lookup succeeded, requires layers:\n"); | |
ff2670ad LP |
1175 | STRV_FOREACH(k, ancestry) |
1176 | log_info("\t%s", *k); | |
72648326 | 1177 | |
ff2670ad LP |
1178 | strv_free(i->ancestry); |
1179 | i->ancestry = ancestry; | |
7079cfef | 1180 | i->n_ancestry = n; |
ff2670ad | 1181 | i->current_ancestry = 0; |
7079cfef | 1182 | |
dc2c282b | 1183 | dkr_pull_report_progress(i, DKR_DOWNLOADING); |
7079cfef | 1184 | |
dc2c282b | 1185 | r = dkr_pull_pull_layer(i); |
72648326 | 1186 | if (r < 0) |
ff2670ad | 1187 | goto finish; |
72648326 | 1188 | |
ff2670ad LP |
1189 | } else if (i->layer_job == j) { |
1190 | assert(i->temp_path); | |
1191 | assert(i->final_path); | |
72648326 | 1192 | |
ff2670ad | 1193 | j->disk_fd = safe_close(j->disk_fd); |
72648326 | 1194 | |
ff2670ad LP |
1195 | if (i->tar_pid > 0) { |
1196 | r = wait_for_terminate_and_warn("tar", i->tar_pid, true); | |
1197 | i->tar_pid = 0; | |
1198 | if (r < 0) | |
1199 | goto finish; | |
72648326 LP |
1200 | } |
1201 | ||
ff2670ad | 1202 | r = aufs_resolve(i->temp_path); |
72648326 | 1203 | if (r < 0) { |
ff2670ad LP |
1204 | log_error_errno(r, "Failed to resolve aufs whiteouts: %m"); |
1205 | goto finish; | |
72648326 LP |
1206 | } |
1207 | ||
ff2670ad | 1208 | r = btrfs_subvol_set_read_only(i->temp_path, true); |
72648326 | 1209 | if (r < 0) { |
cc98b302 | 1210 | log_error_errno(r, "Failed to mark snapshot read-only: %m"); |
ff2670ad | 1211 | goto finish; |
72648326 LP |
1212 | } |
1213 | ||
ff2670ad LP |
1214 | if (rename(i->temp_path, i->final_path) < 0) { |
1215 | log_error_errno(errno, "Failed to rename snaphsot: %m"); | |
1216 | goto finish; | |
72648326 LP |
1217 | } |
1218 | ||
ff2670ad | 1219 | log_info("Completed writing to layer %s.", i->final_path); |
72648326 | 1220 | |
dc2c282b | 1221 | i->layer_job = pull_job_unref(i->layer_job); |
a1e58e8e LP |
1222 | i->temp_path = mfree(i->temp_path); |
1223 | i->final_path = mfree(i->final_path); | |
72648326 | 1224 | |
ff2670ad | 1225 | i->current_ancestry ++; |
dc2c282b | 1226 | r = dkr_pull_pull_layer(i); |
ff2670ad LP |
1227 | if (r < 0) |
1228 | goto finish; | |
72648326 | 1229 | |
ff2670ad LP |
1230 | } else if (i->json_job != j) |
1231 | assert_not_reached("Got finished event for unknown curl object"); | |
72648326 | 1232 | |
dc2c282b | 1233 | if (!dkr_pull_is_done(i)) |
ff2670ad | 1234 | return; |
72648326 | 1235 | |
dc2c282b | 1236 | dkr_pull_report_progress(i, DKR_COPYING); |
7079cfef | 1237 | |
2c4fb0ea | 1238 | r = dkr_pull_make_local_copy(i, DKR_PULL_V1); |
72648326 | 1239 | if (r < 0) |
ff2670ad | 1240 | goto finish; |
72648326 | 1241 | |
ff2670ad | 1242 | r = 0; |
ff2670ad LP |
1243 | finish: |
1244 | if (i->on_finished) | |
1245 | i->on_finished(i, r, i->userdata); | |
1246 | else | |
1247 | sd_event_exit(i->event, r); | |
72648326 LP |
1248 | } |
1249 | ||
2c4fb0ea PO |
1250 | static int get_protocol_address(char **protocol, char **address, const char *url) { |
1251 | const char *sep, *dot; | |
6d9bad91 | 1252 | _cleanup_free_ char *a = NULL, *p = NULL; |
72648326 | 1253 | |
2c4fb0ea PO |
1254 | sep = strstr(url, "://"); |
1255 | if (!sep) | |
1256 | return -EINVAL; | |
e9d73334 | 1257 | |
2c4fb0ea PO |
1258 | dot = strrchr(url, '.'); |
1259 | if (!dot) | |
1260 | return -EINVAL; | |
1261 | dot--; | |
72648326 | 1262 | |
2c4fb0ea PO |
1263 | p = strndup(url, (sep - url) + 3); |
1264 | if (!p) | |
ff2670ad | 1265 | return log_oom(); |
72648326 | 1266 | |
2c4fb0ea PO |
1267 | while (dot > (sep + 3) && *dot != '.') |
1268 | dot--; | |
72648326 | 1269 | |
2c4fb0ea PO |
1270 | a = strdup(dot + 1); |
1271 | if (!a) | |
1272 | return log_oom(); | |
72648326 | 1273 | |
2c4fb0ea PO |
1274 | *address = a; |
1275 | *protocol = p; | |
6d9bad91 | 1276 | a = p = NULL; |
72648326 | 1277 | |
72648326 LP |
1278 | return 0; |
1279 | } | |
1280 | ||
2c4fb0ea | 1281 | int dkr_pull_start(DkrPull *i, const char *name, const char *reference, const char *local, bool force_local, DkrPullVersion version) { |
ff2670ad | 1282 | const char *url; |
72648326 LP |
1283 | int r; |
1284 | ||
ff2670ad | 1285 | assert(i); |
72648326 | 1286 | |
ff2670ad LP |
1287 | if (!dkr_name_is_valid(name)) |
1288 | return -EINVAL; | |
72648326 | 1289 | |
2c4fb0ea | 1290 | if (reference && !dkr_ref_is_valid(reference)) |
ff2670ad | 1291 | return -EINVAL; |
72648326 | 1292 | |
ff2670ad LP |
1293 | if (local && !machine_name_is_valid(local)) |
1294 | return -EINVAL; | |
72648326 | 1295 | |
ff2670ad LP |
1296 | if (i->images_job) |
1297 | return -EBUSY; | |
72648326 | 1298 | |
2c4fb0ea PO |
1299 | if (!reference) |
1300 | reference = "latest"; | |
1301 | ||
1302 | free(i->index_protocol); | |
1303 | free(i->index_address); | |
1304 | r = get_protocol_address(&i->index_protocol, &i->index_address, i->index_url); | |
1305 | if (r < 0) | |
1306 | return r; | |
72648326 | 1307 | |
ff2670ad | 1308 | r = free_and_strdup(&i->local, local); |
72648326 LP |
1309 | if (r < 0) |
1310 | return r; | |
ff2670ad | 1311 | i->force_local = force_local; |
72648326 | 1312 | |
ff2670ad | 1313 | r = free_and_strdup(&i->name, name); |
72648326 LP |
1314 | if (r < 0) |
1315 | return r; | |
2c4fb0ea | 1316 | r = free_and_strdup(&i->reference, reference); |
72648326 LP |
1317 | if (r < 0) |
1318 | return r; | |
1319 | ||
63c372cb | 1320 | url = strjoina(i->index_url, "/v1/repositories/", name, "/images"); |
72648326 | 1321 | |
dc2c282b | 1322 | r = pull_job_new(&i->images_job, url, i->glue, i); |
72648326 LP |
1323 | if (r < 0) |
1324 | return r; | |
1325 | ||
dc2c282b | 1326 | r = dkr_pull_add_token(i, i->images_job); |
72648326 LP |
1327 | if (r < 0) |
1328 | return r; | |
1329 | ||
2c4fb0ea PO |
1330 | if (version == DKR_PULL_V1) |
1331 | i->images_job->on_finished = dkr_pull_job_on_finished; | |
1332 | else | |
1333 | i->images_job->on_finished = dkr_pull_job_on_finished_v2; | |
1334 | ||
dc2c282b LP |
1335 | i->images_job->on_header = dkr_pull_job_on_header; |
1336 | i->images_job->on_progress = dkr_pull_job_on_progress; | |
72648326 | 1337 | |
dc2c282b | 1338 | return pull_job_begin(i->images_job); |
72648326 | 1339 | } |