]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-tar.c
util-lib: split out globbing related calls into glob-util.[ch]
[thirdparty/systemd.git] / src / import / pull-tar.c
CommitLineData
56ebfaf1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 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 <sys/prctl.h>
23#include <curl/curl.h>
24
7079cfef 25#include "sd-daemon.h"
07630cea 26
56ebfaf1 27#include "btrfs-util.h"
07630cea
LP
28#include "copy.h"
29#include "curl-util.h"
3ffd4af2 30#include "fd-util.h"
0d39fa9c 31#include "fileio.h"
f4f15635 32#include "fs-util.h"
07630cea
LP
33#include "hostname-util.h"
34#include "import-common.h"
35#include "import-util.h"
56ebfaf1
LP
36#include "macro.h"
37#include "mkdir.h"
26166c88 38#include "path-util.h"
25300b5a 39#include "process-util.h"
dc2c282b 40#include "pull-common.h"
07630cea 41#include "pull-job.h"
3ffd4af2 42#include "pull-tar.h"
07630cea
LP
43#include "rm-rf.h"
44#include "string-util.h"
45#include "strv.h"
46#include "utf8.h"
47#include "util.h"
49cf4170 48#include "web-util.h"
56ebfaf1 49
7079cfef
LP
50typedef enum TarProgress {
51 TAR_DOWNLOADING,
52 TAR_VERIFYING,
53 TAR_FINALIZING,
54 TAR_COPYING,
55} TarProgress;
56
dc2c282b 57struct TarPull {
56ebfaf1
LP
58 sd_event *event;
59 CurlGlue *glue;
60
61 char *image_root;
62
dc2c282b 63 PullJob *tar_job;
9854730b 64 PullJob *settings_job;
dc2c282b
LP
65 PullJob *checksum_job;
66 PullJob *signature_job;
56ebfaf1 67
dc2c282b 68 TarPullFinished on_finished;
56ebfaf1
LP
69 void *userdata;
70
56ebfaf1
LP
71 char *local;
72 bool force_local;
26166c88 73 bool grow_machine_directory;
9854730b 74 bool settings;
56ebfaf1
LP
75
76 pid_t tar_pid;
77
56ebfaf1 78 char *final_path;
9854730b
LP
79 char *temp_path;
80
81 char *settings_path;
82 char *settings_temp_path;
0100b6e1
LP
83
84 ImportVerify verify;
56ebfaf1
LP
85};
86
dc2c282b 87TarPull* tar_pull_unref(TarPull *i) {
56ebfaf1
LP
88 if (!i)
89 return NULL;
90
0100b6e1 91 if (i->tar_pid > 1) {
8b71fce8 92 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
0100b6e1 93 (void) wait_for_terminate(i->tar_pid, NULL);
56ebfaf1
LP
94 }
95
dc2c282b 96 pull_job_unref(i->tar_job);
9854730b 97 pull_job_unref(i->settings_job);
dc2c282b
LP
98 pull_job_unref(i->checksum_job);
99 pull_job_unref(i->signature_job);
56ebfaf1
LP
100
101 curl_glue_unref(i->glue);
102 sd_event_unref(i->event);
103
104 if (i->temp_path) {
d9e2daaf 105 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
0d6e763b 106 free(i->temp_path);
56ebfaf1
LP
107 }
108
9854730b
LP
109 if (i->settings_temp_path) {
110 (void) unlink(i->settings_temp_path);
111 free(i->settings_temp_path);
112 }
113
56ebfaf1 114 free(i->final_path);
9854730b 115 free(i->settings_path);
56ebfaf1
LP
116 free(i->image_root);
117 free(i->local);
56ebfaf1
LP
118 free(i);
119
120 return NULL;
121}
122
dc2c282b
LP
123int tar_pull_new(
124 TarPull **ret,
8b71fce8
LP
125 sd_event *event,
126 const char *image_root,
dc2c282b 127 TarPullFinished on_finished,
8b71fce8
LP
128 void *userdata) {
129
dc2c282b 130 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
56ebfaf1
LP
131 int r;
132
133 assert(ret);
56ebfaf1 134
dc2c282b 135 i = new0(TarPull, 1);
56ebfaf1
LP
136 if (!i)
137 return -ENOMEM;
138
139 i->on_finished = on_finished;
140 i->userdata = userdata;
141
142 i->image_root = strdup(image_root ?: "/var/lib/machines");
143 if (!i->image_root)
144 return -ENOMEM;
145
26166c88
LP
146 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
147
9854730b
LP
148 if (event)
149 i->event = sd_event_ref(event);
150 else {
151 r = sd_event_default(&i->event);
152 if (r < 0)
153 return r;
154 }
56ebfaf1
LP
155
156 r = curl_glue_new(&i->glue, i->event);
157 if (r < 0)
158 return r;
159
dc2c282b 160 i->glue->on_finished = pull_job_curl_on_finished;
56ebfaf1
LP
161 i->glue->userdata = i;
162
163 *ret = i;
164 i = NULL;
165
166 return 0;
167}
168
dc2c282b 169static void tar_pull_report_progress(TarPull *i, TarProgress p) {
7079cfef
LP
170 unsigned percent;
171
172 assert(i);
173
174 switch (p) {
175
176 case TAR_DOWNLOADING: {
177 unsigned remain = 85;
178
179 percent = 0;
180
9854730b
LP
181 if (i->settings_job) {
182 percent += i->settings_job->progress_percent * 5 / 100;
183 remain -= 5;
184 }
185
7079cfef
LP
186 if (i->checksum_job) {
187 percent += i->checksum_job->progress_percent * 5 / 100;
188 remain -= 5;
189 }
190
191 if (i->signature_job) {
192 percent += i->signature_job->progress_percent * 5 / 100;
193 remain -= 5;
194 }
195
196 if (i->tar_job)
197 percent += i->tar_job->progress_percent * remain / 100;
198 break;
199 }
200
201 case TAR_VERIFYING:
202 percent = 85;
203 break;
204
205 case TAR_FINALIZING:
206 percent = 90;
207 break;
208
209 case TAR_COPYING:
210 percent = 95;
211 break;
212
213 default:
214 assert_not_reached("Unknown progress state");
215 }
216
217 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
218 log_debug("Combined progress %u%%", percent);
219}
220
dc2c282b 221static int tar_pull_make_local_copy(TarPull *i) {
0d6e763b
LP
222 int r;
223
224 assert(i);
225 assert(i->tar_job);
226
227 if (!i->local)
228 return 0;
229
230 if (!i->final_path) {
dc2c282b 231 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
0d6e763b
LP
232 if (r < 0)
233 return log_oom();
0d6e763b
LP
234 }
235
dc2c282b 236 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
0100b6e1
LP
237 if (r < 0)
238 return r;
239
9854730b
LP
240 if (i->settings) {
241 const char *local_settings;
242 assert(i->settings_job);
243
244 if (!i->settings_path) {
245 r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
246 if (r < 0)
247 return log_oom();
248 }
249
250 local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
251
252 r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0);
253 if (r == -EEXIST)
254 log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
255 else if (r < 0 && r != -ENOENT)
79b6198b
LP
256 log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
257 else
258 log_info("Created new settings file '%s.nspawn'", i->local);
9854730b
LP
259 }
260
0d6e763b
LP
261 return 0;
262}
263
dc2c282b 264static bool tar_pull_is_done(TarPull *i) {
8b71fce8
LP
265 assert(i);
266 assert(i->tar_job);
267
9854730b 268 if (!PULL_JOB_IS_COMPLETE(i->tar_job))
8b71fce8 269 return false;
9854730b 270 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 271 return false;
9854730b
LP
272 if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
273 return false;
274 if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
8b71fce8
LP
275 return false;
276
277 return true;
278}
279
dc2c282b
LP
280static void tar_pull_job_on_finished(PullJob *j) {
281 TarPull *i;
56ebfaf1
LP
282 int r;
283
284 assert(j);
285 assert(j->userdata);
286
287 i = j->userdata;
9854730b
LP
288
289 if (j == i->settings_job) {
290 if (j->error != 0)
291 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
292 } else if (j->error != 0) {
0100b6e1
LP
293 if (j == i->checksum_job)
294 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
295 else if (j == i->signature_job)
296 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
297 else
298 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
299
56ebfaf1
LP
300 r = j->error;
301 goto finish;
302 }
303
304 /* This is invoked if either the download completed
305 * successfully, or the download was skipped because we
306 * already have the etag. */
307
dc2c282b 308 if (!tar_pull_is_done(i))
0100b6e1
LP
309 return;
310
9854730b
LP
311 i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
312 if (i->settings_job)
313 i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
56ebfaf1
LP
314
315 if (i->tar_pid > 0) {
316 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
317 i->tar_pid = 0;
318 if (r < 0)
319 goto finish;
9854730b
LP
320 if (r > 0) {
321 r = -EIO;
322 goto finish;
323 }
56ebfaf1
LP
324 }
325
0100b6e1
LP
326 if (!i->tar_job->etag_exists) {
327 /* This is a new download, verify it, and move it into place */
328
dc2c282b 329 tar_pull_report_progress(i, TAR_VERIFYING);
7079cfef 330
9854730b 331 r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job);
0100b6e1
LP
332 if (r < 0)
333 goto finish;
334
dc2c282b 335 tar_pull_report_progress(i, TAR_FINALIZING);
7079cfef 336
b6e676ce 337 r = import_make_read_only(i->temp_path);
56ebfaf1
LP
338 if (r < 0)
339 goto finish;
340
f85ef957
AC
341 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
342 if (r < 0) {
343 log_error_errno(r, "Failed to rename to final image name: %m");
56ebfaf1
LP
344 goto finish;
345 }
56ebfaf1 346
9854730b
LP
347 i->temp_path = mfree(i->temp_path);
348
349 if (i->settings_job &&
350 i->settings_job->error == 0 &&
351 !i->settings_job->etag_exists) {
352
353 assert(i->settings_temp_path);
354 assert(i->settings_path);
355
356 /* Also move the settings file into place, if
357 * it exist. Note that we do so only if we
358 * also moved the tar file in place, to keep
359 * things strictly in sync. */
360
361 r = import_make_read_only(i->settings_temp_path);
362 if (r < 0)
363 goto finish;
364
365 r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
366 if (r < 0) {
367 log_error_errno(r, "Failed to rename settings file: %m");
368 goto finish;
369 }
370
371 i->settings_temp_path = mfree(i->settings_temp_path);
372 }
56ebfaf1
LP
373 }
374
dc2c282b 375 tar_pull_report_progress(i, TAR_COPYING);
7079cfef 376
dc2c282b 377 r = tar_pull_make_local_copy(i);
0d6e763b
LP
378 if (r < 0)
379 goto finish;
380
56ebfaf1
LP
381 r = 0;
382
383finish:
56ebfaf1
LP
384 if (i->on_finished)
385 i->on_finished(i, r, i->userdata);
386 else
387 sd_event_exit(i->event, r);
388}
389
9854730b 390static int tar_pull_job_on_open_disk_tar(PullJob *j) {
dc2c282b 391 TarPull *i;
56ebfaf1
LP
392 int r;
393
394 assert(j);
395 assert(j->userdata);
396
397 i = j->userdata;
8b71fce8
LP
398 assert(i->tar_job == j);
399 assert(!i->final_path);
400 assert(!i->temp_path);
401 assert(i->tar_pid <= 0);
56ebfaf1 402
dc2c282b 403 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
56ebfaf1
LP
404 if (r < 0)
405 return log_oom();
406
14bcf25c 407 r = tempfn_random(i->final_path, NULL, &i->temp_path);
56ebfaf1
LP
408 if (r < 0)
409 return log_oom();
410
411 mkdir_parents_label(i->temp_path, 0700);
412
413 r = btrfs_subvol_make(i->temp_path);
414 if (r == -ENOTTY) {
415 if (mkdir(i->temp_path, 0755) < 0)
416 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
417 } else if (r < 0)
418 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
8c9cfc28
LP
419 else
420 (void) import_assign_pool_quota_and_warn(i->temp_path);
56ebfaf1 421
587fec42 422 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
2c140ded
LP
423 if (j->disk_fd < 0)
424 return j->disk_fd;
56ebfaf1
LP
425
426 return 0;
427}
428
9854730b
LP
429static int tar_pull_job_on_open_disk_settings(PullJob *j) {
430 TarPull *i;
431 int r;
432
433 assert(j);
434 assert(j->userdata);
435
436 i = j->userdata;
437 assert(i->settings_job == j);
438 assert(!i->settings_path);
439 assert(!i->settings_temp_path);
440
441 r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
442 if (r < 0)
443 return log_oom();
444
445 r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
446 if (r < 0)
447 return log_oom();
448
449 mkdir_parents_label(i->settings_temp_path, 0700);
450
451 j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
452 if (j->disk_fd < 0)
453 return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
454
455 return 0;
456}
457
dc2c282b
LP
458static void tar_pull_job_on_progress(PullJob *j) {
459 TarPull *i;
7079cfef
LP
460
461 assert(j);
462 assert(j->userdata);
463
464 i = j->userdata;
465
dc2c282b 466 tar_pull_report_progress(i, TAR_DOWNLOADING);
7079cfef
LP
467}
468
9854730b
LP
469int tar_pull_start(
470 TarPull *i,
471 const char *url,
472 const char *local,
473 bool force_local,
474 ImportVerify verify,
475 bool settings) {
476
56ebfaf1
LP
477 int r;
478
479 assert(i);
9854730b
LP
480 assert(verify < _IMPORT_VERIFY_MAX);
481 assert(verify >= 0);
56ebfaf1 482
56ebfaf1
LP
483 if (!http_url_is_valid(url))
484 return -EINVAL;
485
486 if (local && !machine_name_is_valid(local))
487 return -EINVAL;
488
8b71fce8
LP
489 if (i->tar_job)
490 return -EBUSY;
491
56ebfaf1
LP
492 r = free_and_strdup(&i->local, local);
493 if (r < 0)
494 return r;
9854730b 495
56ebfaf1 496 i->force_local = force_local;
0100b6e1 497 i->verify = verify;
9854730b 498 i->settings = settings;
56ebfaf1 499
9854730b 500 /* Set up download job for TAR file */
dc2c282b 501 r = pull_job_new(&i->tar_job, url, i->glue, i);
56ebfaf1
LP
502 if (r < 0)
503 return r;
504
dc2c282b 505 i->tar_job->on_finished = tar_pull_job_on_finished;
9854730b 506 i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
dc2c282b 507 i->tar_job->on_progress = tar_pull_job_on_progress;
0100b6e1 508 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 509 i->tar_job->grow_machine_directory = i->grow_machine_directory;
56ebfaf1 510
dc2c282b 511 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
56ebfaf1
LP
512 if (r < 0)
513 return r;
514
9854730b
LP
515 /* Set up download job for the settings file (.nspawn) */
516 if (settings) {
517 r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i);
518 if (r < 0)
519 return r;
520
521 i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
522 i->settings_job->on_progress = tar_pull_job_on_progress;
523 i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
524
525 r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
526 if (r < 0)
527 return r;
528 }
529
530 /* Set up download of checksum/signature files */
dc2c282b 531 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
0100b6e1
LP
532 if (r < 0)
533 return r;
534
dc2c282b 535 r = pull_job_begin(i->tar_job);
0100b6e1
LP
536 if (r < 0)
537 return r;
538
9854730b
LP
539 if (i->settings_job) {
540 r = pull_job_begin(i->settings_job);
541 if (r < 0)
542 return r;
543 }
544
0100b6e1 545 if (i->checksum_job) {
dc2c282b 546 i->checksum_job->on_progress = tar_pull_job_on_progress;
7079cfef 547
dc2c282b 548 r = pull_job_begin(i->checksum_job);
0100b6e1
LP
549 if (r < 0)
550 return r;
551 }
552
553 if (i->signature_job) {
dc2c282b 554 i->signature_job->on_progress = tar_pull_job_on_progress;
7079cfef 555
dc2c282b 556 r = pull_job_begin(i->signature_job);
0100b6e1
LP
557 if (r < 0)
558 return r;
559 }
560
561 return 0;
56ebfaf1 562}