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