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