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