]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull-dkr.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / import / pull-dkr.c
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
25 #include "sd-daemon.h"
26
27 #include "alloc-util.h"
28 #include "aufs-util.h"
29 #include "btrfs-util.h"
30 #include "curl-util.h"
31 #include "fd-util.h"
32 #include "fileio.h"
33 #include "fs-util.h"
34 #include "hostname-util.h"
35 #include "import-common.h"
36 #include "import-util.h"
37 #include "json.h"
38 #include "mkdir.h"
39 #include "path-util.h"
40 #include "process-util.h"
41 #include "pull-common.h"
42 #include "pull-dkr.h"
43 #include "pull-job.h"
44 #include "rm-rf.h"
45 #include "string-util.h"
46 #include "strv.h"
47 #include "utf8.h"
48 #include "web-util.h"
49
50 typedef enum DkrProgress {
51 DKR_SEARCHING,
52 DKR_RESOLVING,
53 DKR_METADATA,
54 DKR_DOWNLOADING,
55 DKR_COPYING,
56 } DkrProgress;
57
58 struct DkrPull {
59 sd_event *event;
60 CurlGlue *glue;
61
62 char *index_protocol;
63 char *index_address;
64
65 char *index_url;
66 char *image_root;
67
68 PullJob *images_job;
69 PullJob *tags_job;
70 PullJob *ancestry_job;
71 PullJob *json_job;
72 PullJob *layer_job;
73
74 char *name;
75 char *reference;
76 char *id;
77
78 char *response_digest;
79 char *response_token;
80 char **response_registries;
81
82 char **ancestry;
83 unsigned n_ancestry;
84 unsigned current_ancestry;
85
86 DkrPullFinished on_finished;
87 void *userdata;
88
89 char *local;
90 bool force_local;
91 bool grow_machine_directory;
92
93 char *temp_path;
94 char *final_path;
95
96 pid_t tar_pid;
97 };
98
99 #define PROTOCOL_PREFIX "https://"
100
101 #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
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
105
106 static void dkr_pull_job_on_finished(PullJob *j);
107
108 DkrPull* dkr_pull_unref(DkrPull *i) {
109 if (!i)
110 return NULL;
111
112 if (i->tar_pid > 1) {
113 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
114 (void) wait_for_terminate(i->tar_pid, NULL);
115 }
116
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);
122
123 curl_glue_unref(i->glue);
124 sd_event_unref(i->event);
125
126 if (i->temp_path) {
127 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
128 free(i->temp_path);
129 }
130
131 free(i->name);
132 free(i->reference);
133 free(i->id);
134 free(i->response_token);
135 strv_free(i->ancestry);
136 free(i->final_path);
137 free(i->index_address);
138 free(i->index_protocol);
139 free(i->index_url);
140 free(i->image_root);
141 free(i->local);
142 free(i);
143
144 return NULL;
145 }
146
147 int dkr_pull_new(
148 DkrPull **ret,
149 sd_event *event,
150 const char *index_url,
151 const char *image_root,
152 DkrPullFinished on_finished,
153 void *userdata) {
154
155 _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL;
156 char *e;
157 int r;
158
159 assert(ret);
160 assert(index_url);
161
162 if (!http_url_is_valid(index_url))
163 return -EINVAL;
164
165 i = new0(DkrPull, 1);
166 if (!i)
167 return -ENOMEM;
168
169 i->on_finished = on_finished;
170 i->userdata = userdata;
171
172 i->image_root = strdup(image_root ?: "/var/lib/machines");
173 if (!i->image_root)
174 return -ENOMEM;
175
176 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
177
178 i->index_url = strdup(index_url);
179 if (!i->index_url)
180 return -ENOMEM;
181
182 e = endswith(i->index_url, "/");
183 if (e)
184 *e = 0;
185
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 }
193
194 r = curl_glue_new(&i->glue, i->event);
195 if (r < 0)
196 return r;
197
198 i->glue->on_finished = pull_job_curl_on_finished;
199 i->glue->userdata = i;
200
201 *ret = i;
202 i = NULL;
203
204 return 0;
205 }
206
207 static void dkr_pull_report_progress(DkrPull *i, DkrProgress p) {
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
254 static 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
287 if (!dkr_id_is_valid(id))
288 return -EBADMSG;
289
290 *ret = id;
291 id = NULL;
292
293 return 0;
294 }
295
296 static 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) {
341 if (!dkr_id_is_valid(str))
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
393 static const char *dkr_pull_current_layer(DkrPull *i) {
394 assert(i);
395
396 if (strv_isempty(i->ancestry))
397 return NULL;
398
399 return i->ancestry[i->current_ancestry];
400 }
401
402 static const char *dkr_pull_current_base_layer(DkrPull *i) {
403 assert(i);
404
405 if (strv_isempty(i->ancestry))
406 return NULL;
407
408 if (i->current_ancestry <= 0)
409 return NULL;
410
411 return i->ancestry[i->current_ancestry-1];
412 }
413
414 static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
415 const char *t;
416
417 assert(i);
418 assert(j);
419
420 if (i->response_token)
421 t = strjoina("Authorization: Token ", i->response_token);
422 else
423 t = HEADER_TOKEN " true";
424
425 j->request_header = curl_slist_new("Accept: application/json", t, NULL);
426 if (!j->request_header)
427 return -ENOMEM;
428
429 return 0;
430 }
431
432 static 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
450 static bool dkr_pull_is_done(DkrPull *i) {
451 assert(i);
452 assert(i->images_job);
453 if (i->images_job->state != PULL_JOB_DONE)
454 return false;
455
456 if (!i->tags_job || i->tags_job->state != PULL_JOB_DONE)
457 return false;
458
459 if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
460 return false;
461
462 if (i->json_job && i->json_job->state != PULL_JOB_DONE)
463 return false;
464
465 if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
466 return false;
467
468 if (dkr_pull_current_layer(i))
469 return false;
470
471 return true;
472 }
473
474 static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
475 int r;
476 _cleanup_free_ char *p = NULL;
477
478 assert(i);
479
480 if (!i->local)
481 return 0;
482
483 if (!i->final_path) {
484 i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
485 if (!i->final_path)
486 return -ENOMEM;
487 }
488
489 if (version == DKR_PULL_V2) {
490 p = dirname_malloc(i->image_root);
491 if (!p)
492 return -ENOMEM;
493 }
494
495 r = pull_make_local_copy(i->final_path, p ?: i->image_root, i->local, i->force_local);
496 if (r < 0)
497 return r;
498
499 if (version == DKR_PULL_V2) {
500 char **k;
501
502 STRV_FOREACH(k, i->ancestry) {
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);
510 if (r < 0)
511 return r;
512 }
513
514 r = rmdir(i->image_root);
515 if (r < 0)
516 return r;
517 }
518
519 return 0;
520 }
521
522 static int dkr_pull_job_on_open_disk(PullJob *j) {
523 const char *base;
524 DkrPull *i;
525 int r;
526
527 assert(j);
528 assert(j->userdata);
529
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);
535
536 r = tempfn_random(i->final_path, NULL, &i->temp_path);
537 if (r < 0)
538 return log_oom();
539
540 mkdir_parents_label(i->temp_path, 0700);
541
542 base = dkr_pull_current_base_layer(i);
543 if (base) {
544 const char *base_path;
545
546 base_path = strjoina(i->image_root, "/.dkr-", base);
547 r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_QUOTA);
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);
552
553 (void) import_assign_pool_quota_and_warn(i->temp_path);
554
555 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
556 if (j->disk_fd < 0)
557 return j->disk_fd;
558
559 return 0;
560 }
561
562 static void dkr_pull_job_on_progress(PullJob *j) {
563 DkrPull *i;
564
565 assert(j);
566 assert(j->userdata);
567
568 i = j->userdata;
569
570 dkr_pull_report_progress(
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
578 static void dkr_pull_job_on_finished_v2(PullJob *j);
579
580 static 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
610 path = mfree(path);
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
639 static int dkr_pull_pull_layer(DkrPull *i) {
640 _cleanup_free_ char *path = NULL;
641 const char *url, *layer = NULL;
642 int r;
643
644 assert(i);
645 assert(!i->layer_job);
646 assert(!i->temp_path);
647 assert(!i->final_path);
648
649 for (;;) {
650 layer = dkr_pull_current_layer(i);
651 if (!layer)
652 return 0; /* no more layers */
653
654 path = strjoin(i->image_root, "/.dkr-", layer, NULL);
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
667 i->current_ancestry++;
668
669 path = mfree(path);
670 }
671
672 log_info("Pulling layer %s...", layer);
673
674 i->final_path = path;
675 path = NULL;
676
677 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
678 r = pull_job_new(&i->layer_job, url, i->glue, i);
679 if (r < 0)
680 return log_error_errno(r, "Failed to allocate layer job: %m");
681
682 r = dkr_pull_add_token(i, i->layer_job);
683 if (r < 0)
684 return log_oom();
685
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;
689 i->layer_job->grow_machine_directory = i->grow_machine_directory;
690
691 r = pull_job_begin(i->layer_job);
692 if (r < 0)
693 return log_error_errno(r, "Failed to start layer job: %m");
694
695 return 0;
696 }
697
698 static 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) {
737 if (!hostname_is_valid(*k, false)) {
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
751 static 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;
809 _cleanup_free_ char *buf;
810 _cleanup_json_variant_unref_ JsonVariant *doc = NULL;
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
867 _cleanup_json_variant_unref_ JsonVariant *doc = NULL, *compat = NULL;
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");
880 if (!e || e->type != JSON_VARIANT_ARRAY || e->size == 0) {
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
1026 finish:
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
1034 static void dkr_pull_job_on_finished(PullJob *j) {
1035 DkrPull *i;
1036 int r;
1037
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 }
1057
1058 if (i->images_job == j) {
1059 const char *url;
1060
1061 assert(!i->tags_job);
1062 assert(!i->ancestry_job);
1063 assert(!i->json_job);
1064 assert(!i->layer_job);
1065
1066 if (strv_isempty(i->response_registries)) {
1067 r = -EBADMSG;
1068 log_error("Didn't get registry information.");
1069 goto finish;
1070 }
1071
1072 log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
1073 dkr_pull_report_progress(i, DKR_RESOLVING);
1074
1075 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->reference);
1076 r = pull_job_new(&i->tags_job, url, i->glue, i);
1077 if (r < 0) {
1078 log_error_errno(r, "Failed to allocate tags job: %m");
1079 goto finish;
1080 }
1081
1082 r = dkr_pull_add_token(i, i->tags_job);
1083 if (r < 0) {
1084 log_oom();
1085 goto finish;
1086 }
1087
1088 i->tags_job->on_finished = dkr_pull_job_on_finished;
1089 i->tags_job->on_progress = dkr_pull_job_on_progress;
1090
1091 r = pull_job_begin(i->tags_job);
1092 if (r < 0) {
1093 log_error_errno(r, "Failed to start tags job: %m");
1094 goto finish;
1095 }
1096
1097 } else if (i->tags_job == j) {
1098 const char *url;
1099 char *id = NULL;
1100
1101 assert(!i->ancestry_job);
1102 assert(!i->json_job);
1103 assert(!i->layer_job);
1104
1105 r = parse_id(j->payload, j->payload_size, &id);
1106 if (r < 0) {
1107 log_error_errno(r, "Failed to parse JSON id.");
1108 goto finish;
1109 }
1110
1111 free(i->id);
1112 i->id = id;
1113
1114 log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
1115 dkr_pull_report_progress(i, DKR_METADATA);
1116
1117 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
1118 r = pull_job_new(&i->ancestry_job, url, i->glue, i);
1119 if (r < 0) {
1120 log_error_errno(r, "Failed to allocate ancestry job: %m");
1121 goto finish;
1122 }
1123
1124 r = dkr_pull_add_token(i, i->ancestry_job);
1125 if (r < 0) {
1126 log_oom();
1127 goto finish;
1128 }
1129
1130 i->ancestry_job->on_finished = dkr_pull_job_on_finished;
1131 i->ancestry_job->on_progress = dkr_pull_job_on_progress;
1132
1133 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
1134 r = pull_job_new(&i->json_job, url, i->glue, i);
1135 if (r < 0) {
1136 log_error_errno(r, "Failed to allocate json job: %m");
1137 goto finish;
1138 }
1139
1140 r = dkr_pull_add_token(i, i->json_job);
1141 if (r < 0) {
1142 log_oom();
1143 goto finish;
1144 }
1145
1146 i->json_job->on_finished = dkr_pull_job_on_finished;
1147 i->json_job->on_progress = dkr_pull_job_on_progress;
1148
1149 r = pull_job_begin(i->ancestry_job);
1150 if (r < 0) {
1151 log_error_errno(r, "Failed to start ancestry job: %m");
1152 goto finish;
1153 }
1154
1155 r = pull_job_begin(i->json_job);
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;
1163 unsigned n;
1164
1165 assert(!i->layer_job);
1166
1167 r = parse_ancestry(j->payload, j->payload_size, &ancestry);
1168 if (r < 0) {
1169 log_error_errno(r, "Failed to parse JSON id.");
1170 goto finish;
1171 }
1172
1173 n = strv_length(ancestry);
1174 if (n <= 0 || !streq(ancestry[n-1], i->id)) {
1175 log_error("Ancestry doesn't end in main layer.");
1176 strv_free(ancestry);
1177 r = -EBADMSG;
1178 goto finish;
1179 }
1180
1181 log_info("Ancestor lookup succeeded, requires layers:\n");
1182 STRV_FOREACH(k, ancestry)
1183 log_info("\t%s", *k);
1184
1185 strv_free(i->ancestry);
1186 i->ancestry = ancestry;
1187 i->n_ancestry = n;
1188 i->current_ancestry = 0;
1189
1190 dkr_pull_report_progress(i, DKR_DOWNLOADING);
1191
1192 r = dkr_pull_pull_layer(i);
1193 if (r < 0)
1194 goto finish;
1195
1196 } else if (i->layer_job == j) {
1197 assert(i->temp_path);
1198 assert(i->final_path);
1199
1200 j->disk_fd = safe_close(j->disk_fd);
1201
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;
1207 }
1208
1209 r = aufs_resolve(i->temp_path);
1210 if (r < 0) {
1211 log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
1212 goto finish;
1213 }
1214
1215 r = btrfs_subvol_set_read_only(i->temp_path, true);
1216 if (r < 0) {
1217 log_error_errno(r, "Failed to mark snapshot read-only: %m");
1218 goto finish;
1219 }
1220
1221 if (rename(i->temp_path, i->final_path) < 0) {
1222 log_error_errno(errno, "Failed to rename snaphsot: %m");
1223 goto finish;
1224 }
1225
1226 log_info("Completed writing to layer %s.", i->final_path);
1227
1228 i->layer_job = pull_job_unref(i->layer_job);
1229 i->temp_path = mfree(i->temp_path);
1230 i->final_path = mfree(i->final_path);
1231
1232 i->current_ancestry ++;
1233 r = dkr_pull_pull_layer(i);
1234 if (r < 0)
1235 goto finish;
1236
1237 } else if (i->json_job != j)
1238 assert_not_reached("Got finished event for unknown curl object");
1239
1240 if (!dkr_pull_is_done(i))
1241 return;
1242
1243 dkr_pull_report_progress(i, DKR_COPYING);
1244
1245 r = dkr_pull_make_local_copy(i, DKR_PULL_V1);
1246 if (r < 0)
1247 goto finish;
1248
1249 r = 0;
1250 finish:
1251 if (i->on_finished)
1252 i->on_finished(i, r, i->userdata);
1253 else
1254 sd_event_exit(i->event, r);
1255 }
1256
1257 static int get_protocol_address(char **protocol, char **address, const char *url) {
1258 const char *sep, *dot;
1259 _cleanup_free_ char *a = NULL, *p = NULL;
1260
1261 sep = strstr(url, "://");
1262 if (!sep)
1263 return -EINVAL;
1264
1265 dot = strrchr(url, '.');
1266 if (!dot)
1267 return -EINVAL;
1268 dot--;
1269
1270 p = strndup(url, (sep - url) + 3);
1271 if (!p)
1272 return log_oom();
1273
1274 while (dot > (sep + 3) && *dot != '.')
1275 dot--;
1276
1277 a = strdup(dot + 1);
1278 if (!a)
1279 return log_oom();
1280
1281 *address = a;
1282 *protocol = p;
1283 a = p = NULL;
1284
1285 return 0;
1286 }
1287
1288 int dkr_pull_start(DkrPull *i, const char *name, const char *reference, const char *local, bool force_local, DkrPullVersion version) {
1289 const char *url;
1290 int r;
1291
1292 assert(i);
1293
1294 if (!dkr_name_is_valid(name))
1295 return -EINVAL;
1296
1297 if (reference && !dkr_ref_is_valid(reference))
1298 return -EINVAL;
1299
1300 if (local && !machine_name_is_valid(local))
1301 return -EINVAL;
1302
1303 if (i->images_job)
1304 return -EBUSY;
1305
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;
1314
1315 r = free_and_strdup(&i->local, local);
1316 if (r < 0)
1317 return r;
1318 i->force_local = force_local;
1319
1320 r = free_and_strdup(&i->name, name);
1321 if (r < 0)
1322 return r;
1323 r = free_and_strdup(&i->reference, reference);
1324 if (r < 0)
1325 return r;
1326
1327 url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
1328
1329 r = pull_job_new(&i->images_job, url, i->glue, i);
1330 if (r < 0)
1331 return r;
1332
1333 r = dkr_pull_add_token(i, i->images_job);
1334 if (r < 0)
1335 return r;
1336
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
1342 i->images_job->on_header = dkr_pull_job_on_header;
1343 i->images_job->on_progress = dkr_pull_job_on_progress;
1344
1345 return pull_job_begin(i->images_job);
1346 }