import-raw.c revision 85dbc41dc67ff49fd8a843dbac5b8b5cb0b61155
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen This file is part of systemd.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Copyright 2014 Lennart Poettering
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is free software; you can redistribute it and/or modify it
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen under the terms of the GNU Lesser General Public License as published by
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen the Free Software Foundation; either version 2.1 of the License, or
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen (at your option) any later version.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen systemd is distributed in the hope that it will be useful, but
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen Lesser General Public License for more details.
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen You should have received a copy of the GNU Lesser General Public License
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenint raw_import_new(RawImport **ret, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata) {
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen _cleanup_(raw_import_unrefp) RawImport *i = NULL;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen i->image_root = strdup(image_root ?: "/var/lib/machines");
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen i->glue->on_finished = import_job_curl_on_finished;
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersenstatic int raw_import_maybe_convert_qcow2(RawImport *i) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
3a864fe4a894745ac61f1ecabd7cadf04139a284Tom Gundersen /* This is a QCOW2 image, let's convert it */
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return log_error_errno(errno, "Failed to create %s: %m", t);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen r = chattr_fd(converted_fd, true, FS_NOCOW_FL);
8de4a226c71ef43e652274b33b5d19211a44ac7bTom Gundersen log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
87322b3aee0dc649ff1ae7a403dcc9d7305baba2Tom Gundersen r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen return log_error_errno(r, "Failed to convert qcow2 image: %m");
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersenstatic int raw_import_make_local_copy(RawImport *i) {
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen const char *p;
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen /* We have downloaded this one previously, reopen it */
b44cd8821087f2afebf85fec5b588f5720a9415cTom Gundersen r = import_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return log_error_errno(errno, "Failed to open vendor image: %m");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen /* We freshly downloaded the image, use it */
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
ff734080aa02cd70b13bc0fdeec4a5886166163aTom Gundersen p = strappenda(i->image_root, "/", i->local, ".raw");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen (void) rm_rf_dangerous(p, false, true, false);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return log_error_errno(errno, "Failed to create writable copy of image: %m");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* Turn off COW writing. This should greatly improve
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen * performance on COW file systems like btrfs, since it
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen * reduces fragmentation caused by not allowing in-place
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return log_error_errno(r, "Failed to make writable copy of image: %m");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return log_error_errno(errno, "Failed to move writable image into place: %m");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_info("Created new local image '%s'.", i->local);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic int raw_import_verify_sha256sum(RawImport *i) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen const char *p, *line;
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen r = import_url_last_component(i->raw_job->url, &fn);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_error("Cannot verify checksum, could not determine valid server-side file name.");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen line = strappenda(i->raw_job->sha256, " *", fn, "\n");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (!p || (p != (char*) i->sha256sums_job->payload && p[-1] != '\n')) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_error("Checksum did not check out, payload has been tempered with.");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen log_info("SHA256 checksum of %s is valid.", i->raw_job->url);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (!IMPORT_JOB_STATE_IS_COMPLETE(i->raw_job) ||
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen !IMPORT_JOB_STATE_IS_COMPLETE(i->sha256sums_job))
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen return log_error_errno(errno, "Failed to move RAW file into place: %m");
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen i->raw_job->disk_fd = safe_close(i->raw_job->disk_fd);
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic void raw_import_invoke_finished(RawImport *i, int r) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersenstatic void raw_import_raw_job_on_finished(ImportJob *j) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen if (j->error != 0) {
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen /* This is invoked if either the download completed
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen * successfully, or the download was skipped because we
969b009d9416806911b9b52e7e7bc619c0c1a931Tom Gundersen * already have the etag. In this case ->etag_exists is
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersenstatic void raw_import_sha256sums_job_on_finished(ImportJob *j) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen if (j->error != 0) {
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersenstatic int raw_import_raw_job_on_open_disk(ImportJob *j) {
4dc355680460fdc8e0d590d8572dff1b6a257d88Tom Gundersen r = import_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = tempfn_random(i->final_path, &i->temp_path);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersenint raw_import_pull(RawImport *i, const char *url, const char *local, bool force_local) {
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen /* Queue job for the image itself */
bd57b45029ff25067704c9538e79f31e71c10045Tom Gundersen r = import_job_new(&i->raw_job, url, i->glue, i);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen i->raw_job->on_finished = raw_import_raw_job_on_finished;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen i->raw_job->on_open_disk = raw_import_raw_job_on_open_disk;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen r = import_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen /* Queue job for the SHA256SUMS file for the image */
c7d9ffe6d629cb5b34dd749e4a88b190b11a0f48Tom Gundersen r = import_url_change_last_component(url, "SHA256SUMS", &sha256sums_url);
2dead8129f7b6fe644e17e1dc1739bebacfe1364Tom Gundersen r = import_job_new(&i->sha256sums_job, sha256sums_url, i->glue, i);
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen i->sha256sums_job->on_finished = raw_import_sha256sums_job_on_finished;
816e2e7af96886e4a43194042ef61ba9fec2c77dTom Gundersen i->sha256sums_job->uncompressed_max = i->sha256sums_job->compressed_max = 1ULL * 1024ULL * 1024ULL;