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