]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-raw.c
util: make machine_name_is_valid() a macro and move it to hostname-util.h
[thirdparty/systemd.git] / src / import / pull-raw.c
CommitLineData
90199220
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 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/xattr.h>
dfd1520d 23#include <linux/fs.h>
90199220
LP
24#include <curl/curl.h>
25
7079cfef 26#include "sd-daemon.h"
90199220 27#include "utf8.h"
8620a9a3
LP
28#include "strv.h"
29#include "copy.h"
0d6e763b
LP
30#include "btrfs-util.h"
31#include "util.h"
32#include "macro.h"
33#include "mkdir.h"
c6878637 34#include "rm-rf.h"
26166c88 35#include "path-util.h"
25300b5a 36#include "hostname-util.h"
3d7415f4 37#include "import-util.h"
b6e676ce 38#include "import-common.h"
0d6e763b
LP
39#include "curl-util.h"
40#include "qcow2-util.h"
dc2c282b
LP
41#include "pull-job.h"
42#include "pull-common.h"
43#include "pull-raw.h"
90199220 44
7079cfef
LP
45typedef enum RawProgress {
46 RAW_DOWNLOADING,
47 RAW_VERIFYING,
48 RAW_UNPACKING,
49 RAW_FINALIZING,
50 RAW_COPYING,
51} RawProgress;
90199220 52
dc2c282b 53struct RawPull {
90199220
LP
54 sd_event *event;
55 CurlGlue *glue;
56
087682d1 57 char *image_root;
90199220 58
dc2c282b
LP
59 PullJob *raw_job;
60 PullJob *checksum_job;
61 PullJob *signature_job;
90199220 62
dc2c282b 63 RawPullFinished on_finished;
0d6e763b 64 void *userdata;
90199220 65
0d6e763b
LP
66 char *local;
67 bool force_local;
26166c88 68 bool grow_machine_directory;
8620a9a3 69
0d6e763b
LP
70 char *temp_path;
71 char *final_path;
8f695058
LP
72
73 ImportVerify verify;
0d6e763b 74};
49bb233b 75
dc2c282b 76RawPull* raw_pull_unref(RawPull *i) {
0d6e763b 77 if (!i)
90199220
LP
78 return NULL;
79
dc2c282b
LP
80 pull_job_unref(i->raw_job);
81 pull_job_unref(i->checksum_job);
82 pull_job_unref(i->signature_job);
90199220 83
0d6e763b
LP
84 curl_glue_unref(i->glue);
85 sd_event_unref(i->event);
90199220 86
0d6e763b
LP
87 if (i->temp_path) {
88 (void) unlink(i->temp_path);
89 free(i->temp_path);
90199220
LP
90 }
91
0d6e763b
LP
92 free(i->final_path);
93 free(i->image_root);
94 free(i->local);
95 free(i);
90199220
LP
96
97 return NULL;
98}
99
dc2c282b
LP
100int raw_pull_new(
101 RawPull **ret,
8b71fce8
LP
102 sd_event *event,
103 const char *image_root,
dc2c282b 104 RawPullFinished on_finished,
8b71fce8
LP
105 void *userdata) {
106
dc2c282b 107 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
0d6e763b 108 int r;
8620a9a3 109
0d6e763b 110 assert(ret);
8620a9a3 111
dc2c282b 112 i = new0(RawPull, 1);
0d6e763b 113 if (!i)
8620a9a3
LP
114 return -ENOMEM;
115
0d6e763b
LP
116 i->on_finished = on_finished;
117 i->userdata = userdata;
8620a9a3 118
0d6e763b
LP
119 i->image_root = strdup(image_root ?: "/var/lib/machines");
120 if (!i->image_root)
8620a9a3
LP
121 return -ENOMEM;
122
26166c88
LP
123 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
124
0d6e763b
LP
125 if (event)
126 i->event = sd_event_ref(event);
127 else {
128 r = sd_event_default(&i->event);
dfd1520d 129 if (r < 0)
0d6e763b 130 return r;
2f64ba0e 131 }
8620a9a3 132
0d6e763b 133 r = curl_glue_new(&i->glue, i->event);
2f64ba0e 134 if (r < 0)
0d6e763b 135 return r;
8620a9a3 136
dc2c282b 137 i->glue->on_finished = pull_job_curl_on_finished;
0d6e763b 138 i->glue->userdata = i;
8620a9a3 139
0d6e763b
LP
140 *ret = i;
141 i = NULL;
8620a9a3 142
2f64ba0e
LP
143 return 0;
144}
145
dc2c282b 146static void raw_pull_report_progress(RawPull *i, RawProgress p) {
7079cfef
LP
147 unsigned percent;
148
149 assert(i);
150
151 switch (p) {
152
153 case RAW_DOWNLOADING: {
154 unsigned remain = 80;
155
156 percent = 0;
157
158 if (i->checksum_job) {
159 percent += i->checksum_job->progress_percent * 5 / 100;
160 remain -= 5;
161 }
162
163 if (i->signature_job) {
164 percent += i->signature_job->progress_percent * 5 / 100;
165 remain -= 5;
166 }
167
168 if (i->raw_job)
169 percent += i->raw_job->progress_percent * remain / 100;
170 break;
171 }
172
173 case RAW_VERIFYING:
174 percent = 80;
175 break;
176
177 case RAW_UNPACKING:
178 percent = 85;
179 break;
180
181 case RAW_FINALIZING:
182 percent = 90;
183 break;
184
185 case RAW_COPYING:
186 percent = 95;
187 break;
188
189 default:
190 assert_not_reached("Unknown progress state");
191 }
192
193 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
194 log_debug("Combined progress %u%%", percent);
195}
196
dc2c282b 197static int raw_pull_maybe_convert_qcow2(RawPull *i) {
edce2aed
LP
198 _cleanup_close_ int converted_fd = -1;
199 _cleanup_free_ char *t = NULL;
200 int r;
201
0d6e763b
LP
202 assert(i);
203 assert(i->raw_job);
edce2aed 204
0d6e763b 205 r = qcow2_detect(i->raw_job->disk_fd);
edce2aed
LP
206 if (r < 0)
207 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
208 if (r == 0)
209 return 0;
210
211 /* This is a QCOW2 image, let's convert it */
14bcf25c 212 r = tempfn_random(i->final_path, NULL, &t);
edce2aed
LP
213 if (r < 0)
214 return log_oom();
215
b6e676ce 216 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
edce2aed
LP
217 if (converted_fd < 0)
218 return log_error_errno(errno, "Failed to create %s: %m", t);
219
1ed8f8c1 220 r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
221 if (r < 0)
222 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
223
ec5cb56e
LP
224 log_info("Unpacking QCOW2 file.");
225
0d6e763b 226 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
edce2aed
LP
227 if (r < 0) {
228 unlink(t);
229 return log_error_errno(r, "Failed to convert qcow2 image: %m");
230 }
231
b6e676ce 232 (void) unlink(i->temp_path);
0d6e763b 233 free(i->temp_path);
0d6e763b 234 i->temp_path = t;
edce2aed
LP
235 t = NULL;
236
0d6e763b
LP
237 safe_close(i->raw_job->disk_fd);
238 i->raw_job->disk_fd = converted_fd;
edce2aed
LP
239 converted_fd = -1;
240
241 return 1;
242}
243
dc2c282b 244static int raw_pull_make_local_copy(RawPull *i) {
0d6e763b
LP
245 _cleanup_free_ char *tp = NULL;
246 _cleanup_close_ int dfd = -1;
247 const char *p;
90199220
LP
248 int r;
249
0d6e763b
LP
250 assert(i);
251 assert(i->raw_job);
90199220 252
0d6e763b 253 if (!i->local)
90199220
LP
254 return 0;
255
85dbc41d
LP
256 if (i->raw_job->etag_exists) {
257 /* We have downloaded this one previously, reopen it */
258
259 assert(i->raw_job->disk_fd < 0);
260
0d6e763b 261 if (!i->final_path) {
dc2c282b 262 r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
49bb233b 263 if (r < 0)
0d6e763b 264 return log_oom();
49bb233b 265 }
49bb233b 266
0d6e763b
LP
267 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
268 if (i->raw_job->disk_fd < 0)
269 return log_error_errno(errno, "Failed to open vendor image: %m");
85dbc41d
LP
270 } else {
271 /* We freshly downloaded the image, use it */
272
273 assert(i->raw_job->disk_fd >= 0);
274
275 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
276 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
90199220
LP
277 }
278
63c372cb 279 p = strjoina(i->image_root, "/", i->local, ".raw");
49bb233b 280
d9e2daaf
LP
281 if (i->force_local)
282 (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
49bb233b 283
14bcf25c 284 r = tempfn_random(p, NULL, &tp);
49bb233b 285 if (r < 0)
0d6e763b 286 return log_oom();
49bb233b 287
0d6e763b
LP
288 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
289 if (dfd < 0)
290 return log_error_errno(errno, "Failed to create writable copy of image: %m");
49bb233b 291
0d6e763b
LP
292 /* Turn off COW writing. This should greatly improve
293 * performance on COW file systems like btrfs, since it
294 * reduces fragmentation caused by not allowing in-place
295 * writes. */
1ed8f8c1 296 r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
297 if (r < 0)
298 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
90199220 299
0d6e763b 300 r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
90199220 301 if (r < 0) {
0d6e763b
LP
302 unlink(tp);
303 return log_error_errno(r, "Failed to make writable copy of image: %m");
90199220
LP
304 }
305
0d6e763b
LP
306 (void) copy_times(i->raw_job->disk_fd, dfd);
307 (void) copy_xattr(i->raw_job->disk_fd, dfd);
1e20b411 308
0d6e763b 309 dfd = safe_close(dfd);
1e20b411 310
0d6e763b
LP
311 r = rename(tp, p);
312 if (r < 0) {
313 unlink(tp);
314 return log_error_errno(errno, "Failed to move writable image into place: %m");
1e20b411
LP
315 }
316
0d6e763b 317 log_info("Created new local image '%s'.", i->local);
1e20b411
LP
318 return 0;
319}
320
dc2c282b 321static bool raw_pull_is_done(RawPull *i) {
8b71fce8
LP
322 assert(i);
323 assert(i->raw_job);
324
dc2c282b 325 if (i->raw_job->state != PULL_JOB_DONE)
8b71fce8 326 return false;
dc2c282b 327 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
8b71fce8 328 return false;
dc2c282b 329 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
8b71fce8
LP
330 return false;
331
332 return true;
333}
334
dc2c282b
LP
335static void raw_pull_job_on_finished(PullJob *j) {
336 RawPull *i;
8620a9a3
LP
337 int r;
338
0d6e763b
LP
339 assert(j);
340 assert(j->userdata);
8620a9a3 341
0d6e763b
LP
342 i = j->userdata;
343 if (j->error != 0) {
98c38001 344 if (j == i->checksum_job)
3576d631
LP
345 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
346 else if (j == i->signature_job)
347 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
348 else
349 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
350
0d6e763b
LP
351 r = j->error;
352 goto finish;
8620a9a3
LP
353 }
354
0d6e763b
LP
355 /* This is invoked if either the download completed
356 * successfully, or the download was skipped because we
85dbc41d 357 * already have the etag. In this case ->etag_exists is
3576d631
LP
358 * true.
359 *
360 * We only do something when we got all three files */
85dbc41d 361
dc2c282b 362 if (!raw_pull_is_done(i))
3576d631 363 return;
8620a9a3 364
3576d631 365 if (!i->raw_job->etag_exists) {
98c38001 366 /* This is a new download, verify it, and move it into place */
3576d631
LP
367 assert(i->raw_job->disk_fd >= 0);
368
dc2c282b 369 raw_pull_report_progress(i, RAW_VERIFYING);
7079cfef 370
dc2c282b 371 r = pull_verify(i->raw_job, i->checksum_job, i->signature_job);
0d6e763b
LP
372 if (r < 0)
373 goto finish;
8620a9a3 374
dc2c282b 375 raw_pull_report_progress(i, RAW_UNPACKING);
7079cfef 376
dc2c282b 377 r = raw_pull_maybe_convert_qcow2(i);
0d6e763b
LP
378 if (r < 0)
379 goto finish;
85dbc41d 380
dc2c282b 381 raw_pull_report_progress(i, RAW_FINALIZING);
7079cfef 382
b6e676ce 383 r = import_make_read_only_fd(i->raw_job->disk_fd);
3576d631
LP
384 if (r < 0)
385 goto finish;
85dbc41d 386
f85ef957 387 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
3576d631 388 if (r < 0) {
f85ef957 389 log_error_errno(r, "Failed to move RAW file into place: %m");
3576d631
LP
390 goto finish;
391 }
8f695058 392
3576d631
LP
393 free(i->temp_path);
394 i->temp_path = NULL;
8620a9a3
LP
395 }
396
dc2c282b 397 raw_pull_report_progress(i, RAW_COPYING);
7079cfef 398
dc2c282b 399 r = raw_pull_make_local_copy(i);
8620a9a3 400 if (r < 0)
0d6e763b 401 goto finish;
90199220 402
0d6e763b 403 r = 0;
3576d631 404
0d6e763b 405finish:
3576d631
LP
406 if (i->on_finished)
407 i->on_finished(i, r, i->userdata);
408 else
409 sd_event_exit(i->event, r);
0d6e763b 410}
90199220 411
dc2c282b
LP
412static int raw_pull_job_on_open_disk(PullJob *j) {
413 RawPull *i;
0d6e763b 414 int r;
90199220 415
0d6e763b
LP
416 assert(j);
417 assert(j->userdata);
90199220 418
0d6e763b 419 i = j->userdata;
8b71fce8
LP
420 assert(i->raw_job == j);
421 assert(!i->final_path);
422 assert(!i->temp_path);
90199220 423
dc2c282b 424 r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
0d6e763b
LP
425 if (r < 0)
426 return log_oom();
90199220 427
14bcf25c 428 r = tempfn_random(i->final_path, NULL, &i->temp_path);
b6e676ce 429 if (r < 0)
0d6e763b 430 return log_oom();
1e20b411 431
b6e676ce 432 (void) mkdir_parents_label(i->temp_path, 0700);
1e20b411 433
b6e676ce 434 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
0d6e763b
LP
435 if (j->disk_fd < 0)
436 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
1e20b411 437
1ed8f8c1 438 r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
90199220 439 if (r < 0)
0d6e763b 440 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
90199220
LP
441
442 return 0;
443}
444
dc2c282b
LP
445static void raw_pull_job_on_progress(PullJob *j) {
446 RawPull *i;
7079cfef
LP
447
448 assert(j);
449 assert(j->userdata);
450
451 i = j->userdata;
452
dc2c282b 453 raw_pull_report_progress(i, RAW_DOWNLOADING);
7079cfef
LP
454}
455
dc2c282b 456int raw_pull_start(RawPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
90199220
LP
457 int r;
458
0d6e763b 459 assert(i);
8f695058
LP
460 assert(verify < _IMPORT_VERIFY_MAX);
461 assert(verify >= 0);
90199220 462
0d6e763b
LP
463 if (!http_url_is_valid(url))
464 return -EINVAL;
90199220 465
0d6e763b
LP
466 if (local && !machine_name_is_valid(local))
467 return -EINVAL;
087682d1 468
8b71fce8
LP
469 if (i->raw_job)
470 return -EBUSY;
471
0d6e763b 472 r = free_and_strdup(&i->local, local);
90199220
LP
473 if (r < 0)
474 return r;
0d6e763b 475 i->force_local = force_local;
8f695058 476 i->verify = verify;
90199220 477
85dbc41d 478 /* Queue job for the image itself */
dc2c282b 479 r = pull_job_new(&i->raw_job, url, i->glue, i);
90199220
LP
480 if (r < 0)
481 return r;
482
dc2c282b
LP
483 i->raw_job->on_finished = raw_pull_job_on_finished;
484 i->raw_job->on_open_disk = raw_pull_job_on_open_disk;
485 i->raw_job->on_progress = raw_pull_job_on_progress;
98c38001 486 i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 487 i->raw_job->grow_machine_directory = i->grow_machine_directory;
90199220 488
dc2c282b 489 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
90199220
LP
490 if (r < 0)
491 return r;
492
dc2c282b 493 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
98c38001
LP
494 if (r < 0)
495 return r;
85dbc41d 496
dc2c282b 497 r = pull_job_begin(i->raw_job);
85dbc41d
LP
498 if (r < 0)
499 return r;
500
98c38001 501 if (i->checksum_job) {
dc2c282b 502 i->checksum_job->on_progress = raw_pull_job_on_progress;
7079cfef 503
dc2c282b 504 r = pull_job_begin(i->checksum_job);
3576d631
LP
505 if (r < 0)
506 return r;
507 }
508
509 if (i->signature_job) {
dc2c282b 510 i->signature_job->on_progress = raw_pull_job_on_progress;
7079cfef 511
dc2c282b 512 r = pull_job_begin(i->signature_job);
3576d631
LP
513 if (r < 0)
514 return r;
515 }
516
85dbc41d 517 return 0;
90199220 518}