1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <curl/curl.h>
4 #include <linux/fs.h>
5 #include <sys/xattr.h>
6 
7 #include "sd-daemon.h"
8 
9 #include "alloc-util.h"
10 #include "btrfs-util.h"
11 #include "copy.h"
12 #include "curl-util.h"
13 #include "fd-util.h"
14 #include "fs-util.h"
15 #include "hostname-util.h"
16 #include "import-common.h"
17 #include "import-util.h"
18 #include "install-file.h"
19 #include "macro.h"
20 #include "mkdir-label.h"
21 #include "path-util.h"
22 #include "pull-common.h"
23 #include "pull-job.h"
24 #include "pull-raw.h"
25 #include "qcow2-util.h"
26 #include "rm-rf.h"
27 #include "string-util.h"
28 #include "strv.h"
29 #include "tmpfile-util.h"
30 #include "utf8.h"
31 #include "util.h"
32 #include "web-util.h"
33 
34 typedef enum RawProgress {
35         RAW_DOWNLOADING,
36         RAW_VERIFYING,
37         RAW_UNPACKING,
38         RAW_FINALIZING,
39         RAW_COPYING,
40 } RawProgress;
41 
42 struct RawPull {
43         sd_event *event;
44         CurlGlue *glue;
45 
46         PullFlags flags;
47         ImportVerify verify;
48         char *image_root;
49 
50         uint64_t offset;
51 
52         PullJob *raw_job;
53         PullJob *checksum_job;
54         PullJob *signature_job;
55         PullJob *settings_job;
56         PullJob *roothash_job;
57         PullJob *roothash_signature_job;
58         PullJob *verity_job;
59 
60         RawPullFinished on_finished;
61         void *userdata;
62 
63         char *local; /* In PULL_DIRECT mode the path we are supposed to place things in, otherwise the
64                       * machine name of the final copy we make */
65 
66         char *final_path;
67         char *temp_path;
68 
69         char *settings_path;
70         char *settings_temp_path;
71 
72         char *roothash_path;
73         char *roothash_temp_path;
74 
75         char *roothash_signature_path;
76         char *roothash_signature_temp_path;
77 
78         char *verity_path;
79         char *verity_temp_path;
80 
81         char *checksum;
82 };
83 
raw_pull_unref(RawPull * i)84 RawPull* raw_pull_unref(RawPull *i) {
85         if (!i)
86                 return NULL;
87 
88         pull_job_unref(i->raw_job);
89         pull_job_unref(i->checksum_job);
90         pull_job_unref(i->signature_job);
91         pull_job_unref(i->settings_job);
92         pull_job_unref(i->roothash_job);
93         pull_job_unref(i->roothash_signature_job);
94         pull_job_unref(i->verity_job);
95 
96         curl_glue_unref(i->glue);
97         sd_event_unref(i->event);
98 
99         unlink_and_free(i->temp_path);
100         unlink_and_free(i->settings_temp_path);
101         unlink_and_free(i->roothash_temp_path);
102         unlink_and_free(i->roothash_signature_temp_path);
103         unlink_and_free(i->verity_temp_path);
104 
105         free(i->final_path);
106         free(i->settings_path);
107         free(i->roothash_path);
108         free(i->roothash_signature_path);
109         free(i->verity_path);
110         free(i->image_root);
111         free(i->local);
112         free(i->checksum);
113 
114         return mfree(i);
115 }
116 
raw_pull_new(RawPull ** ret,sd_event * event,const char * image_root,RawPullFinished on_finished,void * userdata)117 int raw_pull_new(
118                 RawPull **ret,
119                 sd_event *event,
120                 const char *image_root,
121                 RawPullFinished on_finished,
122                 void *userdata) {
123 
124         _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL;
125         _cleanup_(sd_event_unrefp) sd_event *e = NULL;
126         _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
127         _cleanup_free_ char *root = NULL;
128         int r;
129 
130         assert(ret);
131 
132         root = strdup(image_root ?: "/var/lib/machines");
133         if (!root)
134                 return -ENOMEM;
135 
136         if (event)
137                 e = sd_event_ref(event);
138         else {
139                 r = sd_event_default(&e);
140                 if (r < 0)
141                         return r;
142         }
143 
144         r = curl_glue_new(&g, e);
145         if (r < 0)
146                 return r;
147 
148         i = new(RawPull, 1);
149         if (!i)
150                 return -ENOMEM;
151 
152         *i = (RawPull) {
153                 .on_finished = on_finished,
154                 .userdata = userdata,
155                 .image_root = TAKE_PTR(root),
156                 .event = TAKE_PTR(e),
157                 .glue = TAKE_PTR(g),
158                 .offset = UINT64_MAX,
159         };
160 
161         i->glue->on_finished = pull_job_curl_on_finished;
162         i->glue->userdata = i;
163 
164         *ret = TAKE_PTR(i);
165 
166         return 0;
167 }
168 
raw_pull_report_progress(RawPull * i,RawProgress p)169 static void raw_pull_report_progress(RawPull *i, RawProgress p) {
170         unsigned percent;
171 
172         assert(i);
173 
174         switch (p) {
175 
176         case RAW_DOWNLOADING: {
177                 unsigned remain = 80;
178 
179                 percent = 0;
180 
181                 if (i->checksum_job) {
182                         percent += i->checksum_job->progress_percent * 5 / 100;
183                         remain -= 5;
184                 }
185 
186                 if (i->signature_job) {
187                         percent += i->signature_job->progress_percent * 5 / 100;
188                         remain -= 5;
189                 }
190 
191                 if (i->settings_job) {
192                         percent += i->settings_job->progress_percent * 5 / 100;
193                         remain -= 5;
194                 }
195 
196                 if (i->roothash_job) {
197                         percent += i->roothash_job->progress_percent * 5 / 100;
198                         remain -= 5;
199                 }
200 
201                 if (i->roothash_signature_job) {
202                         percent += i->roothash_signature_job->progress_percent * 5 / 100;
203                         remain -= 5;
204                 }
205 
206                 if (i->verity_job) {
207                         percent += i->verity_job->progress_percent * 10 / 100;
208                         remain -= 10;
209                 }
210 
211                 if (i->raw_job)
212                         percent += i->raw_job->progress_percent * remain / 100;
213                 break;
214         }
215 
216         case RAW_VERIFYING:
217                 percent = 80;
218                 break;
219 
220         case RAW_UNPACKING:
221                 percent = 85;
222                 break;
223 
224         case RAW_FINALIZING:
225                 percent = 90;
226                 break;
227 
228         case RAW_COPYING:
229                 percent = 95;
230                 break;
231 
232         default:
233                 assert_not_reached();
234         }
235 
236         sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
237         log_debug("Combined progress %u%%", percent);
238 }
239 
raw_pull_maybe_convert_qcow2(RawPull * i)240 static int raw_pull_maybe_convert_qcow2(RawPull *i) {
241         _cleanup_(unlink_and_freep) char *t = NULL;
242         _cleanup_close_ int converted_fd = -1;
243         _cleanup_free_ char *f = NULL;
244         int r;
245 
246         assert(i);
247         assert(i->raw_job);
248         assert(!FLAGS_SET(i->flags, PULL_DIRECT));
249 
250         if (!FLAGS_SET(i->flags, PULL_CONVERT_QCOW2))
251                 return 0;
252 
253         assert(i->final_path);
254         assert(i->raw_job->close_disk_fd);
255 
256         r = qcow2_detect(i->raw_job->disk_fd);
257         if (r < 0)
258                 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
259         if (r == 0)
260                 return 0;
261 
262         /* This is a QCOW2 image, let's convert it */
263         r = tempfn_random(i->final_path, NULL, &f);
264         if (r < 0)
265                 return log_oom();
266 
267         converted_fd = open(f, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
268         if (converted_fd < 0)
269                 return log_error_errno(errno, "Failed to create %s: %m", f);
270 
271         t = TAKE_PTR(f);
272 
273         (void) import_set_nocow_and_log(converted_fd, t);
274 
275         log_info("Unpacking QCOW2 file.");
276 
277         r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
278         if (r < 0)
279                 return log_error_errno(r, "Failed to convert qcow2 image: %m");
280 
281         unlink_and_free(i->temp_path);
282         i->temp_path = TAKE_PTR(t);
283         CLOSE_AND_REPLACE(i->raw_job->disk_fd, converted_fd);
284 
285         return 1;
286 }
287 
raw_pull_determine_path(RawPull * i,const char * suffix,char ** field)288 static int raw_pull_determine_path(
289                 RawPull *i,
290                 const char *suffix,
291                 char **field /* input + output (!) */) {
292         int r;
293 
294         assert(i);
295         assert(field);
296 
297         if (*field)
298                 return 0;
299 
300         assert(i->raw_job);
301 
302         r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field);
303         if (r < 0)
304                 return log_oom();
305 
306         return 1;
307 }
308 
raw_pull_copy_auxiliary_file(RawPull * i,const char * suffix,char ** path)309 static int raw_pull_copy_auxiliary_file(
310                 RawPull *i,
311                 const char *suffix,
312                 char **path /* input + output (!) */) {
313 
314         const char *local;
315         int r;
316 
317         assert(i);
318         assert(suffix);
319         assert(path);
320 
321         r = raw_pull_determine_path(i, suffix, path);
322         if (r < 0)
323                 return r;
324 
325         local = strjoina(i->image_root, "/", i->local, suffix);
326 
327         r = copy_file_atomic(
328                         *path,
329                         local,
330                         0644,
331                         0, 0,
332                         COPY_REFLINK |
333                         (FLAGS_SET(i->flags, PULL_FORCE) ? COPY_REPLACE : 0) |
334                         (FLAGS_SET(i->flags, PULL_SYNC) ? COPY_FSYNC_FULL : 0));
335         if (r == -EEXIST)
336                 log_warning_errno(r, "File %s already exists, not replacing.", local);
337         else if (r == -ENOENT)
338                 log_debug_errno(r, "Skipping creation of auxiliary file, since none was found.");
339         else if (r < 0)
340                 log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local);
341         else
342                 log_info("Created new file %s.", local);
343 
344         return 0;
345 }
346 
raw_pull_make_local_copy(RawPull * i)347 static int raw_pull_make_local_copy(RawPull *i) {
348         _cleanup_(unlink_and_freep) char *tp = NULL;
349         _cleanup_free_ char *f = NULL;
350         _cleanup_close_ int dfd = -1;
351         const char *p;
352         int r;
353 
354         assert(i);
355         assert(i->raw_job);
356         assert(!FLAGS_SET(i->flags, PULL_DIRECT));
357 
358         if (!i->local)
359                 return 0;
360 
361         if (i->raw_job->etag_exists) {
362                 /* We have downloaded this one previously, reopen it */
363 
364                 assert(i->raw_job->disk_fd < 0);
365 
366                 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
367                 if (i->raw_job->disk_fd < 0)
368                         return log_error_errno(errno, "Failed to open vendor image: %m");
369         } else {
370                 /* We freshly downloaded the image, use it */
371 
372                 assert(i->raw_job->disk_fd >= 0);
373                 assert(i->offset == UINT64_MAX);
374 
375                 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
376                         return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
377         }
378 
379         p = strjoina(i->image_root, "/", i->local, ".raw");
380 
381         r = tempfn_random(p, NULL, &f);
382         if (r < 0)
383                 return log_oom();
384 
385         dfd = open(f, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
386         if (dfd < 0)
387                 return log_error_errno(errno, "Failed to create writable copy of image: %m");
388 
389         tp = TAKE_PTR(f);
390 
391         /* Turn off COW writing. This should greatly improve performance on COW file systems like btrfs,
392          * since it reduces fragmentation caused by not allowing in-place writes. */
393         (void) import_set_nocow_and_log(dfd, tp);
394 
395         r = copy_bytes(i->raw_job->disk_fd, dfd, UINT64_MAX, COPY_REFLINK);
396         if (r < 0)
397                 return log_error_errno(r, "Failed to make writable copy of image: %m");
398 
399         (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME);
400         (void) copy_xattr(i->raw_job->disk_fd, dfd, 0);
401 
402         dfd = safe_close(dfd);
403 
404         r = install_file(AT_FDCWD, tp,
405                          AT_FDCWD, p,
406                          (i->flags & PULL_FORCE ? INSTALL_REPLACE : 0) |
407                          (i->flags & PULL_READ_ONLY ? INSTALL_READ_ONLY : 0) |
408                          (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
409         if (r < 0)
410                 return log_error_errno(errno, "Failed to move local image into place '%s': %m", p);
411 
412         tp = mfree(tp);
413 
414         log_info("Created new local image '%s'.", i->local);
415 
416         if (FLAGS_SET(i->flags, PULL_SETTINGS)) {
417                 r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path);
418                 if (r < 0)
419                         return r;
420         }
421 
422         if (FLAGS_SET(i->flags, PULL_ROOTHASH)) {
423                 r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
424                 if (r < 0)
425                         return r;
426         }
427 
428         if (FLAGS_SET(i->flags, PULL_ROOTHASH_SIGNATURE)) {
429                 r = raw_pull_copy_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_path);
430                 if (r < 0)
431                         return r;
432         }
433 
434         if (FLAGS_SET(i->flags, PULL_VERITY)) {
435                 r = raw_pull_copy_auxiliary_file(i, ".verity", &i->verity_path);
436                 if (r < 0)
437                         return r;
438         }
439 
440         return 0;
441 }
442 
raw_pull_is_done(RawPull * i)443 static bool raw_pull_is_done(RawPull *i) {
444         assert(i);
445         assert(i->raw_job);
446 
447         if (!PULL_JOB_IS_COMPLETE(i->raw_job))
448                 return false;
449         if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
450                 return false;
451         if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
452                 return false;
453         if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
454                 return false;
455         if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job))
456                 return false;
457         if (i->roothash_signature_job && !PULL_JOB_IS_COMPLETE(i->roothash_signature_job))
458                 return false;
459         if (i->verity_job && !PULL_JOB_IS_COMPLETE(i->verity_job))
460                 return false;
461 
462         return true;
463 }
464 
raw_pull_rename_auxiliary_file(RawPull * i,const char * suffix,char ** temp_path,char ** path)465 static int raw_pull_rename_auxiliary_file(
466                 RawPull *i,
467                 const char *suffix,
468                 char **temp_path,
469                 char **path) {
470 
471         int r;
472 
473         assert(i);
474         assert(path);
475         assert(temp_path);
476         assert(*temp_path);
477         assert(suffix);
478 
479         /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should
480          * incorporate it in the file name if we can */
481         *path = mfree(*path);
482         r = raw_pull_determine_path(i, suffix, path);
483         if (r < 0)
484                 return r;
485 
486         r = install_file(
487                         AT_FDCWD, *temp_path,
488                         AT_FDCWD, *path,
489                         INSTALL_READ_ONLY|
490                         (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
491         if (r < 0)
492                 return log_error_errno(r, "Failed to move '%s' into place: %m", *path);
493 
494         *temp_path = mfree(*temp_path);
495         return 1;
496 }
497 
raw_pull_job_on_finished(PullJob * j)498 static void raw_pull_job_on_finished(PullJob *j) {
499         RawPull *i;
500         PullJob *jj;
501         int r;
502 
503         assert(j);
504         assert(j->userdata);
505 
506         i = j->userdata;
507 
508         if (j->error != 0) {
509                 /* Only the main job and the checksum job are fatal if they fail. The other fails are just
510                  * "decoration", that we'll download if we can. The signature job isn't fatal here because we
511                  * might not actually need it in case Suse style signatures are used, that are inline in the
512                  * checksum file. */
513 
514                 if (j == i->raw_job) {
515                         if (j->error == ENOMEDIUM) /* HTTP 404 */
516                                 r = log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
517                         else
518                                 r = log_error_errno(j->error, "Failed to retrieve image file.");
519                         goto finish;
520                 } else if (j == i->checksum_job) {
521                         r = log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
522                         goto finish;
523                 } else if (j == i->signature_job)
524                         log_debug_errno(j->error, "Signature job for %s failed, proceeding for now.", j->url);
525                 else if (j == i->settings_job)
526                         log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
527                 else if (j == i->roothash_job)
528                         log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without.");
529                 else if (j == i->roothash_signature_job)
530                         log_info_errno(j->error, "Root hash signature file could not be retrieved, proceeding without.");
531                 else if (j == i->verity_job)
532                         log_info_errno(j->error, "Verity integrity file could not be retrieved, proceeding without.");
533                 else
534                         assert_not_reached();
535         }
536 
537         /* This is invoked if either the download completed successfully, or the download was skipped because
538          * we already have the etag. In this case ->etag_exists is true.
539          *
540          * We only do something when we got all files */
541 
542         if (!raw_pull_is_done(i))
543                 return;
544 
545         if (i->signature_job && i->signature_job->error != 0) {
546                 VerificationStyle style;
547                 PullJob *verify_job;
548 
549                 /* The signature job failed. Let's see if we actually need it */
550 
551                 verify_job = i->checksum_job ?: i->raw_job; /* if the checksum job doesn't exist this must be
552                                                              * because the main job is the checksum file
553                                                              * itself */
554 
555                 assert(verify_job);
556 
557                 r = verification_style_from_url(verify_job->url, &style);
558                 if (r < 0) {
559                         log_error_errno(r, "Failed to determine verification style from checksum URL: %m");
560                         goto finish;
561                 }
562 
563                 if (style == VERIFICATION_PER_DIRECTORY) { /* A failed signature file download only matters
564                                                             * in per-directory verification mode, since only
565                                                             * then the signature is detached, and thus a file
566                                                             * of its own. */
567                         r = log_error_errno(i->signature_job->error,
568                                             "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
569                         goto finish;
570                 }
571         }
572 
573         /* Let's close these auxiliary files now, we don't need access to them anymore. */
574         FOREACH_POINTER(jj, i->settings_job, i->roothash_job, i->roothash_signature_job, i->verity_job)
575                 pull_job_close_disk_fd(jj);
576 
577         if (!i->raw_job->etag_exists) {
578                 raw_pull_report_progress(i, RAW_VERIFYING);
579 
580                 r = pull_verify(i->verify,
581                                 i->checksum,
582                                 i->raw_job,
583                                 i->checksum_job,
584                                 i->signature_job,
585                                 i->settings_job,
586                                 i->roothash_job,
587                                 i->roothash_signature_job,
588                                 i->verity_job);
589                 if (r < 0)
590                         goto finish;
591         }
592 
593         if (i->flags & PULL_DIRECT) {
594                 assert(!i->settings_job);
595                 assert(!i->roothash_job);
596                 assert(!i->roothash_signature_job);
597                 assert(!i->verity_job);
598 
599                 raw_pull_report_progress(i, RAW_FINALIZING);
600 
601                 if (i->local) {
602                         r = install_file(AT_FDCWD, i->local,
603                                          AT_FDCWD, NULL,
604                                          ((i->flags & PULL_READ_ONLY) && i->offset == UINT64_MAX ? INSTALL_READ_ONLY : 0) |
605                                          (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
606                         if (r < 0) {
607                                 log_error_errno(r, "Failed to finalize raw file to '%s': %m", i->local);
608                                 goto finish;
609                         }
610                 }
611         } else {
612                 r = raw_pull_determine_path(i, ".raw", &i->final_path);
613                 if (r < 0)
614                         goto finish;
615 
616                 if (!i->raw_job->etag_exists) {
617                         /* This is a new download, verify it, and move it into place */
618 
619                         assert(i->temp_path);
620                         assert(i->final_path);
621 
622                         raw_pull_report_progress(i, RAW_UNPACKING);
623 
624                         r = raw_pull_maybe_convert_qcow2(i);
625                         if (r < 0)
626                                 goto finish;
627 
628                         raw_pull_report_progress(i, RAW_FINALIZING);
629 
630                         r = install_file(AT_FDCWD, i->temp_path,
631                                          AT_FDCWD, i->final_path,
632                                          INSTALL_READ_ONLY|
633                                          (i->flags & PULL_SYNC ? INSTALL_FSYNC_FULL : 0));
634                         if (r < 0) {
635                                 log_error_errno(r, "Failed to move raw file to '%s': %m", i->final_path);
636                                 goto finish;
637                         }
638 
639                         i->temp_path = mfree(i->temp_path);
640 
641                         if (i->settings_job &&
642                             i->settings_job->error == 0) {
643                                 r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path);
644                                 if (r < 0)
645                                         goto finish;
646                         }
647 
648                         if (i->roothash_job &&
649                             i->roothash_job->error == 0) {
650                                 r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path);
651                                 if (r < 0)
652                                         goto finish;
653                         }
654 
655                         if (i->roothash_signature_job &&
656                             i->roothash_signature_job->error == 0) {
657                                 r = raw_pull_rename_auxiliary_file(i, ".roothash.p7s", &i->roothash_signature_temp_path, &i->roothash_signature_path);
658                                 if (r < 0)
659                                         goto finish;
660                         }
661 
662                         if (i->verity_job &&
663                             i->verity_job->error == 0) {
664                                 r = raw_pull_rename_auxiliary_file(i, ".verity", &i->verity_temp_path, &i->verity_path);
665                                 if (r < 0)
666                                         goto finish;
667                         }
668                 }
669 
670                 raw_pull_report_progress(i, RAW_COPYING);
671 
672                 r = raw_pull_make_local_copy(i);
673                 if (r < 0)
674                         goto finish;
675         }
676 
677         r = 0;
678 
679 finish:
680         if (i->on_finished)
681                 i->on_finished(i, r, i->userdata);
682         else
683                 sd_event_exit(i->event, r);
684 }
685 
raw_pull_job_on_open_disk_generic(RawPull * i,PullJob * j,const char * extra,char ** temp_path)686 static int raw_pull_job_on_open_disk_generic(
687                 RawPull *i,
688                 PullJob *j,
689                 const char *extra,
690                 char **temp_path /* input + output */) {
691 
692         int r;
693 
694         assert(i);
695         assert(j);
696         assert(extra);
697         assert(temp_path);
698 
699         assert(!FLAGS_SET(i->flags, PULL_DIRECT));
700 
701         if (!*temp_path) {
702                 r = tempfn_random_child(i->image_root, extra, temp_path);
703                 if (r < 0)
704                         return log_oom();
705         }
706 
707         (void) mkdir_parents_label(*temp_path, 0700);
708 
709         j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
710         if (j->disk_fd < 0)
711                 return log_error_errno(errno, "Failed to create %s: %m", *temp_path);
712 
713         return 0;
714 }
715 
raw_pull_job_on_open_disk_raw(PullJob * j)716 static int raw_pull_job_on_open_disk_raw(PullJob *j) {
717         RawPull *i;
718         int r;
719 
720         assert(j);
721         assert(j->userdata);
722 
723         i = j->userdata;
724         assert(i->raw_job == j);
725         assert(j->disk_fd < 0);
726 
727         if (i->flags & PULL_DIRECT) {
728 
729                 if (!i->local) { /* If no local name specified, the pull job will write its data to stdout */
730                         j->disk_fd = STDOUT_FILENO;
731                         j->close_disk_fd = false;
732                         return 0;
733                 }
734 
735                 (void) mkdir_parents_label(i->local, 0700);
736 
737                 j->disk_fd = open(i->local, O_RDWR|O_NOCTTY|O_CLOEXEC|(i->offset == UINT64_MAX ? O_TRUNC|O_CREAT : 0), 0664);
738                 if (j->disk_fd < 0)
739                         return log_error_errno(errno, "Failed to open destination '%s': %m", i->local);
740 
741                 if (i->offset == UINT64_MAX)
742                         (void) import_set_nocow_and_log(j->disk_fd, i->local);
743 
744         } else {
745                 r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
746                 if (r < 0)
747                         return r;
748 
749                 assert(i->offset == UINT64_MAX);
750                 (void) import_set_nocow_and_log(j->disk_fd, i->temp_path);
751         }
752 
753         return 0;
754 }
755 
raw_pull_job_on_open_disk_settings(PullJob * j)756 static int raw_pull_job_on_open_disk_settings(PullJob *j) {
757         RawPull *i;
758 
759         assert(j);
760         assert(j->userdata);
761 
762         i = j->userdata;
763         assert(i->settings_job == j);
764 
765         return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path);
766 }
767 
raw_pull_job_on_open_disk_roothash(PullJob * j)768 static int raw_pull_job_on_open_disk_roothash(PullJob *j) {
769         RawPull *i;
770 
771         assert(j);
772         assert(j->userdata);
773 
774         i = j->userdata;
775         assert(i->roothash_job == j);
776 
777         return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
778 }
779 
raw_pull_job_on_open_disk_roothash_signature(PullJob * j)780 static int raw_pull_job_on_open_disk_roothash_signature(PullJob *j) {
781         RawPull *i;
782 
783         assert(j);
784         assert(j->userdata);
785 
786         i = j->userdata;
787         assert(i->roothash_signature_job == j);
788 
789         return raw_pull_job_on_open_disk_generic(i, j, "roothash.p7s", &i->roothash_signature_temp_path);
790 }
791 
raw_pull_job_on_open_disk_verity(PullJob * j)792 static int raw_pull_job_on_open_disk_verity(PullJob *j) {
793         RawPull *i;
794 
795         assert(j);
796         assert(j->userdata);
797 
798         i = j->userdata;
799         assert(i->verity_job == j);
800 
801         return raw_pull_job_on_open_disk_generic(i, j, "verity", &i->verity_temp_path);
802 }
803 
raw_pull_job_on_progress(PullJob * j)804 static void raw_pull_job_on_progress(PullJob *j) {
805         RawPull *i;
806 
807         assert(j);
808         assert(j->userdata);
809 
810         i = j->userdata;
811 
812         raw_pull_report_progress(i, RAW_DOWNLOADING);
813 }
814 
raw_pull_start(RawPull * i,const char * url,const char * local,uint64_t offset,uint64_t size_max,PullFlags flags,ImportVerify verify,const char * checksum)815 int raw_pull_start(
816                 RawPull *i,
817                 const char *url,
818                 const char *local,
819                 uint64_t offset,
820                 uint64_t size_max,
821                 PullFlags flags,
822                 ImportVerify verify,
823                 const char *checksum) {
824 
825         PullJob *j;
826         int r;
827 
828         assert(i);
829         assert(url);
830         assert(verify == _IMPORT_VERIFY_INVALID || verify < _IMPORT_VERIFY_MAX);
831         assert(verify == _IMPORT_VERIFY_INVALID || verify >= 0);
832         assert((verify < 0) || !checksum);
833         assert(!(flags & ~PULL_FLAGS_MASK_RAW));
834         assert(offset == UINT64_MAX || FLAGS_SET(flags, PULL_DIRECT));
835         assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !(flags & PULL_DIRECT));
836         assert(!(flags & (PULL_SETTINGS|PULL_ROOTHASH|PULL_ROOTHASH_SIGNATURE|PULL_VERITY)) || !checksum);
837 
838         if (!http_url_is_valid(url) && !file_url_is_valid(url))
839                 return -EINVAL;
840 
841         if (local && !pull_validate_local(local, flags))
842                 return -EINVAL;
843 
844         if (i->raw_job)
845                 return -EBUSY;
846 
847         r = free_and_strdup(&i->local, local);
848         if (r < 0)
849                 return r;
850 
851         r = free_and_strdup(&i->checksum, checksum);
852         if (r < 0)
853                 return r;
854 
855         i->flags = flags;
856         i->verify = verify;
857 
858         /* Queue job for the image itself */
859         r = pull_job_new(&i->raw_job, url, i->glue, i);
860         if (r < 0)
861                 return r;
862 
863         i->raw_job->on_finished = raw_pull_job_on_finished;
864         i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
865 
866         if (checksum)
867                 i->raw_job->calc_checksum = true;
868         else if (verify != IMPORT_VERIFY_NO) {
869                 /* Calculate checksum of the main download unless the users asks for a SHA256SUM file or its
870                  * signature, which we let gpg verify instead. */
871 
872                 r = pull_url_needs_checksum(url);
873                 if (r < 0)
874                         return r;
875 
876                 i->raw_job->calc_checksum = r;
877                 i->raw_job->force_memory = true; /* make sure this is both written to disk if that's
878                                                   * requested and into memory, since we need to verify it */
879         }
880 
881         if (size_max != UINT64_MAX)
882                 i->raw_job->uncompressed_max = size_max;
883         if (offset != UINT64_MAX)
884                 i->raw_job->offset = i->offset = offset;
885 
886         if (!FLAGS_SET(flags, PULL_DIRECT)) {
887                 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
888                 if (r < 0)
889                         return r;
890         }
891 
892         r = pull_make_verification_jobs(
893                         &i->checksum_job,
894                         &i->signature_job,
895                         verify,
896                         i->checksum,
897                         url,
898                         i->glue,
899                         raw_pull_job_on_finished,
900                         i);
901         if (r < 0)
902                 return r;
903 
904         if (FLAGS_SET(flags, PULL_SETTINGS)) {
905                 r = pull_make_auxiliary_job(
906                                 &i->settings_job,
907                                 url,
908                                 raw_strip_suffixes,
909                                 ".nspawn",
910                                 verify,
911                                 i->glue,
912                                 raw_pull_job_on_open_disk_settings,
913                                 raw_pull_job_on_finished,
914                                 i);
915                 if (r < 0)
916                         return r;
917         }
918 
919         if (FLAGS_SET(flags, PULL_ROOTHASH)) {
920                 r = pull_make_auxiliary_job(
921                                 &i->roothash_job,
922                                 url,
923                                 raw_strip_suffixes,
924                                 ".roothash",
925                                 verify,
926                                 i->glue,
927                                 raw_pull_job_on_open_disk_roothash,
928                                 raw_pull_job_on_finished,
929                                 i);
930                 if (r < 0)
931                         return r;
932         }
933 
934         if (FLAGS_SET(flags, PULL_ROOTHASH_SIGNATURE)) {
935                 r = pull_make_auxiliary_job(
936                                 &i->roothash_signature_job,
937                                 url,
938                                 raw_strip_suffixes,
939                                 ".roothash.p7s",
940                                 verify,
941                                 i->glue,
942                                 raw_pull_job_on_open_disk_roothash_signature,
943                                 raw_pull_job_on_finished,
944                                 i);
945                 if (r < 0)
946                         return r;
947         }
948 
949         if (FLAGS_SET(flags, PULL_VERITY)) {
950                 r = pull_make_auxiliary_job(
951                                 &i->verity_job,
952                                 url,
953                                 raw_strip_suffixes,
954                                 ".verity",
955                                 verify,
956                                 i->glue,
957                                 raw_pull_job_on_open_disk_verity,
958                                 raw_pull_job_on_finished,
959                                 i);
960                 if (r < 0)
961                         return r;
962         }
963 
964         FOREACH_POINTER(j,
965                         i->raw_job,
966                         i->checksum_job,
967                         i->signature_job,
968                         i->settings_job,
969                         i->roothash_job,
970                         i->roothash_signature_job,
971                         i->verity_job) {
972 
973                 if (!j)
974                         continue;
975 
976                 j->on_progress = raw_pull_job_on_progress;
977                 j->sync = FLAGS_SET(flags, PULL_SYNC);
978 
979                 r = pull_job_begin(j);
980                 if (r < 0)
981                         return r;
982         }
983 
984         return 0;
985 }
986