chiark / gitweb /
94b936b43983ccdd2bf975361243fc7eeec39ff9
[elogind.git] / src / import / import-raw.c
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>
23 #include <linux/fs.h>
24 #include <curl/curl.h>
25 #include <lzma.h>
26
27 #include "hashmap.h"
28 #include "utf8.h"
29 #include "curl-util.h"
30 #include "qcow2-util.h"
31 #include "import-raw.h"
32 #include "strv.h"
33 #include "copy.h"
34
35 typedef struct RawImportFile RawImportFile;
36
37 struct RawImportFile {
38         RawImport *import;
39
40         char *url;
41         char *local;
42
43         CURL *curl;
44         struct curl_slist *request_header;
45
46         char *temp_path;
47         char *final_path;
48         char *etag;
49         char **old_etags;
50
51         uint64_t content_length;
52         uint64_t written_compressed;
53         uint64_t written_uncompressed;
54
55         void *payload;
56         size_t payload_size;
57
58         usec_t mtime;
59
60         bool force_local;
61         bool done;
62
63         int disk_fd;
64
65         lzma_stream lzma;
66         bool compressed;
67 };
68
69 struct RawImport {
70         sd_event *event;
71         CurlGlue *glue;
72
73         char *image_root;
74         Hashmap *files;
75
76         raw_import_on_finished on_finished;
77         void *userdata;
78
79         bool finished;
80 };
81
82 #define FILENAME_ESCAPE "/.#\"\'"
83
84 #define RAW_MAX_SIZE (1024LLU*1024LLU*1024LLU*8) /* 8 GB */
85
86 static RawImportFile *raw_import_file_unref(RawImportFile *f) {
87         if (!f)
88                 return NULL;
89
90         if (f->import)
91                 curl_glue_remove_and_free(f->import->glue, f->curl);
92         curl_slist_free_all(f->request_header);
93
94         safe_close(f->disk_fd);
95
96         free(f->final_path);
97
98         if (f->temp_path) {
99                 unlink(f->temp_path);
100                 free(f->temp_path);
101         }
102
103         free(f->url);
104         free(f->local);
105         free(f->etag);
106         strv_free(f->old_etags);
107         free(f->payload);
108         free(f);
109
110         return NULL;
111 }
112
113 DEFINE_TRIVIAL_CLEANUP_FUNC(RawImportFile*, raw_import_file_unref);
114
115 static void raw_import_finish(RawImport *import, int error) {
116         assert(import);
117
118         if (import->finished)
119                 return;
120
121         import->finished = true;
122
123         if (import->on_finished)
124                 import->on_finished(import, error, import->userdata);
125         else
126                 sd_event_exit(import->event, error);
127 }
128
129 static int raw_import_file_make_final_path(RawImportFile *f) {
130         _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
131
132         assert(f);
133
134         if (f->final_path)
135                 return 0;
136
137         escaped_url = xescape(f->url, FILENAME_ESCAPE);
138         if (!escaped_url)
139                 return -ENOMEM;
140
141         if (f->etag) {
142                 escaped_etag = xescape(f->etag, FILENAME_ESCAPE);
143                 if (!escaped_etag)
144                         return -ENOMEM;
145
146                 f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".", escaped_etag, ".raw", NULL);
147         } else
148                 f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".raw", NULL);
149         if (!f->final_path)
150                 return -ENOMEM;
151
152         return 0;
153 }
154
155 static int raw_import_file_make_local_copy(RawImportFile *f) {
156         _cleanup_free_ char *tp = NULL;
157         _cleanup_close_ int dfd = -1;
158         const char *p;
159         int r;
160
161         assert(f);
162
163         if (!f->local)
164                 return 0;
165
166         if (f->disk_fd >= 0) {
167                 if (lseek(f->disk_fd, SEEK_SET, 0) == (off_t) -1)
168                         return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
169         } else {
170                 r = raw_import_file_make_final_path(f);
171                 if (r < 0)
172                         return log_oom();
173
174                 f->disk_fd = open(f->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
175                 if (f->disk_fd < 0)
176                         return log_error_errno(errno, "Failed to open vendor image: %m");
177         }
178
179         p = strappenda(f->import->image_root, "/", f->local, ".raw");
180         if (f->force_local)
181                 (void) rm_rf_dangerous(p, false, true, false);
182
183         r = tempfn_random(p, &tp);
184         if (r < 0)
185                 return log_oom();
186
187         dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
188         if (dfd < 0)
189                 return log_error_errno(errno, "Failed to create writable copy of image: %m");
190
191         /* Turn off COW writing. This should greatly improve
192          * performance on COW file systems like btrfs, since it
193          * reduces fragmentation caused by not allowing in-place
194          * writes. */
195         r = chattr_fd(dfd, true, FS_NOCOW_FL);
196         if (r < 0)
197                 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
198
199         r = copy_bytes(f->disk_fd, dfd, (off_t) -1, true);
200         if (r < 0) {
201                 unlink(tp);
202                 return log_error_errno(r, "Failed to make writable copy of image: %m");
203         }
204
205         (void) copy_times(f->disk_fd, dfd);
206         (void) copy_xattr(f->disk_fd, dfd);
207
208         dfd = safe_close(dfd);
209
210         r = rename(tp, p);
211         if (r < 0)  {
212                 unlink(tp);
213                 return log_error_errno(errno, "Failed to move writable image into place: %m");
214         }
215
216         log_info("Created new local image %s.", p);
217         return 0;
218 }
219
220 static void raw_import_file_success(RawImportFile *f) {
221         int r;
222
223         assert(f);
224
225         f->done = true;
226
227         r = raw_import_file_make_local_copy(f);
228         if (r < 0)
229                 goto finish;
230
231         f->disk_fd = safe_close(f->disk_fd);
232         r = 0;
233
234 finish:
235         raw_import_finish(f->import, r);
236 }
237
238 static int raw_import_maybe_convert_qcow2(RawImportFile *f) {
239         _cleanup_close_ int converted_fd = -1;
240         _cleanup_free_ char *t = NULL;
241         int r;
242
243         assert(f);
244         assert(f->disk_fd);
245         assert(f->temp_path);
246
247         r = qcow2_detect(f->disk_fd);
248         if (r < 0)
249                 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
250         if (r == 0)
251                 return 0;
252
253         /* This is a QCOW2 image, let's convert it */
254         r = tempfn_random(f->final_path, &t);
255         if (r < 0)
256                 return log_oom();
257
258         converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
259         if (converted_fd < 0)
260                 return log_error_errno(errno, "Failed to create %s: %m", t);
261
262         r = qcow2_convert(f->disk_fd, converted_fd);
263         if (r < 0) {
264                 unlink(t);
265                 return log_error_errno(r, "Failed to convert qcow2 image: %m");
266         }
267
268         unlink(f->temp_path);
269         free(f->temp_path);
270
271         f->temp_path = t;
272         t = NULL;
273
274         safe_close(f->disk_fd);
275         f->disk_fd = converted_fd;
276         converted_fd = -1;
277
278         return 1;
279 }
280
281 static void raw_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
282         RawImportFile *f = NULL;
283         struct stat st;
284         CURLcode code;
285         long status;
286         int r;
287
288         if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &f) != CURLE_OK)
289                 return;
290
291         if (!f || f->done)
292                 return;
293
294         f->done = true;
295
296         if (result != CURLE_OK) {
297                 log_error("Transfer failed: %s", curl_easy_strerror(result));
298                 r = -EIO;
299                 goto fail;
300         }
301
302         code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
303         if (code != CURLE_OK) {
304                 log_error("Failed to retrieve response code: %s", curl_easy_strerror(code));
305                 r = -EIO;
306                 goto fail;
307         } else if (status == 304) {
308                 log_info("Image already downloaded. Skipping download.");
309                 raw_import_file_success(f);
310                 return;
311         } else if (status >= 300) {
312                 log_error("HTTP request to %s failed with code %li.", f->url, status);
313                 r = -EIO;
314                 goto fail;
315         } else if (status < 200) {
316                 log_error("HTTP request to %s finished with unexpected code %li.", f->url, status);
317                 r = -EIO;
318                 goto fail;
319         }
320
321         if (f->disk_fd < 0) {
322                 log_error("No data received.");
323                 r = -EIO;
324                 goto fail;
325         }
326
327         if (f->content_length != (uint64_t) -1 &&
328             f->content_length != f->written_compressed) {
329                 log_error("Download truncated.");
330                 r = -EIO;
331                 goto fail;
332         }
333
334         /* Make sure the file size is right, in case the file was
335          * sparse and we just seeked for the last part */
336         if (ftruncate(f->disk_fd, f->written_uncompressed) < 0) {
337                 log_error_errno(errno, "Failed to truncate file: %m");
338                 r = -errno;
339                 goto fail;
340         }
341
342         r = raw_import_maybe_convert_qcow2(f);
343         if (r < 0)
344                 goto fail;
345
346         if (f->etag)
347                 (void) fsetxattr(f->disk_fd, "user.source_etag", f->etag, strlen(f->etag), 0);
348         if (f->url)
349                 (void) fsetxattr(f->disk_fd, "user.source_url", f->url, strlen(f->url), 0);
350
351         if (f->mtime != 0) {
352                 struct timespec ut[2];
353
354                 timespec_store(&ut[0], f->mtime);
355                 ut[1] = ut[0];
356                 (void) futimens(f->disk_fd, ut);
357
358                 fd_setcrtime(f->disk_fd, f->mtime);
359         }
360
361         if (fstat(f->disk_fd, &st) < 0) {
362                 r = log_error_errno(errno, "Failed to stat file: %m");
363                 goto fail;
364         }
365
366         /* Mark read-only */
367         (void) fchmod(f->disk_fd, st.st_mode & 07444);
368
369         assert(f->temp_path);
370         assert(f->final_path);
371
372         r = rename(f->temp_path, f->final_path);
373         if (r < 0) {
374                 r = log_error_errno(errno, "Failed to move RAW file into place: %m");
375                 goto fail;
376         }
377
378         free(f->temp_path);
379         f->temp_path = NULL;
380
381         log_info("Completed writing vendor image %s.", f->final_path);
382
383         raw_import_file_success(f);
384         return;
385
386 fail:
387         raw_import_finish(f->import, r);
388 }
389
390 static int raw_import_file_open_disk_for_write(RawImportFile *f) {
391         int r;
392
393         assert(f);
394
395         if (f->disk_fd >= 0)
396                 return 0;
397
398         r = raw_import_file_make_final_path(f);
399         if (r < 0)
400                 return log_oom();
401
402         if (!f->temp_path) {
403                 r = tempfn_random(f->final_path, &f->temp_path);
404                 if (r < 0)
405                         return log_oom();
406         }
407
408         f->disk_fd = open(f->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
409         if (f->disk_fd < 0)
410                 return log_error_errno(errno, "Failed to create %s: %m", f->temp_path);
411
412         r = chattr_fd(f->disk_fd, true, FS_NOCOW_FL);
413         if (r < 0)
414                 log_warning_errno(errno, "Failed to set file attributes on %s: %m", f->temp_path);
415
416         return 0;
417 }
418
419 static int raw_import_file_write_uncompressed(RawImportFile *f, void *p, size_t sz) {
420         ssize_t n;
421
422         assert(f);
423         assert(p);
424         assert(sz > 0);
425         assert(f->disk_fd >= 0);
426
427         if (f->written_uncompressed + sz < f->written_uncompressed) {
428                 log_error("File too large, overflow");
429                 return -EOVERFLOW;
430         }
431
432         if (f->written_uncompressed + sz > RAW_MAX_SIZE) {
433                 log_error("File overly large, refusing");
434                 return -EFBIG;
435         }
436
437         n = sparse_write(f->disk_fd, p, sz, 64);
438         if (n < 0) {
439                 log_error_errno(errno, "Failed to write file: %m");
440                 return -errno;
441         }
442         if ((size_t) n < sz) {
443                 log_error("Short write");
444                 return -EIO;
445         }
446
447         f->written_uncompressed += sz;
448
449         return 0;
450 }
451
452 static int raw_import_file_write_compressed(RawImportFile *f, void *p, size_t sz) {
453         int r;
454
455         assert(f);
456         assert(p);
457         assert(sz > 0);
458         assert(f->disk_fd >= 0);
459
460         if (f->written_compressed + sz < f->written_compressed) {
461                 log_error("File too large, overflow");
462                 return -EOVERFLOW;
463         }
464
465         if (f->content_length != (uint64_t) -1 &&
466             f->written_compressed + sz > f->content_length) {
467                 log_error("Content length incorrect.");
468                 return -EFBIG;
469         }
470
471         if (!f->compressed) {
472                 r = raw_import_file_write_uncompressed(f, p, sz);
473                 if (r < 0)
474                         return r;
475         } else {
476                 f->lzma.next_in = p;
477                 f->lzma.avail_in = sz;
478
479                 while (f->lzma.avail_in > 0) {
480                         uint8_t buffer[16 * 1024];
481                         lzma_ret lzr;
482
483                         f->lzma.next_out = buffer;
484                         f->lzma.avail_out = sizeof(buffer);
485
486                         lzr = lzma_code(&f->lzma, LZMA_RUN);
487                         if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) {
488                                 log_error("Decompression error.");
489                                 return -EIO;
490                         }
491
492                         r = raw_import_file_write_uncompressed(f, buffer, sizeof(buffer) - f->lzma.avail_out);
493                         if (r < 0)
494                                 return r;
495                 }
496         }
497
498         f->written_compressed += sz;
499
500         return 0;
501 }
502
503 static int raw_import_file_detect_xz(RawImportFile *f) {
504         static const uint8_t xz_signature[] = {
505                 '\xfd', '7', 'z', 'X', 'Z', '\x00'
506         };
507         lzma_ret lzr;
508         int r;
509
510         assert(f);
511
512         if (f->payload_size < sizeof(xz_signature))
513                 return 0;
514
515         f->compressed = memcmp(f->payload, xz_signature, sizeof(xz_signature)) == 0;
516         log_debug("Stream is XZ compressed: %s", yes_no(f->compressed));
517
518         if (f->compressed) {
519                 lzr = lzma_stream_decoder(&f->lzma, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
520                 if (lzr != LZMA_OK) {
521                         log_error("Failed to initialize LZMA decoder.");
522                         return -EIO;
523                 }
524         }
525
526         r = raw_import_file_open_disk_for_write(f);
527         if (r < 0)
528                 return r;
529
530         r = raw_import_file_write_compressed(f, f->payload, f->payload_size);
531         if (r < 0)
532                 return r;
533
534         free(f->payload);
535         f->payload = NULL;
536         f->payload_size = 0;
537
538         return 0;
539 }
540
541 static size_t raw_import_file_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
542         RawImportFile *f = userdata;
543         size_t sz = size * nmemb;
544         int r;
545
546         assert(contents);
547         assert(f);
548
549         if (f->done) {
550                 r = -ESTALE;
551                 goto fail;
552         }
553
554         if (f->disk_fd < 0) {
555                 uint8_t *p;
556
557                 /* We haven't opened the file yet, let's first check what it actually is */
558
559                 p = realloc(f->payload, f->payload_size + sz);
560                 if (!p) {
561                         r = log_oom();
562                         goto fail;
563                 }
564
565                 memcpy(p + f->payload_size, contents, sz);
566                 f->payload_size = sz;
567                 f->payload = p;
568
569                 r = raw_import_file_detect_xz(f);
570                 if (r < 0)
571                         goto fail;
572
573                 return sz;
574         }
575
576         r = raw_import_file_write_compressed(f, contents, sz);
577         if (r < 0)
578                 goto fail;
579
580         return sz;
581
582 fail:
583         raw_import_finish(f->import, r);
584         return 0;
585 }
586
587 static size_t raw_import_file_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
588         RawImportFile *f = userdata;
589         size_t sz = size * nmemb;
590         _cleanup_free_ char *length = NULL, *last_modified = NULL;
591         char *etag;
592         int r;
593
594         assert(contents);
595         assert(f);
596
597         if (f->done) {
598                 r = -ESTALE;
599                 goto fail;
600         }
601
602         r = curl_header_strdup(contents, sz, "ETag:", &etag);
603         if (r < 0) {
604                 log_oom();
605                 goto fail;
606         }
607         if (r > 0) {
608                 free(f->etag);
609                 f->etag = etag;
610
611                 if (strv_contains(f->old_etags, f->etag)) {
612                         log_info("Image already downloaded. Skipping download.");
613                         raw_import_file_success(f);
614                         return sz;
615                 }
616
617                 return sz;
618         }
619
620         r = curl_header_strdup(contents, sz, "Content-Length:", &length);
621         if (r < 0) {
622                 log_oom();
623                 goto fail;
624         }
625         if (r > 0) {
626                 (void) safe_atou64(length, &f->content_length);
627
628                 if (f->content_length != (uint64_t) -1) {
629                         char bytes[FORMAT_BYTES_MAX];
630                         log_info("Downloading %s.", format_bytes(bytes, sizeof(bytes), f->content_length));
631                 }
632
633                 return sz;
634         }
635
636         r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified);
637         if (r < 0) {
638                 log_oom();
639                 goto fail;
640         }
641         if (r > 0) {
642                 (void) curl_parse_http_time(last_modified, &f->mtime);
643                 return sz;
644         }
645
646         return sz;
647
648 fail:
649         raw_import_finish(f->import, r);
650         return 0;
651 }
652
653 static bool etag_is_valid(const char *etag) {
654
655         if (!endswith(etag, "\""))
656                 return false;
657
658         if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
659                 return false;
660
661         return true;
662 }
663
664 static int raw_import_file_find_old_etags(RawImportFile *f) {
665         _cleanup_free_ char *escaped_url = NULL;
666         _cleanup_closedir_ DIR *d = NULL;
667         struct dirent *de;
668         int r;
669
670         escaped_url = xescape(f->url, FILENAME_ESCAPE);
671         if (!escaped_url)
672                 return -ENOMEM;
673
674         d = opendir(f->import->image_root);
675         if (!d) {
676                 if (errno == ENOENT)
677                         return 0;
678
679                 return -errno;
680         }
681
682         FOREACH_DIRENT_ALL(de, d, return -errno) {
683                 const char *a, *b;
684                 char *u;
685
686                 if (de->d_type != DT_UNKNOWN &&
687                     de->d_type != DT_REG)
688                         continue;
689
690                 a = startswith(de->d_name, ".raw-");
691                 if (!a)
692                         continue;
693
694                 a = startswith(a, escaped_url);
695                 if (!a)
696                         continue;
697
698                 a = startswith(a, ".");
699                 if (!a)
700                         continue;
701
702                 b = endswith(de->d_name, ".raw");
703                 if (!b)
704                         continue;
705
706                 if (a >= b)
707                         continue;
708
709                 u = cunescape_length(a, b - a);
710                 if (!u)
711                         return -ENOMEM;
712
713                 if (!etag_is_valid(u)) {
714                         free(u);
715                         continue;
716                 }
717
718                 r = strv_consume(&f->old_etags, u);
719                 if (r < 0)
720                         return r;
721         }
722
723         return 0;
724 }
725
726 static int raw_import_file_begin(RawImportFile *f) {
727         int r;
728
729         assert(f);
730         assert(!f->curl);
731
732         log_info("Getting %s.", f->url);
733
734         r = raw_import_file_find_old_etags(f);
735         if (r < 0)
736                 return r;
737
738         r = curl_glue_make(&f->curl, f->url, f);
739         if (r < 0)
740                 return r;
741
742         if (!strv_isempty(f->old_etags)) {
743                 _cleanup_free_ char *cc = NULL, *hdr = NULL;
744
745                 cc = strv_join(f->old_etags, ", ");
746                 if (!cc)
747                         return -ENOMEM;
748
749                 hdr = strappend("If-None-Match: ", cc);
750                 if (!hdr)
751                         return -ENOMEM;
752
753                 f->request_header = curl_slist_new(hdr, NULL);
754                 if (!f->request_header)
755                         return -ENOMEM;
756
757                 if (curl_easy_setopt(f->curl, CURLOPT_HTTPHEADER, f->request_header) != CURLE_OK)
758                         return -EIO;
759         }
760
761         if (curl_easy_setopt(f->curl, CURLOPT_WRITEFUNCTION, raw_import_file_write_callback) != CURLE_OK)
762                 return -EIO;
763
764         if (curl_easy_setopt(f->curl, CURLOPT_WRITEDATA, f) != CURLE_OK)
765                 return -EIO;
766
767         if (curl_easy_setopt(f->curl, CURLOPT_HEADERFUNCTION, raw_import_file_header_callback) != CURLE_OK)
768                 return -EIO;
769
770         if (curl_easy_setopt(f->curl, CURLOPT_HEADERDATA, f) != CURLE_OK)
771                 return -EIO;
772
773         r = curl_glue_add(f->import->glue, f->curl);
774         if (r < 0)
775                 return r;
776
777         return 0;
778 }
779
780 int raw_import_new(RawImport **import, sd_event *event, const char *image_root, raw_import_on_finished on_finished, void *userdata) {
781         _cleanup_(raw_import_unrefp) RawImport *i = NULL;
782         int r;
783
784         assert(import);
785         assert(image_root);
786
787         i = new0(RawImport, 1);
788         if (!i)
789                 return -ENOMEM;
790
791         i->on_finished = on_finished;
792         i->userdata = userdata;
793
794         i->image_root = strdup(image_root);
795         if (!i->image_root)
796                 return -ENOMEM;
797
798         if (event)
799                 i->event = sd_event_ref(event);
800         else {
801                 r = sd_event_default(&i->event);
802                 if (r < 0)
803                         return r;
804         }
805
806         r = curl_glue_new(&i->glue, i->event);
807         if (r < 0)
808                 return r;
809
810         i->glue->on_finished = raw_import_curl_on_finished;
811         i->glue->userdata = i;
812
813         *import = i;
814         i = NULL;
815
816         return 0;
817 }
818
819 RawImport* raw_import_unref(RawImport *import) {
820         RawImportFile *f;
821
822         if (!import)
823                 return NULL;
824
825         while ((f = hashmap_steal_first(import->files)))
826                 raw_import_file_unref(f);
827         hashmap_free(import->files);
828
829         curl_glue_unref(import->glue);
830         sd_event_unref(import->event);
831
832         free(import->image_root);
833         free(import);
834
835         return NULL;
836 }
837
838 int raw_import_cancel(RawImport *import, const char *url) {
839         RawImportFile *f;
840
841         assert(import);
842         assert(url);
843
844         f = hashmap_remove(import->files, url);
845         if (!f)
846                 return 0;
847
848         raw_import_file_unref(f);
849         return 1;
850 }
851
852 int raw_import_pull(RawImport *import, const char *url, const char *local, bool force_local) {
853         _cleanup_(raw_import_file_unrefp) RawImportFile *f = NULL;
854         int r;
855
856         assert(import);
857         assert(raw_url_is_valid(url));
858         assert(!local || machine_name_is_valid(local));
859
860         if (hashmap_get(import->files, url))
861                 return -EEXIST;
862
863         r = hashmap_ensure_allocated(&import->files, &string_hash_ops);
864         if (r < 0)
865                 return r;
866
867         f = new0(RawImportFile, 1);
868         if (!f)
869                 return -ENOMEM;
870
871         f->import = import;
872         f->disk_fd = -1;
873         f->content_length = (uint64_t) -1;
874
875         f->url = strdup(url);
876         if (!f->url)
877                 return -ENOMEM;
878
879         if (local) {
880                 f->local = strdup(local);
881                 if (!f->local)
882                         return -ENOMEM;
883
884                 f->force_local = force_local;
885         }
886
887         r = hashmap_put(import->files, f->url, f);
888         if (r < 0)
889                 return r;
890
891         r = raw_import_file_begin(f);
892         if (r < 0) {
893                 raw_import_cancel(import, f->url);
894                 f = NULL;
895                 return r;
896         }
897
898         f = NULL;
899         return 0;
900 }
901
902 bool raw_url_is_valid(const char *url) {
903         if (isempty(url))
904                 return false;
905
906         if (!startswith(url, "http://") &&
907             !startswith(url, "https://"))
908                 return false;
909
910         return ascii_is_valid(url);
911 }