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