1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
22 #include <sys/xattr.h>
24 #include <curl/curl.h>
29 #include "btrfs-util.h"
33 #include "curl-util.h"
34 #include "qcow2-util.h"
35 #include "import-job.h"
36 #include "import-util.h"
37 #include "import-raw.h"
39 typedef struct RawImportFile RawImportFile;
49 RawImportFinished on_finished;
59 RawImport* raw_import_unref(RawImport *i) {
63 import_job_unref(i->raw_job);
65 curl_glue_unref(i->glue);
66 sd_event_unref(i->event);
69 (void) unlink(i->temp_path);
81 int raw_import_new(RawImport **ret, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata) {
82 _cleanup_(raw_import_unrefp) RawImport *i = NULL;
87 i = new0(RawImport, 1);
91 i->on_finished = on_finished;
92 i->userdata = userdata;
94 i->image_root = strdup(image_root ?: "/var/lib/machines");
99 i->event = sd_event_ref(event);
101 r = sd_event_default(&i->event);
106 r = curl_glue_new(&i->glue, i->event);
110 i->glue->on_finished = import_job_curl_on_finished;
111 i->glue->userdata = i;
119 static int raw_import_maybe_convert_qcow2(RawImport *i) {
120 _cleanup_close_ int converted_fd = -1;
121 _cleanup_free_ char *t = NULL;
127 r = qcow2_detect(i->raw_job->disk_fd);
129 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
133 /* This is a QCOW2 image, let's convert it */
134 r = tempfn_random(i->final_path, &t);
138 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
139 if (converted_fd < 0)
140 return log_error_errno(errno, "Failed to create %s: %m", t);
142 r = chattr_fd(converted_fd, true, FS_NOCOW_FL);
144 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
146 log_info("Unpacking QCOW2 file.");
148 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
151 return log_error_errno(r, "Failed to convert qcow2 image: %m");
154 unlink(i->temp_path);
160 safe_close(i->raw_job->disk_fd);
161 i->raw_job->disk_fd = converted_fd;
167 static int raw_import_make_local_copy(RawImport *i) {
168 _cleanup_free_ char *tp = NULL;
169 _cleanup_close_ int dfd = -1;
179 if (i->raw_job->disk_fd >= 0) {
180 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
181 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
183 if (!i->final_path) {
184 r = import_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
189 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
190 if (i->raw_job->disk_fd < 0)
191 return log_error_errno(errno, "Failed to open vendor image: %m");
194 p = strappenda(i->image_root, "/", i->local, ".raw");
196 if (i->force_local) {
197 (void) btrfs_subvol_remove(p);
198 (void) rm_rf_dangerous(p, false, true, false);
201 r = tempfn_random(p, &tp);
205 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
207 return log_error_errno(errno, "Failed to create writable copy of image: %m");
209 /* Turn off COW writing. This should greatly improve
210 * performance on COW file systems like btrfs, since it
211 * reduces fragmentation caused by not allowing in-place
213 r = chattr_fd(dfd, true, FS_NOCOW_FL);
215 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
217 r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
220 return log_error_errno(r, "Failed to make writable copy of image: %m");
223 (void) copy_times(i->raw_job->disk_fd, dfd);
224 (void) copy_xattr(i->raw_job->disk_fd, dfd);
226 dfd = safe_close(dfd);
231 return log_error_errno(errno, "Failed to move writable image into place: %m");
234 log_info("Created new local image '%s'.", i->local);
238 static void raw_import_job_on_finished(ImportJob *j) {
251 /* This is invoked if either the download completed
252 * successfully, or the download was skipped because we
253 * already have the etag. */
255 if (j->disk_fd >= 0) {
256 r = raw_import_maybe_convert_qcow2(i);
260 r = import_make_read_only_fd(j->disk_fd);
264 r = rename(i->temp_path, i->final_path);
266 r = log_error_errno(errno, "Failed to move RAW file into place: %m");
274 r = raw_import_make_local_copy(i);
278 j->disk_fd = safe_close(j->disk_fd);
284 i->on_finished(i, r, i->userdata);
286 sd_event_exit(i->event, r);
289 static int raw_import_job_on_open_disk(ImportJob *j) {
298 r = import_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
302 r = tempfn_random(i->final_path, &i->temp_path);
306 mkdir_parents_label(i->temp_path, 0700);
308 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
310 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
312 r = chattr_fd(j->disk_fd, true, FS_NOCOW_FL);
314 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
319 int raw_import_pull(RawImport *i, const char *url, const char *local, bool force_local) {
327 if (!http_url_is_valid(url))
330 if (local && !machine_name_is_valid(local))
333 r = free_and_strdup(&i->local, local);
337 i->force_local = force_local;
339 r = import_job_new(&i->raw_job, url, i->glue, i);
343 i->raw_job->on_finished = raw_import_job_on_finished;
344 i->raw_job->on_open_disk = raw_import_job_on_open_disk;
346 r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
350 return import_job_begin(i->raw_job);