[systemd-commits] 3 commits - Makefile.am TODO man/machinectl.xml man/systemd-nspawn.xml src/import src/nspawn src/shared tmpfiles.d/var.conf

Lennart Poettering lennart at kemper.freedesktop.org
Wed Jan 14 16:49:02 PST 2015


 Makefile.am                |    4 
 TODO                       |    8 
 man/machinectl.xml         |   55 +++
 man/systemd-nspawn.xml     |   15 
 src/import/import-gpt.c    |  719 ---------------------------------------------
 src/import/import-gpt.h    |   37 --
 src/import/import-raw.c    |  719 +++++++++++++++++++++++++++++++++++++++++++++
 src/import/import-raw.h    |   37 ++
 src/import/import.c        |   24 -
 src/nspawn/nspawn.c        |    2 
 src/shared/machine-image.c |   32 +-
 src/shared/machine-image.h |    2 
 tmpfiles.d/var.conf        |    2 
 13 files changed, 848 insertions(+), 808 deletions(-)

New commits:
commit 6dcfca59fc564449e44543c5e9593eaeaf83356a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 01:44:03 2015 +0100

    update TODO

diff --git a/TODO b/TODO
index c56579a..ca542c8 100644
--- a/TODO
+++ b/TODO
@@ -31,24 +31,16 @@ External:
 
 Release 219 preparations:
 
-* support mbr raw disk images in systemd-nspawn, so that we can boot
-  fedora cloud images unmodified (change suffix for search for files
-  .gpt → .raw).
-
 * dkr import hash verification
 
 * rework journald sigbus stuff to use mutex
 
 * create importd daemon, move "systemd-import" tool into machinectl
 
-* change default container location from /var/lib/container to /var/lib/machines
-
 * merge input_id and evdev_id into generic input_id
 
 Features:
 
-* rename "gpt" image type to "raw"
-
 * import: support import from local files, and export to local files
 
 * import: add "pull-tar" support, for downloading/verifying tarballs

commit 5f129649b97bdff2bffefcd9c773157843ede6f6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 01:40:02 2015 +0100

    nspawn,machined: change default container image location from /var/lib/container to /var/lib/machines
    
    Given that this is also the place to store raw disk images which are
    very much bootable with qemu/kvm it sounds like a misnomer to call the
    directory "container". Hence, let's change this sooner rather than
    later, and use the generic name, in particular since we otherwise try to
    use the generic "machine" preferably over the more specific "container"
    or "vm".

diff --git a/man/machinectl.xml b/man/machinectl.xml
index 91bdb5e..1953186 100644
--- a/man/machinectl.xml
+++ b/man/machinectl.xml
@@ -291,10 +291,10 @@
                                 name. <command>systemd-nspawn</command>
                                 looks for a container image by the
                                 specified name in
-                                <filename>/var/lib/container</filename>
-                                and runs it. Use
-                                <command>list-images</command> (see
-                                below), for listing available
+                                <filename>/var/lib/machines/</filename>
+                                (and other search paths, see below) and runs
+                                it. Use <command>list-images</command>
+                                (see below), for listing available
                                 container images to start.</para>
 
                                 <para>Note that
@@ -488,7 +488,7 @@
                                 images. This enumerates all raw disk
                                 images and container directories and
                                 subvolumes in
-                                <filename>/var/lib/container/</filename>. Use
+                                <filename>/var/lib/machines/</filename> (and other search paths, see below). Use
                                 <command>start</command> (see above)
                                 to run a container off one of the
                                 listed images. Note that by default
@@ -596,6 +596,51 @@
         </refsect1>
 
         <refsect1>
+                <title>Files and Directories</title>
+
+                <para>Machine images are preferably stored in
+                <filename>/var/lib/machines/</filename>, but are also
+                searched for in
+                <filename>/usr/local/lib/machines/</filename> and
+                <filename>/usr/lib/machines/</filename>. For
+                compatibility reasons the directory
+                <filename>/var/lib/container/</filename> is searched,
+                too. Note that images stored below
+                <filename>/usr</filename> are always considered
+                read-only. It is possible to symlink machines images
+                from other directories into
+                <filename>/var/lib/machines/</filename> to make them
+                available for control with
+                <command>machinectl</command>.</para>
+
+                <para>Disk images are understood in three formats:</para>
+
+                <itemizedlist>
+                        <listitem><para>A simple directory tree,
+                        containing the files and directories of the
+                        container to boot.</para></listitem>
+
+                        <listitem><para>A subvolume (on btrfs file
+                        systems), which are similar to the simple
+                        directories, described above. However, they
+                        have additional benefits, such as efficient
+                        cloning and quota reporting.</para></listitem>
+
+                        <listitem><para>"Raw" disk images, i.e. binary
+                        images of disks with a GPT or MBR partition
+                        table. Images of this type are regular
+                        files with the suffix
+                        <literal>.raw</literal>.</para></listitem>
+                </itemizedlist>
+
+                <para>See
+                <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+                for more information on image formats, in particular
+                it's <option>--directory=</option> and
+                <option>--image=</option> options.</para>
+        </refsect1>
+
+        <refsect1>
                 <title>Exit status</title>
 
                 <para>On success, 0 is returned, a non-zero failure
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index e67aeec..0c6fb00 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -171,7 +171,7 @@
                                 <option>--directory=</option>, nor
                                 <option>--image=</option> is specified
                                 the directory is determined as
-                                <filename>/var/lib/container/</filename>
+                                <filename>/var/lib/machines/</filename>
                                 suffixed by the machine name as
                                 specified with
                                 <option>--machine=</option>. If
@@ -247,10 +247,13 @@
                                 the root directory for the container
                                 from. Takes a path to a regular file
                                 or to a block device node. The file or
-                                block device must contain a GUID
-                                Partition Table with a root partition
-                                which is mounted as the root directory
-                                of the container. Optionally, it may
+                                block device must contain either an
+                                MBR partition table with a single
+                                partition of type 0x83 that is marked
+                                bootable, or a GUID partition table
+                                with a root partition which is mounted
+                                as the root directory of the
+                                container. Optionally, GPT images may
                                 contain a home and/or a server data
                                 partition which are mounted to the
                                 appropriate places in the
@@ -842,7 +845,7 @@
                 <example>
                         <title>Enable Arch Linux container on boot</title>
 
-                        <programlisting># mv ~/arch-tree /var/lib/container/arch
+                        <programlisting># mv ~/arch-tree /var/lib/machines/arch
 # systemctl enable systemd-nspawn at arch.service
 # systemctl start systemd-nspawn at arch.service</programlisting>
 
diff --git a/src/import/import.c b/src/import/import.c
index 669d7c1..b4d859d 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -29,7 +29,7 @@
 #include "import-dkr.h"
 
 static bool arg_force = false;
-static const char *arg_image_root = "/var/lib/container";
+static const char *arg_image_root = "/var/lib/machines";
 
 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
 
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index a2f49e6..5112d24 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -110,7 +110,7 @@ static int image_make(
         assert(filename);
 
         /* We explicitly *do* follow symlinks here, since we want to
-         * allow symlinking trees into /var/lib/container/, and treat
+         * allow symlinking trees into /var/lib/machines/, and treat
          * them normally. */
 
         if (fstatat(dfd, filename, &st, 0) < 0)
@@ -486,13 +486,13 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
 
         case IMAGE_SUBVOLUME:
         case IMAGE_DIRECTORY:
-                new_path = strappenda("/var/lib/container/", new_name);
+                new_path = strappenda("/var/lib/machines/", new_name);
 
                 r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
                 break;
 
         case IMAGE_RAW:
-                new_path = strappenda("/var/lib/container/", new_name, ".raw");
+                new_path = strappenda("/var/lib/machines/", new_name, ".raw");
 
                 r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
                 break;
diff --git a/tmpfiles.d/var.conf b/tmpfiles.d/var.conf
index d0c759e..9b76444 100644
--- a/tmpfiles.d/var.conf
+++ b/tmpfiles.d/var.conf
@@ -18,6 +18,6 @@ f /var/log/btmp 0600 root utmp -
 d /var/cache 0755 - - -
 
 d /var/lib 0755 - - -
-v /var/lib/container 0700 - - -
+v /var/lib/machines 0700 - - -
 
 d /var/spool 0755 - - -

commit aceac2f0b652dff701e5815c51c2e372e8fee84d
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Jan 15 01:03:33 2015 +0100

    import: rename "gpt" disk image type to "raw"
    
    After all, nspawn can now dissect MBR partition levels, too, hence
    ".gpt" appears a misnomer. Moreover, the the .raw suffix for these files
    is already pretty popular (the Fedora disk images use it for example),
    hence sounds like an OK scheme to adopt.

diff --git a/Makefile.am b/Makefile.am
index 00218b7..520c4e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5218,8 +5218,8 @@ bin_PROGRAMS += \
 
 systemd_import_SOURCES = \
 	src/import/import.c \
-	src/import/import-gpt.c \
-	src/import/import-gpt.h \
+	src/import/import-raw.c \
+	src/import/import-raw.h \
 	src/import/import-dkr.c \
 	src/import/import-dkr.h \
 	src/import/curl-util.c \
diff --git a/src/import/import-gpt.c b/src/import/import-gpt.c
deleted file mode 100644
index d304a39..0000000
--- a/src/import/import-gpt.c
+++ /dev/null
@@ -1,719 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  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 <sys/xattr.h>
-#include <linux/fs.h>
-#include <curl/curl.h>
-
-#include "hashmap.h"
-#include "utf8.h"
-#include "curl-util.h"
-#include "import-gpt.h"
-#include "strv.h"
-#include "copy.h"
-
-typedef struct GptImportFile GptImportFile;
-
-struct GptImportFile {
-        GptImport *import;
-
-        char *url;
-        char *local;
-
-        CURL *curl;
-        struct curl_slist *request_header;
-
-        char *temp_path;
-        char *final_path;
-        char *etag;
-        char **old_etags;
-
-        uint64_t content_length;
-        uint64_t written;
-
-        usec_t mtime;
-
-        bool force_local;
-        bool done;
-
-        int disk_fd;
-};
-
-struct GptImport {
-        sd_event *event;
-        CurlGlue *glue;
-
-        char *image_root;
-        Hashmap *files;
-
-        gpt_import_on_finished on_finished;
-        void *userdata;
-
-        bool finished;
-};
-
-#define FILENAME_ESCAPE "/.#\"\'"
-
-static GptImportFile *gpt_import_file_unref(GptImportFile *f) {
-        if (!f)
-                return NULL;
-
-        if (f->import)
-                curl_glue_remove_and_free(f->import->glue, f->curl);
-        curl_slist_free_all(f->request_header);
-
-        safe_close(f->disk_fd);
-
-        free(f->final_path);
-
-        if (f->temp_path) {
-                unlink(f->temp_path);
-                free(f->temp_path);
-        }
-
-        free(f->url);
-        free(f->local);
-        free(f->etag);
-        strv_free(f->old_etags);
-        free(f);
-
-        return NULL;
-}
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(GptImportFile*, gpt_import_file_unref);
-
-static void gpt_import_finish(GptImport *import, int error) {
-        assert(import);
-
-        if (import->finished)
-                return;
-
-        import->finished = true;
-
-        if (import->on_finished)
-                import->on_finished(import, error, import->userdata);
-        else
-                sd_event_exit(import->event, error);
-}
-
-static int gpt_import_file_make_final_path(GptImportFile *f) {
-        _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
-
-        assert(f);
-
-        if (f->final_path)
-                return 0;
-
-        escaped_url = xescape(f->url, FILENAME_ESCAPE);
-        if (!escaped_url)
-                return -ENOMEM;
-
-        if (f->etag) {
-                escaped_etag = xescape(f->etag, FILENAME_ESCAPE);
-                if (!escaped_etag)
-                        return -ENOMEM;
-
-                f->final_path = strjoin(f->import->image_root, "/.gpt-", escaped_url, ".", escaped_etag, ".gpt", NULL);
-        } else
-                f->final_path = strjoin(f->import->image_root, "/.gpt-", escaped_url, ".gpt", NULL);
-        if (!f->final_path)
-                return -ENOMEM;
-
-        return 0;
-}
-
-static void gpt_import_file_success(GptImportFile *f) {
-        int r;
-
-        assert(f);
-
-        f->done = true;
-
-        if (f->local) {
-                _cleanup_free_ char *tp = NULL;
-                _cleanup_close_ int dfd = -1;
-                const char *p;
-
-                if (f->disk_fd >= 0) {
-                        if (lseek(f->disk_fd, SEEK_SET, 0) == (off_t) -1) {
-                                r = log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
-                                goto finish;
-                        }
-                } else {
-                        r = gpt_import_file_make_final_path(f);
-                        if (r < 0) {
-                                log_oom();
-                                goto finish;
-                        }
-
-                        f->disk_fd = open(f->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
-                        if (f->disk_fd < 0) {
-                                r = log_error_errno(errno, "Failed to open vendor image: %m");
-                                goto finish;
-                        }
-                }
-
-                p = strappenda(f->import->image_root, "/", f->local, ".gpt");
-                if (f->force_local)
-                        (void) rm_rf_dangerous(p, false, true, false);
-
-                r = tempfn_random(p, &tp);
-                if (r < 0) {
-                        log_oom();
-                        goto finish;
-                }
-
-                dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
-                if (dfd < 0) {
-                        r = log_error_errno(errno, "Failed to create writable copy of image: %m");
-                        goto finish;
-                }
-
-                /* 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, true, FS_NOCOW_FL);
-                if (r < 0)
-                        log_warning_errno(errno, "Failed to set file attributes on %s: %m", f->temp_path);
-
-                r = copy_bytes(f->disk_fd, dfd, (off_t) -1, true);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to make writable copy of image: %m");
-                        unlink(tp);
-                        goto finish;
-                }
-
-                (void) copy_times(f->disk_fd, dfd);
-                (void) copy_xattr(f->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);
-                        goto finish;
-                }
-
-                log_info("Created new local image %s.", p);
-        }
-
-        f->disk_fd = safe_close(f->disk_fd);
-        r = 0;
-
-finish:
-        gpt_import_finish(f->import, r);
-}
-
-static void gpt_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
-        GptImportFile *f = NULL;
-        struct stat st;
-        CURLcode code;
-        long status;
-        int r;
-
-        if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &f) != CURLE_OK)
-                return;
-
-        if (!f || f->done)
-                return;
-
-        f->done = true;
-
-        if (result != CURLE_OK) {
-                log_error("Transfer failed: %s", curl_easy_strerror(result));
-                r = -EIO;
-                goto fail;
-        }
-
-        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 fail;
-        } else if (status == 304) {
-                log_info("Image already downloaded. Skipping download.");
-                gpt_import_file_success(f);
-                return;
-        } else if (status >= 300) {
-                log_error("HTTP request to %s failed with code %li.", f->url, status);
-                r = -EIO;
-                goto fail;
-        } else if (status < 200) {
-                log_error("HTTP request to %s finished with unexpected code %li.", f->url, status);
-                r = -EIO;
-                goto fail;
-        }
-
-        if (f->disk_fd < 0) {
-                log_error("No data received.");
-                r = -EIO;
-                goto fail;
-        }
-
-        if (f->content_length != (uint64_t) -1 &&
-            f->content_length != f->written) {
-                log_error("Download truncated.");
-                r = -EIO;
-                goto fail;
-        }
-
-        if (f->etag)
-                (void) fsetxattr(f->disk_fd, "user.source_etag", f->etag, strlen(f->etag), 0);
-        if (f->url)
-                (void) fsetxattr(f->disk_fd, "user.source_url", f->url, strlen(f->url), 0);
-
-        if (f->mtime != 0) {
-                struct timespec ut[2];
-
-                timespec_store(&ut[0], f->mtime);
-                ut[1] = ut[0];
-                (void) futimens(f->disk_fd, ut);
-
-                fd_setcrtime(f->disk_fd, f->mtime);
-        }
-
-        if (fstat(f->disk_fd, &st) < 0) {
-                r = log_error_errno(errno, "Failed to stat file: %m");
-                goto fail;
-        }
-
-        /* Mark read-only */
-        (void) fchmod(f->disk_fd, st.st_mode & 07444);
-
-        assert(f->temp_path);
-        assert(f->final_path);
-
-        r = rename(f->temp_path, f->final_path);
-        if (r < 0) {
-                r = log_error_errno(errno, "Failed to move GPT file into place: %m");
-                goto fail;
-        }
-
-        free(f->temp_path);
-        f->temp_path = NULL;
-
-        log_info("Completed writing vendor image %s.", f->final_path);
-
-        gpt_import_file_success(f);
-        return;
-
-fail:
-        gpt_import_finish(f->import, r);
-}
-
-static int gpt_import_file_open_disk_for_write(GptImportFile *f) {
-        int r;
-
-        assert(f);
-
-        if (f->disk_fd >= 0)
-                return 0;
-
-        r = gpt_import_file_make_final_path(f);
-        if (r < 0)
-                return log_oom();
-
-        if (!f->temp_path) {
-                r = tempfn_random(f->final_path, &f->temp_path);
-                if (r < 0)
-                        return log_oom();
-        }
-
-        f->disk_fd = open(f->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
-        if (f->disk_fd < 0)
-                return log_error_errno(errno, "Failed to create %s: %m", f->temp_path);
-
-        return 0;
-}
-
-static size_t gpt_import_file_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
-        GptImportFile *f = userdata;
-        size_t sz = size * nmemb;
-        ssize_t n;
-        int r;
-
-        assert(contents);
-        assert(f);
-
-        if (f->done) {
-                r = -ESTALE;
-                goto fail;
-        }
-
-        r = gpt_import_file_open_disk_for_write(f);
-        if (r < 0)
-                goto fail;
-
-        if (f->written + sz < f->written) {
-                log_error("File too large, overflow");
-                r = -EOVERFLOW;
-                goto fail;
-        }
-
-        if (f->content_length != (uint64_t) -1 &&
-            f->written + sz > f->content_length) {
-                log_error("Content length incorrect.");
-                r = -EFBIG;
-                goto fail;
-        }
-
-        n = write(f->disk_fd, contents, sz);
-        if (n < 0) {
-                log_error_errno(errno, "Failed to write file: %m");
-                goto fail;
-        }
-
-        if ((size_t) n < sz) {
-                log_error("Short write");
-                r = -EIO;
-                goto fail;
-        }
-
-        f->written += sz;
-
-        return sz;
-
-fail:
-        gpt_import_finish(f->import, r);
-        return 0;
-}
-
-static size_t gpt_import_file_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
-        GptImportFile *f = userdata;
-        size_t sz = size * nmemb;
-        _cleanup_free_ char *length = NULL, *last_modified = NULL;
-        char *etag;
-        int r;
-
-        assert(contents);
-        assert(f);
-
-        if (f->done) {
-                r = -ESTALE;
-                goto fail;
-        }
-
-        r = curl_header_strdup(contents, sz, "ETag:", &etag);
-        if (r < 0) {
-                log_oom();
-                goto fail;
-        }
-        if (r > 0) {
-                free(f->etag);
-                f->etag = etag;
-
-                if (strv_contains(f->old_etags, f->etag)) {
-                        log_info("Image already downloaded. Skipping download.");
-                        gpt_import_file_success(f);
-                        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, &f->content_length);
-                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, &f->mtime);
-                return sz;
-        }
-
-        return sz;
-
-fail:
-        gpt_import_finish(f->import, r);
-        return 0;
-}
-
-static bool etag_is_valid(const char *etag) {
-
-        if (!endswith(etag, "\""))
-                return false;
-
-        if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
-                return false;
-
-        return true;
-}
-
-static int gpt_import_file_find_old_etags(GptImportFile *f) {
-        _cleanup_free_ char *escaped_url = NULL;
-        _cleanup_closedir_ DIR *d = NULL;
-        struct dirent *de;
-        int r;
-
-        escaped_url = xescape(f->url, FILENAME_ESCAPE);
-        if (!escaped_url)
-                return -ENOMEM;
-
-        d = opendir(f->import->image_root);
-        if (!d) {
-                if (errno == ENOENT)
-                        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_REG)
-                        continue;
-
-                a = startswith(de->d_name, ".gpt-");
-                if (!a)
-                        continue;
-
-                a = startswith(a, escaped_url);
-                if (!a)
-                        continue;
-
-                a = startswith(a, ".");
-                if (!a)
-                        continue;
-
-                b = endswith(de->d_name, ".gpt");
-                if (!b)
-                        continue;
-
-                if (a >= b)
-                        continue;
-
-                u = cunescape_length(a, b - a);
-                if (!u)
-                        return -ENOMEM;
-
-                if (!etag_is_valid(u)) {
-                        free(u);
-                        continue;
-                }
-
-                r = strv_consume(&f->old_etags, u);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static int gpt_import_file_begin(GptImportFile *f) {
-        int r;
-
-        assert(f);
-        assert(!f->curl);
-
-        log_info("Getting %s.", f->url);
-
-        r = gpt_import_file_find_old_etags(f);
-        if (r < 0)
-                return r;
-
-        r = curl_glue_make(&f->curl, f->url, f);
-        if (r < 0)
-                return r;
-
-        if (!strv_isempty(f->old_etags)) {
-                _cleanup_free_ char *cc = NULL, *hdr = NULL;
-
-                cc = strv_join(f->old_etags, ", ");
-                if (!cc)
-                        return -ENOMEM;
-
-                hdr = strappend("If-None-Match: ", cc);
-                if (!hdr)
-                        return -ENOMEM;
-
-                f->request_header = curl_slist_new(hdr, NULL);
-                if (!f->request_header)
-                        return -ENOMEM;
-
-                if (curl_easy_setopt(f->curl, CURLOPT_HTTPHEADER, f->request_header) != CURLE_OK)
-                        return -EIO;
-        }
-
-        if (curl_easy_setopt(f->curl, CURLOPT_WRITEFUNCTION, gpt_import_file_write_callback) != CURLE_OK)
-                return -EIO;
-
-        if (curl_easy_setopt(f->curl, CURLOPT_WRITEDATA, f) != CURLE_OK)
-                return -EIO;
-
-        if (curl_easy_setopt(f->curl, CURLOPT_HEADERFUNCTION, gpt_import_file_header_callback) != CURLE_OK)
-                return -EIO;
-
-        if (curl_easy_setopt(f->curl, CURLOPT_HEADERDATA, f) != CURLE_OK)
-                return -EIO;
-
-        r = curl_glue_add(f->import->glue, f->curl);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-int gpt_import_new(GptImport **import, sd_event *event, const char *image_root, gpt_import_on_finished on_finished, void *userdata) {
-        _cleanup_(gpt_import_unrefp) GptImport *i = NULL;
-        int r;
-
-        assert(import);
-        assert(image_root);
-
-        i = new0(GptImport, 1);
-        if (!i)
-                return -ENOMEM;
-
-        i->on_finished = on_finished;
-        i->userdata = userdata;
-
-        i->image_root = strdup(image_root);
-        if (!i->image_root)
-                return -ENOMEM;
-
-        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 = gpt_import_curl_on_finished;
-        i->glue->userdata = i;
-
-        *import = i;
-        i = NULL;
-
-        return 0;
-}
-
-GptImport* gpt_import_unref(GptImport *import) {
-        GptImportFile *f;
-
-        if (!import)
-                return NULL;
-
-        while ((f = hashmap_steal_first(import->files)))
-                gpt_import_file_unref(f);
-        hashmap_free(import->files);
-
-        curl_glue_unref(import->glue);
-        sd_event_unref(import->event);
-
-        free(import->image_root);
-        free(import);
-
-        return NULL;
-}
-
-int gpt_import_cancel(GptImport *import, const char *url) {
-        GptImportFile *f;
-
-        assert(import);
-        assert(url);
-
-        f = hashmap_remove(import->files, url);
-        if (!f)
-                return 0;
-
-        gpt_import_file_unref(f);
-        return 1;
-}
-
-int gpt_import_pull(GptImport *import, const char *url, const char *local, bool force_local) {
-        _cleanup_(gpt_import_file_unrefp) GptImportFile *f = NULL;
-        int r;
-
-        assert(import);
-        assert(gpt_url_is_valid(url));
-        assert(!local || machine_name_is_valid(local));
-
-        if (hashmap_get(import->files, url))
-                return -EEXIST;
-
-        r = hashmap_ensure_allocated(&import->files, &string_hash_ops);
-        if (r < 0)
-                return r;
-
-        f = new0(GptImportFile, 1);
-        if (!f)
-                return -ENOMEM;
-
-        f->import = import;
-        f->disk_fd = -1;
-        f->content_length = (uint64_t) -1;
-
-        f->url = strdup(url);
-        if (!f->url)
-                return -ENOMEM;
-
-        if (local) {
-                f->local = strdup(local);
-                if (!f->local)
-                        return -ENOMEM;
-
-                f->force_local = force_local;
-        }
-
-        r = hashmap_put(import->files, f->url, f);
-        if (r < 0)
-                return r;
-
-        r = gpt_import_file_begin(f);
-        if (r < 0) {
-                gpt_import_cancel(import, f->url);
-                f = NULL;
-                return r;
-        }
-
-        f = NULL;
-        return 0;
-}
-
-bool gpt_url_is_valid(const char *url) {
-        if (isempty(url))
-                return false;
-
-        if (!startswith(url, "http://") &&
-            !startswith(url, "https://"))
-                return false;
-
-        return ascii_is_valid(url);
-}
diff --git a/src/import/import-gpt.h b/src/import/import-gpt.h
deleted file mode 100644
index e9003db..0000000
--- a/src/import/import-gpt.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  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 "util.h"
-
-typedef struct GptImport GptImport;
-
-typedef void (*gpt_import_on_finished)(GptImport *import, int error, void *userdata);
-
-int gpt_import_new(GptImport **import, sd_event *event, const char *image_root, gpt_import_on_finished on_finished, void *userdata);
-GptImport* gpt_import_unref(GptImport *import);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(GptImport*, gpt_import_unref);
-
-int gpt_import_pull(GptImport *import, const char *url, const char *local, bool force_local);
-int gpt_import_cancel(GptImport *import, const char *name);
-
-bool gpt_url_is_valid(const char *url);
diff --git a/src/import/import-raw.c b/src/import/import-raw.c
new file mode 100644
index 0000000..ac9e6eb
--- /dev/null
+++ b/src/import/import-raw.c
@@ -0,0 +1,719 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 <sys/xattr.h>
+#include <linux/fs.h>
+#include <curl/curl.h>
+
+#include "hashmap.h"
+#include "utf8.h"
+#include "curl-util.h"
+#include "import-raw.h"
+#include "strv.h"
+#include "copy.h"
+
+typedef struct RawImportFile RawImportFile;
+
+struct RawImportFile {
+        RawImport *import;
+
+        char *url;
+        char *local;
+
+        CURL *curl;
+        struct curl_slist *request_header;
+
+        char *temp_path;
+        char *final_path;
+        char *etag;
+        char **old_etags;
+
+        uint64_t content_length;
+        uint64_t written;
+
+        usec_t mtime;
+
+        bool force_local;
+        bool done;
+
+        int disk_fd;
+};
+
+struct RawImport {
+        sd_event *event;
+        CurlGlue *glue;
+
+        char *image_root;
+        Hashmap *files;
+
+        raw_import_on_finished on_finished;
+        void *userdata;
+
+        bool finished;
+};
+
+#define FILENAME_ESCAPE "/.#\"\'"
+
+static RawImportFile *raw_import_file_unref(RawImportFile *f) {
+        if (!f)
+                return NULL;
+
+        if (f->import)
+                curl_glue_remove_and_free(f->import->glue, f->curl);
+        curl_slist_free_all(f->request_header);
+
+        safe_close(f->disk_fd);
+
+        free(f->final_path);
+
+        if (f->temp_path) {
+                unlink(f->temp_path);
+                free(f->temp_path);
+        }
+
+        free(f->url);
+        free(f->local);
+        free(f->etag);
+        strv_free(f->old_etags);
+        free(f);
+
+        return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RawImportFile*, raw_import_file_unref);
+
+static void raw_import_finish(RawImport *import, int error) {
+        assert(import);
+
+        if (import->finished)
+                return;
+
+        import->finished = true;
+
+        if (import->on_finished)
+                import->on_finished(import, error, import->userdata);
+        else
+                sd_event_exit(import->event, error);
+}
+
+static int raw_import_file_make_final_path(RawImportFile *f) {
+        _cleanup_free_ char *escaped_url = NULL, *escaped_etag = NULL;
+
+        assert(f);
+
+        if (f->final_path)
+                return 0;
+
+        escaped_url = xescape(f->url, FILENAME_ESCAPE);
+        if (!escaped_url)
+                return -ENOMEM;
+
+        if (f->etag) {
+                escaped_etag = xescape(f->etag, FILENAME_ESCAPE);
+                if (!escaped_etag)
+                        return -ENOMEM;
+
+                f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".", escaped_etag, ".raw", NULL);
+        } else
+                f->final_path = strjoin(f->import->image_root, "/.raw-", escaped_url, ".raw", NULL);
+        if (!f->final_path)
+                return -ENOMEM;
+
+        return 0;
+}
+
+static void raw_import_file_success(RawImportFile *f) {
+        int r;
+
+        assert(f);
+
+        f->done = true;
+
+        if (f->local) {
+                _cleanup_free_ char *tp = NULL;
+                _cleanup_close_ int dfd = -1;
+                const char *p;
+
+                if (f->disk_fd >= 0) {
+                        if (lseek(f->disk_fd, SEEK_SET, 0) == (off_t) -1) {
+                                r = log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
+                                goto finish;
+                        }
+                } else {
+                        r = raw_import_file_make_final_path(f);
+                        if (r < 0) {
+                                log_oom();
+                                goto finish;
+                        }
+
+                        f->disk_fd = open(f->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
+                        if (f->disk_fd < 0) {
+                                r = log_error_errno(errno, "Failed to open vendor image: %m");
+                                goto finish;
+                        }
+                }
+
+                p = strappenda(f->import->image_root, "/", f->local, ".raw");
+                if (f->force_local)
+                        (void) rm_rf_dangerous(p, false, true, false);
+
+                r = tempfn_random(p, &tp);
+                if (r < 0) {
+                        log_oom();
+                        goto finish;
+                }
+
+                dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
+                if (dfd < 0) {
+                        r = log_error_errno(errno, "Failed to create writable copy of image: %m");
+                        goto finish;
+                }
+
+                /* 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, true, FS_NOCOW_FL);
+                if (r < 0)
+                        log_warning_errno(errno, "Failed to set file attributes on %s: %m", f->temp_path);
+
+                r = copy_bytes(f->disk_fd, dfd, (off_t) -1, true);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to make writable copy of image: %m");
+                        unlink(tp);
+                        goto finish;
+                }
+
+                (void) copy_times(f->disk_fd, dfd);
+                (void) copy_xattr(f->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);
+                        goto finish;
+                }
+
+                log_info("Created new local image %s.", p);
+        }
+
+        f->disk_fd = safe_close(f->disk_fd);
+        r = 0;
+
+finish:
+        raw_import_finish(f->import, r);
+}
+
+static void raw_import_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) {
+        RawImportFile *f = NULL;
+        struct stat st;
+        CURLcode code;
+        long status;
+        int r;
+
+        if (curl_easy_getinfo(curl, CURLINFO_PRIVATE, &f) != CURLE_OK)
+                return;
+
+        if (!f || f->done)
+                return;
+
+        f->done = true;
+
+        if (result != CURLE_OK) {
+                log_error("Transfer failed: %s", curl_easy_strerror(result));
+                r = -EIO;
+                goto fail;
+        }
+
+        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 fail;
+        } else if (status == 304) {
+                log_info("Image already downloaded. Skipping download.");
+                raw_import_file_success(f);
+                return;
+        } else if (status >= 300) {
+                log_error("HTTP request to %s failed with code %li.", f->url, status);
+                r = -EIO;
+                goto fail;
+        } else if (status < 200) {
+                log_error("HTTP request to %s finished with unexpected code %li.", f->url, status);
+                r = -EIO;
+                goto fail;
+        }
+
+        if (f->disk_fd < 0) {
+                log_error("No data received.");
+                r = -EIO;
+                goto fail;
+        }
+
+        if (f->content_length != (uint64_t) -1 &&
+            f->content_length != f->written) {
+                log_error("Download truncated.");
+                r = -EIO;
+                goto fail;
+        }
+
+        if (f->etag)
+                (void) fsetxattr(f->disk_fd, "user.source_etag", f->etag, strlen(f->etag), 0);
+        if (f->url)
+                (void) fsetxattr(f->disk_fd, "user.source_url", f->url, strlen(f->url), 0);
+
+        if (f->mtime != 0) {
+                struct timespec ut[2];
+
+                timespec_store(&ut[0], f->mtime);
+                ut[1] = ut[0];
+                (void) futimens(f->disk_fd, ut);
+
+                fd_setcrtime(f->disk_fd, f->mtime);
+        }
+
+        if (fstat(f->disk_fd, &st) < 0) {
+                r = log_error_errno(errno, "Failed to stat file: %m");
+                goto fail;
+        }
+
+        /* Mark read-only */
+        (void) fchmod(f->disk_fd, st.st_mode & 07444);
+
+        assert(f->temp_path);
+        assert(f->final_path);
+
+        r = rename(f->temp_path, f->final_path);
+        if (r < 0) {
+                r = log_error_errno(errno, "Failed to move RAW file into place: %m");
+                goto fail;
+        }
+
+        free(f->temp_path);
+        f->temp_path = NULL;
+
+        log_info("Completed writing vendor image %s.", f->final_path);
+
+        raw_import_file_success(f);
+        return;
+
+fail:
+        raw_import_finish(f->import, r);
+}
+
+static int raw_import_file_open_disk_for_write(RawImportFile *f) {
+        int r;
+
+        assert(f);
+
+        if (f->disk_fd >= 0)
+                return 0;
+
+        r = raw_import_file_make_final_path(f);
+        if (r < 0)
+                return log_oom();
+
+        if (!f->temp_path) {
+                r = tempfn_random(f->final_path, &f->temp_path);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        f->disk_fd = open(f->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0644);
+        if (f->disk_fd < 0)
+                return log_error_errno(errno, "Failed to create %s: %m", f->temp_path);
+
+        return 0;
+}
+
+static size_t raw_import_file_write_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
+        RawImportFile *f = userdata;
+        size_t sz = size * nmemb;
+        ssize_t n;
+        int r;
+
+        assert(contents);
+        assert(f);
+
+        if (f->done) {
+                r = -ESTALE;
+                goto fail;
+        }
+
+        r = raw_import_file_open_disk_for_write(f);
+        if (r < 0)
+                goto fail;
+
+        if (f->written + sz < f->written) {
+                log_error("File too large, overflow");
+                r = -EOVERFLOW;
+                goto fail;
+        }
+
+        if (f->content_length != (uint64_t) -1 &&
+            f->written + sz > f->content_length) {
+                log_error("Content length incorrect.");
+                r = -EFBIG;
+                goto fail;
+        }
+
+        n = write(f->disk_fd, contents, sz);
+        if (n < 0) {
+                log_error_errno(errno, "Failed to write file: %m");
+                goto fail;
+        }
+
+        if ((size_t) n < sz) {
+                log_error("Short write");
+                r = -EIO;
+                goto fail;
+        }
+
+        f->written += sz;
+
+        return sz;
+
+fail:
+        raw_import_finish(f->import, r);
+        return 0;
+}
+
+static size_t raw_import_file_header_callback(void *contents, size_t size, size_t nmemb, void *userdata) {
+        RawImportFile *f = userdata;
+        size_t sz = size * nmemb;
+        _cleanup_free_ char *length = NULL, *last_modified = NULL;
+        char *etag;
+        int r;
+
+        assert(contents);
+        assert(f);
+
+        if (f->done) {
+                r = -ESTALE;
+                goto fail;
+        }
+
+        r = curl_header_strdup(contents, sz, "ETag:", &etag);
+        if (r < 0) {
+                log_oom();
+                goto fail;
+        }
+        if (r > 0) {
+                free(f->etag);
+                f->etag = etag;
+
+                if (strv_contains(f->old_etags, f->etag)) {
+                        log_info("Image already downloaded. Skipping download.");
+                        raw_import_file_success(f);
+                        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, &f->content_length);
+                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, &f->mtime);
+                return sz;
+        }
+
+        return sz;
+
+fail:
+        raw_import_finish(f->import, r);
+        return 0;
+}
+
+static bool etag_is_valid(const char *etag) {
+
+        if (!endswith(etag, "\""))
+                return false;
+
+        if (!startswith(etag, "\"") && !startswith(etag, "W/\""))
+                return false;
+
+        return true;
+}
+
+static int raw_import_file_find_old_etags(RawImportFile *f) {
+        _cleanup_free_ char *escaped_url = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        struct dirent *de;
+        int r;
+
+        escaped_url = xescape(f->url, FILENAME_ESCAPE);
+        if (!escaped_url)
+                return -ENOMEM;
+
+        d = opendir(f->import->image_root);
+        if (!d) {
+                if (errno == ENOENT)
+                        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_REG)
+                        continue;
+
+                a = startswith(de->d_name, ".raw-");
+                if (!a)
+                        continue;
+
+                a = startswith(a, escaped_url);
+                if (!a)
+                        continue;
+
+                a = startswith(a, ".");
+                if (!a)
+                        continue;
+
+                b = endswith(de->d_name, ".raw");
+                if (!b)
+                        continue;
+
+                if (a >= b)
+                        continue;
+
+                u = cunescape_length(a, b - a);
+                if (!u)
+                        return -ENOMEM;
+
+                if (!etag_is_valid(u)) {
+                        free(u);
+                        continue;
+                }
+
+                r = strv_consume(&f->old_etags, u);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int raw_import_file_begin(RawImportFile *f) {
+        int r;
+
+        assert(f);
+        assert(!f->curl);
+
+        log_info("Getting %s.", f->url);
+
+        r = raw_import_file_find_old_etags(f);
+        if (r < 0)
+                return r;
+
+        r = curl_glue_make(&f->curl, f->url, f);
+        if (r < 0)
+                return r;
+
+        if (!strv_isempty(f->old_etags)) {
+                _cleanup_free_ char *cc = NULL, *hdr = NULL;
+
+                cc = strv_join(f->old_etags, ", ");
+                if (!cc)
+                        return -ENOMEM;
+
+                hdr = strappend("If-None-Match: ", cc);
+                if (!hdr)
+                        return -ENOMEM;
+
+                f->request_header = curl_slist_new(hdr, NULL);
+                if (!f->request_header)
+                        return -ENOMEM;
+
+                if (curl_easy_setopt(f->curl, CURLOPT_HTTPHEADER, f->request_header) != CURLE_OK)
+                        return -EIO;
+        }
+
+        if (curl_easy_setopt(f->curl, CURLOPT_WRITEFUNCTION, raw_import_file_write_callback) != CURLE_OK)
+                return -EIO;
+
+        if (curl_easy_setopt(f->curl, CURLOPT_WRITEDATA, f) != CURLE_OK)
+                return -EIO;
+
+        if (curl_easy_setopt(f->curl, CURLOPT_HEADERFUNCTION, raw_import_file_header_callback) != CURLE_OK)
+                return -EIO;
+
+        if (curl_easy_setopt(f->curl, CURLOPT_HEADERDATA, f) != CURLE_OK)
+                return -EIO;
+
+        r = curl_glue_add(f->import->glue, f->curl);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int raw_import_new(RawImport **import, sd_event *event, const char *image_root, raw_import_on_finished on_finished, void *userdata) {
+        _cleanup_(raw_import_unrefp) RawImport *i = NULL;
+        int r;
+
+        assert(import);
+        assert(image_root);
+
+        i = new0(RawImport, 1);
+        if (!i)
+                return -ENOMEM;
+
+        i->on_finished = on_finished;
+        i->userdata = userdata;
+
+        i->image_root = strdup(image_root);
+        if (!i->image_root)
+                return -ENOMEM;
+
+        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 = raw_import_curl_on_finished;
+        i->glue->userdata = i;
+
+        *import = i;
+        i = NULL;
+
+        return 0;
+}
+
+RawImport* raw_import_unref(RawImport *import) {
+        RawImportFile *f;
+
+        if (!import)
+                return NULL;
+
+        while ((f = hashmap_steal_first(import->files)))
+                raw_import_file_unref(f);
+        hashmap_free(import->files);
+
+        curl_glue_unref(import->glue);
+        sd_event_unref(import->event);
+
+        free(import->image_root);
+        free(import);
+
+        return NULL;
+}
+
+int raw_import_cancel(RawImport *import, const char *url) {
+        RawImportFile *f;
+
+        assert(import);
+        assert(url);
+
+        f = hashmap_remove(import->files, url);
+        if (!f)
+                return 0;
+
+        raw_import_file_unref(f);
+        return 1;
+}
+
+int raw_import_pull(RawImport *import, const char *url, const char *local, bool force_local) {
+        _cleanup_(raw_import_file_unrefp) RawImportFile *f = NULL;
+        int r;
+
+        assert(import);
+        assert(raw_url_is_valid(url));
+        assert(!local || machine_name_is_valid(local));
+
+        if (hashmap_get(import->files, url))
+                return -EEXIST;
+
+        r = hashmap_ensure_allocated(&import->files, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        f = new0(RawImportFile, 1);
+        if (!f)
+                return -ENOMEM;
+
+        f->import = import;
+        f->disk_fd = -1;
+        f->content_length = (uint64_t) -1;
+
+        f->url = strdup(url);
+        if (!f->url)
+                return -ENOMEM;
+
+        if (local) {
+                f->local = strdup(local);
+                if (!f->local)
+                        return -ENOMEM;
+
+                f->force_local = force_local;
+        }
+
+        r = hashmap_put(import->files, f->url, f);
+        if (r < 0)
+                return r;
+
+        r = raw_import_file_begin(f);
+        if (r < 0) {
+                raw_import_cancel(import, f->url);
+                f = NULL;
+                return r;
+        }
+
+        f = NULL;
+        return 0;
+}
+
+bool raw_url_is_valid(const char *url) {
+        if (isempty(url))
+                return false;
+
+        if (!startswith(url, "http://") &&
+            !startswith(url, "https://"))
+                return false;
+
+        return ascii_is_valid(url);
+}
diff --git a/src/import/import-raw.h b/src/import/import-raw.h
new file mode 100644
index 0000000..5a38cdc
--- /dev/null
+++ b/src/import/import-raw.h
@@ -0,0 +1,37 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 "util.h"
+
+typedef struct RawImport RawImport;
+
+typedef void (*raw_import_on_finished)(RawImport *import, int error, void *userdata);
+
+int raw_import_new(RawImport **import, sd_event *event, const char *image_root, raw_import_on_finished on_finished, void *userdata);
+RawImport* raw_import_unref(RawImport *import);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(RawImport*, raw_import_unref);
+
+int raw_import_pull(RawImport *import, const char *url, const char *local, bool force_local);
+int raw_import_cancel(RawImport *import, const char *name);
+
+bool raw_url_is_valid(const char *url);
diff --git a/src/import/import.c b/src/import/import.c
index c28ff8f..669d7c1 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -25,7 +25,7 @@
 #include "event-util.h"
 #include "verbs.h"
 #include "build.h"
-#include "import-gpt.h"
+#include "import-raw.h"
 #include "import-dkr.h"
 
 static bool arg_force = false;
@@ -33,7 +33,7 @@ static const char *arg_image_root = "/var/lib/container";
 
 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
 
-static void on_gpt_finished(GptImport *import, int error, void *userdata) {
+static void on_raw_finished(RawImport *import, int error, void *userdata) {
         sd_event *event = userdata;
         assert(import);
 
@@ -45,14 +45,14 @@ static void on_gpt_finished(GptImport *import, int error, void *userdata) {
         sd_event_exit(event, error);
 }
 
-static int pull_gpt(int argc, char *argv[], void *userdata) {
-        _cleanup_(gpt_import_unrefp) GptImport *import = NULL;
+static int pull_raw(int argc, char *argv[], void *userdata) {
+        _cleanup_(raw_import_unrefp) RawImport *import = NULL;
         _cleanup_event_unref_ sd_event *event = NULL;
         const char *url, *local, *suffix;
         int r;
 
         url = argv[1];
-        if (!gpt_url_is_valid(url)) {
+        if (!raw_url_is_valid(url)) {
                 log_error("URL '%s' is not valid.", url);
                 return -EINVAL;
         }
@@ -79,7 +79,7 @@ static int pull_gpt(int argc, char *argv[], void *userdata) {
         if (local) {
                 const char *p;
 
-                suffix = endswith(local, ".gpt");
+                suffix = endswith(local, ".raw");
                 if (suffix)
                         local = strndupa(local, suffix - local);
 
@@ -88,7 +88,7 @@ static int pull_gpt(int argc, char *argv[], void *userdata) {
                         return -EINVAL;
                 }
 
-                p = strappenda(arg_image_root, "/", local, ".gpt");
+                p = strappenda(arg_image_root, "/", local, ".raw");
                 if (laccess(p, F_OK) >= 0) {
                         if (!arg_force) {
                                 log_info("Image '%s' already exists.", local);
@@ -109,11 +109,11 @@ static int pull_gpt(int argc, char *argv[], void *userdata) {
         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
 
-        r = gpt_import_new(&import, event, arg_image_root, on_gpt_finished, event);
+        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 = gpt_import_pull(import, url, local, arg_force);
+        r = raw_import_pull(import, url, local, arg_force);
         if (r < 0)
                 return log_error_errno(r, "Failed to pull image: %m");
 
@@ -238,7 +238,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
                "Commands:\n"
                "  pull-dkr REMOTE [NAME]      Download a DKR image\n"
-               "  pull-gpt URL [NAME]         Download a GPT image\n",
+               "  pull-raw URL [NAME]         Download a RAW image\n",
                program_invocation_short_name);
 
         return 0;
@@ -311,7 +311,7 @@ static int import_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "help",     VERB_ANY, VERB_ANY, 0, help     },
                 { "pull-dkr", 2,        3,        0, pull_dkr },
-                { "pull-gpt", 2,        3,        0, pull_gpt },
+                { "pull-raw", 2,        3,        0, pull_raw },
                 {}
         };
 
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 2783c97..2387c15 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -3321,7 +3321,7 @@ static int determine_names(void) {
                                 return -ENOENT;
                         }
 
-                        if (i->type == IMAGE_GPT)
+                        if (i->type == IMAGE_RAW)
                                 r = set_sanitized_path(&arg_image, i->path);
                         else
                                 r = set_sanitized_path(&arg_directory, i->path);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 752d658..a2f49e6 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -194,10 +194,10 @@ static int image_make(
 
                 return 1;
 
-        } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
+        } else if (S_ISREG(st.st_mode) && endswith(filename, ".raw")) {
                 usec_t crtime = 0;
 
-                /* It's a GPT block device */
+                /* It's a RAW disk image */
 
                 if (!ret)
                         return 1;
@@ -207,7 +207,7 @@ static int image_make(
                 if (!pretty)
                         pretty = strndupa(filename, strlen(filename) - 4);
 
-                r = image_new(IMAGE_GPT,
+                r = image_new(IMAGE_RAW,
                               pretty,
                               path,
                               filename,
@@ -250,13 +250,13 @@ int image_find(const char *name, Image **ret) {
 
                 r = image_make(NULL, dirfd(d), path, name, ret);
                 if (r == 0 || r == -ENOENT) {
-                        _cleanup_free_ char *gpt = NULL;
+                        _cleanup_free_ char *raw = NULL;
 
-                        gpt = strappend(name, ".gpt");
-                        if (!gpt)
+                        raw = strappend(name, ".raw");
+                        if (!raw)
                                 return -ENOMEM;
 
-                        r = image_make(NULL, dirfd(d), path, gpt, ret);
+                        r = image_make(NULL, dirfd(d), path, raw, ret);
                         if (r == 0 || r == -ENOENT)
                                 continue;
                 }
@@ -366,7 +366,7 @@ int image_remove(Image *i) {
 
                 /* fall through */
 
-        case IMAGE_GPT:
+        case IMAGE_RAW:
                 return rm_rf_dangerous(i->path, false, true, false);
 
         default:
@@ -422,10 +422,10 @@ int image_rename(Image *i, const char *new_name) {
                 new_path = file_in_same_dir(i->path, new_name);
                 break;
 
-        case IMAGE_GPT: {
+        case IMAGE_RAW: {
                 const char *fn;
 
-                fn = strappenda(new_name, ".gpt");
+                fn = strappenda(new_name, ".raw");
                 new_path = file_in_same_dir(i->path, fn);
                 break;
         }
@@ -491,8 +491,8 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
                 r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
                 break;
 
-        case IMAGE_GPT:
-                new_path = strappenda("/var/lib/container/", new_name, ".gpt");
+        case IMAGE_RAW:
+                new_path = strappenda("/var/lib/container/", new_name, ".raw");
 
                 r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL);
                 break;
@@ -545,7 +545,7 @@ int image_read_only(Image *i, bool b) {
 
                 break;
 
-        case IMAGE_GPT: {
+        case IMAGE_RAW: {
                 struct stat st;
 
                 if (stat(i->path, &st) < 0)
@@ -654,7 +654,7 @@ bool image_name_is_valid(const char *s) {
 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
         [IMAGE_DIRECTORY] = "directory",
         [IMAGE_SUBVOLUME] = "subvolume",
-        [IMAGE_GPT] = "gpt",
+        [IMAGE_RAW] = "raw",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 4f41b4f..c15b034 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -27,7 +27,7 @@
 typedef enum ImageType {
         IMAGE_DIRECTORY,
         IMAGE_SUBVOLUME,
-        IMAGE_GPT,
+        IMAGE_RAW,
         _IMAGE_TYPE_MAX,
         _IMAGE_TYPE_INVALID = -1
 } ImageType;



More information about the systemd-commits mailing list