diff options
Diffstat (limited to 'src/import')
35 files changed, 0 insertions, 8327 deletions
diff --git a/src/import/.gitignore b/src/import/.gitignore deleted file mode 100644 index 01106e2e68..0000000000 --- a/src/import/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/org.freedesktop.import1.policy diff --git a/src/import/Makefile b/src/import/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/import/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/import/curl-util.c b/src/import/curl-util.c deleted file mode 100644 index 6990c47f48..0000000000 --- a/src/import/curl-util.c +++ /dev/null @@ -1,448 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "alloc-util.h" -#include "curl-util.h" -#include "fd-util.h" -#include "string-util.h" - -static void curl_glue_check_finished(CurlGlue *g) { - CURLMsg *msg; - int k = 0; - - assert(g); - - msg = curl_multi_info_read(g->curl, &k); - if (!msg) - return; - - if (msg->msg != CURLMSG_DONE) - return; - - if (g->on_finished) - g->on_finished(g, msg->easy_handle, msg->data.result); -} - -static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - CurlGlue *g = userdata; - int action, k = 0, translated_fd; - - assert(s); - assert(g); - - translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd))); - - if ((revents & (EPOLLIN|EPOLLOUT)) == (EPOLLIN|EPOLLOUT)) - action = CURL_POLL_INOUT; - else if (revents & EPOLLIN) - action = CURL_POLL_IN; - else if (revents & EPOLLOUT) - action = CURL_POLL_OUT; - else - action = 0; - - if (curl_multi_socket_action(g->curl, translated_fd, action, &k) < 0) { - log_debug("Failed to propagate IO event."); - return -EINVAL; - } - - curl_glue_check_finished(g); - return 0; -} - -static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) { - sd_event_source *io; - CurlGlue *g = userdata; - uint32_t events = 0; - int r; - - assert(curl); - assert(g); - - io = hashmap_get(g->ios, FD_TO_PTR(s)); - - if (action == CURL_POLL_REMOVE) { - if (io) { - int fd; - - fd = sd_event_source_get_io_fd(io); - assert(fd >= 0); - - sd_event_source_set_enabled(io, SD_EVENT_OFF); - sd_event_source_unref(io); - - hashmap_remove(g->ios, FD_TO_PTR(s)); - hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); - - safe_close(fd); - } - - return 0; - } - - r = hashmap_ensure_allocated(&g->ios, &trivial_hash_ops); - if (r < 0) { - log_oom(); - return -1; - } - - r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops); - if (r < 0) { - log_oom(); - return -1; - } - - if (action == CURL_POLL_IN) - events = EPOLLIN; - else if (action == CURL_POLL_OUT) - events = EPOLLOUT; - else if (action == CURL_POLL_INOUT) - events = EPOLLIN|EPOLLOUT; - - if (io) { - if (sd_event_source_set_io_events(io, events) < 0) - return -1; - - if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0) - return -1; - } else { - _cleanup_close_ int fd = -1; - - /* When curl needs to remove an fd from us it closes - * the fd first, and only then calls into us. This is - * nasty, since we cannot pass the fd on to epoll() - * anymore. Hence, duplicate the fds here, and keep a - * copy for epoll which we control after use. */ - - fd = fcntl(s, F_DUPFD_CLOEXEC, 3); - if (fd < 0) - return -1; - - if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0) - return -1; - - (void) sd_event_source_set_description(io, "curl-io"); - - r = hashmap_put(g->ios, FD_TO_PTR(s), io); - if (r < 0) { - log_oom(); - sd_event_source_unref(io); - return -1; - } - - r = hashmap_put(g->translate_fds, FD_TO_PTR(fd), FD_TO_PTR(s)); - if (r < 0) { - log_oom(); - hashmap_remove(g->ios, FD_TO_PTR(s)); - sd_event_source_unref(io); - return -1; - } - - fd = -1; - } - - return 0; -} - -static int curl_glue_on_timer(sd_event_source *s, uint64_t usec, void *userdata) { - CurlGlue *g = userdata; - int k = 0; - - assert(s); - assert(g); - - if (curl_multi_socket_action(g->curl, CURL_SOCKET_TIMEOUT, 0, &k) != CURLM_OK) { - log_debug("Failed to propagate timeout."); - return -EINVAL; - } - - curl_glue_check_finished(g); - return 0; -} - -static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata) { - CurlGlue *g = userdata; - usec_t usec; - - assert(curl); - assert(g); - - if (timeout_ms < 0) { - if (g->timer) { - if (sd_event_source_set_enabled(g->timer, SD_EVENT_OFF) < 0) - return -1; - } - - return 0; - } - - usec = now(clock_boottime_or_monotonic()) + (usec_t) timeout_ms * USEC_PER_MSEC + USEC_PER_MSEC - 1; - - if (g->timer) { - if (sd_event_source_set_time(g->timer, usec) < 0) - return -1; - - if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0) - return -1; - } else { - if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) - return -1; - - (void) sd_event_source_set_description(g->timer, "curl-timer"); - } - - return 0; -} - -CurlGlue *curl_glue_unref(CurlGlue *g) { - sd_event_source *io; - - if (!g) - return NULL; - - if (g->curl) - curl_multi_cleanup(g->curl); - - while ((io = hashmap_steal_first(g->ios))) { - int fd; - - fd = sd_event_source_get_io_fd(io); - assert(fd >= 0); - - hashmap_remove(g->translate_fds, FD_TO_PTR(fd)); - - safe_close(fd); - sd_event_source_unref(io); - } - - hashmap_free(g->ios); - - sd_event_source_unref(g->timer); - sd_event_unref(g->event); - free(g); - - return NULL; -} - -int curl_glue_new(CurlGlue **glue, sd_event *event) { - _cleanup_(curl_glue_unrefp) CurlGlue *g = NULL; - int r; - - g = new0(CurlGlue, 1); - if (!g) - return -ENOMEM; - - if (event) - g->event = sd_event_ref(event); - else { - r = sd_event_default(&g->event); - if (r < 0) - return r; - } - - g->curl = curl_multi_init(); - if (!g->curl) - return -ENOMEM; - - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETDATA, g) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_SOCKETFUNCTION, curl_glue_socket_callback) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERDATA, g) != CURLM_OK) - return -EINVAL; - - if (curl_multi_setopt(g->curl, CURLMOPT_TIMERFUNCTION, curl_glue_timer_callback) != CURLM_OK) - return -EINVAL; - - *glue = g; - g = NULL; - - return 0; -} - -int curl_glue_make(CURL **ret, const char *url, void *userdata) { - const char *useragent; - CURL *c; - int r; - - assert(ret); - assert(url); - - c = curl_easy_init(); - if (!c) - return -ENOMEM; - - /* curl_easy_setopt(c, CURLOPT_VERBOSE, 1L); */ - - if (curl_easy_setopt(c, CURLOPT_URL, url) != CURLE_OK) { - r = -EIO; - goto fail; - } - - if (curl_easy_setopt(c, CURLOPT_PRIVATE, userdata) != CURLE_OK) { - r = -EIO; - goto fail; - } - - useragent = strjoina(program_invocation_short_name, "/" PACKAGE_VERSION); - if (curl_easy_setopt(c, CURLOPT_USERAGENT, useragent) != CURLE_OK) { - r = -EIO; - goto fail; - } - - if (curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L) != CURLE_OK) { - r = -EIO; - goto fail; - } - - *ret = c; - return 0; - -fail: - curl_easy_cleanup(c); - return r; -} - -int curl_glue_add(CurlGlue *g, CURL *c) { - assert(g); - assert(c); - - if (curl_multi_add_handle(g->curl, c) != CURLM_OK) - return -EIO; - - return 0; -} - -void curl_glue_remove_and_free(CurlGlue *g, CURL *c) { - assert(g); - - if (!c) - return; - - if (g->curl) - curl_multi_remove_handle(g->curl, c); - - curl_easy_cleanup(c); -} - -struct curl_slist *curl_slist_new(const char *first, ...) { - struct curl_slist *l; - va_list ap; - - if (!first) - return NULL; - - l = curl_slist_append(NULL, first); - if (!l) - return NULL; - - va_start(ap, first); - - for (;;) { - struct curl_slist *n; - const char *i; - - i = va_arg(ap, const char*); - if (!i) - break; - - n = curl_slist_append(l, i); - if (!n) { - va_end(ap); - curl_slist_free_all(l); - return NULL; - } - - l = n; - } - - va_end(ap); - return l; -} - -int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value) { - const char *p = contents; - size_t l; - char *s; - - l = strlen(field); - if (sz < l) - return 0; - - if (memcmp(p, field, l) != 0) - return 0; - - p += l; - sz -= l; - - if (memchr(p, 0, sz)) - return 0; - - /* Skip over preceeding whitespace */ - while (sz > 0 && strchr(WHITESPACE, p[0])) { - p++; - sz--; - } - - /* Truncate trailing whitespace*/ - while (sz > 0 && strchr(WHITESPACE, p[sz-1])) - sz--; - - s = strndup(p, sz); - if (!s) - return -ENOMEM; - - *value = s; - return 1; -} - -int curl_parse_http_time(const char *t, usec_t *ret) { - const char *e; - locale_t loc; - struct tm tm; - time_t v; - - assert(t); - assert(ret); - - loc = newlocale(LC_TIME_MASK, "C", (locale_t) 0); - if (loc == (locale_t) 0) - return -errno; - - /* RFC822 */ - e = strptime_l(t, "%a, %d %b %Y %H:%M:%S %Z", &tm, loc); - if (!e || *e != 0) - /* RFC 850 */ - e = strptime_l(t, "%A, %d-%b-%y %H:%M:%S %Z", &tm, loc); - if (!e || *e != 0) - /* ANSI C */ - e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc); - freelocale(loc); - if (!e || *e != 0) - return -EINVAL; - - v = timegm(&tm); - if (v == (time_t) -1) - return -EINVAL; - - *ret = (usec_t) v * USEC_PER_SEC; - return 0; -} diff --git a/src/import/curl-util.h b/src/import/curl-util.h deleted file mode 100644 index a758cc5640..0000000000 --- a/src/import/curl-util.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <curl/curl.h> -#include <sys/types.h> - -#include "sd-event.h" - -#include "hashmap.h" - -typedef struct CurlGlue CurlGlue; - -struct CurlGlue { - sd_event *event; - CURLM *curl; - sd_event_source *timer; - Hashmap *ios; - Hashmap *translate_fds; - - void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code); - void *userdata; -}; - -int curl_glue_new(CurlGlue **glue, sd_event *event); -CurlGlue* curl_glue_unref(CurlGlue *glue); - -DEFINE_TRIVIAL_CLEANUP_FUNC(CurlGlue*, curl_glue_unref); - -int curl_glue_make(CURL **ret, const char *url, void *userdata); -int curl_glue_add(CurlGlue *g, CURL *c); -void curl_glue_remove_and_free(CurlGlue *g, CURL *c); - -struct curl_slist *curl_slist_new(const char *first, ...) _sentinel_; -int curl_header_strdup(const void *contents, size_t sz, const char *field, char **value); -int curl_parse_http_time(const char *t, usec_t *ret); - -DEFINE_TRIVIAL_CLEANUP_FUNC(CURL*, curl_easy_cleanup); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct curl_slist*, curl_slist_free_all); diff --git a/src/import/export-raw.c b/src/import/export-raw.c deleted file mode 100644 index db06e11b87..0000000000 --- a/src/import/export-raw.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sys/sendfile.h> - -/* When we include libgen.h because we need dirname() we immediately - * undefine basename() since libgen.h defines it as a macro to the POSIX - * version which is really broken. We prefer GNU basename(). */ -#include <libgen.h> -#undef basename - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "copy.h" -#include "export-raw.h" -#include "fd-util.h" -#include "fileio.h" -#include "import-common.h" -#include "ratelimit.h" -#include "string-util.h" -#include "util.h" - -#define COPY_BUFFER_SIZE (16*1024) - -struct RawExport { - sd_event *event; - - RawExportFinished on_finished; - void *userdata; - - char *path; - - int input_fd; - int output_fd; - - ImportCompress compress; - - sd_event_source *output_event_source; - - void *buffer; - size_t buffer_size; - size_t buffer_allocated; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - unsigned last_percent; - RateLimit progress_rate_limit; - - struct stat st; - - bool eof; - bool tried_reflink; - bool tried_sendfile; -}; - -RawExport *raw_export_unref(RawExport *e) { - if (!e) - return NULL; - - sd_event_source_unref(e->output_event_source); - - import_compress_free(&e->compress); - - sd_event_unref(e->event); - - safe_close(e->input_fd); - - free(e->buffer); - free(e->path); - free(e); - - return NULL; -} - -int raw_export_new( - RawExport **ret, - sd_event *event, - RawExportFinished on_finished, - void *userdata) { - - _cleanup_(raw_export_unrefp) RawExport *e = NULL; - int r; - - assert(ret); - - e = new0(RawExport, 1); - if (!e) - return -ENOMEM; - - e->output_fd = e->input_fd = -1; - e->on_finished = on_finished; - e->userdata = userdata; - - RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - e->last_percent = (unsigned) -1; - - if (event) - e->event = sd_event_ref(event); - else { - r = sd_event_default(&e->event); - if (r < 0) - return r; - } - - *ret = e; - e = NULL; - - return 0; -} - -static void raw_export_report_progress(RawExport *e) { - unsigned percent; - assert(e); - - if (e->written_uncompressed >= (uint64_t) e->st.st_size) - percent = 100; - else - percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / (uint64_t) e->st.st_size); - - if (percent == e->last_percent) - return; - - if (!ratelimit_test(&e->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Exported %u%%.", percent); - - e->last_percent = percent; -} - -static int raw_export_process(RawExport *e) { - ssize_t l; - int r; - - assert(e); - - if (!e->tried_reflink && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - /* If we shall take an uncompressed snapshot we can - * reflink source to destination directly. Let's see - * if this works. */ - - r = btrfs_reflink(e->input_fd, e->output_fd); - if (r >= 0) { - r = 0; - goto finish; - } - - e->tried_reflink = true; - } - - if (!e->tried_sendfile && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - l = sendfile(e->output_fd, e->input_fd, NULL, COPY_BUFFER_SIZE); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - e->tried_sendfile = true; - } else if (l == 0) { - r = 0; - goto finish; - } else { - e->written_uncompressed += l; - e->written_compressed += l; - - raw_export_report_progress(e); - - return 0; - } - } - - while (e->buffer_size <= 0) { - uint8_t input[COPY_BUFFER_SIZE]; - - if (e->eof) { - r = 0; - goto finish; - } - - l = read(e->input_fd, input, sizeof(input)); - if (l < 0) { - r = log_error_errno(errno, "Failed to read raw file: %m"); - goto finish; - } - - if (l == 0) { - e->eof = true; - r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } else { - e->written_uncompressed += l; - r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } - if (r < 0) { - r = log_error_errno(r, "Failed to encode: %m"); - goto finish; - } - } - - l = write(e->output_fd, e->buffer, e->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to write output file: %m"); - goto finish; - } - - assert((size_t) l <= e->buffer_size); - memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); - e->buffer_size -= l; - e->written_compressed += l; - - raw_export_report_progress(e); - - return 0; - -finish: - if (r >= 0) { - (void) copy_times(e->input_fd, e->output_fd); - (void) copy_xattr(e->input_fd, e->output_fd); - } - - if (e->on_finished) - e->on_finished(e, r, e->userdata); - else - sd_event_exit(e->event, r); - - return 0; -} - -static int raw_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - RawExport *i = userdata; - - return raw_export_process(i); -} - -static int raw_export_on_defer(sd_event_source *s, void *userdata) { - RawExport *i = userdata; - - return raw_export_process(i); -} - -static int reflink_snapshot(int fd, const char *path) { - char *p, *d; - int new_fd, r; - - p = strdupa(path); - d = dirname(p); - - new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600); - if (new_fd < 0) { - _cleanup_free_ char *t = NULL; - - r = tempfn_random(path, NULL, &t); - if (r < 0) - return r; - - new_fd = open(t, O_CLOEXEC|O_CREAT|O_NOCTTY|O_RDWR, 0600); - if (new_fd < 0) - return -errno; - - (void) unlink(t); - } - - r = btrfs_reflink(fd, new_fd); - if (r < 0) { - safe_close(new_fd); - return r; - } - - return new_fd; -} - -int raw_export_start(RawExport *e, const char *path, int fd, ImportCompressType compress) { - _cleanup_close_ int sfd = -1, tfd = -1; - int r; - - assert(e); - assert(path); - assert(fd >= 0); - assert(compress < _IMPORT_COMPRESS_TYPE_MAX); - assert(compress != IMPORT_COMPRESS_UNKNOWN); - - if (e->output_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&e->path, path); - if (r < 0) - return r; - - sfd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (sfd < 0) - return -errno; - - if (fstat(sfd, &e->st) < 0) - return -errno; - if (!S_ISREG(e->st.st_mode)) - return -ENOTTY; - - /* Try to take a reflink snapshot of the file, if we can t make the export atomic */ - tfd = reflink_snapshot(sfd, path); - if (tfd >= 0) { - e->input_fd = tfd; - tfd = -1; - } else { - e->input_fd = sfd; - sfd = -1; - } - - r = import_compress_init(&e->compress, compress); - if (r < 0) - return r; - - r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, raw_export_on_output, e); - if (r == -EPERM) { - r = sd_event_add_defer(e->event, &e->output_event_source, raw_export_on_defer, e); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - e->output_fd = fd; - return r; -} diff --git a/src/import/export-raw.h b/src/import/export-raw.h deleted file mode 100644 index 8e723d4908..0000000000 --- a/src/import/export-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-compress.h" -#include "macro.h" - -typedef struct RawExport RawExport; - -typedef void (*RawExportFinished)(RawExport *export, int error, void *userdata); - -int raw_export_new(RawExport **export, sd_event *event, RawExportFinished on_finished, void *userdata); -RawExport* raw_export_unref(RawExport *export); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawExport*, raw_export_unref); - -int raw_export_start(RawExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/import/export-tar.c b/src/import/export-tar.c deleted file mode 100644 index d79c27f2d0..0000000000 --- a/src/import/export-tar.c +++ /dev/null @@ -1,328 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "export-tar.h" -#include "fd-util.h" -#include "fileio.h" -#include "import-common.h" -#include "process-util.h" -#include "ratelimit.h" -#include "string-util.h" -#include "util.h" - -#define COPY_BUFFER_SIZE (16*1024) - -struct TarExport { - sd_event *event; - - TarExportFinished on_finished; - void *userdata; - - char *path; - char *temp_path; - - int output_fd; - int tar_fd; - - ImportCompress compress; - - sd_event_source *output_event_source; - - void *buffer; - size_t buffer_size; - size_t buffer_allocated; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - pid_t tar_pid; - - struct stat st; - uint64_t quota_referenced; - - unsigned last_percent; - RateLimit progress_rate_limit; - - bool eof; - bool tried_splice; -}; - -TarExport *tar_export_unref(TarExport *e) { - if (!e) - return NULL; - - sd_event_source_unref(e->output_event_source); - - if (e->tar_pid > 1) { - (void) kill_and_sigcont(e->tar_pid, SIGKILL); - (void) wait_for_terminate(e->tar_pid, NULL); - } - - if (e->temp_path) { - (void) btrfs_subvol_remove(e->temp_path, BTRFS_REMOVE_QUOTA); - free(e->temp_path); - } - - import_compress_free(&e->compress); - - sd_event_unref(e->event); - - safe_close(e->tar_fd); - - free(e->buffer); - free(e->path); - free(e); - - return NULL; -} - -int tar_export_new( - TarExport **ret, - sd_event *event, - TarExportFinished on_finished, - void *userdata) { - - _cleanup_(tar_export_unrefp) TarExport *e = NULL; - int r; - - assert(ret); - - e = new0(TarExport, 1); - if (!e) - return -ENOMEM; - - e->output_fd = e->tar_fd = -1; - e->on_finished = on_finished; - e->userdata = userdata; - e->quota_referenced = (uint64_t) -1; - - RATELIMIT_INIT(e->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - e->last_percent = (unsigned) -1; - - if (event) - e->event = sd_event_ref(event); - else { - r = sd_event_default(&e->event); - if (r < 0) - return r; - } - - *ret = e; - e = NULL; - - return 0; -} - -static void tar_export_report_progress(TarExport *e) { - unsigned percent; - assert(e); - - /* Do we have any quota info? If not, we don't know anything about the progress */ - if (e->quota_referenced == (uint64_t) -1) - return; - - if (e->written_uncompressed >= e->quota_referenced) - percent = 100; - else - percent = (unsigned) ((e->written_uncompressed * UINT64_C(100)) / e->quota_referenced); - - if (percent == e->last_percent) - return; - - if (!ratelimit_test(&e->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Exported %u%%.", percent); - - e->last_percent = percent; -} - -static int tar_export_process(TarExport *e) { - ssize_t l; - int r; - - assert(e); - - if (!e->tried_splice && e->compress.type == IMPORT_COMPRESS_UNCOMPRESSED) { - - l = splice(e->tar_fd, NULL, e->output_fd, NULL, COPY_BUFFER_SIZE, 0); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - e->tried_splice = true; - } else if (l == 0) { - r = 0; - goto finish; - } else { - e->written_uncompressed += l; - e->written_compressed += l; - - tar_export_report_progress(e); - - return 0; - } - } - - while (e->buffer_size <= 0) { - uint8_t input[COPY_BUFFER_SIZE]; - - if (e->eof) { - r = 0; - goto finish; - } - - l = read(e->tar_fd, input, sizeof(input)); - if (l < 0) { - r = log_error_errno(errno, "Failed to read tar file: %m"); - goto finish; - } - - if (l == 0) { - e->eof = true; - r = import_compress_finish(&e->compress, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } else { - e->written_uncompressed += l; - r = import_compress(&e->compress, input, l, &e->buffer, &e->buffer_size, &e->buffer_allocated); - } - if (r < 0) { - r = log_error_errno(r, "Failed to encode: %m"); - goto finish; - } - } - - l = write(e->output_fd, e->buffer, e->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to write output file: %m"); - goto finish; - } - - assert((size_t) l <= e->buffer_size); - memmove(e->buffer, (uint8_t*) e->buffer + l, e->buffer_size - l); - e->buffer_size -= l; - e->written_compressed += l; - - tar_export_report_progress(e); - - return 0; - -finish: - if (e->on_finished) - e->on_finished(e, r, e->userdata); - else - sd_event_exit(e->event, r); - - return 0; -} - -static int tar_export_on_output(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - TarExport *i = userdata; - - return tar_export_process(i); -} - -static int tar_export_on_defer(sd_event_source *s, void *userdata) { - TarExport *i = userdata; - - return tar_export_process(i); -} - -int tar_export_start(TarExport *e, const char *path, int fd, ImportCompressType compress) { - _cleanup_close_ int sfd = -1; - int r; - - assert(e); - assert(path); - assert(fd >= 0); - assert(compress < _IMPORT_COMPRESS_TYPE_MAX); - assert(compress != IMPORT_COMPRESS_UNKNOWN); - - if (e->output_fd >= 0) - return -EBUSY; - - sfd = open(path, O_DIRECTORY|O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (sfd < 0) - return -errno; - - if (fstat(sfd, &e->st) < 0) - return -errno; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&e->path, path); - if (r < 0) - return r; - - e->quota_referenced = (uint64_t) -1; - - if (e->st.st_ino == 256) { /* might be a btrfs subvolume? */ - BtrfsQuotaInfo q; - - r = btrfs_subvol_get_subtree_quota_fd(sfd, 0, &q); - if (r >= 0) - e->quota_referenced = q.referenced; - - e->temp_path = mfree(e->temp_path); - - r = tempfn_random(path, NULL, &e->temp_path); - if (r < 0) - return r; - - /* Let's try to make a snapshot, if we can, so that the export is atomic */ - r = btrfs_subvol_snapshot_fd(sfd, e->temp_path, BTRFS_SNAPSHOT_READ_ONLY|BTRFS_SNAPSHOT_RECURSIVE); - if (r < 0) { - log_debug_errno(r, "Couldn't create snapshot %s of %s, not exporting atomically: %m", e->temp_path, path); - e->temp_path = mfree(e->temp_path); - } - } - - r = import_compress_init(&e->compress, compress); - if (r < 0) - return r; - - r = sd_event_add_io(e->event, &e->output_event_source, fd, EPOLLOUT, tar_export_on_output, e); - if (r == -EPERM) { - r = sd_event_add_defer(e->event, &e->output_event_source, tar_export_on_defer, e); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(e->output_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - e->tar_fd = import_fork_tar_c(e->temp_path ?: e->path, &e->tar_pid); - if (e->tar_fd < 0) { - e->output_event_source = sd_event_source_unref(e->output_event_source); - return e->tar_fd; - } - - e->output_fd = fd; - return r; -} diff --git a/src/import/export-tar.h b/src/import/export-tar.h deleted file mode 100644 index 1e3c8bb80c..0000000000 --- a/src/import/export-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-compress.h" -#include "macro.h" - -typedef struct TarExport TarExport; - -typedef void (*TarExportFinished)(TarExport *export, int error, void *userdata); - -int tar_export_new(TarExport **export, sd_event *event, TarExportFinished on_finished, void *userdata); -TarExport* tar_export_unref(TarExport *export); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarExport*, tar_export_unref); - -int tar_export_start(TarExport *export, const char *path, int fd, ImportCompressType compress); diff --git a/src/import/export.c b/src/import/export.c deleted file mode 100644 index cc98c33ef6..0000000000 --- a/src/import/export.c +++ /dev/null @@ -1,320 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <getopt.h> - -#include "sd-event.h" - -#include "alloc-util.h" -#include "export-raw.h" -#include "export-tar.h" -#include "fd-util.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-util.h" -#include "machine-image.h" -#include "signal-util.h" -#include "string-util.h" -#include "verbs.h" - -static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN; - -static void determine_compression_from_filename(const char *p) { - - if (arg_compress != IMPORT_COMPRESS_UNKNOWN) - return; - - if (!p) { - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; - return; - } - - if (endswith(p, ".xz")) - arg_compress = IMPORT_COMPRESS_XZ; - else if (endswith(p, ".gz")) - arg_compress = IMPORT_COMPRESS_GZIP; - else if (endswith(p, ".bz2")) - arg_compress = IMPORT_COMPRESS_BZIP2; - else - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; -} - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarExport *export, int error, void *userdata) { - sd_event *event = userdata; - assert(export); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int export_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_export_unrefp) TarExport *export = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(image_unrefp) Image *image = NULL; - const char *path = NULL, *local = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (machine_name_is_valid(argv[1])) { - r = image_find(argv[1], &image); - if (r < 0) - return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); - if (r == 0) { - log_error("Machine image %s not found.", argv[1]); - return -ENOENT; - } - - local = image->path; - } else - local = argv[1]; - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - determine_compression_from_filename(path); - - if (path) { - open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open tar image for export: %m"); - - fd = open_fd; - - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDOUT_FILENO; - - (void) readlink_malloc("/proc/self/fd/1", &pretty); - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = tar_export_new(&export, event, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate exporter: %m"); - - r = tar_export_start(export, local, fd, arg_compress); - if (r < 0) - return log_error_errno(r, "Failed to export image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static void on_raw_finished(RawExport *export, int error, void *userdata) { - sd_event *event = userdata; - assert(export); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int export_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_export_unrefp) RawExport *export = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - _cleanup_(image_unrefp) Image *image = NULL; - const char *path = NULL, *local = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (machine_name_is_valid(argv[1])) { - r = image_find(argv[1], &image); - if (r < 0) - return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]); - if (r == 0) { - log_error("Machine image %s not found.", argv[1]); - return -ENOENT; - } - - local = image->path; - } else - local = argv[1]; - - if (argc >= 3) - path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - determine_compression_from_filename(path); - - if (path) { - open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open raw image for export: %m"); - - fd = open_fd; - - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress)); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDOUT_FILENO; - - (void) readlink_malloc("/proc/self/fd/1", &pretty); - log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress)); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = raw_export_new(&export, event, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate exporter: %m"); - - r = raw_export_start(export, local, fd, arg_compress); - if (r < 0) - return log_error_errno(r, "Failed to export image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Export container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --format=FORMAT Select format\n\n" - "Commands:\n" - " tar NAME [FILE] Export a TAR image\n" - " raw NAME [FILE] Export a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORMAT, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "format", required_argument, NULL, ARG_FORMAT }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORMAT: - if (streq(optarg, "uncompressed")) - arg_compress = IMPORT_COMPRESS_UNCOMPRESSED; - else if (streq(optarg, "xz")) - arg_compress = IMPORT_COMPRESS_XZ; - else if (streq(optarg, "gzip")) - arg_compress = IMPORT_COMPRESS_GZIP; - else if (streq(optarg, "bzip2")) - arg_compress = IMPORT_COMPRESS_BZIP2; - else { - log_error("Unknown format: %s", optarg); - return -EINVAL; - } - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int export_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, export_tar }, - { "raw", 2, 3, 0, export_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = export_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/import-common.c b/src/import/import-common.c deleted file mode 100644 index 287a3382a1..0000000000 --- a/src/import/import-common.c +++ /dev/null @@ -1,225 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sched.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "btrfs-util.h" -#include "capability-util.h" -#include "fd-util.h" -#include "import-common.h" -#include "signal-util.h" -#include "util.h" - -int import_make_read_only_fd(int fd) { - int r; - - assert(fd >= 0); - - /* First, let's make this a read-only subvolume if it refers - * to a subvolume */ - r = btrfs_subvol_set_read_only_fd(fd, true); - if (r == -ENOTTY || r == -ENOTDIR || r == -EINVAL) { - struct stat st; - - /* This doesn't refer to a subvolume, or the file - * system isn't even btrfs. In that, case fall back to - * chmod()ing */ - - r = fstat(fd, &st); - if (r < 0) - return log_error_errno(errno, "Failed to stat temporary image: %m"); - - /* Drop "w" flag */ - if (fchmod(fd, st.st_mode & 07555) < 0) - return log_error_errno(errno, "Failed to chmod() final image: %m"); - - return 0; - - } else if (r < 0) - return log_error_errno(r, "Failed to make subvolume read-only: %m"); - - return 0; -} - -int import_make_read_only(const char *path) { - _cleanup_close_ int fd = 1; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", path); - - return import_make_read_only_fd(fd); -} - -int import_fork_tar_x(const char *path, pid_t *ret) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - pid_t pid; - int r; - - assert(path); - assert(ret); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to create pipe for tar: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { - int null_fd; - uint64_t retain = - (1ULL << CAP_CHOWN) | - (1ULL << CAP_FOWNER) | - (1ULL << CAP_FSETID) | - (1ULL << CAP_MKNOD) | - (1ULL << CAP_SETFCAP) | - (1ULL << CAP_DAC_OVERRIDE); - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[1] = safe_close(pipefd[1]); - - if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (pipefd[0] != STDIN_FILENO) - pipefd[0] = safe_close(pipefd[0]); - - null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDOUT_FILENO) - null_fd = safe_close(null_fd); - - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); - - if (unshare(CLONE_NEWNET) < 0) - log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); - - r = capability_bounding_set_drop(retain, true); - if (r < 0) - log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - - execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); - log_error_errno(errno, "Failed to execute tar: %m"); - _exit(EXIT_FAILURE); - } - - pipefd[0] = safe_close(pipefd[0]); - r = pipefd[1]; - pipefd[1] = -1; - - *ret = pid; - - return r; -} - -int import_fork_tar_c(const char *path, pid_t *ret) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - pid_t pid; - int r; - - assert(path); - assert(ret); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to create pipe for tar: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { - int null_fd; - uint64_t retain = (1ULL << CAP_DAC_OVERRIDE); - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[0] = safe_close(pipefd[0]); - - if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (pipefd[1] != STDOUT_FILENO) - pipefd[1] = safe_close(pipefd[1]); - - null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDIN_FILENO) - null_fd = safe_close(null_fd); - - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); - - if (unshare(CLONE_NEWNET) < 0) - log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); - - r = capability_bounding_set_drop(retain, true); - if (r < 0) - log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - - execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL); - log_error_errno(errno, "Failed to execute tar: %m"); - _exit(EXIT_FAILURE); - } - - pipefd[1] = safe_close(pipefd[1]); - r = pipefd[0]; - pipefd[0] = -1; - - *ret = pid; - - return r; -} diff --git a/src/import/import-common.h b/src/import/import-common.h deleted file mode 100644 index 07d3250e71..0000000000 --- a/src/import/import-common.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -int import_make_read_only_fd(int fd); -int import_make_read_only(const char *path); - -int import_fork_tar_c(const char *path, pid_t *ret); -int import_fork_tar_x(const char *path, pid_t *ret); diff --git a/src/import/import-compress.c b/src/import/import-compress.c deleted file mode 100644 index f1766bbe3b..0000000000 --- a/src/import/import-compress.c +++ /dev/null @@ -1,469 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "import-compress.h" -#include "string-table.h" -#include "util.h" - -void import_compress_free(ImportCompress *c) { - assert(c); - - if (c->type == IMPORT_COMPRESS_XZ) - lzma_end(&c->xz); - else if (c->type == IMPORT_COMPRESS_GZIP) { - if (c->encoding) - deflateEnd(&c->gzip); - else - inflateEnd(&c->gzip); - } else if (c->type == IMPORT_COMPRESS_BZIP2) { - if (c->encoding) - BZ2_bzCompressEnd(&c->bzip2); - else - BZ2_bzDecompressEnd(&c->bzip2); - } - - c->type = IMPORT_COMPRESS_UNKNOWN; -} - -int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) { - static const uint8_t xz_signature[] = { - 0xfd, '7', 'z', 'X', 'Z', 0x00 - }; - static const uint8_t gzip_signature[] = { - 0x1f, 0x8b - }; - static const uint8_t bzip2_signature[] = { - 'B', 'Z', 'h' - }; - - int r; - - assert(c); - - if (c->type != IMPORT_COMPRESS_UNKNOWN) - return 1; - - if (size < MAX3(sizeof(xz_signature), - sizeof(gzip_signature), - sizeof(bzip2_signature))) - return 0; - - assert(data); - - if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) { - lzma_ret xzr; - - xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); - if (xzr != LZMA_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_XZ; - - } else if (memcmp(data, gzip_signature, sizeof(gzip_signature)) == 0) { - r = inflateInit2(&c->gzip, 15+16); - if (r != Z_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_GZIP; - - } else if (memcmp(data, bzip2_signature, sizeof(bzip2_signature)) == 0) { - r = BZ2_bzDecompressInit(&c->bzip2, 0, 0); - if (r != BZ_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_BZIP2; - } else - c->type = IMPORT_COMPRESS_UNCOMPRESSED; - - c->encoding = false; - - return 1; -} - -int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata) { - int r; - - assert(c); - assert(callback); - - r = import_uncompress_detect(c, data, size); - if (r <= 0) - return r; - - if (c->encoding) - return -EINVAL; - - if (size <= 0) - return 1; - - assert(data); - - switch (c->type) { - - case IMPORT_COMPRESS_UNCOMPRESSED: - r = callback(data, size, userdata); - if (r < 0) - return r; - - break; - - case IMPORT_COMPRESS_XZ: - c->xz.next_in = data; - c->xz.avail_in = size; - - while (c->xz.avail_in > 0) { - uint8_t buffer[16 * 1024]; - lzma_ret lzr; - - c->xz.next_out = buffer; - c->xz.avail_out = sizeof(buffer); - - lzr = lzma_code(&c->xz, LZMA_RUN); - if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->xz.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - case IMPORT_COMPRESS_GZIP: - c->gzip.next_in = (void*) data; - c->gzip.avail_in = size; - - while (c->gzip.avail_in > 0) { - uint8_t buffer[16 * 1024]; - - c->gzip.next_out = buffer; - c->gzip.avail_out = sizeof(buffer); - - r = inflate(&c->gzip, Z_NO_FLUSH); - if (r != Z_OK && r != Z_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->gzip.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - case IMPORT_COMPRESS_BZIP2: - c->bzip2.next_in = (void*) data; - c->bzip2.avail_in = size; - - while (c->bzip2.avail_in > 0) { - uint8_t buffer[16 * 1024]; - - c->bzip2.next_out = (char*) buffer; - c->bzip2.avail_out = sizeof(buffer); - - r = BZ2_bzDecompress(&c->bzip2); - if (r != BZ_OK && r != BZ_STREAM_END) - return -EIO; - - r = callback(buffer, sizeof(buffer) - c->bzip2.avail_out, userdata); - if (r < 0) - return r; - } - - break; - - default: - assert_not_reached("Unknown compression"); - } - - return 1; -} - -int import_compress_init(ImportCompress *c, ImportCompressType t) { - int r; - - assert(c); - - switch (t) { - - case IMPORT_COMPRESS_XZ: { - lzma_ret xzr; - - xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64); - if (xzr != LZMA_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_XZ; - break; - } - - case IMPORT_COMPRESS_GZIP: - r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); - if (r != Z_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_GZIP; - break; - - case IMPORT_COMPRESS_BZIP2: - r = BZ2_bzCompressInit(&c->bzip2, 9, 0, 0); - if (r != BZ_OK) - return -EIO; - - c->type = IMPORT_COMPRESS_BZIP2; - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - c->type = IMPORT_COMPRESS_UNCOMPRESSED; - break; - - default: - return -EOPNOTSUPP; - } - - c->encoding = true; - return 0; -} - -static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - size_t l; - void *p; - - if (*buffer_allocated > *buffer_size) - return 0; - - l = MAX(16*1024U, (*buffer_size * 2)); - p = realloc(*buffer, l); - if (!p) - return -ENOMEM; - - *buffer = p; - *buffer_allocated = l; - - return 1; -} - -int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - int r; - - assert(c); - assert(buffer); - assert(buffer_size); - assert(buffer_allocated); - - if (!c->encoding) - return -EINVAL; - - if (size <= 0) - return 0; - - assert(data); - - *buffer_size = 0; - - switch (c->type) { - - case IMPORT_COMPRESS_XZ: - - c->xz.next_in = data; - c->xz.avail_in = size; - - while (c->xz.avail_in > 0) { - lzma_ret lzr; - - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->xz.next_out = (uint8_t*) *buffer + *buffer_size; - c->xz.avail_out = *buffer_allocated - *buffer_size; - - lzr = lzma_code(&c->xz, LZMA_RUN); - if (lzr != LZMA_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; - } - - break; - - case IMPORT_COMPRESS_GZIP: - - c->gzip.next_in = (void*) data; - c->gzip.avail_in = size; - - while (c->gzip.avail_in > 0) { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; - c->gzip.avail_out = *buffer_allocated - *buffer_size; - - r = deflate(&c->gzip, Z_NO_FLUSH); - if (r != Z_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; - } - - break; - - case IMPORT_COMPRESS_BZIP2: - - c->bzip2.next_in = (void*) data; - c->bzip2.avail_in = size; - - while (c->bzip2.avail_in > 0) { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); - c->bzip2.avail_out = *buffer_allocated - *buffer_size; - - r = BZ2_bzCompress(&c->bzip2, BZ_RUN); - if (r != BZ_RUN_OK) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; - } - - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - - if (*buffer_allocated < size) { - void *p; - - p = realloc(*buffer, size); - if (!p) - return -ENOMEM; - - *buffer = p; - *buffer_allocated = size; - } - - memcpy(*buffer, data, size); - *buffer_size = size; - break; - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) { - int r; - - assert(c); - assert(buffer); - assert(buffer_size); - assert(buffer_allocated); - - if (!c->encoding) - return -EINVAL; - - *buffer_size = 0; - - switch (c->type) { - - case IMPORT_COMPRESS_XZ: { - lzma_ret lzr; - - c->xz.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->xz.next_out = (uint8_t*) *buffer + *buffer_size; - c->xz.avail_out = *buffer_allocated - *buffer_size; - - lzr = lzma_code(&c->xz, LZMA_FINISH); - if (lzr != LZMA_OK && lzr != LZMA_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out; - } while (lzr != LZMA_STREAM_END); - - break; - } - - case IMPORT_COMPRESS_GZIP: - c->gzip.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->gzip.next_out = (uint8_t*) *buffer + *buffer_size; - c->gzip.avail_out = *buffer_allocated - *buffer_size; - - r = deflate(&c->gzip, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->gzip.avail_out; - } while (r != Z_STREAM_END); - - break; - - case IMPORT_COMPRESS_BZIP2: - c->bzip2.avail_in = 0; - - do { - r = enlarge_buffer(buffer, buffer_size, buffer_allocated); - if (r < 0) - return r; - - c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size); - c->bzip2.avail_out = *buffer_allocated - *buffer_size; - - r = BZ2_bzCompress(&c->bzip2, BZ_FINISH); - if (r != BZ_FINISH_OK && r != BZ_STREAM_END) - return -EIO; - - *buffer_size += (*buffer_allocated - *buffer_size) - c->bzip2.avail_out; - } while (r != BZ_STREAM_END); - - break; - - case IMPORT_COMPRESS_UNCOMPRESSED: - break; - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static const char* const import_compress_type_table[_IMPORT_COMPRESS_TYPE_MAX] = { - [IMPORT_COMPRESS_UNKNOWN] = "unknown", - [IMPORT_COMPRESS_UNCOMPRESSED] = "uncompressed", - [IMPORT_COMPRESS_XZ] = "xz", - [IMPORT_COMPRESS_GZIP] = "gzip", - [IMPORT_COMPRESS_BZIP2] = "bzip2", -}; - -DEFINE_STRING_TABLE_LOOKUP(import_compress_type, ImportCompressType); diff --git a/src/import/import-compress.h b/src/import/import-compress.h deleted file mode 100644 index 6b59d0724b..0000000000 --- a/src/import/import-compress.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <bzlib.h> -#include <lzma.h> -#include <sys/types.h> -#include <zlib.h> - -#include "macro.h" - -typedef enum ImportCompressType { - IMPORT_COMPRESS_UNKNOWN, - IMPORT_COMPRESS_UNCOMPRESSED, - IMPORT_COMPRESS_XZ, - IMPORT_COMPRESS_GZIP, - IMPORT_COMPRESS_BZIP2, - _IMPORT_COMPRESS_TYPE_MAX, - _IMPORT_COMPRESS_TYPE_INVALID = -1, -} ImportCompressType; - -typedef struct ImportCompress { - ImportCompressType type; - bool encoding; - union { - lzma_stream xz; - z_stream gzip; - bz_stream bzip2; - }; -} ImportCompress; - -typedef int (*ImportCompressCallback)(const void *data, size_t size, void *userdata); - -void import_compress_free(ImportCompress *c); - -int import_uncompress_detect(ImportCompress *c, const void *data, size_t size); -int import_uncompress(ImportCompress *c, const void *data, size_t size, ImportCompressCallback callback, void *userdata); - -int import_compress_init(ImportCompress *c, ImportCompressType t); -int import_compress(ImportCompress *c, const void *data, size_t size, void **buffer, size_t *buffer_size, size_t *buffer_allocated); -int import_compress_finish(ImportCompress *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated); - -const char* import_compress_type_to_string(ImportCompressType t) _const_; -ImportCompressType import_compress_type_from_string(const char *s) _pure_; diff --git a/src/import/import-pubring.gpg b/src/import/import-pubring.gpg Binary files differdeleted file mode 100644 index be27776896..0000000000 --- a/src/import/import-pubring.gpg +++ /dev/null diff --git a/src/import/import-raw.c b/src/import/import-raw.c deleted file mode 100644 index fd6b9f7703..0000000000 --- a/src/import/import-raw.c +++ /dev/null @@ -1,467 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <linux/fs.h> - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "chattr-util.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-common.h" -#include "import-compress.h" -#include "import-raw.h" -#include "io-util.h" -#include "machine-pool.h" -#include "mkdir.h" -#include "path-util.h" -#include "qcow2-util.h" -#include "ratelimit.h" -#include "rm-rf.h" -#include "string-util.h" -#include "util.h" - -struct RawImport { - sd_event *event; - - char *image_root; - - RawImportFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool read_only; - bool grow_machine_directory; - - char *temp_path; - char *final_path; - - int input_fd; - int output_fd; - - ImportCompress compress; - - uint64_t written_since_last_grow; - - sd_event_source *input_event_source; - - uint8_t buffer[16*1024]; - size_t buffer_size; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - struct stat st; - - unsigned last_percent; - RateLimit progress_rate_limit; -}; - -RawImport* raw_import_unref(RawImport *i) { - if (!i) - return NULL; - - sd_event_unref(i->event); - - if (i->temp_path) { - (void) unlink(i->temp_path); - free(i->temp_path); - } - - import_compress_free(&i->compress); - - sd_event_source_unref(i->input_event_source); - - safe_close(i->output_fd); - - free(i->final_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int raw_import_new( - RawImport **ret, - sd_event *event, - const char *image_root, - RawImportFinished on_finished, - void *userdata) { - - _cleanup_(raw_import_unrefp) RawImport *i = NULL; - int r; - - assert(ret); - - i = new0(RawImport, 1); - if (!i) - return -ENOMEM; - - i->input_fd = i->output_fd = -1; - i->on_finished = on_finished; - i->userdata = userdata; - - RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - i->last_percent = (unsigned) -1; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - *ret = i; - i = NULL; - - return 0; -} - -static void raw_import_report_progress(RawImport *i) { - unsigned percent; - assert(i); - - /* We have no size information, unless the source is a regular file */ - if (!S_ISREG(i->st.st_mode)) - return; - - if (i->written_compressed >= (uint64_t) i->st.st_size) - percent = 100; - else - percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); - - if (percent == i->last_percent) - return; - - if (!ratelimit_test(&i->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Imported %u%%.", percent); - - i->last_percent = percent; -} - -static int raw_import_maybe_convert_qcow2(RawImport *i) { - _cleanup_close_ int converted_fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - assert(i); - - r = qcow2_detect(i->output_fd); - if (r < 0) - return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); - if (r == 0) - return 0; - - /* This is a QCOW2 image, let's convert it */ - r = tempfn_random(i->final_path, NULL, &t); - if (r < 0) - return log_oom(); - - converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (converted_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", t); - - r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", t); - - log_info("Unpacking QCOW2 file."); - - r = qcow2_convert(i->output_fd, converted_fd); - if (r < 0) { - unlink(t); - return log_error_errno(r, "Failed to convert qcow2 image: %m"); - } - - (void) unlink(i->temp_path); - free(i->temp_path); - i->temp_path = t; - t = NULL; - - safe_close(i->output_fd); - i->output_fd = converted_fd; - converted_fd = -1; - - return 1; -} - -static int raw_import_finish(RawImport *i) { - int r; - - assert(i); - assert(i->output_fd >= 0); - assert(i->temp_path); - assert(i->final_path); - - /* In case this was a sparse file, make sure the file system is right */ - if (i->written_uncompressed > 0) { - if (ftruncate(i->output_fd, i->written_uncompressed) < 0) - return log_error_errno(errno, "Failed to truncate file: %m"); - } - - r = raw_import_maybe_convert_qcow2(i); - if (r < 0) - return r; - - if (S_ISREG(i->st.st_mode)) { - (void) copy_times(i->input_fd, i->output_fd); - (void) copy_xattr(i->input_fd, i->output_fd); - } - - if (i->read_only) { - r = import_make_read_only_fd(i->output_fd); - if (r < 0) - return r; - } - - if (i->force_local) - (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) - return log_error_errno(r, "Failed to move image into place: %m"); - - i->temp_path = mfree(i->temp_path); - - return 0; -} - -static int raw_import_open_disk(RawImport *i) { - int r; - - assert(i); - - assert(!i->final_path); - assert(!i->temp_path); - assert(i->output_fd < 0); - - i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL); - if (!i->final_path) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - i->output_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (i->output_fd < 0) - return log_error_errno(errno, "Failed to open destination %s: %m", i->temp_path); - - r = chattr_fd(i->output_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); - - return 0; -} - -static int raw_import_try_reflink(RawImport *i) { - off_t p; - int r; - - assert(i); - assert(i->input_fd >= 0); - assert(i->output_fd >= 0); - - if (i->compress.type != IMPORT_COMPRESS_UNCOMPRESSED) - return 0; - - if (!S_ISREG(i->st.st_mode)) - return 0; - - p = lseek(i->input_fd, 0, SEEK_CUR); - if (p == (off_t) -1) - return log_error_errno(errno, "Failed to read file offset of input file: %m"); - - /* Let's only try a btrfs reflink, if we are reading from the beginning of the file */ - if ((uint64_t) p != (uint64_t) i->buffer_size) - return 0; - - r = btrfs_reflink(i->input_fd, i->output_fd); - if (r >= 0) - return 1; - - return 0; -} - -static int raw_import_write(const void *p, size_t sz, void *userdata) { - RawImport *i = userdata; - ssize_t n; - - if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { - i->written_since_last_grow = 0; - grow_machine_directory(); - } - - n = sparse_write(i->output_fd, p, sz, 64); - if (n < 0) - return -errno; - if ((size_t) n < sz) - return -EIO; - - i->written_uncompressed += sz; - i->written_since_last_grow += sz; - - return 0; -} - -static int raw_import_process(RawImport *i) { - ssize_t l; - int r; - - assert(i); - assert(i->buffer_size < sizeof(i->buffer)); - - l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to read input file: %m"); - goto finish; - } - if (l == 0) { - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); - r = -EIO; - goto finish; - } - - r = raw_import_finish(i); - goto finish; - } - - i->buffer_size += l; - - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); - if (r < 0) { - log_error("Failed to detect file compression: %m"); - goto finish; - } - if (r == 0) /* Need more data */ - return 0; - - r = raw_import_open_disk(i); - if (r < 0) - goto finish; - - r = raw_import_try_reflink(i); - if (r < 0) - goto finish; - if (r > 0) { - r = raw_import_finish(i); - goto finish; - } - } - - r = import_uncompress(&i->compress, i->buffer, i->buffer_size, raw_import_write, i); - if (r < 0) { - log_error_errno(r, "Failed to decode and write: %m"); - goto finish; - } - - i->written_compressed += i->buffer_size; - i->buffer_size = 0; - - raw_import_report_progress(i); - - return 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); - - return 0; -} - -static int raw_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - RawImport *i = userdata; - - return raw_import_process(i); -} - -static int raw_import_on_defer(sd_event_source *s, void *userdata) { - RawImport *i = userdata; - - return raw_import_process(i); -} - -int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only) { - int r; - - assert(i); - assert(fd >= 0); - assert(local); - - if (!machine_name_is_valid(local)) - return -EINVAL; - - if (i->input_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - i->force_local = force_local; - i->read_only = read_only; - - if (fstat(fd, &i->st) < 0) - return -errno; - - r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, raw_import_on_input, i); - if (r == -EPERM) { - /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ - r = sd_event_add_defer(i->event, &i->input_event_source, raw_import_on_defer, i); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - i->input_fd = fd; - return r; -} diff --git a/src/import/import-raw.h b/src/import/import-raw.h deleted file mode 100644 index 4f543e0883..0000000000 --- a/src/import/import-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct RawImport RawImport; - -typedef void (*RawImportFinished)(RawImport *import, int error, void *userdata); - -int raw_import_new(RawImport **import, sd_event *event, const char *image_root, RawImportFinished on_finished, void *userdata); -RawImport* raw_import_unref(RawImport *import); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref); - -int raw_import_start(RawImport *i, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/import/import-tar.c b/src/import/import-tar.c deleted file mode 100644 index 8b81324fde..0000000000 --- a/src/import/import-tar.c +++ /dev/null @@ -1,388 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <linux/fs.h> - -#include "sd-daemon.h" -#include "sd-event.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-common.h" -#include "import-compress.h" -#include "import-tar.h" -#include "io-util.h" -#include "machine-pool.h" -#include "mkdir.h" -#include "path-util.h" -#include "process-util.h" -#include "qcow2-util.h" -#include "ratelimit.h" -#include "rm-rf.h" -#include "string-util.h" -#include "util.h" - -struct TarImport { - sd_event *event; - - char *image_root; - - TarImportFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool read_only; - bool grow_machine_directory; - - char *temp_path; - char *final_path; - - int input_fd; - int tar_fd; - - ImportCompress compress; - - uint64_t written_since_last_grow; - - sd_event_source *input_event_source; - - uint8_t buffer[16*1024]; - size_t buffer_size; - - uint64_t written_compressed; - uint64_t written_uncompressed; - - struct stat st; - - pid_t tar_pid; - - unsigned last_percent; - RateLimit progress_rate_limit; -}; - -TarImport* tar_import_unref(TarImport *i) { - if (!i) - return NULL; - - sd_event_source_unref(i->input_event_source); - - if (i->tar_pid > 1) { - (void) kill_and_sigcont(i->tar_pid, SIGKILL); - (void) wait_for_terminate(i->tar_pid, NULL); - } - - if (i->temp_path) { - (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - free(i->temp_path); - } - - import_compress_free(&i->compress); - - sd_event_unref(i->event); - - safe_close(i->tar_fd); - - free(i->final_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int tar_import_new( - TarImport **ret, - sd_event *event, - const char *image_root, - TarImportFinished on_finished, - void *userdata) { - - _cleanup_(tar_import_unrefp) TarImport *i = NULL; - int r; - - assert(ret); - - i = new0(TarImport, 1); - if (!i) - return -ENOMEM; - - i->input_fd = i->tar_fd = -1; - i->on_finished = on_finished; - i->userdata = userdata; - - RATELIMIT_INIT(i->progress_rate_limit, 100 * USEC_PER_MSEC, 1); - i->last_percent = (unsigned) -1; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - *ret = i; - i = NULL; - - return 0; -} - -static void tar_import_report_progress(TarImport *i) { - unsigned percent; - assert(i); - - /* We have no size information, unless the source is a regular file */ - if (!S_ISREG(i->st.st_mode)) - return; - - if (i->written_compressed >= (uint64_t) i->st.st_size) - percent = 100; - else - percent = (unsigned) ((i->written_compressed * UINT64_C(100)) / (uint64_t) i->st.st_size); - - if (percent == i->last_percent) - return; - - if (!ratelimit_test(&i->progress_rate_limit)) - return; - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_info("Imported %u%%.", percent); - - i->last_percent = percent; -} - -static int tar_import_finish(TarImport *i) { - int r; - - assert(i); - assert(i->tar_fd >= 0); - assert(i->temp_path); - assert(i->final_path); - - i->tar_fd = safe_close(i->tar_fd); - - if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); - i->tar_pid = 0; - if (r < 0) - return r; - } - - if (i->read_only) { - r = import_make_read_only(i->temp_path); - if (r < 0) - return r; - } - - if (i->force_local) - (void) rm_rf(i->final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) - return log_error_errno(r, "Failed to move image into place: %m"); - - i->temp_path = mfree(i->temp_path); - - return 0; -} - -static int tar_import_fork_tar(TarImport *i) { - int r; - - assert(i); - - assert(!i->final_path); - assert(!i->temp_path); - assert(i->tar_fd < 0); - - i->final_path = strjoin(i->image_root, "/", i->local, NULL); - if (!i->final_path) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path); - else - (void) import_assign_pool_quota_and_warn(i->temp_path); - - i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); - if (i->tar_fd < 0) - return i->tar_fd; - - return 0; -} - -static int tar_import_write(const void *p, size_t sz, void *userdata) { - TarImport *i = userdata; - int r; - - if (i->grow_machine_directory && i->written_since_last_grow >= GROW_INTERVAL_BYTES) { - i->written_since_last_grow = 0; - grow_machine_directory(); - } - - r = loop_write(i->tar_fd, p, sz, false); - if (r < 0) - return r; - - i->written_uncompressed += sz; - i->written_since_last_grow += sz; - - return 0; -} - -static int tar_import_process(TarImport *i) { - ssize_t l; - int r; - - assert(i); - assert(i->buffer_size < sizeof(i->buffer)); - - l = read(i->input_fd, i->buffer + i->buffer_size, sizeof(i->buffer) - i->buffer_size); - if (l < 0) { - if (errno == EAGAIN) - return 0; - - r = log_error_errno(errno, "Failed to read input file: %m"); - goto finish; - } - if (l == 0) { - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); - r = -EIO; - goto finish; - } - - r = tar_import_finish(i); - goto finish; - } - - i->buffer_size += l; - - if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); - if (r < 0) { - log_error("Failed to detect file compression: %m"); - goto finish; - } - if (r == 0) /* Need more data */ - return 0; - - r = tar_import_fork_tar(i); - if (r < 0) - goto finish; - } - - r = import_uncompress(&i->compress, i->buffer, i->buffer_size, tar_import_write, i); - if (r < 0) { - log_error_errno(r, "Failed to decode and write: %m"); - goto finish; - } - - i->written_compressed += i->buffer_size; - i->buffer_size = 0; - - tar_import_report_progress(i); - - return 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); - - return 0; -} - -static int tar_import_on_input(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - TarImport *i = userdata; - - return tar_import_process(i); -} - -static int tar_import_on_defer(sd_event_source *s, void *userdata) { - TarImport *i = userdata; - - return tar_import_process(i); -} - -int tar_import_start(TarImport *i, int fd, const char *local, bool force_local, bool read_only) { - int r; - - assert(i); - assert(fd >= 0); - assert(local); - - if (!machine_name_is_valid(local)) - return -EINVAL; - - if (i->input_fd >= 0) - return -EBUSY; - - r = fd_nonblock(fd, true); - if (r < 0) - return r; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - i->force_local = force_local; - i->read_only = read_only; - - if (fstat(fd, &i->st) < 0) - return -errno; - - r = sd_event_add_io(i->event, &i->input_event_source, fd, EPOLLIN, tar_import_on_input, i); - if (r == -EPERM) { - /* This fd does not support epoll, for example because it is a regular file. Busy read in that case */ - r = sd_event_add_defer(i->event, &i->input_event_source, tar_import_on_defer, i); - if (r < 0) - return r; - - r = sd_event_source_set_enabled(i->input_event_source, SD_EVENT_ON); - } - if (r < 0) - return r; - - i->input_fd = fd; - return r; -} diff --git a/src/import/import-tar.h b/src/import/import-tar.h deleted file mode 100644 index 24abe06c8f..0000000000 --- a/src/import/import-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct TarImport TarImport; - -typedef void (*TarImportFinished)(TarImport *import, int error, void *userdata); - -int tar_import_new(TarImport **import, sd_event *event, const char *image_root, TarImportFinished on_finished, void *userdata); -TarImport* tar_import_unref(TarImport *import); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarImport*, tar_import_unref); - -int tar_import_start(TarImport *import, int fd, const char *local, bool force_local, bool read_only); diff --git a/src/import/import.c b/src/import/import.c deleted file mode 100644 index 4e442ee84a..0000000000 --- a/src/import/import.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <getopt.h> - -#include "sd-event.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-raw.h" -#include "import-tar.h" -#include "import-util.h" -#include "machine-image.h" -#include "signal-util.h" -#include "string-util.h" -#include "verbs.h" - -static bool arg_force = false; -static bool arg_read_only = false; -static const char *arg_image_root = "/var/lib/machines"; - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarImport *import, int error, void *userdata) { - sd_event *event = userdata; - assert(import); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int import_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_import_unrefp) TarImport *import = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *path = NULL, *local = NULL; - _cleanup_free_ char *ll = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); - return -EEXIST; - } - } - } else - local = "imported"; - - if (path) { - open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open tar image to import: %m"); - - fd = open_fd; - - log_info("Importing '%s', saving as '%s'.", path, local); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDIN_FILENO; - - (void) readlink_malloc("/proc/self/fd/0", &pretty); - log_info("Importing '%s', saving as '%s'.", strna(pretty), local); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate importer: %m"); - - r = tar_import_start(import, fd, local, arg_force, arg_read_only); - if (r < 0) - return log_error_errno(r, "Failed to import image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static void on_raw_finished(RawImport *import, int error, void *userdata) { - sd_event *event = userdata; - assert(import); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int import_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_import_unrefp) RawImport *import = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *path = NULL, *local = NULL; - _cleanup_free_ char *ll = NULL; - _cleanup_close_ int open_fd = -1; - int r, fd; - - if (argc >= 2) - path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; - - if (argc >= 3) - local = argv[2]; - else if (path) - local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); - return -EEXIST; - } - } - } else - local = "imported"; - - if (path) { - open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (open_fd < 0) - return log_error_errno(errno, "Failed to open raw image to import: %m"); - - fd = open_fd; - - log_info("Importing '%s', saving as '%s'.", path, local); - } else { - _cleanup_free_ char *pretty = NULL; - - fd = STDIN_FILENO; - - (void) readlink_malloc("/proc/self/fd/0", &pretty); - log_info("Importing '%s', saving as '%s'.", pretty, local); - } - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate importer: %m"); - - r = raw_import_start(import, fd, local, arg_force, arg_read_only); - if (r < 0) - return log_error_errno(r, "Failed to import image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Import container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --force Force creation of image\n" - " --image-root=PATH Image root directory\n" - " --read-only Create a read-only image\n\n" - "Commands:\n" - " tar FILE [NAME] Import a TAR image\n" - " raw FILE [NAME] Import a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORCE, - ARG_IMAGE_ROOT, - ARG_READ_ONLY, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "force", no_argument, NULL, ARG_FORCE }, - { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, - { "read-only", no_argument, NULL, ARG_READ_ONLY }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORCE: - arg_force = true; - break; - - case ARG_IMAGE_ROOT: - arg_image_root = optarg; - break; - - case ARG_READ_ONLY: - arg_read_only = true; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int import_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, import_tar }, - { "raw", 2, 3, 0, import_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = import_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/importd.c b/src/import/importd.c deleted file mode 100644 index 956a82945c..0000000000 --- a/src/import/importd.c +++ /dev/null @@ -1,1221 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sys/prctl.h> - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-common-errors.h" -#include "bus-util.h" -#include "def.h" -#include "fd-util.h" -#include "hostname-util.h" -#include "import-util.h" -#include "machine-pool.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "signal-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "strv.h" -#include "syslog-util.h" -#include "user-util.h" -#include "util.h" -#include "web-util.h" - -typedef struct Transfer Transfer; -typedef struct Manager Manager; - -typedef enum TransferType { - TRANSFER_IMPORT_TAR, - TRANSFER_IMPORT_RAW, - TRANSFER_EXPORT_TAR, - TRANSFER_EXPORT_RAW, - TRANSFER_PULL_TAR, - TRANSFER_PULL_RAW, - _TRANSFER_TYPE_MAX, - _TRANSFER_TYPE_INVALID = -1, -} TransferType; - -struct Transfer { - Manager *manager; - - uint32_t id; - char *object_path; - - TransferType type; - ImportVerify verify; - - char *remote; - char *local; - bool force_local; - bool read_only; - - char *format; - - pid_t pid; - - int log_fd; - - char log_message[LINE_MAX]; - size_t log_message_size; - - sd_event_source *pid_event_source; - sd_event_source *log_event_source; - - unsigned n_canceled; - unsigned progress_percent; - - int stdin_fd; - int stdout_fd; -}; - -struct Manager { - sd_event *event; - sd_bus *bus; - - uint32_t current_transfer_id; - Hashmap *transfers; - - Hashmap *polkit_registry; - - int notify_fd; - - sd_event_source *notify_event_source; -}; - -#define TRANSFERS_MAX 64 - -static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = { - [TRANSFER_IMPORT_TAR] = "import-tar", - [TRANSFER_IMPORT_RAW] = "import-raw", - [TRANSFER_EXPORT_TAR] = "export-tar", - [TRANSFER_EXPORT_RAW] = "export-raw", - [TRANSFER_PULL_TAR] = "pull-tar", - [TRANSFER_PULL_RAW] = "pull-raw", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType); - -static Transfer *transfer_unref(Transfer *t) { - if (!t) - return NULL; - - if (t->manager) - hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id)); - - sd_event_source_unref(t->pid_event_source); - sd_event_source_unref(t->log_event_source); - - free(t->remote); - free(t->local); - free(t->format); - free(t->object_path); - - if (t->pid > 0) { - (void) kill_and_sigcont(t->pid, SIGKILL); - (void) wait_for_terminate(t->pid, NULL); - } - - safe_close(t->log_fd); - safe_close(t->stdin_fd); - safe_close(t->stdout_fd); - - free(t); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref); - -static int transfer_new(Manager *m, Transfer **ret) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - uint32_t id; - int r; - - assert(m); - assert(ret); - - if (hashmap_size(m->transfers) >= TRANSFERS_MAX) - return -E2BIG; - - r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops); - if (r < 0) - return r; - - t = new0(Transfer, 1); - if (!t) - return -ENOMEM; - - t->type = _TRANSFER_TYPE_INVALID; - t->log_fd = -1; - t->stdin_fd = -1; - t->stdout_fd = -1; - t->verify = _IMPORT_VERIFY_INVALID; - - id = m->current_transfer_id + 1; - - if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0) - return -ENOMEM; - - r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t); - if (r < 0) - return r; - - m->current_transfer_id = id; - - t->manager = m; - t->id = id; - - *ret = t; - t = NULL; - - return 0; -} - -static void transfer_send_log_line(Transfer *t, const char *line) { - int r, priority = LOG_INFO; - - assert(t); - assert(line); - - syslog_parse_priority(&line, &priority, true); - - log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line); - - r = sd_bus_emit_signal( - t->manager->bus, - t->object_path, - "org.freedesktop.import1.Transfer", - "LogMessage", - "us", - priority, - line); - if (r < 0) - log_error_errno(r, "Cannot emit message: %m"); - } - -static void transfer_send_logs(Transfer *t, bool flush) { - assert(t); - - /* Try to send out all log messages, if we can. But if we - * can't we remove the messages from the buffer, but don't - * fail */ - - while (t->log_message_size > 0) { - _cleanup_free_ char *n = NULL; - char *e; - - if (t->log_message_size >= sizeof(t->log_message)) - e = t->log_message + sizeof(t->log_message); - else { - char *a, *b; - - a = memchr(t->log_message, 0, t->log_message_size); - b = memchr(t->log_message, '\n', t->log_message_size); - - if (a && b) - e = a < b ? a : b; - else if (a) - e = a; - else - e = b; - } - - if (!e) { - if (!flush) - return; - - e = t->log_message + t->log_message_size; - } - - n = strndup(t->log_message, e - t->log_message); - - /* Skip over NUL and newlines */ - while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n')) - e++; - - memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e); - t->log_message_size -= e - t->log_message; - - if (!n) { - log_oom(); - continue; - } - - if (isempty(n)) - continue; - - transfer_send_log_line(t, n); - } -} - -static int transfer_finalize(Transfer *t, bool success) { - int r; - - assert(t); - - transfer_send_logs(t, true); - - r = sd_bus_emit_signal( - t->manager->bus, - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "TransferRemoved", - "uos", - t->id, - t->object_path, - success ? "done" : - t->n_canceled > 0 ? "canceled" : "failed"); - - if (r < 0) - log_error_errno(r, "Cannot emit message: %m"); - - transfer_unref(t); - return 0; -} - -static int transfer_cancel(Transfer *t) { - int r; - - assert(t); - - r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL); - if (r < 0) - return r; - - t->n_canceled++; - return 0; -} - -static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) { - Transfer *t = userdata; - bool success = false; - - assert(s); - assert(t); - - if (si->si_code == CLD_EXITED) { - if (si->si_status != 0) - log_error("Import process failed with exit code %i.", si->si_status); - else { - log_debug("Import process succeeded."); - success = true; - } - - } else if (si->si_code == CLD_KILLED || - si->si_code == CLD_DUMPED) - - log_error("Import process terminated by signal %s.", signal_to_string(si->si_status)); - else - log_error("Import process failed due to unknown reason."); - - t->pid = 0; - - return transfer_finalize(t, success); -} - -static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - Transfer *t = userdata; - ssize_t l; - - assert(s); - assert(t); - - l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size); - if (l <= 0) { - /* EOF/read error. We just close the pipe here, and - * close the watch, waiting for the SIGCHLD to arrive, - * before we do anything else. */ - - if (l < 0) - log_error_errno(errno, "Failed to read log message: %m"); - - t->log_event_source = sd_event_source_unref(t->log_event_source); - return 0; - } - - t->log_message_size += l; - - transfer_send_logs(t, false); - - return 0; -} - -static int transfer_start(Transfer *t) { - _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; - int r; - - assert(t); - assert(t->pid <= 0); - - if (pipe2(pipefd, O_CLOEXEC) < 0) - return -errno; - - t->pid = fork(); - if (t->pid < 0) - return -errno; - if (t->pid == 0) { - const char *cmd[] = { - NULL, /* systemd-import, systemd-export or systemd-pull */ - NULL, /* tar, raw */ - NULL, /* --verify= */ - NULL, /* verify argument */ - NULL, /* maybe --force */ - NULL, /* maybe --read-only */ - NULL, /* if so: the actual URL */ - NULL, /* maybe --format= */ - NULL, /* if so: the actual format */ - NULL, /* remote */ - NULL, /* local */ - NULL - }; - unsigned k = 0; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pipefd[0] = safe_close(pipefd[0]); - - if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdout_fd >= 0) { - if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdout_fd != STDOUT_FILENO) - safe_close(t->stdout_fd); - } else { - if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - } - - if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO) - pipefd[1] = safe_close(pipefd[1]); - - if (t->stdin_fd >= 0) { - if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (t->stdin_fd != STDIN_FILENO) - safe_close(t->stdin_fd); - } else { - int null_fd; - - null_fd = open("/dev/null", O_RDONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDIN_FILENO) - safe_close(null_fd); - } - - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); - - setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1); - setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1); - - if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW)) - cmd[k++] = SYSTEMD_IMPORT_PATH; - else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) - cmd[k++] = SYSTEMD_EXPORT_PATH; - else - cmd[k++] = SYSTEMD_PULL_PATH; - - if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR)) - cmd[k++] = "tar"; - else - cmd[k++] = "raw"; - - if (t->verify != _IMPORT_VERIFY_INVALID) { - cmd[k++] = "--verify"; - cmd[k++] = import_verify_to_string(t->verify); - } - - if (t->force_local) - cmd[k++] = "--force"; - if (t->read_only) - cmd[k++] = "--read-only"; - - if (t->format) { - cmd[k++] = "--format"; - cmd[k++] = t->format; - } - - if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) { - if (t->remote) - cmd[k++] = t->remote; - else - cmd[k++] = "-"; - } - - if (t->local) - cmd[k++] = t->local; - cmd[k] = NULL; - - execv(cmd[0], (char * const *) cmd); - log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]); - _exit(EXIT_FAILURE); - } - - pipefd[1] = safe_close(pipefd[1]); - t->log_fd = pipefd[0]; - pipefd[0] = -1; - - t->stdin_fd = safe_close(t->stdin_fd); - - r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t); - if (r < 0) - return r; - - r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t); - if (r < 0) - return r; - - /* Make sure always process logging before SIGCHLD */ - r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5); - if (r < 0) - return r; - - r = sd_bus_emit_signal( - t->manager->bus, - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "TransferNew", - "uo", - t->id, - t->object_path); - if (r < 0) - return r; - - return 0; -} - -static Manager *manager_unref(Manager *m) { - Transfer *t; - - if (!m) - return NULL; - - sd_event_source_unref(m->notify_event_source); - safe_close(m->notify_fd); - - while ((t = hashmap_first(m->transfers))) - transfer_unref(t); - - hashmap_free(m->transfers); - - bus_verify_polkit_async_registry_free(m->polkit_registry); - - m->bus = sd_bus_flush_close_unref(m->bus); - sd_event_unref(m->event); - - free(m); - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref); - -static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - - char buf[NOTIFY_BUFFER_MAX+1]; - struct iovec iovec = { - .iov_base = buf, - .iov_len = sizeof(buf)-1, - }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)]; - } control = {}; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct ucred *ucred = NULL; - Manager *m = userdata; - struct cmsghdr *cmsg; - unsigned percent; - char *p, *e; - Transfer *t; - Iterator i; - ssize_t n; - int r; - - n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return -errno; - } - - cmsg_close_all(&msghdr); - - CMSG_FOREACH(cmsg, &msghdr) - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_CREDENTIALS && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) - ucred = (struct ucred*) CMSG_DATA(cmsg); - - if (msghdr.msg_flags & MSG_TRUNC) { - log_warning("Got overly long notification datagram, ignoring."); - return 0; - } - - if (!ucred || ucred->pid <= 0) { - log_warning("Got notification datagram lacking credential information, ignoring."); - return 0; - } - - HASHMAP_FOREACH(t, m->transfers, i) - if (ucred->pid == t->pid) - break; - - if (!t) { - log_warning("Got notification datagram from unexpected peer, ignoring."); - return 0; - } - - buf[n] = 0; - - p = startswith(buf, "X_IMPORT_PROGRESS="); - if (!p) { - p = strstr(buf, "\nX_IMPORT_PROGRESS="); - if (!p) - return 0; - - p += 19; - } - - e = strchrnul(p, '\n'); - *e = 0; - - r = safe_atou(p, &percent); - if (r < 0 || percent > 100) { - log_warning("Got invalid percent value, ignoring."); - return 0; - } - - t->progress_percent = percent; - - log_debug("Got percentage from client: %u%%", percent); - return 0; -} - -static int manager_new(Manager **ret) { - _cleanup_(manager_unrefp) Manager *m = NULL; - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/import/notify", - }; - static const int one = 1; - int r; - - assert(ret); - - m = new0(Manager, 1); - if (!m) - return -ENOMEM; - - r = sd_event_default(&m->event); - if (r < 0) - return r; - - sd_event_set_watchdog(m->event, true); - - r = sd_bus_default_system(&m->bus); - if (r < 0) - return r; - - m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (m->notify_fd < 0) - return -errno; - - (void) mkdir_parents_label(sa.un.sun_path, 0755); - (void) unlink(sa.un.sun_path); - - if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) - return -errno; - - if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - return -errno; - - r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m); - if (r < 0) - return r; - - *ret = m; - m = NULL; - - return 0; -} - -static Transfer *manager_find(Manager *m, TransferType type, const char *remote) { - Transfer *t; - Iterator i; - - assert(m); - assert(type >= 0); - assert(type < _TRANSFER_TYPE_MAX); - - HASHMAP_FOREACH(t, m->transfers, i) { - - if (t->type == type && - streq_ptr(t->remote, remote)) - return t; - } - - return NULL; -} - -static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - int fd, force, read_only, r; - const char *local, *object; - Manager *m = userdata; - TransferType type; - uint32_t id; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.import", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only); - if (r < 0) - return r; - - if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); - - r = setup_machine_directory((uint64_t) -1, error); - if (r < 0) - return r; - - type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW; - - r = transfer_new(m, &t); - if (r < 0) - return r; - - t->type = type; - t->force_local = force; - t->read_only = read_only; - - t->local = strdup(local); - if (!t->local) - return -ENOMEM; - - t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (t->stdin_fd < 0) - return -errno; - - r = transfer_start(t); - if (r < 0) - return r; - - object = t->object_path; - id = t->id; - t = NULL; - - return sd_bus_reply_method_return(msg, "uo", id, object); -} - -static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - int fd, r; - const char *local, *object, *format; - Manager *m = userdata; - TransferType type; - uint32_t id; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.export", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(msg, "shs", &local, &fd, &format); - if (r < 0) - return r; - - if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); - - type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW; - - r = transfer_new(m, &t); - if (r < 0) - return r; - - t->type = type; - - if (!isempty(format)) { - t->format = strdup(format); - if (!t->format) - return -ENOMEM; - } - - t->local = strdup(local); - if (!t->local) - return -ENOMEM; - - t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (t->stdout_fd < 0) - return -errno; - - r = transfer_start(t); - if (r < 0) - return r; - - object = t->object_path; - id = t->id; - t = NULL; - - return sd_bus_reply_method_return(msg, "uo", id, object); -} - -static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(transfer_unrefp) Transfer *t = NULL; - const char *remote, *local, *verify, *object; - Manager *m = userdata; - ImportVerify v; - TransferType type; - int force, r; - uint32_t id; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force); - if (r < 0) - return r; - - if (!http_url_is_valid(remote)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote); - - if (isempty(local)) - local = NULL; - else if (!machine_name_is_valid(local)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local); - - if (isempty(verify)) - v = IMPORT_VERIFY_SIGNATURE; - else - v = import_verify_from_string(verify); - if (v < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify); - - r = setup_machine_directory((uint64_t) -1, error); - if (r < 0) - return r; - - type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW; - - if (manager_find(m, type, remote)) - return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote); - - r = transfer_new(m, &t); - if (r < 0) - return r; - - t->type = type; - t->verify = v; - t->force_local = force; - - t->remote = strdup(remote); - if (!t->remote) - return -ENOMEM; - - if (local) { - t->local = strdup(local); - if (!t->local) - return -ENOMEM; - } - - r = transfer_start(t); - if (r < 0) - return r; - - object = t->object_path; - id = t->id; - t = NULL; - - return sd_bus_reply_method_return(msg, "uo", id, object); -} - -static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Manager *m = userdata; - Transfer *t; - Iterator i; - int r; - - assert(msg); - assert(m); - - r = sd_bus_message_new_method_return(msg, &reply); - if (r < 0) - return r; - - r = sd_bus_message_open_container(reply, 'a', "(usssdo)"); - if (r < 0) - return r; - - HASHMAP_FOREACH(t, m->transfers, i) { - - r = sd_bus_message_append( - reply, - "(usssdo)", - t->id, - transfer_type_to_string(t->type), - t->remote, - t->local, - (double) t->progress_percent / 100.0, - t->object_path); - if (r < 0) - return r; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return r; - - return sd_bus_send(NULL, reply, NULL); -} - -static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - Transfer *t = userdata; - int r; - - assert(msg); - assert(t); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, - &t->manager->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = transfer_cancel(t); - if (r < 0) - return r; - - return sd_bus_reply_method_return(msg, NULL); -} - -static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) { - Manager *m = userdata; - Transfer *t; - uint32_t id; - int r; - - assert(msg); - assert(m); - - r = bus_verify_polkit_async( - msg, - CAP_SYS_ADMIN, - "org.freedesktop.import1.pull", - NULL, - false, - UID_INVALID, - &m->polkit_registry, - error); - if (r < 0) - return r; - if (r == 0) - return 1; /* Will call us back */ - - r = sd_bus_message_read(msg, "u", &id); - if (r < 0) - return r; - if (id <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id"); - - t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); - if (!t) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id); - - r = transfer_cancel(t); - if (r < 0) - return r; - - return sd_bus_reply_method_return(msg, NULL); -} - -static int property_get_progress( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - Transfer *t = userdata; - - assert(bus); - assert(reply); - assert(t); - - return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0); -} - -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify); - -static const sd_bus_vtable transfer_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0), - SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("LogMessage", "us", 0), - SD_BUS_VTABLE_END, -}; - -static const sd_bus_vtable manager_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("TransferNew", "uo", 0), - SD_BUS_SIGNAL("TransferRemoved", "uos", 0), - SD_BUS_VTABLE_END, -}; - -static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) { - Manager *m = userdata; - Transfer *t; - const char *p; - uint32_t id; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(found); - assert(m); - - p = startswith(path, "/org/freedesktop/import1/transfer/_"); - if (!p) - return 0; - - r = safe_atou32(p, &id); - if (r < 0 || id == 0) - return 0; - - t = hashmap_get(m->transfers, UINT32_TO_PTR(id)); - if (!t) - return 0; - - *found = t; - return 1; -} - -static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_strv_free_ char **l = NULL; - Manager *m = userdata; - Transfer *t; - unsigned k = 0; - Iterator i; - - l = new0(char*, hashmap_size(m->transfers) + 1); - if (!l) - return -ENOMEM; - - HASHMAP_FOREACH(t, m->transfers, i) { - - l[k] = strdup(t->object_path); - if (!l[k]) - return -ENOMEM; - - k++; - } - - *nodes = l; - l = NULL; - - return 1; -} - -static int manager_add_bus_objects(Manager *m) { - int r; - - assert(m); - - r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m); - if (r < 0) - return log_error_errno(r, "Failed to register object: %m"); - - r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m); - if (r < 0) - return log_error_errno(r, "Failed to add transfer enumerator: %m"); - - r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0); - if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); - - r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop: %m"); - - return 0; -} - -static bool manager_check_idle(void *userdata) { - Manager *m = userdata; - - return hashmap_isempty(m->transfers); -} - -static int manager_run(Manager *m) { - assert(m); - - return bus_event_loop_with_idle( - m->event, - m->bus, - "org.freedesktop.import1", - DEFAULT_EXIT_USEC, - manager_check_idle, - m); -} - -int main(int argc, char *argv[]) { - _cleanup_(manager_unrefp) Manager *m = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - if (argc != 1) { - log_error("This program takes no arguments."); - r = -EINVAL; - goto finish; - } - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - r = manager_new(&m); - if (r < 0) { - log_error_errno(r, "Failed to allocate manager object: %m"); - goto finish; - } - - r = manager_add_bus_objects(m); - if (r < 0) - goto finish; - - r = manager_run(m); - if (r < 0) { - log_error_errno(r, "Failed to run event loop: %m"); - goto finish; - } - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/org.freedesktop.import1.conf b/src/import/org.freedesktop.import1.conf deleted file mode 100644 index ed2539a03b..0000000000 --- a/src/import/org.freedesktop.import1.conf +++ /dev/null @@ -1,62 +0,0 @@ -<?xml version="1.0"?> <!--*-nxml-*--> -<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" - "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> - -<!-- - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. ---> - -<busconfig> - - <policy user="root"> - <allow own="org.freedesktop.import1"/> - <allow send_destination="org.freedesktop.import1"/> - <allow receive_sender="org.freedesktop.import1"/> - </policy> - - <policy context="default"> - <deny send_destination="org.freedesktop.import1"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.DBus.Introspectable"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.DBus.Peer"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.DBus.Properties" - send_member="Get"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.DBus.Properties" - send_member="GetAll"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.import1.Manager" - send_member="ListTransfers"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.import1.Manager" - send_member="CancelTransfer"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.import1.Manager" - send_member="PullTar"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.import1.Manager" - send_member="PullRaw"/> - - <allow send_destination="org.freedesktop.import1" - send_interface="org.freedesktop.import1.Transfer" - send_member="Cancel"/> - - <allow receive_sender="org.freedesktop.import1"/> - </policy> - -</busconfig> diff --git a/src/import/org.freedesktop.import1.policy.in b/src/import/org.freedesktop.import1.policy.in deleted file mode 100644 index 85924ed743..0000000000 --- a/src/import/org.freedesktop.import1.policy.in +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*--> -<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN" - "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd"> - -<!-- - This file is part of systemd. - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. ---> - -<policyconfig> - - <vendor>The systemd Project</vendor> - <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url> - - <action id="org.freedesktop.import1.import"> - <_description>Import a VM or container image</_description> - <_message>Authentication is required to import a VM or container image</_message> - <defaults> - <allow_any>auth_admin</allow_any> - <allow_inactive>auth_admin</allow_inactive> - <allow_active>auth_admin_keep</allow_active> - </defaults> - </action> - - <action id="org.freedesktop.import1.export"> - <_description>Export a VM or container image</_description> - <_message>Authentication is required to export a VM or container image</_message> - <defaults> - <allow_any>auth_admin</allow_any> - <allow_inactive>auth_admin</allow_inactive> - <allow_active>auth_admin_keep</allow_active> - </defaults> - </action> - - <action id="org.freedesktop.import1.pull"> - <_description>Download a VM or container image</_description> - <_message>Authentication is required to download a VM or container image</_message> - <defaults> - <allow_any>auth_admin</allow_any> - <allow_inactive>auth_admin</allow_inactive> - <allow_active>auth_admin_keep</allow_active> - </defaults> - </action> - -</policyconfig> diff --git a/src/import/org.freedesktop.import1.service b/src/import/org.freedesktop.import1.service deleted file mode 100644 index 8fc4c47881..0000000000 --- a/src/import/org.freedesktop.import1.service +++ /dev/null @@ -1,12 +0,0 @@ -# This file is part of systemd. -# -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. - -[D-BUS Service] -Name=org.freedesktop.import1 -Exec=/bin/false -User=root -SystemdService=dbus-org.freedesktop.import1.service diff --git a/src/import/pull-common.c b/src/import/pull-common.c deleted file mode 100644 index dc4e4667a9..0000000000 --- a/src/import/pull-common.c +++ /dev/null @@ -1,549 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sys/prctl.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "capability-util.h" -#include "copy.h" -#include "dirent-util.h" -#include "escape.h" -#include "fd-util.h" -#include "io-util.h" -#include "path-util.h" -#include "process-util.h" -#include "pull-common.h" -#include "pull-job.h" -#include "rm-rf.h" -#include "signal-util.h" -#include "siphash24.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" -#include "web-util.h" - -#define FILENAME_ESCAPE "/.#\"\'" -#define HASH_URL_THRESHOLD_LENGTH (_POSIX_PATH_MAX - 16) - -int pull_find_old_etags( - const char *url, - const char *image_root, - int dt, - const char *prefix, - const char *suffix, - char ***etags) { - - _cleanup_free_ char *escaped_url = NULL; - _cleanup_closedir_ DIR *d = NULL; - _cleanup_strv_free_ char **l = NULL; - struct dirent *de; - int r; - - assert(url); - assert(etags); - - if (!image_root) - image_root = "/var/lib/machines"; - - escaped_url = xescape(url, FILENAME_ESCAPE); - if (!escaped_url) - return -ENOMEM; - - d = opendir(image_root); - if (!d) { - if (errno == ENOENT) { - *etags = NULL; - return 0; - } - - return -errno; - } - - FOREACH_DIRENT_ALL(de, d, return -errno) { - const char *a, *b; - char *u; - - if (de->d_type != DT_UNKNOWN && - de->d_type != dt) - continue; - - if (prefix) { - a = startswith(de->d_name, prefix); - if (!a) - continue; - } else - a = de->d_name; - - a = startswith(a, escaped_url); - if (!a) - continue; - - a = startswith(a, "."); - if (!a) - continue; - - if (suffix) { - b = endswith(de->d_name, suffix); - if (!b) - continue; - } else - b = strchr(de->d_name, 0); - - if (a >= b) - continue; - - r = cunescape_length(a, b - a, 0, &u); - if (r < 0) - return r; - - if (!http_etag_is_valid(u)) { - free(u); - continue; - } - - r = strv_consume(&l, u); - if (r < 0) - return r; - } - - *etags = l; - l = NULL; - - return 0; -} - -int pull_make_local_copy(const char *final, const char *image_root, const char *local, bool force_local) { - const char *p; - int r; - - assert(final); - assert(local); - - if (!image_root) - image_root = "/var/lib/machines"; - - p = strjoina(image_root, "/", local); - - if (force_local) - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = btrfs_subvol_snapshot(final, p, BTRFS_SNAPSHOT_QUOTA); - if (r == -ENOTTY) { - r = copy_tree(final, p, false); - if (r < 0) - return log_error_errno(r, "Failed to copy image: %m"); - } else if (r < 0) - return log_error_errno(r, "Failed to create local image: %m"); - - log_info("Created new local image '%s'.", local); - - return 0; -} - -static int hash_url(const char *url, char **ret) { - uint64_t h; - static const sd_id128_t k = SD_ID128_ARRAY(df,89,16,87,01,cc,42,30,98,ab,4a,19,a6,a5,63,4f); - - assert(url); - - h = siphash24(url, strlen(url), k.bytes); - if (asprintf(ret, "%"PRIx64, h) < 0) - return -ENOMEM; - - return 0; -} - -int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret) { - _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL; - char *path; - - assert(url); - assert(ret); - - if (!image_root) - image_root = "/var/lib/machines"; - - escaped_url = xescape(url, FILENAME_ESCAPE); - if (!escaped_url) - return -ENOMEM; - - if (etag) { - escaped_etag = xescape(etag, FILENAME_ESCAPE); - if (!escaped_etag) - return -ENOMEM; - } - - path = strjoin(image_root, "/", strempty(prefix), escaped_url, escaped_etag ? "." : "", - strempty(escaped_etag), strempty(suffix), NULL); - if (!path) - return -ENOMEM; - - /* URLs might make the path longer than the maximum allowed length for a file name. - * When that happens, a URL hash is used instead. Paths returned by this function - * can be later used with tempfn_random() which adds 16 bytes to the resulting name. */ - if (strlen(path) >= HASH_URL_THRESHOLD_LENGTH) { - _cleanup_free_ char *hash = NULL; - int r; - - free(path); - - r = hash_url(url, &hash); - if (r < 0) - return r; - - path = strjoin(image_root, "/", strempty(prefix), hash, escaped_etag ? "." : "", - strempty(escaped_etag), strempty(suffix), NULL); - if (!path) - return -ENOMEM; - } - - *ret = path; - return 0; -} - -int pull_make_settings_job( - PullJob **ret, - const char *url, - CurlGlue *glue, - PullJobFinished on_finished, - void *userdata) { - - _cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL; - _cleanup_(pull_job_unrefp) PullJob *job = NULL; - const char *q; - int r; - - assert(ret); - assert(url); - assert(glue); - - r = import_url_last_component(url, &last_component); - if (r < 0) - return r; - - r = tar_strip_suffixes(last_component, &ll); - if (r < 0) - return r; - - q = strjoina(ll, ".nspawn"); - - r = import_url_change_last_component(url, q, &settings_url); - if (r < 0) - return r; - - r = pull_job_new(&job, settings_url, glue, userdata); - if (r < 0) - return r; - - job->on_finished = on_finished; - job->compressed_max = job->uncompressed_max = 1ULL * 1024ULL * 1024ULL; - - *ret = job; - job = NULL; - - return 0; -} - -int pull_make_verification_jobs( - PullJob **ret_checksum_job, - PullJob **ret_signature_job, - ImportVerify verify, - const char *url, - CurlGlue *glue, - PullJobFinished on_finished, - void *userdata) { - - _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL; - int r; - - assert(ret_checksum_job); - assert(ret_signature_job); - assert(verify >= 0); - assert(verify < _IMPORT_VERIFY_MAX); - assert(url); - assert(glue); - - if (verify != IMPORT_VERIFY_NO) { - _cleanup_free_ char *checksum_url = NULL; - - /* Queue job for the SHA256SUMS file for the image */ - r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url); - if (r < 0) - return r; - - r = pull_job_new(&checksum_job, checksum_url, glue, userdata); - if (r < 0) - return r; - - checksum_job->on_finished = on_finished; - checksum_job->uncompressed_max = checksum_job->compressed_max = 1ULL * 1024ULL * 1024ULL; - } - - if (verify == IMPORT_VERIFY_SIGNATURE) { - _cleanup_free_ char *signature_url = NULL; - - /* Queue job for the SHA256SUMS.gpg file for the image. */ - r = import_url_change_last_component(url, "SHA256SUMS.gpg", &signature_url); - if (r < 0) - return r; - - r = pull_job_new(&signature_job, signature_url, glue, userdata); - if (r < 0) - return r; - - signature_job->on_finished = on_finished; - signature_job->uncompressed_max = signature_job->compressed_max = 1ULL * 1024ULL * 1024ULL; - } - - *ret_checksum_job = checksum_job; - *ret_signature_job = signature_job; - - checksum_job = signature_job = NULL; - - return 0; -} - -int pull_verify(PullJob *main_job, - PullJob *settings_job, - PullJob *checksum_job, - PullJob *signature_job) { - - _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; - _cleanup_free_ char *fn = NULL; - _cleanup_close_ int sig_file = -1; - const char *p, *line; - char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_(sigkill_waitp) pid_t pid = 0; - bool gpg_home_created = false; - int r; - - assert(main_job); - assert(main_job->state == PULL_JOB_DONE); - - if (!checksum_job) - return 0; - - assert(main_job->calc_checksum); - assert(main_job->checksum); - assert(checksum_job->state == PULL_JOB_DONE); - - if (!checksum_job->payload || checksum_job->payload_size <= 0) { - log_error("Checksum is empty, cannot verify."); - return -EBADMSG; - } - - r = import_url_last_component(main_job->url, &fn); - if (r < 0) - return log_oom(); - - if (!filename_is_valid(fn)) { - log_error("Cannot verify checksum, could not determine valid server-side file name."); - return -EBADMSG; - } - - line = strjoina(main_job->checksum, " *", fn, "\n"); - - p = memmem(checksum_job->payload, - checksum_job->payload_size, - line, - strlen(line)); - - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with."); - return -EBADMSG; - } - - log_info("SHA256 checksum of %s is valid.", main_job->url); - - assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED)); - - if (settings_job && - settings_job->state == PULL_JOB_DONE && - settings_job->error == 0 && - !settings_job->etag_exists) { - - _cleanup_free_ char *settings_fn = NULL; - - assert(settings_job->calc_checksum); - assert(settings_job->checksum); - - r = import_url_last_component(settings_job->url, &settings_fn); - if (r < 0) - return log_oom(); - - if (!filename_is_valid(settings_fn)) { - log_error("Cannot verify checksum, could not determine server-side settings file name."); - return -EBADMSG; - } - - line = strjoina(settings_job->checksum, " *", settings_fn, "\n"); - - p = memmem(checksum_job->payload, - checksum_job->payload_size, - line, - strlen(line)); - - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with."); - return -EBADMSG; - } - - log_info("SHA256 checksum of %s is valid.", settings_job->url); - } - - if (!signature_job) - return 0; - - assert(signature_job->state == PULL_JOB_DONE); - - if (!signature_job->payload || signature_job->payload_size <= 0) { - log_error("Signature is empty, cannot verify."); - return -EBADMSG; - } - - r = pipe2(gpg_pipe, O_CLOEXEC); - if (r < 0) - return log_error_errno(errno, "Failed to create pipe for gpg: %m"); - - sig_file = mkostemp(sig_file_path, O_RDWR); - if (sig_file < 0) - return log_error_errno(errno, "Failed to create temporary file: %m"); - - r = loop_write(sig_file, signature_job->payload, signature_job->payload_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write to temporary file: %m"); - goto finish; - } - - if (!mkdtemp(gpg_home)) { - r = log_error_errno(errno, "Failed to create tempory home for gpg: %m"); - goto finish; - } - - gpg_home_created = true; - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off gpg: %m"); - if (pid == 0) { - const char *cmd[] = { - "gpg", - "--no-options", - "--no-default-keyring", - "--no-auto-key-locate", - "--no-auto-check-trustdb", - "--batch", - "--trust-model=always", - NULL, /* --homedir= */ - NULL, /* --keyring= */ - NULL, /* --verify */ - NULL, /* signature file */ - NULL, /* dash */ - NULL /* trailing NULL */ - }; - unsigned k = ELEMENTSOF(cmd) - 6; - int null_fd; - - /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - gpg_pipe[1] = safe_close(gpg_pipe[1]); - - if (dup2(gpg_pipe[0], STDIN_FILENO) != STDIN_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (gpg_pipe[0] != STDIN_FILENO) - gpg_pipe[0] = safe_close(gpg_pipe[0]); - - null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); - if (null_fd < 0) { - log_error_errno(errno, "Failed to open /dev/null: %m"); - _exit(EXIT_FAILURE); - } - - if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { - log_error_errno(errno, "Failed to dup2() fd: %m"); - _exit(EXIT_FAILURE); - } - - if (null_fd != STDOUT_FILENO) - null_fd = safe_close(null_fd); - - cmd[k++] = strjoina("--homedir=", gpg_home); - - /* We add the user keyring only to the command line - * arguments, if it's around since gpg fails - * otherwise. */ - if (access(USER_KEYRING_PATH, F_OK) >= 0) - cmd[k++] = "--keyring=" USER_KEYRING_PATH; - else - cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH; - - cmd[k++] = "--verify"; - cmd[k++] = sig_file_path; - cmd[k++] = "-"; - cmd[k++] = NULL; - - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); - - execvp("gpg2", (char * const *) cmd); - execvp("gpg", (char * const *) cmd); - log_error_errno(errno, "Failed to execute gpg: %m"); - _exit(EXIT_FAILURE); - } - - gpg_pipe[0] = safe_close(gpg_pipe[0]); - - r = loop_write(gpg_pipe[1], checksum_job->payload, checksum_job->payload_size, false); - if (r < 0) { - log_error_errno(r, "Failed to write to pipe: %m"); - goto finish; - } - - gpg_pipe[1] = safe_close(gpg_pipe[1]); - - r = wait_for_terminate_and_warn("gpg", pid, true); - pid = 0; - if (r < 0) - goto finish; - if (r > 0) { - log_error("DOWNLOAD INVALID: Signature verification failed."); - r = -EBADMSG; - } else { - log_info("Signature verification succeeded."); - r = 0; - } - -finish: - if (sig_file >= 0) - (void) unlink(sig_file_path); - - if (gpg_home_created) - (void) rm_rf(gpg_home, REMOVE_ROOT|REMOVE_PHYSICAL); - - return r; -} diff --git a/src/import/pull-common.h b/src/import/pull-common.h deleted file mode 100644 index 929a131c88..0000000000 --- a/src/import/pull-common.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <stdbool.h> - -#include "import-util.h" -#include "pull-job.h" - -int pull_make_local_copy(const char *final, const char *root, const char *local, bool force_local); - -int pull_find_old_etags(const char *url, const char *root, int dt, const char *prefix, const char *suffix, char ***etags); - -int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); - -int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); -int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); - -int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); diff --git a/src/import/pull-job.c b/src/import/pull-job.c deleted file mode 100644 index 6bcf35ef4e..0000000000 --- a/src/import/pull-job.c +++ /dev/null @@ -1,618 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sys/xattr.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "hexdecoct.h" -#include "io-util.h" -#include "machine-pool.h" -#include "parse-util.h" -#include "pull-job.h" -#include "string-util.h" -#include "strv.h" -#include "xattr-util.h" - -PullJob* pull_job_unref(PullJob *j) { - if (!j) - return NULL; - - curl_glue_remove_and_free(j->glue, j->curl); - curl_slist_free_all(j->request_header); - - safe_close(j->disk_fd); - - import_compress_free(&j->compress); - - if (j->checksum_context) - gcry_md_close(j->checksum_context); - - free(j->url); - free(j->etag); - strv_free(j->old_etags); - free(j->payload); - free(j->checksum); - - free(j); - - return NULL; -} - -static void pull_job_finish(PullJob *j, int ret) { - assert(j); - - if (j->state == PULL_JOB_DONE || - j->state == PULL_JOB_FAILED) - return; - - if (ret == 0) { - j->state = PULL_JOB_DONE; - j->progress_percent = 100; - log_info("Download of %s complete.", j->url); - } else { - j->state = PULL_JOB_FAILED; - j->error = ret; - } - - if (j->on_finished) - j->on_finished(j); -} - -void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { - PullJob *j = NULL; - CURLcode code; - long status; - int r; - - if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&j) != CURLE_OK) - return; - - if (!j || j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) - return; - - if (result != CURLE_OK) { - log_error("Transfer failed: %s", curl_easy_strerror(result)); - r = -EIO; - goto finish; - } - - code = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); - if (code != CURLE_OK) { - log_error("Failed to retrieve response code: %s", curl_easy_strerror(code)); - r = -EIO; - goto finish; - } else if (status == 304) { - log_info("Image already downloaded. Skipping download."); - j->etag_exists = true; - r = 0; - goto finish; - } else if (status >= 300) { - log_error("HTTP request to %s failed with code %li.", j->url, status); - r = -EIO; - goto finish; - } else if (status < 200) { - log_error("HTTP request to %s finished with unexpected code %li.", j->url, status); - r = -EIO; - goto finish; - } - - if (j->state != PULL_JOB_RUNNING) { - log_error("Premature connection termination."); - r = -EIO; - goto finish; - } - - if (j->content_length != (uint64_t) -1 && - j->content_length != j->written_compressed) { - log_error("Download truncated."); - r = -EIO; - goto finish; - } - - if (j->checksum_context) { - uint8_t *k; - - k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256); - if (!k) { - log_error("Failed to get checksum."); - r = -EIO; - goto finish; - } - - j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256)); - if (!j->checksum) { - r = log_oom(); - goto finish; - } - - log_debug("SHA256 of %s is %s.", j->url, j->checksum); - } - - if (j->disk_fd >= 0 && j->allow_sparse) { - /* Make sure the file size is right, in case the file was - * sparse and we just seeked for the last part */ - - if (ftruncate(j->disk_fd, j->written_uncompressed) < 0) { - r = log_error_errno(errno, "Failed to truncate file: %m"); - goto finish; - } - - if (j->etag) - (void) fsetxattr(j->disk_fd, "user.source_etag", j->etag, strlen(j->etag), 0); - if (j->url) - (void) fsetxattr(j->disk_fd, "user.source_url", j->url, strlen(j->url), 0); - - if (j->mtime != 0) { - struct timespec ut[2]; - - timespec_store(&ut[0], j->mtime); - ut[1] = ut[0]; - (void) futimens(j->disk_fd, ut); - - (void) fd_setcrtime(j->disk_fd, j->mtime); - } - } - - r = 0; - -finish: - pull_job_finish(j, r); -} - -static int pull_job_write_uncompressed(const void *p, size_t sz, void *userdata) { - PullJob *j = userdata; - ssize_t n; - - assert(j); - assert(p); - - if (sz <= 0) - return 0; - - if (j->written_uncompressed + sz < j->written_uncompressed) { - log_error("File too large, overflow"); - return -EOVERFLOW; - } - - if (j->written_uncompressed + sz > j->uncompressed_max) { - log_error("File overly large, refusing"); - return -EFBIG; - } - - if (j->disk_fd >= 0) { - - if (j->grow_machine_directory && j->written_since_last_grow >= GROW_INTERVAL_BYTES) { - j->written_since_last_grow = 0; - grow_machine_directory(); - } - - if (j->allow_sparse) - n = sparse_write(j->disk_fd, p, sz, 64); - else - n = write(j->disk_fd, p, sz); - if (n < 0) - return log_error_errno(errno, "Failed to write file: %m"); - if ((size_t) n < sz) { - log_error("Short write"); - return -EIO; - } - } else { - - if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) - return log_oom(); - - memcpy(j->payload + j->payload_size, p, sz); - j->payload_size += sz; - } - - j->written_uncompressed += sz; - j->written_since_last_grow += sz; - - return 0; -} - -static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { - int r; - - assert(j); - assert(p); - - if (sz <= 0) - return 0; - - if (j->written_compressed + sz < j->written_compressed) { - log_error("File too large, overflow"); - return -EOVERFLOW; - } - - if (j->written_compressed + sz > j->compressed_max) { - log_error("File overly large, refusing."); - return -EFBIG; - } - - if (j->content_length != (uint64_t) -1 && - j->written_compressed + sz > j->content_length) { - log_error("Content length incorrect."); - return -EFBIG; - } - - if (j->checksum_context) - gcry_md_write(j->checksum_context, p, sz); - - r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); - if (r < 0) - return r; - - j->written_compressed += sz; - - return 0; -} - -static int pull_job_open_disk(PullJob *j) { - int r; - - assert(j); - - if (j->on_open_disk) { - r = j->on_open_disk(j); - if (r < 0) - return r; - } - - if (j->disk_fd >= 0) { - /* Check if we can do sparse files */ - - if (lseek(j->disk_fd, SEEK_SET, 0) == 0) - j->allow_sparse = true; - else { - if (errno != ESPIPE) - return log_error_errno(errno, "Failed to seek on file descriptor: %m"); - - j->allow_sparse = false; - } - } - - if (j->calc_checksum) { - if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) { - log_error("Failed to initialize hash context."); - return -EIO; - } - } - - return 0; -} - -static int pull_job_detect_compression(PullJob *j) { - _cleanup_free_ uint8_t *stub = NULL; - size_t stub_size; - - int r; - - assert(j); - - r = import_uncompress_detect(&j->compress, j->payload, j->payload_size); - if (r < 0) - return log_error_errno(r, "Failed to initialize compressor: %m"); - if (r == 0) - return 0; - - log_debug("Stream is compressed: %s", import_compress_type_to_string(j->compress.type)); - - r = pull_job_open_disk(j); - if (r < 0) - return r; - - /* Now, take the payload we read so far, and decompress it */ - stub = j->payload; - stub_size = j->payload_size; - - j->payload = NULL; - j->payload_size = 0; - j->payload_allocated = 0; - - j->state = PULL_JOB_RUNNING; - - r = pull_job_write_compressed(j, stub, stub_size); - if (r < 0) - return r; - - return 0; -} - -static size_t pull_job_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) { - PullJob *j = userdata; - size_t sz = size * nmemb; - int r; - - assert(contents); - assert(j); - - switch (j->state) { - - case PULL_JOB_ANALYZING: - /* Let's first check what it actually is */ - - if (!GREEDY_REALLOC(j->payload, j->payload_allocated, j->payload_size + sz)) { - r = log_oom(); - goto fail; - } - - memcpy(j->payload + j->payload_size, contents, sz); - j->payload_size += sz; - - r = pull_job_detect_compression(j); - if (r < 0) - goto fail; - - break; - - case PULL_JOB_RUNNING: - - r = pull_job_write_compressed(j, contents, sz); - if (r < 0) - goto fail; - - break; - - case PULL_JOB_DONE: - case PULL_JOB_FAILED: - r = -ESTALE; - goto fail; - - default: - assert_not_reached("Impossible state."); - } - - return sz; - -fail: - pull_job_finish(j, r); - return 0; -} - -static size_t pull_job_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) { - PullJob *j = userdata; - size_t sz = size * nmemb; - _cleanup_free_ char *length = NULL, *last_modified = NULL; - char *etag; - int r; - - assert(contents); - assert(j); - - if (j->state == PULL_JOB_DONE || j->state == PULL_JOB_FAILED) { - r = -ESTALE; - goto fail; - } - - assert(j->state == PULL_JOB_ANALYZING); - - r = curl_header_strdup(contents, sz, "ETag:", &etag); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - free(j->etag); - j->etag = etag; - - if (strv_contains(j->old_etags, j->etag)) { - log_info("Image already downloaded. Skipping download."); - j->etag_exists = true; - pull_job_finish(j, 0); - return sz; - } - - return sz; - } - - r = curl_header_strdup(contents, sz, "Content-Length:", &length); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - (void) safe_atou64(length, &j->content_length); - - if (j->content_length != (uint64_t) -1) { - char bytes[FORMAT_BYTES_MAX]; - - if (j->content_length > j->compressed_max) { - log_error("Content too large."); - r = -EFBIG; - goto fail; - } - - log_info("Downloading %s for %s.", format_bytes(bytes, sizeof(bytes), j->content_length), j->url); - } - - return sz; - } - - r = curl_header_strdup(contents, sz, "Last-Modified:", &last_modified); - if (r < 0) { - log_oom(); - goto fail; - } - if (r > 0) { - (void) curl_parse_http_time(last_modified, &j->mtime); - return sz; - } - - if (j->on_header) { - r = j->on_header(j, contents, sz); - if (r < 0) - goto fail; - } - - return sz; - -fail: - pull_job_finish(j, r); - return 0; -} - -static int pull_job_progress_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - PullJob *j = userdata; - unsigned percent; - usec_t n; - - assert(j); - - if (dltotal <= 0) - return 0; - - percent = ((100 * dlnow) / dltotal); - n = now(CLOCK_MONOTONIC); - - if (n > j->last_status_usec + USEC_PER_SEC && - percent != j->progress_percent && - dlnow < dltotal) { - char buf[FORMAT_TIMESPAN_MAX]; - - if (n - j->start_usec > USEC_PER_SEC && dlnow > 0) { - char y[FORMAT_BYTES_MAX]; - usec_t left, done; - - done = n - j->start_usec; - left = (usec_t) (((double) done * (double) dltotal) / dlnow) - done; - - log_info("Got %u%% of %s. %s left at %s/s.", - percent, - j->url, - format_timespan(buf, sizeof(buf), left, USEC_PER_SEC), - format_bytes(y, sizeof(y), (uint64_t) ((double) dlnow / ((double) done / (double) USEC_PER_SEC)))); - } else - log_info("Got %u%% of %s.", percent, j->url); - - j->progress_percent = percent; - j->last_status_usec = n; - - if (j->on_progress) - j->on_progress(j); - } - - return 0; -} - -int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) { - _cleanup_(pull_job_unrefp) PullJob *j = NULL; - - assert(url); - assert(glue); - assert(ret); - - j = new0(PullJob, 1); - if (!j) - return -ENOMEM; - - j->state = PULL_JOB_INIT; - j->disk_fd = -1; - j->userdata = userdata; - j->glue = glue; - j->content_length = (uint64_t) -1; - j->start_usec = now(CLOCK_MONOTONIC); - j->compressed_max = j->uncompressed_max = 8LLU * 1024LLU * 1024LLU * 1024LLU; /* 8GB */ - - j->url = strdup(url); - if (!j->url) - return -ENOMEM; - - *ret = j; - j = NULL; - - return 0; -} - -int pull_job_begin(PullJob *j) { - int r; - - assert(j); - - if (j->state != PULL_JOB_INIT) - return -EBUSY; - - if (j->grow_machine_directory) - grow_machine_directory(); - - r = curl_glue_make(&j->curl, j->url, j); - if (r < 0) - return r; - - if (!strv_isempty(j->old_etags)) { - _cleanup_free_ char *cc = NULL, *hdr = NULL; - - cc = strv_join(j->old_etags, ", "); - if (!cc) - return -ENOMEM; - - hdr = strappend("If-None-Match: ", cc); - if (!hdr) - return -ENOMEM; - - if (!j->request_header) { - j->request_header = curl_slist_new(hdr, NULL); - if (!j->request_header) - return -ENOMEM; - } else { - struct curl_slist *l; - - l = curl_slist_append(j->request_header, hdr); - if (!l) - return -ENOMEM; - - j->request_header = l; - } - } - - if (j->request_header) { - if (curl_easy_setopt(j->curl, CURLOPT_HTTPHEADER, j->request_header) != CURLE_OK) - return -EIO; - } - - if (curl_easy_setopt(j->curl, CURLOPT_WRITEFUNCTION, pull_job_write_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_WRITEDATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_HEADERFUNCTION, pull_job_header_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_HEADERDATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFOFUNCTION, pull_job_progress_callback) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_XFERINFODATA, j) != CURLE_OK) - return -EIO; - - if (curl_easy_setopt(j->curl, CURLOPT_NOPROGRESS, 0) != CURLE_OK) - return -EIO; - - r = curl_glue_add(j->glue, j->curl); - if (r < 0) - return r; - - j->state = PULL_JOB_ANALYZING; - - return 0; -} diff --git a/src/import/pull-job.h b/src/import/pull-job.h deleted file mode 100644 index 3a152a50e3..0000000000 --- a/src/import/pull-job.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <gcrypt.h> - -#include "curl-util.h" -#include "import-compress.h" -#include "macro.h" - -typedef struct PullJob PullJob; - -typedef void (*PullJobFinished)(PullJob *job); -typedef int (*PullJobOpenDisk)(PullJob *job); -typedef int (*PullJobHeader)(PullJob *job, const char *header, size_t sz); -typedef void (*PullJobProgress)(PullJob *job); - -typedef enum PullJobState { - PULL_JOB_INIT, - PULL_JOB_ANALYZING, /* Still reading into ->payload, to figure out what we have */ - PULL_JOB_RUNNING, /* Writing to destination */ - PULL_JOB_DONE, - PULL_JOB_FAILED, - _PULL_JOB_STATE_MAX, - _PULL_JOB_STATE_INVALID = -1, -} PullJobState; - -#define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) - -struct PullJob { - PullJobState state; - int error; - - char *url; - - void *userdata; - PullJobFinished on_finished; - PullJobOpenDisk on_open_disk; - PullJobHeader on_header; - PullJobProgress on_progress; - - CurlGlue *glue; - CURL *curl; - struct curl_slist *request_header; - - char *etag; - char **old_etags; - bool etag_exists; - - uint64_t content_length; - uint64_t written_compressed; - uint64_t written_uncompressed; - - uint64_t uncompressed_max; - uint64_t compressed_max; - - uint8_t *payload; - size_t payload_size; - size_t payload_allocated; - - int disk_fd; - - usec_t mtime; - - ImportCompress compress; - - unsigned progress_percent; - usec_t start_usec; - usec_t last_status_usec; - - bool allow_sparse; - - bool calc_checksum; - gcry_md_hd_t checksum_context; - - char *checksum; - - bool grow_machine_directory; - uint64_t written_since_last_grow; -}; - -int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata); -PullJob* pull_job_unref(PullJob *job); - -int pull_job_begin(PullJob *j); - -void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result); - -DEFINE_TRIVIAL_CLEANUP_FUNC(PullJob*, pull_job_unref); diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c deleted file mode 100644 index 8993402821..0000000000 --- a/src/import/pull-raw.c +++ /dev/null @@ -1,651 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <curl/curl.h> -#include <linux/fs.h> -#include <sys/xattr.h> - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "chattr-util.h" -#include "copy.h" -#include "curl-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-common.h" -#include "import-util.h" -#include "macro.h" -#include "mkdir.h" -#include "path-util.h" -#include "pull-common.h" -#include "pull-job.h" -#include "pull-raw.h" -#include "qcow2-util.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" -#include "util.h" -#include "web-util.h" - -typedef enum RawProgress { - RAW_DOWNLOADING, - RAW_VERIFYING, - RAW_UNPACKING, - RAW_FINALIZING, - RAW_COPYING, -} RawProgress; - -struct RawPull { - sd_event *event; - CurlGlue *glue; - - char *image_root; - - PullJob *raw_job; - PullJob *settings_job; - PullJob *checksum_job; - PullJob *signature_job; - - RawPullFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool grow_machine_directory; - bool settings; - - char *final_path; - char *temp_path; - - char *settings_path; - char *settings_temp_path; - - ImportVerify verify; -}; - -RawPull* raw_pull_unref(RawPull *i) { - if (!i) - return NULL; - - pull_job_unref(i->raw_job); - pull_job_unref(i->settings_job); - pull_job_unref(i->checksum_job); - pull_job_unref(i->signature_job); - - curl_glue_unref(i->glue); - sd_event_unref(i->event); - - if (i->temp_path) { - (void) unlink(i->temp_path); - free(i->temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } - - free(i->final_path); - free(i->settings_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int raw_pull_new( - RawPull **ret, - sd_event *event, - const char *image_root, - RawPullFinished on_finished, - void *userdata) { - - _cleanup_(raw_pull_unrefp) RawPull *i = NULL; - int r; - - assert(ret); - - i = new0(RawPull, 1); - if (!i) - return -ENOMEM; - - i->on_finished = on_finished; - i->userdata = userdata; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - r = curl_glue_new(&i->glue, i->event); - if (r < 0) - return r; - - i->glue->on_finished = pull_job_curl_on_finished; - i->glue->userdata = i; - - *ret = i; - i = NULL; - - return 0; -} - -static void raw_pull_report_progress(RawPull *i, RawProgress p) { - unsigned percent; - - assert(i); - - switch (p) { - - case RAW_DOWNLOADING: { - unsigned remain = 80; - - percent = 0; - - if (i->settings_job) { - percent += i->settings_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->checksum_job) { - percent += i->checksum_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->signature_job) { - percent += i->signature_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->raw_job) - percent += i->raw_job->progress_percent * remain / 100; - break; - } - - case RAW_VERIFYING: - percent = 80; - break; - - case RAW_UNPACKING: - percent = 85; - break; - - case RAW_FINALIZING: - percent = 90; - break; - - case RAW_COPYING: - percent = 95; - break; - - default: - assert_not_reached("Unknown progress state"); - } - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_debug("Combined progress %u%%", percent); -} - -static int raw_pull_maybe_convert_qcow2(RawPull *i) { - _cleanup_close_ int converted_fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - assert(i); - assert(i->raw_job); - - r = qcow2_detect(i->raw_job->disk_fd); - if (r < 0) - return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m"); - if (r == 0) - return 0; - - /* This is a QCOW2 image, let's convert it */ - r = tempfn_random(i->final_path, NULL, &t); - if (r < 0) - return log_oom(); - - converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (converted_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", t); - - r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", t); - - log_info("Unpacking QCOW2 file."); - - r = qcow2_convert(i->raw_job->disk_fd, converted_fd); - if (r < 0) { - unlink(t); - return log_error_errno(r, "Failed to convert qcow2 image: %m"); - } - - (void) unlink(i->temp_path); - free(i->temp_path); - i->temp_path = t; - t = NULL; - - safe_close(i->raw_job->disk_fd); - i->raw_job->disk_fd = converted_fd; - converted_fd = -1; - - return 1; -} - -static int raw_pull_make_local_copy(RawPull *i) { - _cleanup_free_ char *tp = NULL; - _cleanup_close_ int dfd = -1; - const char *p; - int r; - - assert(i); - assert(i->raw_job); - - if (!i->local) - return 0; - - if (!i->final_path) { - r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - } - - if (i->raw_job->etag_exists) { - /* We have downloaded this one previously, reopen it */ - - assert(i->raw_job->disk_fd < 0); - - i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (i->raw_job->disk_fd < 0) - return log_error_errno(errno, "Failed to open vendor image: %m"); - } else { - /* We freshly downloaded the image, use it */ - - assert(i->raw_job->disk_fd >= 0); - - if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1) - return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m"); - } - - p = strjoina(i->image_root, "/", i->local, ".raw"); - - if (i->force_local) - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - - r = tempfn_random(p, NULL, &tp); - if (r < 0) - return log_oom(); - - dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (dfd < 0) - return log_error_errno(errno, "Failed to create writable copy of image: %m"); - - /* Turn off COW writing. This should greatly improve - * performance on COW file systems like btrfs, since it - * reduces fragmentation caused by not allowing in-place - * writes. */ - r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", tp); - - r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true); - if (r < 0) { - unlink(tp); - return log_error_errno(r, "Failed to make writable copy of image: %m"); - } - - (void) copy_times(i->raw_job->disk_fd, dfd); - (void) copy_xattr(i->raw_job->disk_fd, dfd); - - dfd = safe_close(dfd); - - r = rename(tp, p); - if (r < 0) { - r = log_error_errno(errno, "Failed to move writable image into place: %m"); - unlink(tp); - return r; - } - - log_info("Created new local image '%s'.", i->local); - - if (i->settings) { - const char *local_settings; - assert(i->settings_job); - - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } - - local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - - r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0); - if (r == -EEXIST) - log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r == -ENOENT) - log_debug_errno(r, "Skipping creation of settings file, since none was found."); - else if (r < 0) - log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); - else - log_info("Created new settings file %s.", local_settings); - } - - return 0; -} - -static bool raw_pull_is_done(RawPull *i) { - assert(i); - assert(i->raw_job); - - if (!PULL_JOB_IS_COMPLETE(i->raw_job)) - return false; - if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) - return false; - if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) - return false; - if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) - return false; - - return true; -} - -static void raw_pull_job_on_finished(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - if (j == i->settings_job) { - if (j->error != 0) - log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { - if (j == i->checksum_job) - log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); - else - log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); - - r = j->error; - goto finish; - } - - /* This is invoked if either the download completed - * successfully, or the download was skipped because we - * already have the etag. In this case ->etag_exists is - * true. - * - * We only do something when we got all three files */ - - if (!raw_pull_is_done(i)) - return; - - if (i->settings_job) - i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); - - if (!i->raw_job->etag_exists) { - /* This is a new download, verify it, and move it into place */ - assert(i->raw_job->disk_fd >= 0); - - raw_pull_report_progress(i, RAW_VERIFYING); - - r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job); - if (r < 0) - goto finish; - - raw_pull_report_progress(i, RAW_UNPACKING); - - r = raw_pull_maybe_convert_qcow2(i); - if (r < 0) - goto finish; - - raw_pull_report_progress(i, RAW_FINALIZING); - - r = import_make_read_only_fd(i->raw_job->disk_fd); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) { - log_error_errno(r, "Failed to move RAW file into place: %m"); - goto finish; - } - - i->temp_path = mfree(i->temp_path); - - if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { - - assert(i->settings_temp_path); - assert(i->settings_path); - - r = import_make_read_only(i->settings_temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); - if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); - goto finish; - } - - i->settings_temp_path = mfree(i->settings_temp_path); - } - } - - raw_pull_report_progress(i, RAW_COPYING); - - r = raw_pull_make_local_copy(i); - if (r < 0) - goto finish; - - r = 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); -} - -static int raw_pull_job_on_open_disk_raw(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->raw_job == j); - assert(!i->final_path); - assert(!i->temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->temp_path); - - r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL); - if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); - - return 0; -} - -static int raw_pull_job_on_open_disk_settings(PullJob *j) { - RawPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->settings_temp_path, 0700); - - j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); - - return 0; -} - -static void raw_pull_job_on_progress(PullJob *j) { - RawPull *i; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - raw_pull_report_progress(i, RAW_DOWNLOADING); -} - -int raw_pull_start( - RawPull *i, - const char *url, - const char *local, - bool force_local, - ImportVerify verify, - bool settings) { - - int r; - - assert(i); - assert(verify < _IMPORT_VERIFY_MAX); - assert(verify >= 0); - - if (!http_url_is_valid(url)) - return -EINVAL; - - if (local && !machine_name_is_valid(local)) - return -EINVAL; - - if (i->raw_job) - return -EBUSY; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - - i->force_local = force_local; - i->verify = verify; - i->settings = settings; - - /* Queue job for the image itself */ - r = pull_job_new(&i->raw_job, url, i->glue, i); - if (r < 0) - return r; - - i->raw_job->on_finished = raw_pull_job_on_finished; - i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw; - i->raw_job->on_progress = raw_pull_job_on_progress; - i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO; - i->raw_job->grow_machine_directory = i->grow_machine_directory; - - r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags); - if (r < 0) - return r; - - if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i); - if (r < 0) - return r; - - i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; - i->settings_job->on_progress = raw_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; - } - - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); - if (r < 0) - return r; - - r = pull_job_begin(i->raw_job); - if (r < 0) - return r; - - if (i->settings_job) { - r = pull_job_begin(i->settings_job); - if (r < 0) - return r; - } - - if (i->checksum_job) { - i->checksum_job->on_progress = raw_pull_job_on_progress; - - r = pull_job_begin(i->checksum_job); - if (r < 0) - return r; - } - - if (i->signature_job) { - i->signature_job->on_progress = raw_pull_job_on_progress; - - r = pull_job_begin(i->signature_job); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h deleted file mode 100644 index 8f6d16eb3a..0000000000 --- a/src/import/pull-raw.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct RawPull RawPull; - -typedef void (*RawPullFinished)(RawPull *pull, int error, void *userdata); - -int raw_pull_new(RawPull **pull, sd_event *event, const char *image_root, RawPullFinished on_finished, void *userdata); -RawPull* raw_pull_unref(RawPull *pull); - -DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); - -int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c deleted file mode 100644 index 8c61c46f73..0000000000 --- a/src/import/pull-tar.c +++ /dev/null @@ -1,563 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <curl/curl.h> -#include <sys/prctl.h> - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "copy.h" -#include "curl-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hostname-util.h" -#include "import-common.h" -#include "import-util.h" -#include "macro.h" -#include "mkdir.h" -#include "path-util.h" -#include "process-util.h" -#include "pull-common.h" -#include "pull-job.h" -#include "pull-tar.h" -#include "rm-rf.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" -#include "util.h" -#include "web-util.h" - -typedef enum TarProgress { - TAR_DOWNLOADING, - TAR_VERIFYING, - TAR_FINALIZING, - TAR_COPYING, -} TarProgress; - -struct TarPull { - sd_event *event; - CurlGlue *glue; - - char *image_root; - - PullJob *tar_job; - PullJob *settings_job; - PullJob *checksum_job; - PullJob *signature_job; - - TarPullFinished on_finished; - void *userdata; - - char *local; - bool force_local; - bool grow_machine_directory; - bool settings; - - pid_t tar_pid; - - char *final_path; - char *temp_path; - - char *settings_path; - char *settings_temp_path; - - ImportVerify verify; -}; - -TarPull* tar_pull_unref(TarPull *i) { - if (!i) - return NULL; - - if (i->tar_pid > 1) { - (void) kill_and_sigcont(i->tar_pid, SIGKILL); - (void) wait_for_terminate(i->tar_pid, NULL); - } - - pull_job_unref(i->tar_job); - pull_job_unref(i->settings_job); - pull_job_unref(i->checksum_job); - pull_job_unref(i->signature_job); - - curl_glue_unref(i->glue); - sd_event_unref(i->event); - - if (i->temp_path) { - (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); - free(i->temp_path); - } - - if (i->settings_temp_path) { - (void) unlink(i->settings_temp_path); - free(i->settings_temp_path); - } - - free(i->final_path); - free(i->settings_path); - free(i->image_root); - free(i->local); - free(i); - - return NULL; -} - -int tar_pull_new( - TarPull **ret, - sd_event *event, - const char *image_root, - TarPullFinished on_finished, - void *userdata) { - - _cleanup_(tar_pull_unrefp) TarPull *i = NULL; - int r; - - assert(ret); - - i = new0(TarPull, 1); - if (!i) - return -ENOMEM; - - i->on_finished = on_finished; - i->userdata = userdata; - - i->image_root = strdup(image_root ?: "/var/lib/machines"); - if (!i->image_root) - return -ENOMEM; - - i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines"); - - if (event) - i->event = sd_event_ref(event); - else { - r = sd_event_default(&i->event); - if (r < 0) - return r; - } - - r = curl_glue_new(&i->glue, i->event); - if (r < 0) - return r; - - i->glue->on_finished = pull_job_curl_on_finished; - i->glue->userdata = i; - - *ret = i; - i = NULL; - - return 0; -} - -static void tar_pull_report_progress(TarPull *i, TarProgress p) { - unsigned percent; - - assert(i); - - switch (p) { - - case TAR_DOWNLOADING: { - unsigned remain = 85; - - percent = 0; - - if (i->settings_job) { - percent += i->settings_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->checksum_job) { - percent += i->checksum_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->signature_job) { - percent += i->signature_job->progress_percent * 5 / 100; - remain -= 5; - } - - if (i->tar_job) - percent += i->tar_job->progress_percent * remain / 100; - break; - } - - case TAR_VERIFYING: - percent = 85; - break; - - case TAR_FINALIZING: - percent = 90; - break; - - case TAR_COPYING: - percent = 95; - break; - - default: - assert_not_reached("Unknown progress state"); - } - - sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent); - log_debug("Combined progress %u%%", percent); -} - -static int tar_pull_make_local_copy(TarPull *i) { - int r; - - assert(i); - assert(i->tar_job); - - if (!i->local) - return 0; - - if (!i->final_path) { - r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - } - - r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); - if (r < 0) - return r; - - if (i->settings) { - const char *local_settings; - assert(i->settings_job); - - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } - - local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - - r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0); - if (r == -EEXIST) - log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r == -ENOENT) - log_debug_errno(r, "Skipping creation of settings file, since none was found."); - else if (r < 0) - log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); - else - log_info("Created new settings file %s.", local_settings); - } - - return 0; -} - -static bool tar_pull_is_done(TarPull *i) { - assert(i); - assert(i->tar_job); - - if (!PULL_JOB_IS_COMPLETE(i->tar_job)) - return false; - if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) - return false; - if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) - return false; - if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job)) - return false; - - return true; -} - -static void tar_pull_job_on_finished(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - if (j == i->settings_job) { - if (j->error != 0) - log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { - if (j == i->checksum_job) - log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); - else - log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); - - r = j->error; - goto finish; - } - - /* This is invoked if either the download completed - * successfully, or the download was skipped because we - * already have the etag. */ - - if (!tar_pull_is_done(i)) - return; - - i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd); - if (i->settings_job) - i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); - - if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); - i->tar_pid = 0; - if (r < 0) - goto finish; - if (r > 0) { - r = -EIO; - goto finish; - } - } - - if (!i->tar_job->etag_exists) { - /* This is a new download, verify it, and move it into place */ - - tar_pull_report_progress(i, TAR_VERIFYING); - - r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job); - if (r < 0) - goto finish; - - tar_pull_report_progress(i, TAR_FINALIZING); - - r = import_make_read_only(i->temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); - if (r < 0) { - log_error_errno(r, "Failed to rename to final image name: %m"); - goto finish; - } - - i->temp_path = mfree(i->temp_path); - - if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { - - assert(i->settings_temp_path); - assert(i->settings_path); - - /* Also move the settings file into place, if - * it exist. Note that we do so only if we - * also moved the tar file in place, to keep - * things strictly in sync. */ - - r = import_make_read_only(i->settings_temp_path); - if (r < 0) - goto finish; - - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); - if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); - goto finish; - } - - i->settings_temp_path = mfree(i->settings_temp_path); - } - } - - tar_pull_report_progress(i, TAR_COPYING); - - r = tar_pull_make_local_copy(i); - if (r < 0) - goto finish; - - r = 0; - -finish: - if (i->on_finished) - i->on_finished(i, r, i->userdata); - else - sd_event_exit(i->event, r); -} - -static int tar_pull_job_on_open_disk_tar(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->tar_job == j); - assert(!i->final_path); - assert(!i->temp_path); - assert(i->tar_pid <= 0); - - r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->temp_path, 0700); - - r = btrfs_subvol_make(i->temp_path); - if (r == -ENOTTY) { - if (mkdir(i->temp_path, 0755) < 0) - return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path); - } else if (r < 0) - return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path); - else - (void) import_assign_pool_quota_and_warn(i->temp_path); - - j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid); - if (j->disk_fd < 0) - return j->disk_fd; - - return 0; -} - -static int tar_pull_job_on_open_disk_settings(PullJob *j) { - TarPull *i; - int r; - - assert(j); - assert(j->userdata); - - i = j->userdata; - assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); - - mkdir_parents_label(i->settings_temp_path, 0700); - - j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); - - return 0; -} - -static void tar_pull_job_on_progress(PullJob *j) { - TarPull *i; - - assert(j); - assert(j->userdata); - - i = j->userdata; - - tar_pull_report_progress(i, TAR_DOWNLOADING); -} - -int tar_pull_start( - TarPull *i, - const char *url, - const char *local, - bool force_local, - ImportVerify verify, - bool settings) { - - int r; - - assert(i); - assert(verify < _IMPORT_VERIFY_MAX); - assert(verify >= 0); - - if (!http_url_is_valid(url)) - return -EINVAL; - - if (local && !machine_name_is_valid(local)) - return -EINVAL; - - if (i->tar_job) - return -EBUSY; - - r = free_and_strdup(&i->local, local); - if (r < 0) - return r; - - i->force_local = force_local; - i->verify = verify; - i->settings = settings; - - /* Set up download job for TAR file */ - r = pull_job_new(&i->tar_job, url, i->glue, i); - if (r < 0) - return r; - - i->tar_job->on_finished = tar_pull_job_on_finished; - i->tar_job->on_open_disk = tar_pull_job_on_open_disk_tar; - i->tar_job->on_progress = tar_pull_job_on_progress; - i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO; - i->tar_job->grow_machine_directory = i->grow_machine_directory; - - r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags); - if (r < 0) - return r; - - /* Set up download job for the settings file (.nspawn) */ - if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i); - if (r < 0) - return r; - - i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings; - i->settings_job->on_progress = tar_pull_job_on_progress; - i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; - } - - /* Set up download of checksum/signature files */ - r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i); - if (r < 0) - return r; - - r = pull_job_begin(i->tar_job); - if (r < 0) - return r; - - if (i->settings_job) { - r = pull_job_begin(i->settings_job); - if (r < 0) - return r; - } - - if (i->checksum_job) { - i->checksum_job->on_progress = tar_pull_job_on_progress; - - r = pull_job_begin(i->checksum_job); - if (r < 0) - return r; - } - - if (i->signature_job) { - i->signature_job->on_progress = tar_pull_job_on_progress; - - r = pull_job_begin(i->signature_job); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/import/pull-tar.h b/src/import/pull-tar.h deleted file mode 100644 index 7e63e496d8..0000000000 --- a/src/import/pull-tar.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-event.h" - -#include "import-util.h" -#include "macro.h" - -typedef struct TarPull TarPull; - -typedef void (*TarPullFinished)(TarPull *pull, int error, void *userdata); - -int tar_pull_new(TarPull **pull, sd_event *event, const char *image_root, TarPullFinished on_finished, void *userdata); -TarPull* tar_pull_unref(TarPull *pull); - -DEFINE_TRIVIAL_CLEANUP_FUNC(TarPull*, tar_pull_unref); - -int tar_pull_start(TarPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); diff --git a/src/import/pull.c b/src/import/pull.c deleted file mode 100644 index 72604a6a74..0000000000 --- a/src/import/pull.c +++ /dev/null @@ -1,337 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <getopt.h> - -#include "sd-event.h" - -#include "alloc-util.h" -#include "hostname-util.h" -#include "import-util.h" -#include "machine-image.h" -#include "parse-util.h" -#include "pull-raw.h" -#include "pull-tar.h" -#include "signal-util.h" -#include "string-util.h" -#include "verbs.h" -#include "web-util.h" - -static bool arg_force = false; -static const char *arg_image_root = "/var/lib/machines"; -static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; -static bool arg_settings = true; - -static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { - log_notice("Transfer aborted."); - sd_event_exit(sd_event_source_get_event(s), EINTR); - return 0; -} - -static void on_tar_finished(TarPull *pull, int error, void *userdata) { - sd_event *event = userdata; - assert(pull); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int pull_tar(int argc, char *argv[], void *userdata) { - _cleanup_(tar_pull_unrefp) TarPull *pull = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *url, *local; - _cleanup_free_ char *l = NULL, *ll = NULL; - int r; - - url = argv[1]; - if (!http_url_is_valid(url)) { - log_error("URL '%s' is not valid.", url); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(url, &l); - if (r < 0) - return log_error_errno(r, "Failed get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = tar_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); - return -EEXIST; - } - } - - log_info("Pulling '%s', saving as '%s'.", url, local); - } else - log_info("Pulling '%s'.", url); - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate puller: %m"); - - r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); - if (r < 0) - return log_error_errno(r, "Failed to pull image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static void on_raw_finished(RawPull *pull, int error, void *userdata) { - sd_event *event = userdata; - assert(pull); - - if (error == 0) - log_info("Operation completed successfully."); - - sd_event_exit(event, abs(error)); -} - -static int pull_raw(int argc, char *argv[], void *userdata) { - _cleanup_(raw_pull_unrefp) RawPull *pull = NULL; - _cleanup_(sd_event_unrefp) sd_event *event = NULL; - const char *url, *local; - _cleanup_free_ char *l = NULL, *ll = NULL; - int r; - - url = argv[1]; - if (!http_url_is_valid(url)) { - log_error("URL '%s' is not valid.", url); - return -EINVAL; - } - - if (argc >= 3) - local = argv[2]; - else { - r = import_url_last_component(url, &l); - if (r < 0) - return log_error_errno(r, "Failed get final component of URL: %m"); - - local = l; - } - - if (isempty(local) || streq(local, "-")) - local = NULL; - - if (local) { - r = raw_strip_suffixes(local, &ll); - if (r < 0) - return log_oom(); - - local = ll; - - if (!machine_name_is_valid(local)) { - log_error("Local image name '%s' is not valid.", local); - return -EINVAL; - } - - if (!arg_force) { - r = image_find(local, NULL); - if (r < 0) - return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); - else if (r > 0) { - log_error_errno(EEXIST, "Image '%s' already exists.", local); - return -EEXIST; - } - } - - log_info("Pulling '%s', saving as '%s'.", url, local); - } else - log_info("Pulling '%s'.", url); - - r = sd_event_default(&event); - if (r < 0) - return log_error_errno(r, "Failed to allocate event loop: %m"); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); - (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); - - r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event); - if (r < 0) - return log_error_errno(r, "Failed to allocate puller: %m"); - - r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); - if (r < 0) - return log_error_errno(r, "Failed to pull image: %m"); - - r = sd_event_loop(event); - if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); - - log_info("Exiting."); - return -r; -} - -static int help(int argc, char *argv[], void *userdata) { - - printf("%s [OPTIONS...] {COMMAND} ...\n\n" - "Download container or virtual machine images.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --force Force creation of image\n" - " --verify=MODE Verify downloaded image, one of: 'no',\n" - " 'checksum', 'signature'\n" - " --settings=BOOL Download settings file with image\n" - " --image-root=PATH Image root directory\n\n" - "Commands:\n" - " tar URL [NAME] Download a TAR image\n" - " raw URL [NAME] Download a RAW image\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_FORCE, - ARG_IMAGE_ROOT, - ARG_VERIFY, - ARG_SETTINGS, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "force", no_argument, NULL, ARG_FORCE }, - { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, - { "verify", required_argument, NULL, ARG_VERIFY }, - { "settings", required_argument, NULL, ARG_SETTINGS }, - {} - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - return help(0, NULL, NULL); - - case ARG_VERSION: - return version(); - - case ARG_FORCE: - arg_force = true; - break; - - case ARG_IMAGE_ROOT: - arg_image_root = optarg; - break; - - case ARG_VERIFY: - arg_verify = import_verify_from_string(optarg); - if (arg_verify < 0) { - log_error("Invalid verification setting '%s'", optarg); - return -EINVAL; - } - - break; - - case ARG_SETTINGS: - r = parse_boolean(optarg); - if (r < 0) - return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg); - - arg_settings = r; - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - return 1; -} - -static int pull_main(int argc, char *argv[]) { - - static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "tar", 2, 3, 0, pull_tar }, - { "raw", 2, 3, 0, pull_raw }, - {} - }; - - return dispatch_verb(argc, argv, verbs, NULL); -} - -int main(int argc, char *argv[]) { - int r; - - setlocale(LC_ALL, ""); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - (void) ignore_signals(SIGPIPE, -1); - - r = pull_main(argc, argv); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/import/qcow2-util.c b/src/import/qcow2-util.c deleted file mode 100644 index ee2121cc36..0000000000 --- a/src/import/qcow2-util.c +++ /dev/null @@ -1,352 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <zlib.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "qcow2-util.h" -#include "sparse-endian.h" -#include "util.h" - -#define QCOW2_MAGIC 0x514649fb - -#define QCOW2_COPIED (1ULL << 63) -#define QCOW2_COMPRESSED (1ULL << 62) -#define QCOW2_ZERO (1ULL << 0) - -typedef struct _packed_ Header { - be32_t magic; - be32_t version; - - be64_t backing_file_offset; - be32_t backing_file_size; - - be32_t cluster_bits; - be64_t size; - be32_t crypt_method; - - be32_t l1_size; - be64_t l1_table_offset; - - be64_t refcount_table_offset; - be32_t refcount_table_clusters; - - be32_t nb_snapshots; - be64_t snapshots_offset; - - /* The remainder is only present on QCOW3 */ - be64_t incompatible_features; - be64_t compatible_features; - be64_t autoclear_features; - - be32_t refcount_order; - be32_t header_length; -} Header; - -#define HEADER_MAGIC(header) be32toh((header)->magic) -#define HEADER_VERSION(header) be32toh((header)->version) -#define HEADER_CLUSTER_BITS(header) be32toh((header)->cluster_bits) -#define HEADER_CLUSTER_SIZE(header) (1ULL << HEADER_CLUSTER_BITS(header)) -#define HEADER_L2_BITS(header) (HEADER_CLUSTER_BITS(header) - 3) -#define HEADER_SIZE(header) be64toh((header)->size) -#define HEADER_CRYPT_METHOD(header) be32toh((header)->crypt_method) -#define HEADER_L1_SIZE(header) be32toh((header)->l1_size) -#define HEADER_L2_SIZE(header) (HEADER_CLUSTER_SIZE(header)/sizeof(uint64_t)) -#define HEADER_L1_TABLE_OFFSET(header) be64toh((header)->l1_table_offset) - -static uint32_t HEADER_HEADER_LENGTH(const Header *h) { - if (HEADER_VERSION(h) < 3) - return offsetof(Header, incompatible_features); - - return be32toh(h->header_length); -} - -static int copy_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t cluster_size, - void *buffer) { - - ssize_t l; - int r; - - r = btrfs_clone_range(sfd, soffset, dfd, doffset, cluster_size); - if (r >= 0) - return r; - - l = pread(sfd, buffer, cluster_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int decompress_cluster( - int sfd, uint64_t soffset, - int dfd, uint64_t doffset, - uint64_t compressed_size, - uint64_t cluster_size, - void *buffer1, - void *buffer2) { - - _cleanup_free_ void *large_buffer = NULL; - z_stream s = {}; - uint64_t sz; - ssize_t l; - int r; - - if (compressed_size > cluster_size) { - /* The usual cluster buffer doesn't suffice, let's - * allocate a larger one, temporarily */ - - large_buffer = malloc(compressed_size); - if (!large_buffer) - return -ENOMEM; - - buffer1 = large_buffer; - } - - l = pread(sfd, buffer1, compressed_size, soffset); - if (l < 0) - return -errno; - if ((uint64_t) l != compressed_size) - return -EIO; - - s.next_in = buffer1; - s.avail_in = compressed_size; - s.next_out = buffer2; - s.avail_out = cluster_size; - - r = inflateInit2(&s, -12); - if (r != Z_OK) - return -EIO; - - r = inflate(&s, Z_FINISH); - sz = (uint8_t*) s.next_out - (uint8_t*) buffer2; - inflateEnd(&s); - if (r != Z_STREAM_END || sz != cluster_size) - return -EIO; - - l = pwrite(dfd, buffer2, cluster_size, doffset); - if (l < 0) - return -errno; - if ((uint64_t) l != cluster_size) - return -EIO; - - return 0; -} - -static int normalize_offset( - const Header *header, - uint64_t p, - uint64_t *ret, - bool *compressed, - uint64_t *compressed_size) { - - uint64_t q; - - q = be64toh(p); - - if (q & QCOW2_COMPRESSED) { - uint64_t sz, csize_shift, csize_mask; - - if (!compressed) - return -EOPNOTSUPP; - - csize_shift = 64 - 2 - (HEADER_CLUSTER_BITS(header) - 8); - csize_mask = (1ULL << (HEADER_CLUSTER_BITS(header) - 8)) - 1; - sz = (((q >> csize_shift) & csize_mask) + 1) * 512 - (q & 511); - q &= ((1ULL << csize_shift) - 1); - - if (compressed_size) - *compressed_size = sz; - - *compressed = true; - - } else { - if (compressed) { - *compressed = false; - *compressed_size = 0; - } - - if (q & QCOW2_ZERO) { - /* We make no distinction between zero blocks and holes */ - *ret = 0; - return 0; - } - - q &= ~QCOW2_COPIED; - } - - *ret = q; - return q > 0; /* returns positive if not a hole */ -} - -static int verify_header(const Header *header) { - assert(header); - - if (HEADER_MAGIC(header) != QCOW2_MAGIC) - return -EBADMSG; - - if (HEADER_VERSION(header) != 2 && - HEADER_VERSION(header) != 3) - return -EOPNOTSUPP; - - if (HEADER_CRYPT_METHOD(header) != 0) - return -EOPNOTSUPP; - - if (HEADER_CLUSTER_BITS(header) < 9) /* 512K */ - return -EBADMSG; - - if (HEADER_CLUSTER_BITS(header) > 21) /* 2MB */ - return -EBADMSG; - - if (HEADER_SIZE(header) % HEADER_CLUSTER_SIZE(header) != 0) - return -EBADMSG; - - if (HEADER_L1_SIZE(header) > 32*1024*1024) /* 32MB */ - return -EBADMSG; - - if (HEADER_VERSION(header) == 3) { - - if (header->incompatible_features != 0) - return -EOPNOTSUPP; - - if (HEADER_HEADER_LENGTH(header) < sizeof(Header)) - return -EBADMSG; - } - - return 0; -} - -int qcow2_convert(int qcow2_fd, int raw_fd) { - _cleanup_free_ void *buffer1 = NULL, *buffer2 = NULL; - _cleanup_free_ be64_t *l1_table = NULL, *l2_table = NULL; - uint64_t sz, i; - Header header; - ssize_t l; - int r; - - l = pread(qcow2_fd, &header, sizeof(header), 0); - if (l < 0) - return -errno; - if (l != sizeof(header)) - return -EIO; - - r = verify_header(&header); - if (r < 0) - return r; - - l1_table = new(be64_t, HEADER_L1_SIZE(&header)); - if (!l1_table) - return -ENOMEM; - - l2_table = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!l2_table) - return -ENOMEM; - - buffer1 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer1) - return -ENOMEM; - - buffer2 = malloc(HEADER_CLUSTER_SIZE(&header)); - if (!buffer2) - return -ENOMEM; - - /* Empty the file if it exists, we rely on zero bits */ - if (ftruncate(raw_fd, 0) < 0) - return -errno; - - if (ftruncate(raw_fd, HEADER_SIZE(&header)) < 0) - return -errno; - - sz = sizeof(uint64_t) * HEADER_L1_SIZE(&header); - l = pread(qcow2_fd, l1_table, sz, HEADER_L1_TABLE_OFFSET(&header)); - if (l < 0) - return -errno; - if ((uint64_t) l != sz) - return -EIO; - - for (i = 0; i < HEADER_L1_SIZE(&header); i ++) { - uint64_t l2_begin, j; - - r = normalize_offset(&header, l1_table[i], &l2_begin, NULL, NULL); - if (r < 0) - return r; - if (r == 0) - continue; - - l = pread(qcow2_fd, l2_table, HEADER_CLUSTER_SIZE(&header), l2_begin); - if (l < 0) - return -errno; - if ((uint64_t) l != HEADER_CLUSTER_SIZE(&header)) - return -EIO; - - for (j = 0; j < HEADER_L2_SIZE(&header); j++) { - uint64_t data_begin, p, compressed_size; - bool compressed; - - p = ((i << HEADER_L2_BITS(&header)) + j) << HEADER_CLUSTER_BITS(&header); - - r = normalize_offset(&header, l2_table[j], &data_begin, &compressed, &compressed_size); - if (r < 0) - return r; - if (r == 0) - continue; - - if (compressed) - r = decompress_cluster( - qcow2_fd, data_begin, - raw_fd, p, - compressed_size, HEADER_CLUSTER_SIZE(&header), - buffer1, buffer2); - else - r = copy_cluster( - qcow2_fd, data_begin, - raw_fd, p, - HEADER_CLUSTER_SIZE(&header), buffer1); - if (r < 0) - return r; - } - } - - return 0; -} - -int qcow2_detect(int fd) { - be32_t id; - ssize_t l; - - l = pread(fd, &id, sizeof(id), 0); - if (l < 0) - return -errno; - if (l != sizeof(id)) - return -EIO; - - return htobe32(QCOW2_MAGIC) == id; -} diff --git a/src/import/qcow2-util.h b/src/import/qcow2-util.h deleted file mode 100644 index 6dddac8cdf..0000000000 --- a/src/import/qcow2-util.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -int qcow2_detect(int fd); -int qcow2_convert(int qcow2_fd, int raw_fd); diff --git a/src/import/test-qcow2.c b/src/import/test-qcow2.c deleted file mode 100644 index b820253d71..0000000000 --- a/src/import/test-qcow2.c +++ /dev/null @@ -1,53 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "fd-util.h" -#include "log.h" -#include "qcow2-util.h" -#include "util.h" - -int main(int argc, char *argv[]) { - _cleanup_close_ int sfd = -1, dfd = -1; - int r; - - if (argc != 3) { - log_error("Needs two arguments."); - return EXIT_FAILURE; - } - - sfd = open(argv[1], O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (sfd < 0) { - log_error_errno(errno, "Can't open source file: %m"); - return EXIT_FAILURE; - } - - dfd = open(argv[2], O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666); - if (dfd < 0) { - log_error_errno(errno, "Can't open destination file: %m"); - return EXIT_FAILURE; - } - - r = qcow2_convert(sfd, dfd); - if (r < 0) { - log_error_errno(r, "Failed to unpack: %m"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} |