]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import-dkr.c
treewide: fix multiple typos
[thirdparty/systemd.git] / src / import / import-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"
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"
3d7415f4 31#include "import-util.h"
ff2670ad
LP
32#include "curl-util.h"
33#include "aufs-util.h"
ff2670ad 34#include "import-job.h"
3d7415f4 35#include "import-common.h"
ff2670ad 36#include "import-dkr.h"
72648326 37
7079cfef
LP
38typedef enum DkrProgress {
39 DKR_SEARCHING,
40 DKR_RESOLVING,
41 DKR_METADATA,
42 DKR_DOWNLOADING,
43 DKR_COPYING,
44} DkrProgress;
45
ff2670ad
LP
46struct DkrImport {
47 sd_event *event;
48 CurlGlue *glue;
72648326 49
ff2670ad
LP
50 char *index_url;
51 char *image_root;
72648326 52
ff2670ad
LP
53 ImportJob *images_job;
54 ImportJob *tags_job;
55 ImportJob *ancestry_job;
56 ImportJob *json_job;
57 ImportJob *layer_job;
72648326
LP
58
59 char *name;
60 char *tag;
61 char *id;
72648326 62
ff2670ad
LP
63 char *response_token;
64 char **response_registries;
72648326
LP
65
66 char **ancestry;
7079cfef 67 unsigned n_ancestry;
72648326
LP
68 unsigned current_ancestry;
69
ff2670ad
LP
70 DkrImportFinished on_finished;
71 void *userdata;
ea1ae8c3 72
ff2670ad
LP
73 char *local;
74 bool force_local;
72648326 75
ff2670ad
LP
76 char *temp_path;
77 char *final_path;
14ed8b92 78
ff2670ad 79 pid_t tar_pid;
72648326
LP
80};
81
82#define PROTOCOL_PREFIX "https://"
72648326
LP
83
84#define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
85#define HEADER_REGISTRY "X-Do" /*the HTTP header for the registry */ "cker-Endpoints:"
86
72648326
LP
87#define LAYERS_MAX 2048
88
ff2670ad 89static void dkr_import_job_on_finished(ImportJob *j);
72648326 90
ff2670ad
LP
91DkrImport* dkr_import_unref(DkrImport *i) {
92 if (!i)
72648326
LP
93 return NULL;
94
ff2670ad
LP
95 if (i->tar_pid > 1) {
96 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
97 (void) wait_for_terminate(i->tar_pid, NULL);
72648326
LP
98 }
99
ff2670ad
LP
100 import_job_unref(i->images_job);
101 import_job_unref(i->tags_job);
102 import_job_unref(i->ancestry_job);
103 import_job_unref(i->json_job);
104 import_job_unref(i->layer_job);
72648326 105
ff2670ad
LP
106 curl_glue_unref(i->glue);
107 sd_event_unref(i->event);
72648326 108
ff2670ad
LP
109 if (i->temp_path) {
110 (void) btrfs_subvol_remove(i->temp_path);
111 (void) rm_rf_dangerous(i->temp_path, false, true, false);
112 free(i->temp_path);
113 }
72648326 114
ff2670ad
LP
115 free(i->name);
116 free(i->tag);
117 free(i->id);
118 free(i->response_token);
119 free(i->response_registries);
120 strv_free(i->ancestry);
121 free(i->final_path);
122 free(i->index_url);
123 free(i->image_root);
124 free(i->local);
125 free(i);
72648326
LP
126
127 return NULL;
128}
129
ff2670ad
LP
130int dkr_import_new(
131 DkrImport **ret,
132 sd_event *event,
133 const char *index_url,
134 const char *image_root,
135 DkrImportFinished on_finished,
136 void *userdata) {
72648326 137
ff2670ad
LP
138 _cleanup_(dkr_import_unrefp) DkrImport *i = NULL;
139 char *e;
140 int r;
72648326 141
ff2670ad
LP
142 assert(ret);
143 assert(index_url);
72648326 144
ff2670ad
LP
145 if (!http_url_is_valid(index_url))
146 return -EINVAL;
72648326 147
ff2670ad
LP
148 i = new0(DkrImport, 1);
149 if (!i)
150 return -ENOMEM;
72648326 151
ff2670ad
LP
152 i->on_finished = on_finished;
153 i->userdata = userdata;
72648326 154
ff2670ad
LP
155 i->image_root = strdup(image_root ?: "/var/lib/machines");
156 if (!i->image_root)
157 return -ENOMEM;
72648326 158
ff2670ad
LP
159 i->index_url = strdup(index_url);
160 if (!i->index_url)
161 return -ENOMEM;
72648326 162
ff2670ad
LP
163 e = endswith(i->index_url, "/");
164 if (e)
165 *e = 0;
72648326 166
ff2670ad
LP
167 if (event)
168 i->event = sd_event_ref(event);
169 else {
170 r = sd_event_default(&i->event);
171 if (r < 0)
172 return r;
173 }
72648326 174
ff2670ad
LP
175 r = curl_glue_new(&i->glue, i->event);
176 if (r < 0)
177 return r;
72648326 178
ff2670ad
LP
179 i->glue->on_finished = import_job_curl_on_finished;
180 i->glue->userdata = i;
14ed8b92 181
ff2670ad
LP
182 *ret = i;
183 i = NULL;
14ed8b92 184
ff2670ad 185 return 0;
72648326
LP
186}
187
7079cfef
LP
188static void dkr_import_report_progress(DkrImport *i, DkrProgress p) {
189 unsigned percent;
190
191 assert(i);
192
193 switch (p) {
194
195 case DKR_SEARCHING:
196 percent = 0;
197 if (i->images_job)
198 percent += i->images_job->progress_percent * 5 / 100;
199 break;
200
201 case DKR_RESOLVING:
202 percent = 5;
203 if (i->tags_job)
204 percent += i->tags_job->progress_percent * 5 / 100;
205 break;
206
207 case DKR_METADATA:
208 percent = 10;
209 if (i->ancestry_job)
210 percent += i->ancestry_job->progress_percent * 5 / 100;
211 if (i->json_job)
212 percent += i->json_job->progress_percent * 5 / 100;
213 break;
214
215 case DKR_DOWNLOADING:
216 percent = 20;
217 percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
218 if (i->layer_job)
219 percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
220
221 break;
222
223 case DKR_COPYING:
224 percent = 95;
225 break;
226
227 default:
228 assert_not_reached("Unknown progress state");
229 }
230
231 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
232 log_debug("Combined progress %u%%", percent);
233}
234
72648326
LP
235static int parse_id(const void *payload, size_t size, char **ret) {
236 _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
237 union json_value v = {};
238 void *json_state = NULL;
239 const char *p;
240 int t;
241
242 assert(payload);
243 assert(ret);
244
245 if (size <= 0)
246 return -EBADMSG;
247
248 if (memchr(payload, 0, size))
249 return -EBADMSG;
250
251 buf = strndup(payload, size);
252 if (!buf)
253 return -ENOMEM;
254
255 p = buf;
256 t = json_tokenize(&p, &id, &v, &json_state, NULL);
257 if (t < 0)
258 return t;
259 if (t != JSON_STRING)
260 return -EBADMSG;
261
262 t = json_tokenize(&p, &other, &v, &json_state, NULL);
263 if (t < 0)
264 return t;
265 if (t != JSON_END)
266 return -EBADMSG;
267
91f4347e 268 if (!dkr_id_is_valid(id))
72648326
LP
269 return -EBADMSG;
270
271 *ret = id;
272 id = NULL;
273
274 return 0;
275}
276
277static int parse_ancestry(const void *payload, size_t size, char ***ret) {
278 _cleanup_free_ char *buf = NULL;
279 void *json_state = NULL;
280 const char *p;
281 enum {
282 STATE_BEGIN,
283 STATE_ITEM,
284 STATE_COMMA,
285 STATE_END,
286 } state = STATE_BEGIN;
287 _cleanup_strv_free_ char **l = NULL;
288 size_t n = 0, allocated = 0;
289
290 if (size <= 0)
291 return -EBADMSG;
292
293 if (memchr(payload, 0, size))
294 return -EBADMSG;
295
296 buf = strndup(payload, size);
297 if (!buf)
298 return -ENOMEM;
299
300 p = buf;
301 for (;;) {
302 _cleanup_free_ char *str;
303 union json_value v = {};
304 int t;
305
306 t = json_tokenize(&p, &str, &v, &json_state, NULL);
307 if (t < 0)
308 return t;
309
310 switch (state) {
311
312 case STATE_BEGIN:
313 if (t == JSON_ARRAY_OPEN)
314 state = STATE_ITEM;
315 else
316 return -EBADMSG;
317
318 break;
319
320 case STATE_ITEM:
321 if (t == JSON_STRING) {
91f4347e 322 if (!dkr_id_is_valid(str))
72648326
LP
323 return -EBADMSG;
324
325 if (n+1 > LAYERS_MAX)
326 return -EFBIG;
327
328 if (!GREEDY_REALLOC(l, allocated, n + 2))
329 return -ENOMEM;
330
331 l[n++] = str;
332 str = NULL;
333 l[n] = NULL;
334
335 state = STATE_COMMA;
336
337 } else if (t == JSON_ARRAY_CLOSE)
338 state = STATE_END;
339 else
340 return -EBADMSG;
341
342 break;
343
344 case STATE_COMMA:
345 if (t == JSON_COMMA)
346 state = STATE_ITEM;
347 else if (t == JSON_ARRAY_CLOSE)
348 state = STATE_END;
349 else
350 return -EBADMSG;
351 break;
352
353 case STATE_END:
354 if (t == JSON_END) {
355
356 if (strv_isempty(l))
357 return -EBADMSG;
358
359 if (!strv_is_uniq(l))
360 return -EBADMSG;
361
362 l = strv_reverse(l);
363
364 *ret = l;
365 l = NULL;
366 return 0;
367 } else
368 return -EBADMSG;
369 }
370
371 }
372}
373
ff2670ad
LP
374static const char *dkr_import_current_layer(DkrImport *i) {
375 assert(i);
72648326 376
ff2670ad 377 if (strv_isempty(i->ancestry))
72648326
LP
378 return NULL;
379
ff2670ad 380 return i->ancestry[i->current_ancestry];
72648326
LP
381}
382
ff2670ad
LP
383static const char *dkr_import_current_base_layer(DkrImport *i) {
384 assert(i);
72648326 385
ff2670ad 386 if (strv_isempty(i->ancestry))
72648326
LP
387 return NULL;
388
ff2670ad 389 if (i->current_ancestry <= 0)
72648326
LP
390 return NULL;
391
ff2670ad 392 return i->ancestry[i->current_ancestry-1];
72648326
LP
393}
394
ff2670ad
LP
395static int dkr_import_add_token(DkrImport *i, ImportJob *j) {
396 const char *t;
72648326 397
ff2670ad
LP
398 assert(i);
399 assert(j);
72648326 400
ff2670ad
LP
401 if (i->response_token)
402 t = strappenda("Authorization: Token ", i->response_token);
403 else
404 t = HEADER_TOKEN " true";
72648326 405
ff2670ad
LP
406 j->request_header = curl_slist_new("Accept: application/json", t, NULL);
407 if (!j->request_header)
408 return -ENOMEM;
72648326 409
ff2670ad 410 return 0;
72648326
LP
411}
412
ff2670ad
LP
413static bool dkr_import_is_done(DkrImport *i) {
414 assert(i);
415 assert(i->images_job);
72648326 416
ff2670ad
LP
417 if (i->images_job->state != IMPORT_JOB_DONE)
418 return false;
72648326 419
ff2670ad
LP
420 if (!i->tags_job || i->tags_job->state != IMPORT_JOB_DONE)
421 return false;
72648326 422
ff2670ad
LP
423 if (!i->ancestry_job || i->ancestry_job->state != IMPORT_JOB_DONE)
424 return false;
72648326 425
ff2670ad
LP
426 if (!i->json_job || i->json_job->state != IMPORT_JOB_DONE)
427 return false;
72648326 428
ff2670ad
LP
429 if (i->layer_job && i->layer_job->state != IMPORT_JOB_DONE)
430 return false;
72648326 431
ff2670ad
LP
432 if (dkr_import_current_layer(i))
433 return false;
72648326 434
ff2670ad
LP
435 return true;
436}
72648326 437
ff2670ad
LP
438static int dkr_import_make_local_copy(DkrImport *i) {
439 int r;
72648326 440
ff2670ad 441 assert(i);
72648326 442
ff2670ad
LP
443 if (!i->local)
444 return 0;
72648326 445
ff2670ad
LP
446 if (!i->final_path) {
447 i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
448 if (!i->final_path)
449 return log_oom();
72648326
LP
450 }
451
ff2670ad
LP
452 r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
453 if (r < 0)
454 return r;
455
456 return 0;
72648326
LP
457}
458
ff2670ad 459static int dkr_import_job_on_open_disk(ImportJob *j) {
ff2670ad
LP
460 const char *base;
461 DkrImport *i;
462 int r;
72648326 463
ff2670ad
LP
464 assert(j);
465 assert(j->userdata);
72648326 466
ff2670ad
LP
467 i = j->userdata;
468 assert(i->layer_job == j);
469 assert(i->final_path);
470 assert(!i->temp_path);
471 assert(i->tar_pid <= 0);
72648326 472
ff2670ad
LP
473 r = tempfn_random(i->final_path, &i->temp_path);
474 if (r < 0)
475 return log_oom();
72648326 476
ff2670ad 477 mkdir_parents_label(i->temp_path, 0700);
72648326 478
ff2670ad
LP
479 base = dkr_import_current_base_layer(i);
480 if (base) {
481 const char *base_path;
72648326 482
ff2670ad
LP
483 base_path = strappenda(i->image_root, "/.dkr-", base);
484 r = btrfs_subvol_snapshot(base_path, i->temp_path, false, true);
485 } else
486 r = btrfs_subvol_make(i->temp_path);
487 if (r < 0)
488 return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
72648326 489
2c140ded
LP
490 j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
491 if (j->disk_fd < 0)
492 return j->disk_fd;
72648326 493
72648326
LP
494 return 0;
495}
496
7079cfef
LP
497static void dkr_import_job_on_progress(ImportJob *j) {
498 DkrImport *i;
499
500 assert(j);
501 assert(j->userdata);
502
503 i = j->userdata;
504
505 dkr_import_report_progress(
506 i,
507 j == i->images_job ? DKR_SEARCHING :
508 j == i->tags_job ? DKR_RESOLVING :
509 j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
510 DKR_DOWNLOADING);
511}
512
ff2670ad
LP
513static int dkr_import_pull_layer(DkrImport *i) {
514 _cleanup_free_ char *path = NULL;
515 const char *url, *layer = NULL;
72648326
LP
516 int r;
517
ff2670ad
LP
518 assert(i);
519 assert(!i->layer_job);
520 assert(!i->temp_path);
521 assert(!i->final_path);
72648326
LP
522
523 for (;;) {
ff2670ad
LP
524 layer = dkr_import_current_layer(i);
525 if (!layer)
526 return 0; /* no more layers */
72648326 527
ff2670ad 528 path = strjoin(i->image_root, "/.dkr-", layer, NULL);
72648326
LP
529 if (!path)
530 return log_oom();
531
532 if (laccess(path, F_OK) < 0) {
533 if (errno == ENOENT)
534 break;
535
536 return log_error_errno(errno, "Failed to check for container: %m");
537 }
538
539 log_info("Layer %s already exists, skipping.", layer);
540
ff2670ad 541 i->current_ancestry++;
72648326
LP
542
543 free(path);
544 path = NULL;
545 }
546
ff2670ad 547 log_info("Pulling layer %s...", layer);
72648326 548
ff2670ad
LP
549 i->final_path = path;
550 path = NULL;
72648326 551
ff2670ad
LP
552 url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
553 r = import_job_new(&i->layer_job, url, i->glue, i);
554 if (r < 0)
555 return log_error_errno(r, "Failed to allocate layer job: %m");
72648326 556
ff2670ad 557 r = dkr_import_add_token(i, i->layer_job);
72648326
LP
558 if (r < 0)
559 return log_oom();
560
ff2670ad
LP
561 i->layer_job->on_finished = dkr_import_job_on_finished;
562 i->layer_job->on_open_disk = dkr_import_job_on_open_disk;
7079cfef 563 i->layer_job->on_progress = dkr_import_job_on_progress;
72648326 564
ff2670ad 565 r = import_job_begin(i->layer_job);
72648326 566 if (r < 0)
ff2670ad 567 return log_error_errno(r, "Failed to start layer job: %m");
72648326
LP
568
569 return 0;
570}
571
ff2670ad
LP
572static void dkr_import_job_on_finished(ImportJob *j) {
573 DkrImport *i;
72648326
LP
574 int r;
575
ff2670ad
LP
576 assert(j);
577 assert(j->userdata);
578
579 i = j->userdata;
580 if (j->error != 0) {
581 if (j == i->images_job)
582 log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
583 else if (j == i->tags_job)
584 log_error_errno(j->error, "Failed to retrieve tags list.");
585 else if (j == i->ancestry_job)
586 log_error_errno(j->error, "Failed to retrieve ancestry list.");
587 else if (j == i->json_job)
588 log_error_errno(j->error, "Failed to retrieve json data.");
589 else
590 log_error_errno(j->error, "Failed to retrieve layer data.");
591
592 r = j->error;
593 goto finish;
594 }
72648326 595
ff2670ad 596 if (i->images_job == j) {
72648326 597 const char *url;
72648326 598
ff2670ad
LP
599 assert(!i->tags_job);
600 assert(!i->ancestry_job);
601 assert(!i->json_job);
602 assert(!i->layer_job);
72648326 603
ff2670ad 604 if (strv_isempty(i->response_registries)) {
72648326 605 r = -EBADMSG;
ff2670ad
LP
606 log_error("Didn't get registry information.");
607 goto finish;
72648326
LP
608 }
609
ff2670ad 610 log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
7079cfef 611 dkr_import_report_progress(i, DKR_RESOLVING);
72648326 612
ff2670ad
LP
613 url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
614 r = import_job_new(&i->tags_job, url, i->glue, i);
615 if (r < 0) {
616 log_error_errno(r, "Failed to allocate tags job: %m");
617 goto finish;
618 }
619
620 r = dkr_import_add_token(i, i->tags_job);
621 if (r < 0) {
622 log_oom();
623 goto finish;
624 }
625
626 i->tags_job->on_finished = dkr_import_job_on_finished;
7079cfef 627 i->tags_job->on_progress = dkr_import_job_on_progress;
72648326 628
ff2670ad 629 r = import_job_begin(i->tags_job);
72648326 630 if (r < 0) {
ff2670ad
LP
631 log_error_errno(r, "Failed to start tags job: %m");
632 goto finish;
72648326
LP
633 }
634
ff2670ad 635 } else if (i->tags_job == j) {
72648326 636 const char *url;
ff2670ad 637 char *id = NULL;
72648326 638
ff2670ad
LP
639 assert(!i->ancestry_job);
640 assert(!i->json_job);
641 assert(!i->layer_job);
72648326 642
ff2670ad 643 r = parse_id(j->payload, j->payload_size, &id);
72648326
LP
644 if (r < 0) {
645 log_error_errno(r, "Failed to parse JSON id.");
ff2670ad 646 goto finish;
72648326
LP
647 }
648
ff2670ad
LP
649 free(i->id);
650 i->id = id;
72648326 651
ff2670ad 652 log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
7079cfef 653 dkr_import_report_progress(i, DKR_METADATA);
ff2670ad
LP
654
655 url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
656 r = import_job_new(&i->ancestry_job, url, i->glue, i);
657 if (r < 0) {
658 log_error_errno(r, "Failed to allocate ancestry job: %m");
659 goto finish;
660 }
661
662 r = dkr_import_add_token(i, i->ancestry_job);
663 if (r < 0) {
664 log_oom();
665 goto finish;
666 }
72648326 667
ff2670ad 668 i->ancestry_job->on_finished = dkr_import_job_on_finished;
7079cfef 669 i->ancestry_job->on_progress = dkr_import_job_on_progress;
72648326 670
ff2670ad
LP
671 url = strappenda(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
672 r = import_job_new(&i->json_job, url, i->glue, i);
72648326 673 if (r < 0) {
ff2670ad
LP
674 log_error_errno(r, "Failed to allocate json job: %m");
675 goto finish;
72648326
LP
676 }
677
ff2670ad 678 r = dkr_import_add_token(i, i->json_job);
72648326 679 if (r < 0) {
ff2670ad
LP
680 log_oom();
681 goto finish;
72648326
LP
682 }
683
ff2670ad 684 i->json_job->on_finished = dkr_import_job_on_finished;
7079cfef 685 i->json_job->on_progress = dkr_import_job_on_progress;
ff2670ad
LP
686
687 r = import_job_begin(i->ancestry_job);
688 if (r < 0) {
689 log_error_errno(r, "Failed to start ancestry job: %m");
690 goto finish;
691 }
692
693 r = import_job_begin(i->json_job);
694 if (r < 0) {
695 log_error_errno(r, "Failed to start json job: %m");
696 goto finish;
697 }
698
699 } else if (i->ancestry_job == j) {
700 char **ancestry = NULL, **k;
72648326
LP
701 unsigned n;
702
ff2670ad
LP
703 assert(!i->layer_job);
704
705 r = parse_ancestry(j->payload, j->payload_size, &ancestry);
72648326
LP
706 if (r < 0) {
707 log_error_errno(r, "Failed to parse JSON id.");
ff2670ad 708 goto finish;
72648326
LP
709 }
710
711 n = strv_length(ancestry);
ff2670ad 712 if (n <= 0 || !streq(ancestry[n-1], i->id)) {
72648326 713 log_error("Ancestry doesn't end in main layer.");
ff2670ad 714 strv_free(ancestry);
72648326 715 r = -EBADMSG;
ff2670ad 716 goto finish;
72648326
LP
717 }
718
719 log_info("Ancestor lookup succeeded, requires layers:\n");
ff2670ad
LP
720 STRV_FOREACH(k, ancestry)
721 log_info("\t%s", *k);
72648326 722
ff2670ad
LP
723 strv_free(i->ancestry);
724 i->ancestry = ancestry;
7079cfef 725 i->n_ancestry = n;
ff2670ad 726 i->current_ancestry = 0;
7079cfef
LP
727
728 dkr_import_report_progress(i, DKR_DOWNLOADING);
729
ff2670ad 730 r = dkr_import_pull_layer(i);
72648326 731 if (r < 0)
ff2670ad 732 goto finish;
72648326 733
ff2670ad
LP
734 } else if (i->layer_job == j) {
735 assert(i->temp_path);
736 assert(i->final_path);
72648326 737
ff2670ad 738 j->disk_fd = safe_close(j->disk_fd);
72648326 739
ff2670ad
LP
740 if (i->tar_pid > 0) {
741 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
742 i->tar_pid = 0;
743 if (r < 0)
744 goto finish;
72648326
LP
745 }
746
ff2670ad 747 r = aufs_resolve(i->temp_path);
72648326 748 if (r < 0) {
ff2670ad
LP
749 log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
750 goto finish;
72648326
LP
751 }
752
ff2670ad 753 r = btrfs_subvol_set_read_only(i->temp_path, true);
72648326 754 if (r < 0) {
cc98b302 755 log_error_errno(r, "Failed to mark snapshot read-only: %m");
ff2670ad 756 goto finish;
72648326
LP
757 }
758
ff2670ad
LP
759 if (rename(i->temp_path, i->final_path) < 0) {
760 log_error_errno(errno, "Failed to rename snaphsot: %m");
761 goto finish;
72648326
LP
762 }
763
ff2670ad 764 log_info("Completed writing to layer %s.", i->final_path);
72648326 765
ff2670ad
LP
766 i->layer_job = import_job_unref(i->layer_job);
767 free(i->temp_path);
768 i->temp_path = NULL;
769 free(i->final_path);
770 i->final_path = NULL;
72648326 771
ff2670ad
LP
772 i->current_ancestry ++;
773 r = dkr_import_pull_layer(i);
774 if (r < 0)
775 goto finish;
72648326 776
ff2670ad
LP
777 } else if (i->json_job != j)
778 assert_not_reached("Got finished event for unknown curl object");
72648326 779
ff2670ad
LP
780 if (!dkr_import_is_done(i))
781 return;
72648326 782
7079cfef
LP
783 dkr_import_report_progress(i, DKR_COPYING);
784
ff2670ad 785 r = dkr_import_make_local_copy(i);
72648326 786 if (r < 0)
ff2670ad 787 goto finish;
72648326 788
ff2670ad 789 r = 0;
72648326 790
ff2670ad
LP
791finish:
792 if (i->on_finished)
793 i->on_finished(i, r, i->userdata);
794 else
795 sd_event_exit(i->event, r);
72648326
LP
796}
797
ff2670ad 798static int dkr_import_job_on_header(ImportJob *j, const char *header, size_t sz) {
72648326 799 _cleanup_free_ char *registry = NULL;
72648326 800 char *token;
ff2670ad 801 DkrImport *i;
72648326
LP
802 int r;
803
72648326 804 assert(j);
ff2670ad 805 assert(j->userdata);
72648326 806
ff2670ad 807 i = j->userdata;
e9d73334 808
ff2670ad
LP
809 r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
810 if (r < 0)
811 return log_oom();
72648326 812 if (r > 0) {
ff2670ad
LP
813 free(i->response_token);
814 i->response_token = token;
815 return 0;
72648326
LP
816 }
817
ff2670ad
LP
818 r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
819 if (r < 0)
820 return log_oom();
72648326 821 if (r > 0) {
ff2670ad 822 char **l, **k;
72648326
LP
823
824 l = strv_split(registry, ",");
ff2670ad
LP
825 if (!l)
826 return log_oom();
72648326 827
ff2670ad
LP
828 STRV_FOREACH(k, l) {
829 if (!hostname_is_valid(*k)) {
72648326
LP
830 log_error("Registry hostname is not valid.");
831 strv_free(l);
ff2670ad 832 return -EBADMSG;
72648326
LP
833 }
834 }
835
ff2670ad
LP
836 strv_free(i->response_registries);
837 i->response_registries = l;
72648326
LP
838 }
839
72648326
LP
840 return 0;
841}
842
ff2670ad
LP
843int dkr_import_pull(DkrImport *i, const char *name, const char *tag, const char *local, bool force_local) {
844 const char *url;
72648326
LP
845 int r;
846
ff2670ad 847 assert(i);
72648326 848
ff2670ad
LP
849 if (!dkr_name_is_valid(name))
850 return -EINVAL;
72648326 851
ff2670ad
LP
852 if (tag && !dkr_tag_is_valid(tag))
853 return -EINVAL;
72648326 854
ff2670ad
LP
855 if (local && !machine_name_is_valid(local))
856 return -EINVAL;
72648326 857
ff2670ad
LP
858 if (i->images_job)
859 return -EBUSY;
72648326 860
ff2670ad
LP
861 if (!tag)
862 tag = "latest";
72648326 863
ff2670ad 864 r = free_and_strdup(&i->local, local);
72648326
LP
865 if (r < 0)
866 return r;
ff2670ad 867 i->force_local = force_local;
72648326 868
ff2670ad 869 r = free_and_strdup(&i->name, name);
72648326
LP
870 if (r < 0)
871 return r;
ff2670ad 872 r = free_and_strdup(&i->tag, tag);
72648326
LP
873 if (r < 0)
874 return r;
875
ff2670ad 876 url = strappenda(i->index_url, "/v1/repositories/", name, "/images");
72648326 877
ff2670ad 878 r = import_job_new(&i->images_job, url, i->glue, i);
72648326
LP
879 if (r < 0)
880 return r;
881
ff2670ad 882 r = dkr_import_add_token(i, i->images_job);
72648326
LP
883 if (r < 0)
884 return r;
885
ff2670ad
LP
886 i->images_job->on_finished = dkr_import_job_on_finished;
887 i->images_job->on_header = dkr_import_job_on_header;
7079cfef 888 i->images_job->on_progress = dkr_import_job_on_progress;
72648326 889
ff2670ad 890 return import_job_begin(i->images_job);
72648326 891}