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