]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-tar.c
Merge pull request #7834 from jkloetzke/disable-watchdog
[thirdparty/systemd.git] / src / import / pull-tar.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
56ebfaf1
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
56ebfaf1 21#include <curl/curl.h>
cf0fbc49 22#include <sys/prctl.h>
56ebfaf1 23
7079cfef 24#include "sd-daemon.h"
07630cea 25
b5efdb8a 26#include "alloc-util.h"
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);
e0061812 118
6b430fdb 119 return mfree(i);
56ebfaf1
LP
120}
121
dc2c282b
LP
122int tar_pull_new(
123 TarPull **ret,
8b71fce8
LP
124 sd_event *event,
125 const char *image_root,
dc2c282b 126 TarPullFinished on_finished,
8b71fce8
LP
127 void *userdata) {
128
dc2c282b 129 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
56ebfaf1
LP
130 int r;
131
132 assert(ret);
56ebfaf1 133
dc2c282b 134 i = new0(TarPull, 1);
56ebfaf1
LP
135 if (!i)
136 return -ENOMEM;
137
138 i->on_finished = on_finished;
139 i->userdata = userdata;
140
141 i->image_root = strdup(image_root ?: "/var/lib/machines");
142 if (!i->image_root)
143 return -ENOMEM;
144
26166c88
LP
145 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
146
9854730b
LP
147 if (event)
148 i->event = sd_event_ref(event);
149 else {
150 r = sd_event_default(&i->event);
151 if (r < 0)
152 return r;
153 }
56ebfaf1
LP
154
155 r = curl_glue_new(&i->glue, i->event);
156 if (r < 0)
157 return r;
158
dc2c282b 159 i->glue->on_finished = pull_job_curl_on_finished;
56ebfaf1
LP
160 i->glue->userdata = i;
161
162 *ret = i;
163 i = NULL;
164
165 return 0;
166}
167
dc2c282b 168static void tar_pull_report_progress(TarPull *i, TarProgress p) {
7079cfef
LP
169 unsigned percent;
170
171 assert(i);
172
173 switch (p) {
174
175 case TAR_DOWNLOADING: {
176 unsigned remain = 85;
177
178 percent = 0;
179
9854730b
LP
180 if (i->settings_job) {
181 percent += i->settings_job->progress_percent * 5 / 100;
182 remain -= 5;
183 }
184
7079cfef
LP
185 if (i->checksum_job) {
186 percent += i->checksum_job->progress_percent * 5 / 100;
187 remain -= 5;
188 }
189
190 if (i->signature_job) {
191 percent += i->signature_job->progress_percent * 5 / 100;
192 remain -= 5;
193 }
194
195 if (i->tar_job)
196 percent += i->tar_job->progress_percent * remain / 100;
197 break;
198 }
199
200 case TAR_VERIFYING:
201 percent = 85;
202 break;
203
204 case TAR_FINALIZING:
205 percent = 90;
206 break;
207
208 case TAR_COPYING:
209 percent = 95;
210 break;
211
212 default:
213 assert_not_reached("Unknown progress state");
214 }
215
216 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
217 log_debug("Combined progress %u%%", percent);
218}
219
91359193
LP
220static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) {
221 int r;
222
223 assert(i);
224 assert(field);
225
226 if (*field)
227 return 0;
228
229 assert(i->tar_job);
230
231 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", suffix, field);
232 if (r < 0)
233 return log_oom();
234
235 return 1;
236}
237
dc2c282b 238static int tar_pull_make_local_copy(TarPull *i) {
0d6e763b
LP
239 int r;
240
241 assert(i);
242 assert(i->tar_job);
243
244 if (!i->local)
245 return 0;
246
dc2c282b 247 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
0100b6e1
LP
248 if (r < 0)
249 return r;
250
9854730b
LP
251 if (i->settings) {
252 const char *local_settings;
253 assert(i->settings_job);
254
91359193
LP
255 r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
256 if (r < 0)
257 return r;
9854730b
LP
258
259 local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
260
1c876927 261 r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0));
9854730b
LP
262 if (r == -EEXIST)
263 log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
33859a6b
LP
264 else if (r == -ENOENT)
265 log_debug_errno(r, "Skipping creation of settings file, since none was found.");
266 else if (r < 0)
79b6198b
LP
267 log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings);
268 else
33859a6b 269 log_info("Created new settings file %s.", local_settings);
9854730b
LP
270 }
271
0d6e763b
LP
272 return 0;
273}
274
dc2c282b 275static bool tar_pull_is_done(TarPull *i) {
8b71fce8
LP
276 assert(i);
277 assert(i->tar_job);
278
9854730b 279 if (!PULL_JOB_IS_COMPLETE(i->tar_job))
8b71fce8 280 return false;
9854730b 281 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 282 return false;
9854730b
LP
283 if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
284 return false;
285 if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
8b71fce8
LP
286 return false;
287
288 return true;
289}
290
dc2c282b
LP
291static void tar_pull_job_on_finished(PullJob *j) {
292 TarPull *i;
56ebfaf1
LP
293 int r;
294
295 assert(j);
296 assert(j->userdata);
297
298 i = j->userdata;
9854730b
LP
299
300 if (j == i->settings_job) {
301 if (j->error != 0)
302 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
697be0be 303 } else if (j->error != 0 && j != i->signature_job) {
0100b6e1
LP
304 if (j == i->checksum_job)
305 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
0100b6e1
LP
306 else
307 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
308
56ebfaf1
LP
309 r = j->error;
310 goto finish;
311 }
312
313 /* This is invoked if either the download completed
314 * successfully, or the download was skipped because we
315 * already have the etag. */
316
dc2c282b 317 if (!tar_pull_is_done(i))
0100b6e1
LP
318 return;
319
c9fb8c7c 320 if (i->signature_job && i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) {
697be0be
TB
321 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
322
323 r = i->signature_job->error;
324 goto finish;
325 }
326
9854730b
LP
327 i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd);
328 if (i->settings_job)
329 i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
56ebfaf1 330
91359193
LP
331 r = tar_pull_determine_path(i, NULL, &i->final_path);
332 if (r < 0)
333 goto finish;
334
56ebfaf1 335 if (i->tar_pid > 0) {
7d4904fe 336 r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG);
56ebfaf1
LP
337 i->tar_pid = 0;
338 if (r < 0)
339 goto finish;
b4a34311 340 if (r != EXIT_SUCCESS) {
9854730b
LP
341 r = -EIO;
342 goto finish;
343 }
56ebfaf1
LP
344 }
345
0100b6e1
LP
346 if (!i->tar_job->etag_exists) {
347 /* This is a new download, verify it, and move it into place */
348
dc2c282b 349 tar_pull_report_progress(i, TAR_VERIFYING);
7079cfef 350
91359193 351 r = pull_verify(i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job);
0100b6e1
LP
352 if (r < 0)
353 goto finish;
354
dc2c282b 355 tar_pull_report_progress(i, TAR_FINALIZING);
7079cfef 356
b6e676ce 357 r = import_make_read_only(i->temp_path);
56ebfaf1
LP
358 if (r < 0)
359 goto finish;
360
f85ef957
AC
361 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
362 if (r < 0) {
dc38f65a 363 log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path);
56ebfaf1
LP
364 goto finish;
365 }
56ebfaf1 366
9854730b
LP
367 i->temp_path = mfree(i->temp_path);
368
369 if (i->settings_job &&
91359193 370 i->settings_job->error == 0) {
9854730b 371
e0061812 372 /* Also move the settings file into place, if it exists. Note that we do so only if we also
91359193 373 * moved the tar file in place, to keep things strictly in sync. */
e0061812 374 assert(i->settings_temp_path);
91359193 375
e0061812
LP
376 /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and
377 * we should incorporate it in the file name if we can */
91359193 378 i->settings_path = mfree(i->settings_path);
e0061812 379
91359193
LP
380 r = tar_pull_determine_path(i, ".nspawn", &i->settings_path);
381 if (r < 0)
382 goto finish;
9854730b
LP
383
384 r = import_make_read_only(i->settings_temp_path);
385 if (r < 0)
386 goto finish;
387
388 r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
389 if (r < 0) {
dc38f65a 390 log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path);
9854730b
LP
391 goto finish;
392 }
393
394 i->settings_temp_path = mfree(i->settings_temp_path);
395 }
56ebfaf1
LP
396 }
397
dc2c282b 398 tar_pull_report_progress(i, TAR_COPYING);
7079cfef 399
dc2c282b 400 r = tar_pull_make_local_copy(i);
0d6e763b
LP
401 if (r < 0)
402 goto finish;
403
56ebfaf1
LP
404 r = 0;
405
406finish:
56ebfaf1
LP
407 if (i->on_finished)
408 i->on_finished(i, r, i->userdata);
409 else
410 sd_event_exit(i->event, r);
411}
412
9854730b 413static int tar_pull_job_on_open_disk_tar(PullJob *j) {
dc2c282b 414 TarPull *i;
56ebfaf1
LP
415 int r;
416
417 assert(j);
418 assert(j->userdata);
419
420 i = j->userdata;
8b71fce8 421 assert(i->tar_job == j);
8b71fce8 422 assert(i->tar_pid <= 0);
56ebfaf1 423
91359193
LP
424 if (!i->temp_path) {
425 r = tempfn_random_child(i->image_root, "tar", &i->temp_path);
426 if (r < 0)
427 return log_oom();
428 }
56ebfaf1
LP
429
430 mkdir_parents_label(i->temp_path, 0700);
431
432 r = btrfs_subvol_make(i->temp_path);
433 if (r == -ENOTTY) {
434 if (mkdir(i->temp_path, 0755) < 0)
435 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
436 } else if (r < 0)
709f6e46 437 return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
8c9cfc28
LP
438 else
439 (void) import_assign_pool_quota_and_warn(i->temp_path);
56ebfaf1 440
587fec42 441 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
2c140ded
LP
442 if (j->disk_fd < 0)
443 return j->disk_fd;
56ebfaf1
LP
444
445 return 0;
446}
447
9854730b
LP
448static int tar_pull_job_on_open_disk_settings(PullJob *j) {
449 TarPull *i;
450 int r;
451
452 assert(j);
453 assert(j->userdata);
454
455 i = j->userdata;
456 assert(i->settings_job == j);
9854730b 457
91359193
LP
458 if (!i->settings_temp_path) {
459 r = tempfn_random_child(i->image_root, "settings", &i->settings_temp_path);
460 if (r < 0)
461 return log_oom();
462 }
9854730b
LP
463
464 mkdir_parents_label(i->settings_temp_path, 0700);
465
466 j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
467 if (j->disk_fd < 0)
468 return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
469
470 return 0;
471}
472
dc2c282b
LP
473static void tar_pull_job_on_progress(PullJob *j) {
474 TarPull *i;
7079cfef
LP
475
476 assert(j);
477 assert(j->userdata);
478
479 i = j->userdata;
480
dc2c282b 481 tar_pull_report_progress(i, TAR_DOWNLOADING);
7079cfef
LP
482}
483
9854730b
LP
484int tar_pull_start(
485 TarPull *i,
486 const char *url,
487 const char *local,
488 bool force_local,
489 ImportVerify verify,
490 bool settings) {
491
56ebfaf1
LP
492 int r;
493
494 assert(i);
9854730b
LP
495 assert(verify < _IMPORT_VERIFY_MAX);
496 assert(verify >= 0);
56ebfaf1 497
56ebfaf1
LP
498 if (!http_url_is_valid(url))
499 return -EINVAL;
500
501 if (local && !machine_name_is_valid(local))
502 return -EINVAL;
503
8b71fce8
LP
504 if (i->tar_job)
505 return -EBUSY;
506
56ebfaf1
LP
507 r = free_and_strdup(&i->local, local);
508 if (r < 0)
509 return r;
9854730b 510
56ebfaf1 511 i->force_local = force_local;
0100b6e1 512 i->verify = verify;
9854730b 513 i->settings = settings;
56ebfaf1 514
9854730b 515 /* Set up download job for TAR file */
dc2c282b 516 r = pull_job_new(&i->tar_job, url, i->glue, i);
56ebfaf1
LP
517 if (r < 0)
518 return r;
519
dc2c282b 520 i->tar_job->on_finished = tar_pull_job_on_finished;
9854730b 521 i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar;
dc2c282b 522 i->tar_job->on_progress = tar_pull_job_on_progress;
0100b6e1 523 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 524 i->tar_job->grow_machine_directory = i->grow_machine_directory;
56ebfaf1 525
dc2c282b 526 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
56ebfaf1
LP
527 if (r < 0)
528 return r;
529
9854730b
LP
530 /* Set up download job for the settings file (.nspawn) */
531 if (settings) {
91359193 532 r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i);
9854730b
LP
533 if (r < 0)
534 return r;
535
536 i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings;
537 i->settings_job->on_progress = tar_pull_job_on_progress;
538 i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
9854730b
LP
539 }
540
541 /* Set up download of checksum/signature files */
dc2c282b 542 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
0100b6e1
LP
543 if (r < 0)
544 return r;
545
dc2c282b 546 r = pull_job_begin(i->tar_job);
0100b6e1
LP
547 if (r < 0)
548 return r;
549
9854730b
LP
550 if (i->settings_job) {
551 r = pull_job_begin(i->settings_job);
552 if (r < 0)
553 return r;
554 }
555
0100b6e1 556 if (i->checksum_job) {
dc2c282b 557 i->checksum_job->on_progress = tar_pull_job_on_progress;
697be0be 558 i->checksum_job->style = VERIFICATION_PER_FILE;
7079cfef 559
dc2c282b 560 r = pull_job_begin(i->checksum_job);
0100b6e1
LP
561 if (r < 0)
562 return r;
563 }
564
565 if (i->signature_job) {
dc2c282b 566 i->signature_job->on_progress = tar_pull_job_on_progress;
7079cfef 567
dc2c282b 568 r = pull_job_begin(i->signature_job);
0100b6e1
LP
569 if (r < 0)
570 return r;
571 }
572
573 return 0;
56ebfaf1 574}