[systemd-commits] 10 commits - Makefile.am TODO configure.ac man/tmpfiles.d.xml src/core src/hostname src/locale src/login src/machine src/nspawn src/resolve src/shared src/sysusers src/test src/timedate src/tmpfiles src/udev src/update-done tmpfiles.d/tmp.conf tmpfiles.d/var.conf

Lennart Poettering lennart at kemper.freedesktop.org
Sat Dec 27 18:09:00 PST 2014


 Makefile.am                   |    7 
 TODO                          |   18 -
 configure.ac                  |    2 
 man/tmpfiles.d.xml            |   11 
 src/core/main.c               |    7 
 src/core/namespace.c          |    3 
 src/core/socket.c             |    1 
 src/hostname/hostnamed.c      |    1 
 src/locale/localed.c          |    1 
 src/login/logind-dbus.c       |    3 
 src/machine/image-dbus.c      |  245 +++++++++-----------
 src/machine/image-dbus.h      |   36 +++
 src/machine/image.c           |  325 ---------------------------
 src/machine/image.h           |   63 -----
 src/machine/machine-dbus.c    |    1 
 src/machine/machine-dbus.h    |   41 +++
 src/machine/machine.c         |    3 
 src/machine/machine.h         |   16 -
 src/machine/machinectl.c      |  190 ++++++++++++++--
 src/machine/machined-dbus.c   |  109 ++++++++-
 src/machine/machined.c        |    9 
 src/machine/machined.h        |    6 
 src/nspawn/nspawn.c           |   29 ++
 src/resolve/resolved.c        |    1 
 src/shared/btrfs-ctree.h      |   23 +
 src/shared/btrfs-util.c       |  200 ++++++++++++++--
 src/shared/btrfs-util.h       |    9 
 src/shared/copy.c             |   46 +++
 src/shared/copy.h             |    1 
 src/shared/fileio-label.c     |    5 
 src/shared/label.c            |    4 
 src/shared/label.h            |    4 
 src/shared/machine-image.c    |  497 ++++++++++++++++++++++++++++++++++++++++++
 src/shared/machine-image.h    |   65 +++++
 src/shared/missing.h          |   30 ++
 src/shared/socket-label.c     |    3 
 src/shared/util.c             |   17 -
 src/sysusers/sysusers.c       |    1 
 src/test/test-btrfs.c         |   13 +
 src/test/test-udev.c          |    1 
 src/timedate/timedated.c      |    1 
 src/tmpfiles/tmpfiles.c       |   28 +-
 src/udev/udev-node.c          |    1 
 src/udev/udevadm.c            |    1 
 src/udev/udevd.c              |    7 
 src/update-done/update-done.c |    1 
 tmpfiles.d/tmp.conf           |    4 
 tmpfiles.d/var.conf           |    2 
 48 files changed, 1437 insertions(+), 655 deletions(-)

New commits:
commit 1ddb263d21099ae42195c2bc382bdf72a7f24f82
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 28 02:44:37 2014 +0100

    machined: don't look for images on each property get, but cache the image object inbetween

diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index cae7b22..5ac689c 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -22,317 +22,24 @@
 #include "bus-label.h"
 #include "bus-common-errors.h"
 #include "strv.h"
+#include "bus-util.h"
 #include "machine-image.h"
 #include "image-dbus.h"
 
-static int image_find_by_bus_path(const char *path, Image **ret) {
-        _cleanup_free_ char *e = NULL;
-        const char *p;
-
-        assert(path);
-
-        p = startswith(path, "/org/freedesktop/machine1/image/");
-        if (!p)
-                return 0;
-
-        e = bus_label_unescape(p);
-        if (!e)
-                return -ENOMEM;
-
-        return image_find(e, ret);
-}
-
-static int image_find_by_bus_path_with_error(const char *path, Image **ret, sd_bus_error *error) {
-        int r;
-
-        assert(path);
-
-        r = image_find_by_bus_path(path, ret);
-        if (r == 0)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "Image doesn't exist.");
-
-        return r;
-}
-
-static int property_get_name(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "s", image->name);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_path(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "s", image->path);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_type(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "s", image_type_to_string(image->type));
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_read_only(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "b", image->read_only);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_crtime(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "t", image->crtime);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_mtime(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, image_type, ImageType);
 
-        r = sd_bus_message_append(reply, "t", image->mtime);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_size(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "t", image->size);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-
-static int property_get_limit(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "t", image->limit);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_size_exclusive(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "t", image->size_exclusive);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int property_get_limit_exclusive(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_(image_unrefp) Image *image = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = image_find_by_bus_path_with_error(path, &image, error);
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_append(reply, "t", image->limit_exclusive);
-        if (r < 0)
-                return r;
-
-        return 1;
-}
-
-static int method_remove(
+int bus_image_method_remove(
                 sd_bus *bus,
                 sd_bus_message *message,
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_(image_unrefp) Image *image = NULL;
+        Image *image = userdata;
         int r;
 
         assert(bus);
         assert(message);
-
-        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
-        if (r < 0)
-                return r;
+        assert(image);
 
         r = image_remove(image);
         if (r < 0)
@@ -341,22 +48,19 @@ static int method_remove(
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_rename(
+int bus_image_method_rename(
                 sd_bus *bus,
                 sd_bus_message *message,
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_(image_unrefp) Image *image = NULL;
+        Image *image = userdata;
         const char *new_name;
         int r;
 
         assert(bus);
         assert(message);
-
-        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
-        if (r < 0)
-                return r;
+        assert(image);
 
         r = sd_bus_message_read(message, "s", &new_name);
         if (r < 0)
@@ -372,22 +76,19 @@ static int method_rename(
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_clone(
+int bus_image_method_clone(
                 sd_bus *bus,
                 sd_bus_message *message,
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_(image_unrefp) Image *image = NULL;
+        Image *image = userdata;
         const char *new_name;
         int r, read_only;
 
         assert(bus);
         assert(message);
-
-        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
-        if (r < 0)
-                return r;
+        assert(image);
 
         r = sd_bus_message_read(message, "sb", &new_name, &read_only);
         if (r < 0)
@@ -403,22 +104,18 @@ static int method_clone(
         return sd_bus_reply_method_return(message, NULL);
 }
 
-static int method_mark_read_only(
+int bus_image_method_mark_read_only(
                 sd_bus *bus,
                 sd_bus_message *message,
                 void *userdata,
                 sd_bus_error *error) {
 
-        _cleanup_(image_unrefp) Image *image = NULL;
+        Image *image = userdata;
         int r, read_only;
 
         assert(bus);
         assert(message);
 
-        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
-        if (r < 0)
-                return r;
-
         r = sd_bus_message_read(message, "b", &read_only);
         if (r < 0)
                 return r;
@@ -432,24 +129,41 @@ static int method_mark_read_only(
 
 const sd_bus_vtable image_vtable[] = {
         SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Name",                  "s", property_get_name,            0, 0),
-        SD_BUS_PROPERTY("Path",                  "s", property_get_path,            0, 0),
-        SD_BUS_PROPERTY("Type",                  "s", property_get_type,            0, 0),
-        SD_BUS_PROPERTY("ReadOnly",              "b", property_get_read_only,       0, 0),
-        SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,          0, 0),
-        SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,           0, 0),
-        SD_BUS_PROPERTY("Size",                  "t", property_get_size,            0, 0),
-        SD_BUS_PROPERTY("Limit",                 "t", property_get_limit,           0, 0),
-        SD_BUS_PROPERTY("SizeExclusive",         "t", property_get_size_exclusive,  0, 0),
-        SD_BUS_PROPERTY("LimitExclusive",        "t", property_get_limit_exclusive, 0, 0),
-        SD_BUS_METHOD("Remove", NULL, NULL, method_remove, 0),
-        SD_BUS_METHOD("Rename", "s", NULL, method_rename, 0),
-        SD_BUS_METHOD("Clone", "sb", NULL, method_clone, 0),
-        SD_BUS_METHOD("MarkeReadOnly", "b", NULL, method_mark_read_only, 0),
+        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
+        SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
+        SD_BUS_PROPERTY("Type", "s", property_get_type,  offsetof(Image, type), 0),
+        SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
+        SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
+        SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
+        SD_BUS_PROPERTY("Size", "t", NULL, offsetof(Image, size), 0),
+        SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
+        SD_BUS_PROPERTY("SizeExclusive", "t", NULL, offsetof(Image, size_exclusive), 0),
+        SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
+        SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, 0),
+        SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, 0),
+        SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, 0),
+        SD_BUS_METHOD("MarkeReadOnly", "b", NULL, bus_image_method_mark_read_only, 0),
         SD_BUS_VTABLE_END
 };
 
+static int image_flush_cache(sd_event_source *s, void *userdata) {
+        Manager *m = userdata;
+        Image *i;
+
+        assert(s);
+        assert(m);
+
+        while ((i = hashmap_steal_first(m->image_cache)))
+                image_unref(i);
+
+        return 0;
+}
+
 int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+        _cleanup_free_ char *e = NULL;
+        Manager *m = userdata;
+        Image *image = NULL;
+        const char *p;
         int r;
 
         assert(bus);
@@ -457,11 +171,49 @@ int image_object_find(sd_bus *bus, const char *path, const char *interface, void
         assert(interface);
         assert(found);
 
-        r = image_find_by_bus_path(path, NULL);
+        p = startswith(path, "/org/freedesktop/machine1/image/");
+        if (!p)
+                return 0;
+
+        e = bus_label_unescape(p);
+        if (!e)
+                return -ENOMEM;
+
+        image = hashmap_get(m->image_cache, e);
+        if (image) {
+                *found = image;
+                return 1;
+        }
+
+        r = hashmap_ensure_allocated(&m->image_cache, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        if (!m->image_cache_defer_event) {
+                r = sd_event_add_defer(m->event, &m->image_cache_defer_event, image_flush_cache, m);
+                if (r < 0)
+                        return r;
+
+                r = sd_event_source_set_priority(m->image_cache_defer_event, SD_EVENT_PRIORITY_IDLE);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_event_source_set_enabled(m->image_cache_defer_event, SD_EVENT_ONESHOT);
+        if (r < 0)
+                return r;
+
+        r = image_find(e, &image);
         if (r <= 0)
                 return r;
 
-        *found = NULL;
+        r = hashmap_put(m->image_cache, image->name, image);
+        if (r < 0) {
+                image_unref(image);
+                return r;
+        }
+
+        *found = image;
         return 1;
 }
 
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
index d176ff7..1b4364c 100644
--- a/src/machine/image-dbus.h
+++ b/src/machine/image-dbus.h
@@ -29,3 +29,8 @@ char *image_bus_path(const char *name);
 
 int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
 int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+
+int bus_image_method_remove(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_rename(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_clone(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_image_method_mark_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 8bb0146..94dd128 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -582,29 +582,23 @@ static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userd
         if (r == 0)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
 
-        r = image_remove(i);
-        if (r < 0)
-                return r;
-
-        return sd_bus_reply_method_return(message, NULL);
+        return bus_image_method_remove(bus, message, i, error);
 }
 
 static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(image_unrefp) Image* i = NULL;
-        const char *old_name, *new_name;
+        const char *old_name;
         int r;
 
         assert(bus);
         assert(message);
 
-        r = sd_bus_message_read(message, "ss", &old_name, &new_name);
+        r = sd_bus_message_read(message, "s", &old_name);
         if (r < 0)
                 return r;
 
         if (!image_name_is_valid(old_name))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
-        if (!image_name_is_valid(new_name))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
 
         r = image_find(old_name, &i);
         if (r < 0)
@@ -612,27 +606,21 @@ static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userd
         if (r == 0)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
 
-        r = image_rename(i, new_name);
-        if (r < 0)
-                return r;
-
-        return sd_bus_reply_method_return(message, NULL);
+        return bus_image_method_rename(bus, message, i, error);
 }
 
 static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(image_unrefp) Image *i = NULL;
-        const char *old_name, *new_name;
-        int read_only, r;
+        const char *old_name;
+        int r;
 
         assert(bus);
-        r = sd_bus_message_read(message, "ssb", &old_name, &new_name, &read_only);
+        r = sd_bus_message_read(message, "s", &old_name);
         if (r < 0)
                 return r;
 
         if (!image_name_is_valid(old_name))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
-        if (!image_name_is_valid(new_name))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
 
         r = image_find(old_name, &i);
         if (r < 0)
@@ -640,20 +628,16 @@ static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userda
         if (r == 0)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
 
-        r = image_clone(i, new_name, read_only);
-        if (r < 0)
-                return r;
-
-        return sd_bus_reply_method_return(message, NULL);
+        return bus_image_method_clone(bus, message, i, error);
 }
 
 static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(image_unrefp) Image *i = NULL;
         const char *name;
-        int read_only, r;
+        int r;
 
         assert(bus);
-        r = sd_bus_message_read(message, "sb", &name, &read_only);
+        r = sd_bus_message_read(message, "s", &name);
         if (r < 0)
                 return r;
 
@@ -666,11 +650,7 @@ static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, voi
         if (r == 0)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
 
-        r = image_read_only(i, read_only);
-        if (r < 0)
-                return r;
-
-        return sd_bus_reply_method_return(message, NULL);
+        return bus_image_method_mark_read_only(bus, message, i, error);
 }
 
 const sd_bus_vtable manager_vtable[] = {
diff --git a/src/machine/machined.c b/src/machine/machined.c
index c5c20ab..6877c2b 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -34,6 +34,7 @@
 #include "bus-util.h"
 #include "bus-error.h"
 #include "label.h"
+#include "machine-image.h"
 #include "machined.h"
 
 Manager *manager_new(void) {
@@ -66,6 +67,7 @@ Manager *manager_new(void) {
 
 void manager_free(Manager *m) {
         Machine *machine;
+        Image *i;
 
         assert(m);
 
@@ -76,6 +78,13 @@ void manager_free(Manager *m) {
         hashmap_free(m->machine_units);
         hashmap_free(m->machine_leaders);
 
+        while ((i = hashmap_steal_first(m->image_cache)))
+                image_unref(i);
+
+        hashmap_free(m->image_cache);
+
+        sd_event_source_unref(m->image_cache_defer_event);
+
         bus_verify_polkit_async_registry_free(m->polkit_registry);
 
         sd_bus_unref(m->bus);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 695ba29..df0cb82 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -46,6 +46,9 @@ struct Manager {
 
         Hashmap *polkit_registry;
 
+        Hashmap *image_cache;
+        sd_event_source *image_cache_defer_event;
+
         LIST_HEAD(Machine, machine_gc_queue);
 };
 

commit 3b9c7d955da357a45c0bcd03397160153539bc57
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 17:45:30 2014 +0100

    update TODO

diff --git a/TODO b/TODO
index ea9933d..799bcae 100644
--- a/TODO
+++ b/TODO
@@ -33,8 +33,6 @@ Features:
 
 * change default container location from /var/lib/container to /var/lib/machines
 
-* import pull-gpt: create writable snapshot of downloaded image, by the right name
-
 * nspawn: when start a container "foobar" look for its configuration in a file "foobar.nspawn" in /etc/systemd/nspawn/ as well as next to the actualy directory or image to boot
 
 * Port various tools to make use of verbs.[ch], where applicable
@@ -43,34 +41,18 @@ Features:
 
 * "machinectl start/enable/disable foo" as aliases for "systemctl start/enable/disable systemd-nspawn at foo.service"
 
-* "machinectl snapshot" to make a snapshot of a tree or container into /var/lib/containers
-
-* "machinectl rm" to remove a container tree from /var/lib/containers
-
 * "machinectl history"
 
 * "machinectl diff"
 
-* "machinectl read-only"
-
-* show btrfs quota in machinectl
-
 * "machinectl commit" that takes a writable snapshot of a tree, invokes a shell in it, and marks it read-only after use
 
 * "machinectl status" should show 10 most recent log lines of both the host logs of the unit of the machine, plus the logs generated in the machine
 
 * add transparent btrfs pool in a loopback file in /var if btrfs operations (such as systemd-import pull-dkr) are used and /var is not a btrfs file system
 
-* systemd-nspawn -M should look for gpt images if neither -D nor -i are specified.
-
-* systemd-nspawn -i should run things read-only if image file has no writable bits
-
 * systemd-nspawn -x should support ephemeral instances of gpt images
 
-* systemd-import pull-gpt should make a read-only download, plus a writable reflinked copy
-
-* machinectl list should show crtimes of snapshots and GPT images
-
 * hostnamectl: show root image uuid
 
 * sysfs set api in libudev is not const

commit f02ca52281315cb6067a3798450bc814837c85ef
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 28 02:07:32 2014 +0100

    util: treat -1 as special size in format_bytes()

diff --git a/src/shared/util.c b/src/shared/util.c
index 92b4d49..dfaf7f7 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5184,6 +5184,9 @@ char *format_bytes(char *buf, size_t l, off_t t) {
                 { "K", 1024ULL },
         };
 
+        if (t == (off_t) -1)
+                return NULL;
+
         for (i = 0; i < ELEMENTSOF(table); i++) {
 
                 if (t >= table[i].factor) {

commit b6b1849830f5e4a6065c3b0c993668e500c954d3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Dec 28 02:05:28 2014 +0100

    machined: add support for reporting image size via btrfs quota

diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 1030cd3..cae7b22 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -213,6 +213,111 @@ static int property_get_mtime(
         return 1;
 }
 
+static int property_get_size(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = image_find_by_bus_path_with_error(path, &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "t", image->size);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+
+static int property_get_limit(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = image_find_by_bus_path_with_error(path, &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "t", image->limit);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static int property_get_size_exclusive(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = image_find_by_bus_path_with_error(path, &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "t", image->size_exclusive);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static int property_get_limit_exclusive(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = image_find_by_bus_path_with_error(path, &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_append(reply, "t", image->limit_exclusive);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
 static int method_remove(
                 sd_bus *bus,
                 sd_bus_message *message,
@@ -327,12 +432,16 @@ static int method_mark_read_only(
 
 const sd_bus_vtable image_vtable[] = {
         SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Name",                  "s", property_get_name,      0, 0),
-        SD_BUS_PROPERTY("Path",                  "s", property_get_path,      0, 0),
-        SD_BUS_PROPERTY("Type",                  "s", property_get_type,      0, 0),
-        SD_BUS_PROPERTY("ReadOnly",              "b", property_get_read_only, 0, 0),
-        SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,    0, 0),
-        SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,     0, 0),
+        SD_BUS_PROPERTY("Name",                  "s", property_get_name,            0, 0),
+        SD_BUS_PROPERTY("Path",                  "s", property_get_path,            0, 0),
+        SD_BUS_PROPERTY("Type",                  "s", property_get_type,            0, 0),
+        SD_BUS_PROPERTY("ReadOnly",              "b", property_get_read_only,       0, 0),
+        SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,          0, 0),
+        SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,           0, 0),
+        SD_BUS_PROPERTY("Size",                  "t", property_get_size,            0, 0),
+        SD_BUS_PROPERTY("Limit",                 "t", property_get_limit,           0, 0),
+        SD_BUS_PROPERTY("SizeExclusive",         "t", property_get_size_exclusive,  0, 0),
+        SD_BUS_PROPERTY("LimitExclusive",        "t", property_get_limit_exclusive, 0, 0),
         SD_BUS_METHOD("Remove", NULL, NULL, method_remove, 0),
         SD_BUS_METHOD("Rename", "s", NULL, method_rename, 0),
         SD_BUS_METHOD("Clone", "sb", NULL, method_clone, 0),
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 893b429..17fbe51 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -131,6 +131,7 @@ typedef struct ImageInfo {
         bool read_only;
         usec_t crtime;
         usec_t mtime;
+        uint64_t size;
 } ImageInfo;
 
 static int compare_image_info(const void *a, const void *b) {
@@ -142,13 +143,13 @@ static int compare_image_info(const void *a, const void *b) {
 static int list_images(int argc, char *argv[], void *userdata) {
 
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
+        size_t max_name = strlen("NAME"), max_type = strlen("TYPE"), max_size = strlen("SIZE"), max_crtime = strlen("CREATED"), max_mtime = strlen("MODIFIED");
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_free_ ImageInfo *images = NULL;
         size_t n_images = 0, n_allocated = 0, j;
         const char *name, *type, *object;
         sd_bus *bus = userdata;
-        uint64_t crtime, mtime;
+        uint64_t crtime, mtime, size;
         int read_only, r;
 
         assert(bus);
@@ -169,12 +170,12 @@ static int list_images(int argc, char *argv[], void *userdata) {
                 return r;
         }
 
-        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbtto)");
+        r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssbttto)");
         if (r < 0)
                 return bus_log_parse_error(r);
 
-        while ((r = sd_bus_message_read(reply, "(ssbtto)", &name, &type, &read_only, &crtime, &mtime, &object)) > 0) {
-                char buf[FORMAT_TIMESTAMP_MAX];
+        while ((r = sd_bus_message_read(reply, "(ssbttto)", &name, &type, &read_only, &crtime, &mtime, &size, &object)) > 0) {
+                char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_BYTES_MAX)];
                 size_t l;
 
                 if (name[0] == '.' && !arg_all)
@@ -188,6 +189,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
                 images[n_images].read_only = read_only;
                 images[n_images].crtime = crtime;
                 images[n_images].mtime = mtime;
+                images[n_images].size = size;
 
                 l = strlen(name);
                 if (l > max_name)
@@ -198,17 +200,23 @@ static int list_images(int argc, char *argv[], void *userdata) {
                         max_type = l;
 
                 if (crtime != 0) {
-                        l = strlen(format_timestamp(buf, sizeof(buf), crtime));
+                        l = strlen(strna(format_timestamp(buf, sizeof(buf), crtime)));
                         if (l > max_crtime)
                                 max_crtime = l;
                 }
 
                 if (mtime != 0) {
-                        l = strlen(format_timestamp(buf, sizeof(buf), mtime));
+                        l = strlen(strna(format_timestamp(buf, sizeof(buf), mtime)));
                         if (l > max_mtime)
                                 max_mtime = l;
                 }
 
+                if (size != (uint64_t) -1) {
+                        l = strlen(strna(format_bytes(buf, sizeof(buf), size)));
+                        if (l > max_size)
+                                max_size = l;
+                }
+
                 n_images++;
         }
         if (r < 0)
@@ -221,22 +229,24 @@ static int list_images(int argc, char *argv[], void *userdata) {
         qsort_safe(images, n_images, sizeof(ImageInfo), compare_image_info);
 
         if (arg_legend)
-                printf("%-*s %-*s %-3s %-*s %-*s\n",
+                printf("%-*s %-*s %-3s %-*s %-*s %-*s\n",
                        (int) max_name, "NAME",
                        (int) max_type, "TYPE",
                        "RO",
+                       (int) max_size, "SIZE",
                        (int) max_crtime, "CREATED",
                        (int) max_mtime, "MODIFIED");
 
         for (j = 0; j < n_images; j++) {
-                char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX];
+                char crtime_buf[FORMAT_TIMESTAMP_MAX], mtime_buf[FORMAT_TIMESTAMP_MAX], size_buf[FORMAT_BYTES_MAX];
 
-                printf("%-*s %-*s %s%-3s%s %-*s %-*s\n",
+                printf("%-*s %-*s %s%-3s%s %-*s %-*s %-*s\n",
                        (int) max_name, images[j].name,
                        (int) max_type, images[j].type,
                        images[j].read_only ? ansi_highlight_red() : "", yes_no(images[j].read_only), images[j].read_only ? ansi_highlight_off() : "",
-                       (int) max_crtime, images[j].crtime != 0 ? format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime) : "-",
-                       (int) max_mtime, images[j].mtime != 0 ? format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime) : "-");
+                       (int) max_size, strna(format_bytes(size_buf, sizeof(size_buf), images[j].size)),
+                       (int) max_crtime, strna(format_timestamp(crtime_buf, sizeof(crtime_buf), images[j].crtime)),
+                       (int) max_mtime, strna(format_timestamp(mtime_buf, sizeof(mtime_buf), images[j].mtime)));
         }
 
         if (r < 0)
@@ -651,11 +661,17 @@ typedef struct ImageStatusInfo {
         int read_only;
         usec_t crtime;
         usec_t mtime;
+        uint64_t size;
+        uint64_t limit;
+        uint64_t size_exclusive;
+        uint64_t limit_exclusive;
 } ImageStatusInfo;
 
 static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
         char ts_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
         char ts_absolute[FORMAT_TIMESTAMP_MAX], *s2;
+        char bs[FORMAT_BYTES_MAX], *s3;
+        char bs_exclusive[FORMAT_BYTES_MAX], *s4;
 
         assert(bus);
         assert(i);
@@ -678,28 +694,46 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) {
 
         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->crtime);
         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->crtime);
-        if (s1)
+        if (s1 && s2)
                 printf("\t Created: %s; %s\n", s2, s1);
         else if (s2)
                 printf("\t Created: %s\n", s2);
 
         s1 = format_timestamp_relative(ts_relative, sizeof(ts_relative), i->mtime);
         s2 = format_timestamp(ts_absolute, sizeof(ts_absolute), i->mtime);
-        if (s1)
+        if (s1 && s2)
                 printf("\tModified: %s; %s\n", s2, s1);
         else if (s2)
                 printf("\tModified: %s\n", s2);
+
+        s3 = format_bytes(bs, sizeof(bs), i->size);
+        s4 = i->size_exclusive != i->size ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->size_exclusive) : NULL;
+        if (s3 && s4)
+                printf("\t    Size: %s (exclusive: %s)\n", s3, s4);
+        else if (s3)
+                printf("\t    Size: %s\n", s3);
+
+        s3 = format_bytes(bs, sizeof(bs), i->limit);
+        s4 = i->limit_exclusive != i->limit ? format_bytes(bs_exclusive, sizeof(bs_exclusive), i->limit_exclusive) : NULL;
+        if (s3 && s4)
+                printf("\t   Limit: %s (exclusive: %s)\n", s3, s4);
+        else if (s3)
+                printf("\t   Limit: %s\n", s3);
 }
 
 static int show_image_info(const char *verb, sd_bus *bus, const char *path, bool *new_line) {
 
         static const struct bus_properties_map map[]  = {
-                { "Name",                  "s",  NULL, offsetof(ImageStatusInfo, name)      },
-                { "Path",                  "s",  NULL, offsetof(ImageStatusInfo, path)      },
-                { "Type",                  "s",  NULL, offsetof(ImageStatusInfo, type)      },
-                { "ReadOnly",              "b",  NULL, offsetof(ImageStatusInfo, read_only) },
-                { "CreationTimestamp",     "t",  NULL, offsetof(ImageStatusInfo, crtime)    },
-                { "ModificationTimestamp", "t",  NULL, offsetof(ImageStatusInfo, mtime)     },
+                { "Name",                  "s",  NULL, offsetof(ImageStatusInfo, name)            },
+                { "Path",                  "s",  NULL, offsetof(ImageStatusInfo, path)            },
+                { "Type",                  "s",  NULL, offsetof(ImageStatusInfo, type)            },
+                { "ReadOnly",              "b",  NULL, offsetof(ImageStatusInfo, read_only)       },
+                { "CreationTimestamp",     "t",  NULL, offsetof(ImageStatusInfo, crtime)          },
+                { "ModificationTimestamp", "t",  NULL, offsetof(ImageStatusInfo, mtime)           },
+                { "Size",                  "t",  NULL, offsetof(ImageStatusInfo, size)            },
+                { "Limit",                 "t",  NULL, offsetof(ImageStatusInfo, limit)           },
+                { "SizeExclusive",         "t",  NULL, offsetof(ImageStatusInfo, size_exclusive)  },
+                { "LimitExclusive",        "t",  NULL, offsetof(ImageStatusInfo, limit_exclusive) },
                 {}
         };
 
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 18b772d..8bb0146 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -489,7 +489,7 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_open_container(reply, 'a', "(ssbtto)");
+        r = sd_bus_message_open_container(reply, 'a', "(ssbttto)");
         if (r < 0)
                 return r;
 
@@ -500,12 +500,13 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda
                 if (!p)
                         return -ENOMEM;
 
-                r = sd_bus_message_append(reply, "(ssbtto)",
+                r = sd_bus_message_append(reply, "(ssbttto)",
                                           image->name,
                                           image_type_to_string(image->type),
                                           image->read_only,
                                           image->crtime,
                                           image->mtime,
+                                          image->size,
                                           p);
                 if (r < 0)
                         return r;
@@ -678,7 +679,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListImages", NULL, "a(ssbtto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
         SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0),
         SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
diff --git a/src/shared/btrfs-ctree.h b/src/shared/btrfs-ctree.h
index 45b94cd..7bdf3fe 100644
--- a/src/shared/btrfs-ctree.h
+++ b/src/shared/btrfs-ctree.h
@@ -66,3 +66,26 @@ struct btrfs_root_item {
 } _packed_;
 
 #define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0)
+
+struct btrfs_qgroup_info_item {
+        le64_t generation;
+        le64_t rfer;
+        le64_t rfer_cmpr;
+        le64_t excl;
+        le64_t excl_cmpr;
+} _packed_;
+
+#define BTRFS_QGROUP_LIMIT_MAX_RFER     (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL     (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER     (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL     (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR    (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR    (1ULL << 5)
+
+struct btrfs_qgroup_limit_item {
+        le64_t flags;
+        le64_t max_rfer;
+        le64_t max_excl;
+        le64_t rsv_rfer;
+        le64_t rsv_excl;
+} _packed_;
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index 9b47330..164ac9f 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -341,7 +341,7 @@ int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) {
 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
         struct btrfs_ioctl_search_args args = {
                 /* Tree of tree roots */
-                .key.tree_id = 1,
+                .key.tree_id = BTRFS_ROOT_TREE_OBJECTID,
 
                 /* Look precisely for the subvolume items */
                 .key.min_type = BTRFS_ROOT_ITEM_KEY,
@@ -352,14 +352,10 @@ int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
                 .key.max_offset = (uint64_t) -1,
                 .key.min_transid = 0,
                 .key.max_transid = (uint64_t) -1,
-
-                /* Some large value */
-                .key.nr_items = 2,
         };
 
-        struct btrfs_ioctl_search_header *sh;
-        struct btrfs_root_item *ri;
         uint64_t subvol_id;
+        bool found = false;
         int r;
 
         assert(fd >= 0);
@@ -370,30 +366,168 @@ int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *ret) {
                 return r;
 
         args.key.min_objectid = args.key.max_objectid = subvol_id;
-        if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
-                return -errno;
 
-        if (args.key.nr_items != 1)
-                return -EIO;
+        for (;;) {
+                const struct btrfs_ioctl_search_header *sh;
+                unsigned i;
+
+                args.key.nr_items = 256;
+                if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+                        return -errno;
+
+                if (args.key.nr_items <= 0)
+                        break;
 
-        sh = (struct btrfs_ioctl_search_header*) args.buf;
-        assert(sh->type == BTRFS_ROOT_ITEM_KEY);
-        assert(sh->objectid == subvol_id);
+                for (i = 0,
+                     sh = (const struct btrfs_ioctl_search_header*) args.buf;
+                     i < args.key.nr_items;
+                     i++,
+                     args.key.min_type = sh->type,
+                     args.key.min_offset = sh->offset,
+                     args.key.min_objectid = sh->objectid,
+                     sh = (const struct btrfs_ioctl_search_header*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header) + sh->len)) {
 
-        if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
-                return -ENOTSUP;
+                        const struct btrfs_root_item *ri;
+
+                        if (sh->objectid != subvol_id)
+                                continue;
+                        if (sh->type != BTRFS_ROOT_ITEM_KEY)
+                                continue;
+                        if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec))
+                                continue;
 
-        ri = (struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header));
+                        ri = (const struct btrfs_root_item *)(args.buf + sizeof(struct btrfs_ioctl_search_header));
 
-        ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
-                     (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
+                        ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC +
+                                (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC;
 
-        ret->subvol_id = subvol_id;
-        ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
+                        ret->subvol_id = subvol_id;
+                        ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY);
 
-        assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
-        memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
-        memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
+                        assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid));
+                        memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid));
+                        memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid));
+
+                        found = true;
+                        goto finish;
+                }
+
+                args.key.min_offset++;
+                if (!args.key.min_offset) /* overflow */
+                        break;
+        }
+
+finish:
+        if (!found)
+                return -ENODATA;
+
+        return 0;
+}
+
+int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *ret) {
+
+        struct btrfs_ioctl_search_args args = {
+                /* Tree of quota items */
+                .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+
+                /* Look precisely for the quota items */
+                .key.min_type = BTRFS_QGROUP_STATUS_KEY,
+                .key.max_type = BTRFS_QGROUP_LIMIT_KEY,
+
+                .key.min_objectid = 0,
+                .key.max_objectid = 0,
+
+                /* No restrictions on the other components */
+                .key.min_transid = 0,
+                .key.max_transid = (uint64_t) -1,
+        };
+
+        uint64_t subvol_id;
+        bool found_info = false, found_limit = false;
+        int r;
+
+        assert(fd >= 0);
+        assert(ret);
+
+        r = btrfs_subvol_get_id_fd(fd, &subvol_id);
+        if (r < 0)
+                return r;
+
+        args.key.min_offset = args.key.max_offset = subvol_id;
+
+        for (;;) {
+                const struct btrfs_ioctl_search_header *sh;
+                unsigned i;
+
+                args.key.nr_items = 256;
+                if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0)
+                        return -errno;
+
+                if (args.key.nr_items <= 0)
+                        break;
+
+                for (i = 0,
+                     sh = (const struct btrfs_ioctl_search_header*) args.buf;
+                     i < args.key.nr_items;
+                     i++,
+                     args.key.min_type = sh->type,
+                     args.key.min_offset = sh->offset,
+                     args.key.min_objectid = sh->objectid,
+                     sh = (const struct btrfs_ioctl_search_header*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header) + sh->len)) {
+
+                        const void *body;
+
+                        if (sh->objectid != 0)
+                                continue;
+                        if (sh->offset != subvol_id)
+                                continue;
+
+                        body = (uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header);
+
+                        if (sh->type == BTRFS_QGROUP_INFO_KEY) {
+                                const struct btrfs_qgroup_info_item *qii = body;
+
+                                ret->referred = le64toh(qii->rfer);
+                                ret->exclusive = le64toh(qii->excl);
+
+                                found_info = true;
+
+                        } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) {
+                                const struct btrfs_qgroup_limit_item *qli = body;
+
+                                ret->referred_max = le64toh(qli->max_rfer);
+                                ret->exclusive_max = le64toh(qli->max_excl);
+
+                                if (ret->referred_max == 0)
+                                        ret->referred_max = (uint64_t) -1;
+                                if (ret->exclusive_max == 0)
+                                        ret->exclusive_max = (uint64_t) -1;
+
+                                found_limit = true;
+                        }
+
+                        if (found_info && found_limit)
+                                goto finish;
+                }
+
+                args.key.min_offset++;
+                if (!args.key.min_offset)
+                        break;
+        }
+
+finish:
+        if (!found_limit && !found_info)
+                return -ENODATA;
+
+        if (!found_info) {
+                ret->referred = (uint64_t) -1;
+                ret->exclusive = (uint64_t) -1;
+        }
+
+        if (!found_limit) {
+                ret->referred_max = (uint64_t) -1;
+                ret->exclusive_max = (uint64_t) -1;
+        }
 
         return 0;
 }
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index dff8c01..1532c12 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -34,6 +34,13 @@ typedef struct BtrfsSubvolInfo {
         bool read_only;
 } BtrfsSubvolInfo;
 
+typedef struct BtrfsQuotaInfo {
+        uint64_t referred;
+        uint64_t exclusive;
+        uint64_t referred_max;
+        uint64_t exclusive_max;
+} BtrfsQuotaInfo;
+
 int btrfs_is_snapshot(int fd);
 
 int btrfs_subvol_make(const char *path);
@@ -45,6 +52,7 @@ int btrfs_subvol_set_read_only(const char *path, bool b);
 int btrfs_subvol_get_read_only_fd(int fd);
 int btrfs_subvol_get_id_fd(int fd, uint64_t *ret);
 int btrfs_subvol_get_info_fd(int fd, BtrfsSubvolInfo *info);
+int btrfs_subvol_get_quota_fd(int fd, BtrfsQuotaInfo *quota);
 
 int btrfs_reflink(int infd, int outfd);
 
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 7c041fa..36b64e1 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -71,6 +71,8 @@ static int image_new(
         i->read_only = read_only;
         i->crtime = crtime;
         i->mtime = mtime;
+        i->size = i->size_exclusive = (uint64_t) -1;
+        i->limit = i->limit_exclusive = (uint64_t) -1;
 
         i->name = strdup(pretty);
         if (!i->name)
@@ -138,6 +140,7 @@ static int image_make(
 
                         if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
                                 BtrfsSubvolInfo info;
+                                BtrfsQuotaInfo quota;
 
                                 /* It's a btrfs subvolume */
 
@@ -156,6 +159,15 @@ static int image_make(
                                 if (r < 0)
                                         return r;
 
+                                r = btrfs_subvol_get_quota_fd(fd, &quota);
+                                if (r >= 0) {
+                                        (*ret)->size = quota.referred;
+                                        (*ret)->size_exclusive = quota.exclusive;
+
+                                        (*ret)->limit = quota.referred_max;
+                                        (*ret)->limit_exclusive = quota.exclusive_max;
+                                }
+
                                 return 1;
                         }
                 }
@@ -199,6 +211,9 @@ static int image_make(
                 if (r < 0)
                         return r;
 
+                (*ret)->size = (*ret)->size_exclusive = st.st_blocks * 512;
+                (*ret)->limit = (*ret)->limit_exclusive = st.st_size;
+
                 return 1;
         }
 
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 9e0f6ae..10e5d0a 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -40,6 +40,11 @@ typedef struct Image {
 
         usec_t crtime;
         usec_t mtime;
+
+        uint64_t size;
+        uint64_t size_exclusive;
+        uint64_t limit;
+        uint64_t limit_exclusive;
 } Image;
 
 Image *image_unref(Image *i);
diff --git a/src/shared/missing.h b/src/shared/missing.h
index 08cf836..94d9d8d 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -254,10 +254,30 @@ struct btrfs_ioctl_fs_info_args {
 #define BTRFS_FIRST_FREE_OBJECTID 256
 #endif
 
+#ifndef BTRFS_ROOT_TREE_OBJECTID
+#define BTRFS_ROOT_TREE_OBJECTID 1
+#endif
+
+#ifndef BTRFS_QUOTA_TREE_OBJECTID
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
+#endif
+
 #ifndef BTRFS_ROOT_ITEM_KEY
 #define BTRFS_ROOT_ITEM_KEY 132
 #endif
 
+#ifndef BTRFS_QGROUP_STATUS_KEY
+#define BTRFS_QGROUP_STATUS_KEY 240
+#endif
+
+#ifndef BTRFS_QGROUP_INFO_KEY
+#define BTRFS_QGROUP_INFO_KEY 242
+#endif
+
+#ifndef BTRFS_QGROUP_LIMIT_KEY
+#define BTRFS_QGROUP_LIMIT_KEY 244
+#endif
+
 #ifndef BTRFS_SUPER_MAGIC
 #define BTRFS_SUPER_MAGIC 0x9123683E
 #endif
diff --git a/src/test/test-btrfs.c b/src/test/test-btrfs.c
index 43b445d..150a32a 100644
--- a/src/test/test-btrfs.c
+++ b/src/test/test-btrfs.c
@@ -36,7 +36,8 @@ int main(int argc, char *argv[]) {
                 log_error_errno(errno, "Failed to open root directory: %m");
         else {
                 BtrfsSubvolInfo info;
-                char ts[FORMAT_TIMESTAMP_MAX];
+                BtrfsQuotaInfo quota;
+                char ts[FORMAT_TIMESTAMP_MAX], bs[FORMAT_BYTES_MAX];
 
                 r = btrfs_subvol_get_info_fd(fd, &info);
                 if (r < 0)
@@ -46,6 +47,16 @@ int main(int argc, char *argv[]) {
                         log_info("read-only (search): %s", yes_no(info.read_only));
                 }
 
+                r = btrfs_subvol_get_quota_fd(fd, &quota);
+                if (r < 0)
+                        log_error_errno(r, "Failed to get quota info: %m");
+                else {
+                        log_info("referred: %s", strna(format_bytes(bs, sizeof(bs), quota.referred)));
+                        log_info("exclusive: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive)));
+                        log_info("referred_max: %s", strna(format_bytes(bs, sizeof(bs), quota.referred_max)));
+                        log_info("exclusive_max: %s", strna(format_bytes(bs, sizeof(bs), quota.exclusive_max)));
+                }
+
                 r = btrfs_subvol_get_read_only_fd(fd);
                 if (r < 0)
                         log_error_errno(r, "Failed to get read only flag: %m");

commit 814f000872fc2d254250831607bdca9b27e5705e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 18:47:17 2014 +0100

    tmpfiles.d: upgrade a couple of directories we create at boot to subvolumes
    
    In particular we upgrade /var/lib/container, /var/tmp and /tmp to
    subvolumes.

diff --git a/tmpfiles.d/tmp.conf b/tmpfiles.d/tmp.conf
index b80dab4..ffdd82f 100644
--- a/tmpfiles.d/tmp.conf
+++ b/tmpfiles.d/tmp.conf
@@ -8,8 +8,8 @@
 # See tmpfiles.d(5) for details
 
 # Clear tmp directories separately, to make them easier to override
-d /tmp 1777 root root 10d
-d /var/tmp 1777 root root 30d
+v /tmp 1777 root root 10d
+v /var/tmp 1777 root root 30d
 
 # Exclude namespace mountpoints created with PrivateTmp=yes
 x /tmp/systemd-private-%b-*
diff --git a/tmpfiles.d/var.conf b/tmpfiles.d/var.conf
index d9545fe..d0c759e 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 - - -
-d /var/lib/container 0700 - - -
+v /var/lib/container 0700 - - -
 
 d /var/spool 0755 - - -

commit d7b8eec7dc7fe307d3a08b32cf1a9ad4276ce6d5
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 18:46:36 2014 +0100

    tmpfiles: add new line type 'v' for creating btrfs subvolumes

diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml
index 4f2e640..398b3f7 100644
--- a/man/tmpfiles.d.xml
+++ b/man/tmpfiles.d.xml
@@ -168,6 +168,17 @@ L    /tmp/foobar -    -    -    -   /dev/null</programlisting>
                                 </varlistentry>
 
                                 <varlistentry>
+                                        <term><varname>v</varname></term>
+                                        <listitem><para>Create a
+                                        subvolume if the path does not
+                                        exist yet and the file system
+                                        supports this (btrfs). Otherwise
+                                        create a normal directory, in
+                                        the same way as
+                                        <varname>d</varname>.</para></listitem>
+                                </varlistentry>
+
+                                <varlistentry>
                                         <term><varname>p</varname></term>
                                         <term><varname>p+</varname></term>
                                         <listitem><para>Create a named
diff --git a/src/core/main.c b/src/core/main.c
index 300567a..87d54ca 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -42,9 +42,7 @@
 #include "sd-daemon.h"
 #include "sd-messages.h"
 #include "sd-bus.h"
-#include "manager.h"
 #include "log.h"
-#include "load-fragment.h"
 #include "fdset.h"
 #include "special.h"
 #include "conf-parser.h"
@@ -64,9 +62,12 @@
 #include "env-util.h"
 #include "clock-util.h"
 #include "fileio.h"
-#include "dbus-manager.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "selinux-util.h"
+#include "manager.h"
+#include "dbus-manager.h"
+#include "load-fragment.h"
 
 #include "mount-setup.h"
 #include "loopback-setup.h"
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 4c41109..5b408e0 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -35,7 +35,6 @@
 #include "strv.h"
 #include "util.h"
 #include "path-util.h"
-#include "namespace.h"
 #include "missing.h"
 #include "execute.h"
 #include "loopback-setup.h"
@@ -43,6 +42,8 @@
 #include "dev-setup.h"
 #include "def.h"
 #include "label.h"
+#include "selinux-util.h"
+#include "namespace.h"
 
 typedef enum MountMode {
         /* This is ordered by priority! */
diff --git a/src/core/socket.c b/src/core/socket.c
index 8fa55e0..b671fff 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -48,6 +48,7 @@
 #include "smack-util.h"
 #include "bus-util.h"
 #include "bus-error.h"
+#include "selinux-util.h"
 #include "dbus-socket.h"
 #include "unit.h"
 #include "socket.h"
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index b230ff6..7cd4a1d 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -33,6 +33,7 @@
 #include "label.h"
 #include "bus-util.h"
 #include "event-util.h"
+#include "selinux-util.h"
 
 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
 
diff --git a/src/locale/localed.c b/src/locale/localed.c
index 0723541..529a9ab 100644
--- a/src/locale/localed.c
+++ b/src/locale/localed.c
@@ -39,6 +39,7 @@
 #include "bus-message.h"
 #include "event-util.h"
 #include "locale-util.h"
+#include "selinux-util.h"
 
 #ifdef HAVE_XKBCOMMON
 #include <xkbcommon/xkbcommon.h>
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index c0d1309..26ce8fe 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -39,9 +39,10 @@
 #include "audit.h"
 #include "bus-util.h"
 #include "bus-error.h"
-#include "logind.h"
 #include "bus-common-errors.h"
 #include "udev-util.h"
+#include "selinux-util.h"
+#include "logind.h"
 
 static int property_get_idle_hint(
                 sd_bus *bus,
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index c0ab947..ce15a80 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -24,6 +24,7 @@
 #include "mkdir.h"
 #include "label.h"
 #include "capability.h"
+#include "selinux-util.h"
 
 #include "resolved-manager.h"
 #include "resolved-conf.h"
diff --git a/src/shared/btrfs-util.c b/src/shared/btrfs-util.c
index 84c8110..9b47330 100644
--- a/src/shared/btrfs-util.c
+++ b/src/shared/btrfs-util.c
@@ -33,6 +33,8 @@
 #include "macro.h"
 #include "strv.h"
 #include "copy.h"
+#include "selinux-util.h"
+#include "smack-util.h"
 #include "btrfs-ctree.h"
 #include "btrfs-util.h"
 
@@ -184,6 +186,24 @@ int btrfs_subvol_make(const char *path) {
         return 0;
 }
 
+int btrfs_subvol_make_label(const char *path) {
+        int r;
+
+        assert(path);
+
+        r = mac_selinux_create_file_prepare(path, S_IFDIR);
+        if (r < 0)
+                return r;
+
+        r = btrfs_subvol_make(path);
+        mac_selinux_create_file_clear();
+
+        if (r < 0)
+                return r;
+
+        return mac_smack_fix(path, false, false);
+}
+
 int btrfs_subvol_remove(const char *path) {
         struct btrfs_ioctl_vol_args args = {};
         _cleanup_close_ int fd = -1;
diff --git a/src/shared/btrfs-util.h b/src/shared/btrfs-util.h
index f51f37a..dff8c01 100644
--- a/src/shared/btrfs-util.h
+++ b/src/shared/btrfs-util.h
@@ -37,6 +37,7 @@ typedef struct BtrfsSubvolInfo {
 int btrfs_is_snapshot(int fd);
 
 int btrfs_subvol_make(const char *path);
+int btrfs_subvol_make_label(const char *path);
 int btrfs_subvol_remove(const char *path);
 int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_only, bool fallback_copy);
 
diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c
index 294c9e6..5fd69e0 100644
--- a/src/shared/fileio-label.c
+++ b/src/shared/fileio-label.c
@@ -23,9 +23,10 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "fileio-label.h"
-#include "label.h"
 #include "util.h"
+#include "selinux-util.h"
+#include "label.h"
+#include "fileio-label.h"
 
 int write_string_file_atomic_label(const char *fn, const char *line) {
         int r;
diff --git a/src/shared/label.c b/src/shared/label.c
index 0af41af..82f10b2 100644
--- a/src/shared/label.c
+++ b/src/shared/label.c
@@ -19,8 +19,10 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "label.h"
+#include "selinux-util.h"
+#include "smack-util.h"
 #include "util.h"
+#include "label.h"
 
 int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
         int r, q;
diff --git a/src/shared/label.h b/src/shared/label.h
index 3428a8b..8070bcb 100644
--- a/src/shared/label.h
+++ b/src/shared/label.h
@@ -21,8 +21,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include "selinux-util.h"
-#include "smack-util.h"
+#include <stdbool.h>
+#include <sys/types.h>
 
 int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
 
diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c
index b1ef19f..6806c51 100644
--- a/src/shared/socket-label.c
+++ b/src/shared/socket-label.c
@@ -35,9 +35,10 @@
 #include "macro.h"
 #include "util.h"
 #include "mkdir.h"
-#include "socket-util.h"
 #include "missing.h"
 #include "label.h"
+#include "selinux-util.h"
+#include "socket-util.h"
 
 int socket_address_listen(
                 const SocketAddress *a,
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index 0baa2c3..5d5f5ea 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -39,6 +39,7 @@
 #include "label.h"
 #include "fileio-label.h"
 #include "uid-range.h"
+#include "selinux-util.h"
 
 typedef enum ItemType {
         ADD_USER = 'u',
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index f2283ec..b57d275 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -32,6 +32,7 @@
 #include <sys/signalfd.h>
 
 #include "missing.h"
+#include "selinux-util.h"
 #include "udev.h"
 #include "udev-util.h"
 
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index d507200..753c3d1 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -40,6 +40,7 @@
 #include "bus-error.h"
 #include "bus-common-errors.h"
 #include "event-util.h"
+#include "selinux-util.h"
 
 #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n"
 #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n"
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 44ea51e..b830850 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -54,6 +54,8 @@
 #include "specifier.h"
 #include "build.h"
 #include "copy.h"
+#include "selinux-util.h"
+#include "btrfs-util.h"
 
 /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
  * them in the file system. This is intended to be used to create
@@ -66,6 +68,7 @@ typedef enum ItemType {
         TRUNCATE_FILE = 'F',
         CREATE_DIRECTORY = 'd',
         TRUNCATE_DIRECTORY = 'D',
+        CREATE_SUBVOLUME = 'v',
         CREATE_FIFO = 'p',
         CREATE_SYMLINK = 'L',
         CREATE_CHAR_DEVICE = 'c',
@@ -758,17 +761,27 @@ static int create_item(Item *i) {
 
                 break;
 
-        case TRUNCATE_DIRECTORY:
         case CREATE_DIRECTORY:
+        case TRUNCATE_DIRECTORY:
+        case CREATE_SUBVOLUME:
 
-                RUN_WITH_UMASK(0000) {
+                RUN_WITH_UMASK(0000)
                         mkdir_parents_label(i->path, 0755);
-                        r = mkdir_label(i->path, i->mode);
+
+                if (i->type == CREATE_SUBVOLUME) {
+                        RUN_WITH_UMASK((~i->mode) & 0777)
+                                r = btrfs_subvol_make(i->path);
+                } else
+                        r = 0;
+
+                if (i->type == CREATE_DIRECTORY || r == -ENOTTY) {
+                        RUN_WITH_UMASK(0000)
+                                r = mkdir_label(i->path, i->mode);
                 }
 
                 if (r < 0) {
                         if (r != -EEXIST)
-                                return log_error_errno(r, "Failed to create directory %s: %m", i->path);
+                                return log_error_errno(r, "Failed to create directory or subvolume %s: %m", i->path);
 
                         if (stat(i->path, &st) < 0)
                                 return log_error_errno(errno, "stat(%s) failed: %m", i->path);
@@ -970,6 +983,7 @@ static int remove_item_instance(Item *i, const char *instance) {
         case CREATE_FILE:
         case TRUNCATE_FILE:
         case CREATE_DIRECTORY:
+        case CREATE_SUBVOLUME:
         case CREATE_FIFO:
         case CREATE_SYMLINK:
         case CREATE_BLOCK_DEVICE:
@@ -1014,6 +1028,7 @@ static int remove_item(Item *i) {
         case CREATE_FILE:
         case TRUNCATE_FILE:
         case CREATE_DIRECTORY:
+        case CREATE_SUBVOLUME:
         case CREATE_FIFO:
         case CREATE_SYMLINK:
         case CREATE_CHAR_DEVICE:
@@ -1091,6 +1106,7 @@ static int clean_item(Item *i) {
 
         switch (i->type) {
         case CREATE_DIRECTORY:
+        case CREATE_SUBVOLUME:
         case TRUNCATE_DIRECTORY:
         case IGNORE_PATH:
         case COPY_FILES:
@@ -1289,6 +1305,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         case CREATE_FILE:
         case TRUNCATE_FILE:
         case CREATE_DIRECTORY:
+        case CREATE_SUBVOLUME:
         case TRUNCATE_DIRECTORY:
         case CREATE_FIFO:
         case IGNORE_PATH:
@@ -1429,6 +1446,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
         } else
                 i->mode =
                         i->type == CREATE_DIRECTORY ||
+                        i->type == CREATE_SUBVOLUME ||
                         i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
 
         if (age && !streq(age, "-")) {
@@ -1636,7 +1654,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
                         continue;
 
                 HASHMAP_FOREACH(j, items, iter) {
-                        if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY)
+                        if (j->type != CREATE_DIRECTORY && j->type != TRUNCATE_DIRECTORY && j->type != CREATE_SUBVOLUME)
                                 continue;
 
                         if (path_equal(j->path, i->path)) {
diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c
index c30a428..499feef 100644
--- a/src/udev/udev-node.c
+++ b/src/udev/udev-node.c
@@ -31,6 +31,7 @@
 
 #include "udev.h"
 #include "smack-util.h"
+#include "selinux-util.h"
 
 static int node_symlink(struct udev_device *dev, const char *node, const char *slink) {
         struct stat stats;
diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
index d9bd697..a957e92 100644
--- a/src/udev/udevadm.c
+++ b/src/udev/udevadm.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 #include <getopt.h>
 
+#include "selinux-util.h"
 #include "udev.h"
 
 static int adm_version(struct udev *udev, int argc, char *argv[]) {
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index c367825..3edb29b 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -46,13 +46,14 @@
 #include <sys/inotify.h>
 #include <sys/utsname.h>
 
-#include "udev.h"
-#include "udev-util.h"
-#include "rtnl-util.h"
 #include "sd-daemon.h"
+#include "rtnl-util.h"
 #include "cgroup-util.h"
 #include "dev-setup.h"
 #include "fileio.h"
+#include "selinux-util.h"
+#include "udev.h"
+#include "udev-util.h"
 
 static struct udev_rules *rules;
 static struct udev_ctrl *udev_ctrl;
diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c
index a910808..561963e 100644
--- a/src/update-done/update-done.c
+++ b/src/update-done/update-done.c
@@ -21,6 +21,7 @@
 
 #include "util.h"
 #include "label.h"
+#include "selinux-util.h"
 
 #define MESSAGE                                                         \
         "This file was created by systemd-update-done. Its only \n"     \

commit ebd93cb684806ac0f352139e69ac8f53eb49f5e4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 17:44:04 2014 +0100

    machinectl/machined: implement "rename", "clone", "read-only" verbs for machine images

diff --git a/configure.ac b/configure.ac
index 1df3a18..0496f5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -310,7 +310,7 @@ LIBS="$save_LIBS"
 
 AC_CHECK_FUNCS([memfd_create])
 AC_CHECK_FUNCS([__secure_getenv secure_getenv])
-AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, LO_FLAGS_PARTSCAN],
+AC_CHECK_DECLS([gettid, pivot_root, name_to_handle_at, setns, getrandom, renameat2, LO_FLAGS_PARTSCAN],
                [], [], [[
 #include <sys/types.h>
 #include <unistd.h>
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index be23699..1030cd3 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -236,6 +236,95 @@ static int method_remove(
         return sd_bus_reply_method_return(message, NULL);
 }
 
+static int method_rename(
+                sd_bus *bus,
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        const char *new_name;
+        int r;
+
+        assert(bus);
+        assert(message);
+
+        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "s", &new_name);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(new_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+
+        r = image_rename(image, new_name);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_clone(
+                sd_bus *bus,
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        const char *new_name;
+        int r, read_only;
+
+        assert(bus);
+        assert(message);
+
+        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "sb", &new_name, &read_only);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(new_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+
+        r = image_clone(image, new_name, read_only);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_mark_read_only(
+                sd_bus *bus,
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r, read_only;
+
+        assert(bus);
+        assert(message);
+
+        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read(message, "b", &read_only);
+        if (r < 0)
+                return r;
+
+        r = image_read_only(image, read_only);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable image_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Name",                  "s", property_get_name,      0, 0),
@@ -245,6 +334,9 @@ const sd_bus_vtable image_vtable[] = {
         SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,    0, 0),
         SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,     0, 0),
         SD_BUS_METHOD("Remove", NULL, NULL, method_remove, 0),
+        SD_BUS_METHOD("Rename", "s", NULL, method_rename, 0),
+        SD_BUS_METHOD("Clone", "sb", NULL, method_clone, 0),
+        SD_BUS_METHOD("MarkeReadOnly", "b", NULL, method_mark_read_only, 0),
         SD_BUS_VTABLE_END
 };
 
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 500e5b7..893b429 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1286,13 +1286,11 @@ static int login_machine(int argc, char *argv[], void *userdata) {
 static int remove_image(int argc, char *argv[], void *userdata) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int i;
+        int r, i;
 
         assert(bus);
 
         for (i = 1; i < argc; i++) {
-                int r;
-
                 r = sd_bus_call_method(
                                 bus,
                                 "org.freedesktop.machine1",
@@ -1311,6 +1309,80 @@ static int remove_image(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
+static int rename_image(int argc, char *argv[], void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        sd_bus *bus = userdata;
+        int r;
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        "/org/freedesktop/machine1",
+                        "org.freedesktop.machine1.Manager",
+                        "RenameImage",
+                        &error,
+                        NULL,
+                        "ss", argv[1], argv[2]);
+        if (r < 0) {
+                log_error("Could not rename image: %s", bus_error_message(&error, -r));
+                return r;
+        }
+
+        return 0;
+}
+
+static int clone_image(int argc, char *argv[], void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        sd_bus *bus = userdata;
+        int r;
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        "/org/freedesktop/machine1",
+                        "org.freedesktop.machine1.Manager",
+                        "CloneImage",
+                        &error,
+                        NULL,
+                        "ssb", argv[1], argv[2], arg_read_only);
+        if (r < 0) {
+                log_error("Could not clone image: %s", bus_error_message(&error, -r));
+                return r;
+        }
+
+        return 0;
+}
+
+static int read_only_image(int argc, char *argv[], void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        sd_bus *bus = userdata;
+        int b = true, r;
+
+        if (argc > 2) {
+                b = parse_boolean(argv[2]);
+                if (b < 0) {
+                        log_error("Failed to parse boolean argument: %s", argv[2]);
+                        return -EINVAL;
+                }
+        }
+
+        r = sd_bus_call_method(
+                        bus,
+                        "org.freedesktop.machine1",
+                        "/org/freedesktop/machine1",
+                        "org.freedesktop.machine1.Manager",
+                        "MarkImageReadOnly",
+                        &error,
+                        NULL,
+                        "sb", argv[1], b);
+        if (r < 0) {
+                log_error("Could not mark image read-only: %s", bus_error_message(&error, -r));
+                return r;
+        }
+
+        return 0;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -1336,15 +1408,18 @@ static int help(int argc, char *argv[], void *userdata) {
                "  login NAME                  Get a login prompt on a container\n"
                "  poweroff NAME...            Power off one or more containers\n"
                "  reboot NAME...              Reboot one or more containers\n"
-               "  kill NAME...                Send signal to processes of a VM/container\n"
                "  terminate NAME...           Terminate one or more VMs/containers\n"
-               "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n"
+               "  kill NAME...                Send signal to processes of a VM/container\n"
                "  copy-to NAME PATH [PATH]    Copy files from the host to a container\n"
-               "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n\n"
+               "  copy-from NAME PATH [PATH]  Copy files from a container to the host\n"
+               "  bind NAME PATH [PATH]       Bind mount a path from the host into a container\n\n"
                "Image Commands:\n"
                "  list-images                 Show available images\n"
                "  image-status NAME...        Show image details\n"
                "  show-image NAME...          Show properties of image\n"
+               "  clone NAME NAME             Clone an image\n"
+               "  rename NAME NAME            Rename an image\n"
+               "  read-only NAME [BOOL]       Mark or unmark image read-only\n"
                "  remove NAME...              Remove an image\n",
                program_invocation_short_name);
 
@@ -1482,6 +1557,9 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
                 { "copy-to",     3,        4,        0,            copy_files        },
                 { "copy-from",   3,        4,        0,            copy_files        },
                 { "remove",      2,        VERB_ANY, 0,            remove_image      },
+                { "rename",      3,        3,        0,            rename_image      },
+                { "clone",       3,        3,        0,            clone_image       },
+                { "read-only",   2,        3,        0,            read_only_image   },
                 {}
         };
 
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 9296377..18b772d 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -588,6 +588,90 @@ static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userd
         return sd_bus_reply_method_return(message, NULL);
 }
 
+static int method_rename_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(image_unrefp) Image* i = NULL;
+        const char *old_name, *new_name;
+        int r;
+
+        assert(bus);
+        assert(message);
+
+        r = sd_bus_message_read(message, "ss", &old_name, &new_name);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(old_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
+        if (!image_name_is_valid(new_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+
+        r = image_find(old_name, &i);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
+
+        r = image_rename(i, new_name);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_clone_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(image_unrefp) Image *i = NULL;
+        const char *old_name, *new_name;
+        int read_only, r;
+
+        assert(bus);
+        r = sd_bus_message_read(message, "ssb", &old_name, &new_name, &read_only);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(old_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", old_name);
+        if (!image_name_is_valid(new_name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name);
+
+        r = image_find(old_name, &i);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", old_name);
+
+        r = image_clone(i, new_name, read_only);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_mark_image_read_only(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(image_unrefp) Image *i = NULL;
+        const char *name;
+        int read_only, r;
+
+        assert(bus);
+        r = sd_bus_message_read(message, "sb", &name, &read_only);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
+
+        r = image_find(name, &i);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
+
+        r = image_read_only(i, read_only);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -606,6 +690,9 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
         SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, 0),
+        SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, 0),
+        SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, 0),
+        SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, 0),
         SD_BUS_SIGNAL("MachineNew", "so", 0),
         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
         SD_BUS_VTABLE_END
diff --git a/src/shared/copy.c b/src/shared/copy.c
index 92f6e1e..3df6367 100644
--- a/src/shared/copy.c
+++ b/src/shared/copy.c
@@ -120,6 +120,7 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
 
 static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
         _cleanup_close_ int fdf = -1, fdt = -1;
+        struct timespec ts[2];
         int r, q;
 
         assert(from);
@@ -146,7 +147,10 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
         if (fchmod(fdt, st->st_mode & 07777) < 0)
                 r = -errno;
 
-        (void) copy_times(fdf, fdt);
+        ts[0] = st->st_atim;
+        ts[1] = st->st_mtim;
+        (void) futimens(fdt, ts);
+
         (void) copy_xattr(fdf, fdt);
 
         q = close(fdt);
@@ -243,14 +247,19 @@ static int fd_copy_directory(
         r = 0;
 
         if (created) {
+                struct timespec ut[2] = {
+                        st->st_atim,
+                        st->st_mtim
+                };
+
                 if (fchown(fdt, st->st_uid, st->st_gid) < 0)
                         r = -errno;
 
                 if (fchmod(fdt, st->st_mode & 07777) < 0)
                         r = -errno;
 
-                (void) copy_times(fdf, fdt);
-                (void) copy_xattr(fdf, fdt);
+                (void) futimens(fdt, ut);
+                (void) copy_xattr(dirfd(d), fdt);
         }
 
         FOREACH_DIRENT(de, d, return -errno) {
@@ -356,9 +365,11 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode) {
         assert(from);
         assert(to);
 
-        fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
-        if (fdt < 0)
-                return -errno;
+        RUN_WITH_UMASK(0000) {
+                fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode);
+                if (fdt < 0)
+                        return -errno;
+        }
 
         r = copy_file_fd(from, fdt, true);
         if (r < 0) {
@@ -375,6 +386,29 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode) {
         return 0;
 }
 
+int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace) {
+        _cleanup_free_ char *t;
+        int r;
+
+        assert(from);
+        assert(to);
+
+        r = tempfn_random(to, &t);
+        if (r < 0)
+                return r;
+
+        r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode);
+        if (r < 0)
+                return r;
+
+        if (renameat2(AT_FDCWD, t, AT_FDCWD, to, replace ? 0 : RENAME_NOREPLACE) < 0) {
+                unlink_noerrno(t);
+                return -errno;
+        }
+
+        return 0;
+}
+
 int copy_times(int fdf, int fdt) {
         struct timespec ut[2];
         struct stat st;
diff --git a/src/shared/copy.h b/src/shared/copy.h
index 6d725ef..58159a0 100644
--- a/src/shared/copy.h
+++ b/src/shared/copy.h
@@ -26,6 +26,7 @@
 
 int copy_file_fd(const char *from, int to, bool try_reflink);
 int copy_file(const char *from, const char *to, int flags, mode_t mode);
+int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace);
 int copy_tree(const char *from, const char *to, bool merge);
 int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
 int copy_directory_fd(int dirfd, const char *to, bool merge);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index fa06a0d..7c041fa 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -20,11 +20,13 @@
 ***/
 
 #include <sys/statfs.h>
+#include <fcntl.h>
 
 #include "strv.h"
 #include "utf8.h"
 #include "btrfs-util.h"
 #include "path-util.h"
+#include "copy.h"
 #include "machine-image.h"
 
 static const char image_search_path[] =
@@ -317,18 +319,158 @@ void image_hashmap_free(Hashmap *map) {
 }
 
 int image_remove(Image *i) {
-        int r;
-
         assert(i);
 
         if (path_equal(i->path, "/") ||
             path_startswith(i->path, "/usr"))
                 return -EROFS;
 
-        if (i->type == IMAGE_SUBVOLUME)
+        switch (i->type) {
+
+        case IMAGE_SUBVOLUME:
                 return btrfs_subvol_remove(i->path);
-        else
+
+        case IMAGE_DIRECTORY:
+        case IMAGE_GPT:
                 return rm_rf_dangerous(i->path, false, true, false);
+
+        default:
+                return -ENOTSUP;
+        }
+}
+
+int image_rename(Image *i, const char *new_name) {
+        _cleanup_free_ char *new_path = NULL, *nn = NULL;
+        int r;
+
+        assert(i);
+
+        if (!image_name_is_valid(new_name))
+                return -EINVAL;
+
+        if (path_equal(i->path, "/") ||
+            path_startswith(i->path, "/usr"))
+                return -EROFS;
+
+        r = image_find(new_name, NULL);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return -EEXIST;
+
+        switch (i->type) {
+
+        case IMAGE_SUBVOLUME:
+        case IMAGE_DIRECTORY:
+                new_path = file_in_same_dir(i->path, new_name);
+                break;
+
+        case IMAGE_GPT: {
+                const char *fn;
+
+                fn = strappenda(new_name, ".gpt");
+                new_path = file_in_same_dir(i->path, fn);
+                break;
+        }
+
+        default:
+                return -ENOTSUP;
+        }
+
+        if (!new_path)
+                return -ENOMEM;
+
+        nn = strdup(new_name);
+        if (!nn)
+                return -ENOMEM;
+
+        if (renameat2(AT_FDCWD, i->path, AT_FDCWD, new_path, RENAME_NOREPLACE) < 0)
+                return -errno;
+
+        free(i->path);
+        i->path = new_path;
+        new_path = NULL;
+
+        free(i->name);
+        i->name = nn;
+        nn = NULL;
+
+        return 0;
+}
+
+int image_clone(Image *i, const char *new_name, bool read_only) {
+        const char *new_path;
+        int r;
+
+        assert(i);
+
+        if (!image_name_is_valid(new_name))
+                return -EINVAL;
+
+        r = image_find(new_name, NULL);
+        if (r < 0)
+                return r;
+        if (r > 0)
+                return -EEXIST;
+
+        switch (i->type) {
+
+        case IMAGE_SUBVOLUME:
+        case IMAGE_DIRECTORY:
+                new_path = strappenda("/var/lib/container/", new_name);
+
+                r = btrfs_subvol_snapshot(i->path, new_path, read_only, true);
+                break;
+
+        case IMAGE_GPT:
+                new_path = strappenda("/var/lib/container/", new_name, ".gpt");
+
+                r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false);
+                break;
+
+        default:
+                return -ENOTSUP;
+        }
+
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int image_read_only(Image *i, bool b) {
+        int r;
+        assert(i);
+
+        if (path_equal(i->path, "/") ||
+            path_startswith(i->path, "/usr"))
+                return -EROFS;
+
+        switch (i->type) {
+
+        case IMAGE_SUBVOLUME:
+                r = btrfs_subvol_set_read_only(i->path, b);
+                if (r < 0)
+                        return r;
+                break;
+
+        case IMAGE_GPT: {
+                struct stat st;
+
+                if (stat(i->path, &st) < 0)
+                        return -errno;
+
+                if (chmod(i->path, (st.st_mode & 0444) | (b ? 0000 : 0200)) < 0)
+                        return -errno;
+                break;
+        }
+
+        case IMAGE_DIRECTORY:
+        default:
+                return -ENOTSUP;
+        }
+
+        return 0;
 }
 
 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index e17e32f..9e0f6ae 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -52,6 +52,9 @@ int image_find(const char *name, Image **ret);
 int image_discover(Hashmap *map);
 
 int image_remove(Image *i);
+int image_rename(Image *i, const char *new_name);
+int image_clone(Image *i, const char *new_name, bool read_only);
+int image_read_only(Image *i, bool b);
 
 const char* image_type_to_string(ImageType t) _const_;
 ImageType image_type_from_string(const char *s) _pure_;
diff --git a/src/shared/missing.h b/src/shared/missing.h
index dd7bef4..08cf836 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -657,3 +657,13 @@ static inline int raw_clone(unsigned long flags, void *child_stack) {
 static inline pid_t raw_getpid(void) {
         return (pid_t) syscall(__NR_getpid);
 }
+
+#if !HAVE_DECL_RENAMEAT2
+static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) {
+        return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags);
+}
+#endif
+
+#ifndef RENAME_NOREPLACE
+#define RENAME_NOREPLACE (1 << 0)
+#endif
diff --git a/src/shared/util.c b/src/shared/util.c
index d04d738..92b4d49 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -1152,7 +1152,7 @@ char *delete_chars(char *s, const char *bad) {
 }
 
 char *file_in_same_dir(const char *path, const char *filename) {
-        char *e, *r;
+        char *e, *ret;
         size_t k;
 
         assert(path);
@@ -1165,17 +1165,17 @@ char *file_in_same_dir(const char *path, const char *filename) {
         if (path_is_absolute(filename))
                 return strdup(filename);
 
-        if (!(e = strrchr(path, '/')))
+        e = strrchr(path, '/');
+        if (!e)
                 return strdup(filename);
 
         k = strlen(filename);
-        if (!(r = new(char, e-path+1+k+1)))
+        ret = new(char, (e + 1 - path) + k + 1);
+        if (!ret)
                 return NULL;
 
-        memcpy(r, path, e-path+1);
-        memcpy(r+(e-path)+1, filename, k+1);
-
-        return r;
+        memcpy(mempcpy(ret, path, e + 1 - path), filename, k + 1);
+        return ret;
 }
 
 int rmdir_parents(const char *path, const char *stop) {

commit 086821244b5113f00a0ef993b78dc56aae2a8f6c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 02:35:47 2014 +0100

    machined: add "machinectl remove" for removing images

diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index c8d1328..be23699 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -196,7 +196,6 @@ static int property_get_mtime(
                 void *userdata,
                 sd_bus_error *error) {
 
-
         _cleanup_(image_unrefp) Image *image = NULL;
         int r;
 
@@ -214,6 +213,29 @@ static int property_get_mtime(
         return 1;
 }
 
+static int method_remove(
+                sd_bus *bus,
+                sd_bus_message *message,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_(image_unrefp) Image *image = NULL;
+        int r;
+
+        assert(bus);
+        assert(message);
+
+        r = image_find_by_bus_path_with_error(sd_bus_message_get_path(message), &image, error);
+        if (r < 0)
+                return r;
+
+        r = image_remove(image);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable image_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Name",                  "s", property_get_name,      0, 0),
@@ -222,6 +244,7 @@ const sd_bus_vtable image_vtable[] = {
         SD_BUS_PROPERTY("ReadOnly",              "b", property_get_read_only, 0, 0),
         SD_BUS_PROPERTY("CreationTimestamp",     "t", property_get_crtime,    0, 0),
         SD_BUS_PROPERTY("ModificationTimestamp", "t", property_get_mtime,     0, 0),
+        SD_BUS_METHOD("Remove", NULL, NULL, method_remove, 0),
         SD_BUS_VTABLE_END
 };
 
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index 0f69734..500e5b7 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1283,6 +1283,34 @@ static int login_machine(int argc, char *argv[], void *userdata) {
         return ret;
 }
 
+static int remove_image(int argc, char *argv[], void *userdata) {
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        sd_bus *bus = userdata;
+        int i;
+
+        assert(bus);
+
+        for (i = 1; i < argc; i++) {
+                int r;
+
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.machine1",
+                                "/org/freedesktop/machine1",
+                                "org.freedesktop.machine1.Manager",
+                                "RemoveImage",
+                                &error,
+                                NULL,
+                                "s", argv[i]);
+                if (r < 0) {
+                        log_error("Could not remove image: %s", bus_error_message(&error, -r));
+                        return r;
+                }
+        }
+
+        return 0;
+}
+
 static int help(int argc, char *argv[], void *userdata) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
@@ -1316,7 +1344,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "Image Commands:\n"
                "  list-images                 Show available images\n"
                "  image-status NAME...        Show image details\n"
-               "  show-image NAME...          Show properties of image\n",
+               "  show-image NAME...          Show properties of image\n"
+               "  remove NAME...              Remove an image\n",
                program_invocation_short_name);
 
         return 0;
@@ -1452,6 +1481,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
                 { "bind",        3,        4,        0,            bind_mount        },
                 { "copy-to",     3,        4,        0,            copy_files        },
                 { "copy-from",   3,        4,        0,            copy_files        },
+                { "remove",      2,        VERB_ANY, 0,            remove_image      },
                 {}
         };
 
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 66594ab..9296377 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -551,7 +551,7 @@ static int method_open_machine_login(sd_bus *bus, sd_bus_message *message, void
 
         r = sd_bus_message_read(message, "s", &name);
         if (r < 0)
-                return sd_bus_error_set_errno(error, r);
+                return r;
 
         machine = hashmap_get(m->machines, name);
         if (!machine)
@@ -560,6 +560,34 @@ static int method_open_machine_login(sd_bus *bus, sd_bus_message *message, void
         return bus_machine_method_open_login(bus, message, machine, error);
 }
 
+static int method_remove_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_(image_unrefp) Image* i = NULL;
+        const char *name;
+        int r;
+
+        assert(bus);
+        assert(message);
+
+        r = sd_bus_message_read(message, "s", &name);
+        if (r < 0)
+                return r;
+
+        if (!image_name_is_valid(name))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", name);
+
+        r = image_find(name, &i);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name);
+
+        r = image_remove(i);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -577,6 +605,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
         SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, 0),
         SD_BUS_SIGNAL("MachineNew", "so", 0),
         SD_BUS_SIGNAL("MachineRemoved", "so", 0),
         SD_BUS_VTABLE_END
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index 51f89d5..fa06a0d 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -316,6 +316,21 @@ void image_hashmap_free(Hashmap *map) {
         hashmap_free(map);
 }
 
+int image_remove(Image *i) {
+        int r;
+
+        assert(i);
+
+        if (path_equal(i->path, "/") ||
+            path_startswith(i->path, "/usr"))
+                return -EROFS;
+
+        if (i->type == IMAGE_SUBVOLUME)
+                return btrfs_subvol_remove(i->path);
+        else
+                return rm_rf_dangerous(i->path, false, true, false);
+}
+
 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
         [IMAGE_DIRECTORY] = "directory",
         [IMAGE_SUBVOLUME] = "subvolume",
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
index 646598f..e17e32f 100644
--- a/src/shared/machine-image.h
+++ b/src/shared/machine-image.h
@@ -51,5 +51,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
 int image_find(const char *name, Image **ret);
 int image_discover(Hashmap *map);
 
+int image_remove(Image *i);
+
 const char* image_type_to_string(ImageType t) _const_;
 ImageType image_type_from_string(const char *s) _pure_;

commit 1b9cebf6385b5ea0e5ea099da163687013ac7790
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 02:07:29 2014 +0100

    nspawn: use the same image discovery logic in nspawn as in machined

diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 04396eb..f255e57 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -92,6 +92,7 @@
 #include "capability.h"
 #include "cap-list.h"
 #include "btrfs-util.h"
+#include "machine-image.h"
 
 #ifdef HAVE_SECCOMP
 #include "seccomp-util.h"
@@ -2882,15 +2883,33 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo
 }
 
 static int determine_names(void) {
+        int r;
 
         if (!arg_image && !arg_directory) {
-                if (arg_machine)
-                        arg_directory = strappend("/var/lib/container/", arg_machine);
-                else
+                if (arg_machine) {
+                        _cleanup_(image_unrefp) Image *i = NULL;
+
+                        r = image_find(arg_machine, &i);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine);
+                        else if (r == 0) {
+                                log_error("No image for machine '%s': %m", arg_machine);
+                                return -ENOENT;
+                        }
+
+                        if (i->type == IMAGE_GPT)
+                                r = set_sanitized_path(&arg_image, i->path);
+                        else
+                                r = set_sanitized_path(&arg_directory, i->path);
+                        if (r < 0)
+                                return log_error_errno(r, "Invalid image directory: %m");
+
+                        arg_read_only = arg_read_only || i->read_only;
+                } else
                         arg_directory = get_current_dir_name();
 
-                if (!arg_directory) {
-                        log_error("Failed to determine path, please use -D.");
+                if (!arg_directory && !arg_machine) {
+                        log_error("Failed to determine path, please use -D or -i.");
                         return -EINVAL;
                 }
         }

commit 003dffde2c1b93afbc9aff24b277276f65424406
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sat Dec 27 01:57:03 2014 +0100

    machined: Move image discovery logic into src/shared, so that we can make use of it from nspawn

diff --git a/Makefile.am b/Makefile.am
index f71bc9a..e1e0843 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -899,6 +899,8 @@ libsystemd_shared_la_SOURCES = \
 	src/shared/btrfs-ctree.h \
 	src/shared/verbs.c \
 	src/shared/verbs.h \
+	src/shared/machine-image.c \
+	src/shared/machine-image.h \
 	src/shared/build.h
 
 if HAVE_UTMP
@@ -5039,11 +5041,10 @@ rootlibexec_PROGRAMS += \
 libsystemd_machine_core_la_SOURCES = \
 	src/machine/machine.c \
 	src/machine/machine.h \
-	src/machine/image.c \
-	src/machine/image.h \
 	src/machine/machined-dbus.c \
 	src/machine/machine-dbus.c \
-	src/machine/image-dbus.c
+	src/machine/image-dbus.c \
+	src/machine/image-dbus.h
 
 libsystemd_machine_core_la_LIBADD = \
 	libsystemd-label.la \
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index 8d9ad55..c8d1328 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -22,7 +22,8 @@
 #include "bus-label.h"
 #include "bus-common-errors.h"
 #include "strv.h"
-#include "image.h"
+#include "machine-image.h"
+#include "image-dbus.h"
 
 static int image_find_by_bus_path(const char *path, Image **ret) {
         _cleanup_free_ char *e = NULL;
diff --git a/src/machine/image-dbus.h b/src/machine/image-dbus.h
new file mode 100644
index 0000000..d176ff7
--- /dev/null
+++ b/src/machine/image-dbus.h
@@ -0,0 +1,31 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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 "machined.h"
+
+extern const sd_bus_vtable image_vtable[];
+
+char *image_bus_path(const char *name);
+
+int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
diff --git a/src/machine/image.c b/src/machine/image.c
deleted file mode 100644
index 46a216b..0000000
--- a/src/machine/image.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2013 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/statfs.h>
-
-#include "strv.h"
-#include "utf8.h"
-#include "btrfs-util.h"
-#include "path-util.h"
-#include "image.h"
-
-static const char image_search_path[] =
-        "/var/lib/machines\0"
-        "/var/lib/container\0"
-        "/usr/local/lib/machines\0"
-        "/usr/lib/machines\0";
-
-Image *image_unref(Image *i) {
-        if (!i)
-                return NULL;
-
-        free(i->name);
-        free(i->path);
-        free(i);
-        return NULL;
-}
-
-static int image_new(
-                ImageType t,
-                const char *pretty,
-                const char *path,
-                const char *filename,
-                bool read_only,
-                usec_t crtime,
-                usec_t mtime,
-                Image **ret) {
-
-        _cleanup_(image_unrefp) Image *i = NULL;
-
-        assert(t >= 0);
-        assert(t < _IMAGE_TYPE_MAX);
-        assert(pretty);
-        assert(filename);
-        assert(ret);
-
-        i = new0(Image, 1);
-        if (!i)
-                return -ENOMEM;
-
-        i->type = t;
-        i->read_only = read_only;
-        i->crtime = crtime;
-        i->mtime = mtime;
-
-        i->name = strdup(pretty);
-        if (!i->name)
-                return -ENOMEM;
-
-        if (path)
-                i->path = strjoin(path, "/", filename, NULL);
-        else
-                i->path = strdup(filename);
-
-        if (!i->path)
-                return -ENOMEM;
-
-        path_kill_slashes(i->path);
-
-        *ret = i;
-        i = NULL;
-
-        return 0;
-}
-
-static int image_make(
-                const char *pretty,
-                int dfd,
-                const char *path,
-                const char *filename,
-                Image **ret) {
-
-        struct stat st;
-        bool read_only;
-        int r;
-
-        assert(filename);
-
-        /* We explicitly *do* follow symlinks here, since we want to
-         * allow symlinking trees into /var/lib/container/, and treat
-         * them normally. */
-
-        if (fstatat(dfd, filename, &st, 0) < 0)
-                return -errno;
-
-        read_only =
-                (path && path_startswith(path, "/usr")) ||
-                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
-
-        if (S_ISDIR(st.st_mode)) {
-
-                if (!ret)
-                        return 1;
-
-                if (!pretty)
-                        pretty = filename;
-
-                /* btrfs subvolumes have inode 256 */
-                if (st.st_ino == 256) {
-                        _cleanup_close_ int fd = -1;
-                        struct statfs sfs;
-
-                        fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
-                        if (fd < 0)
-                                return -errno;
-
-                        if (fstatfs(fd, &sfs) < 0)
-                                return -errno;
-
-                        if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
-                                BtrfsSubvolInfo info;
-
-                                /* It's a btrfs subvolume */
-
-                                r = btrfs_subvol_get_info_fd(fd, &info);
-                                if (r < 0)
-                                        return r;
-
-                                r = image_new(IMAGE_SUBVOLUME,
-                                              pretty,
-                                              path,
-                                              filename,
-                                              info.read_only || read_only,
-                                              info.otime,
-                                              0,
-                                              ret);
-                                if (r < 0)
-                                        return r;
-
-                                return 1;
-                        }
-                }
-
-                /* It's just a normal directory. */
-
-                r = image_new(IMAGE_DIRECTORY,
-                              pretty,
-                              path,
-                              filename,
-                              read_only,
-                              0,
-                              0,
-                              ret);
-                if (r < 0)
-                        return r;
-
-                return 1;
-
-        } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
-                usec_t crtime = 0;
-
-                /* It's a GPT block device */
-
-                if (!ret)
-                        return 1;
-
-                fd_getcrtime_at(dfd, filename, &crtime, 0);
-
-                if (!pretty)
-                        pretty = strndupa(filename, strlen(filename) - 4);
-
-                r = image_new(IMAGE_GPT,
-                              pretty,
-                              path,
-                              filename,
-                              !(st.st_mode & 0222) || read_only,
-                              crtime,
-                              timespec_load(&st.st_mtim),
-                              ret);
-                if (r < 0)
-                        return r;
-
-                return 1;
-        }
-
-        return 0;
-}
-
-int image_find(const char *name, Image **ret) {
-        const char *path;
-        int r;
-
-        assert(name);
-
-        /* There are no images with invalid names */
-        if (!image_name_is_valid(name))
-                return 0;
-
-        NULSTR_FOREACH(path, image_search_path) {
-                _cleanup_closedir_ DIR *d = NULL;
-
-                d = opendir(path);
-                if (!d) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        return -errno;
-                }
-
-                r = image_make(NULL, dirfd(d), path, name, ret);
-                if (r == 0 || r == -ENOENT) {
-                        _cleanup_free_ char *gpt = NULL;
-
-                        gpt = strappend(name, ".gpt");
-                        if (!gpt)
-                                return -ENOMEM;
-
-                        r = image_make(NULL, dirfd(d), path, gpt, ret);
-                        if (r == 0 || r == -ENOENT)
-                                continue;
-                }
-                if (r < 0)
-                        return r;
-
-                return 1;
-        }
-
-        if (streq(name, ".host"))
-                return image_make(".host", AT_FDCWD, NULL, "/", ret);
-
-        return 0;
-};
-
-int image_discover(Hashmap *h) {
-        const char *path;
-        int r;
-
-        assert(h);
-
-        NULSTR_FOREACH(path, image_search_path) {
-                _cleanup_closedir_ DIR *d = NULL;
-                struct dirent *de;
-
-                d = opendir(path);
-                if (!d) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        return -errno;
-                }
-
-                FOREACH_DIRENT_ALL(de, d, return -errno) {
-                        _cleanup_(image_unrefp) Image *image = NULL;
-
-                        if (!image_name_is_valid(de->d_name))
-                                continue;
-
-                        if (hashmap_contains(h, de->d_name))
-                                continue;
-
-                        r = image_make(NULL, dirfd(d), path, de->d_name, &image);
-                        if (r == 0 || r == -ENOENT)
-                                continue;
-                        if (r < 0)
-                                return r;
-
-                        r = hashmap_put(h, image->name, image);
-                        if (r < 0)
-                                return r;
-
-                        image = NULL;
-                }
-        }
-
-        if (!hashmap_contains(h, ".host")) {
-                _cleanup_(image_unrefp) Image *image = NULL;
-
-                r = image_make(".host", AT_FDCWD, NULL, "/", &image);
-                if (r < 0)
-                        return r;
-
-                r = hashmap_put(h, image->name, image);
-                if (r < 0)
-                        return r;
-
-                image = NULL;
-
-        }
-
-        return 0;
-}
-
-void image_hashmap_free(Hashmap *map) {
-        Image *i;
-
-        while ((i = hashmap_steal_first(map)))
-                image_unref(i);
-
-        hashmap_free(map);
-}
-
-static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
-        [IMAGE_DIRECTORY] = "directory",
-        [IMAGE_SUBVOLUME] = "subvolume",
-        [IMAGE_GPT] = "gpt",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
diff --git a/src/machine/image.h b/src/machine/image.h
deleted file mode 100644
index 2e8f781..0000000
--- a/src/machine/image.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#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 "time-util.h"
-#include "hashmap.h"
-#include "machined.h"
-
-typedef enum ImageType {
-        IMAGE_DIRECTORY,
-        IMAGE_SUBVOLUME,
-        IMAGE_GPT,
-        _IMAGE_TYPE_MAX,
-        _IMAGE_TYPE_INVALID = -1
-} ImageType;
-
-typedef struct Image {
-        ImageType type;
-        char *name;
-        char *path;
-        bool read_only;
-
-        usec_t crtime;
-        usec_t mtime;
-} Image;
-
-Image *image_unref(Image *i);
-void image_hashmap_free(Hashmap *map);
-
-DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
-DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
-
-int image_find(const char *name, Image **ret);
-int image_discover(Hashmap *map);
-
-extern const sd_bus_vtable image_vtable[];
-
-char *image_bus_path(const char *name);
-
-int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-
-const char* image_type_to_string(ImageType t) _const_;
-ImageType image_type_from_string(const char *s) _pure_;
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 31a6fc4..cbdbc52 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -34,6 +34,7 @@
 #include "path-util.h"
 #include "bus-internal.h"
 #include "machine.h"
+#include "machine-dbus.h"
 
 static int property_get_id(
                 sd_bus *bus,
diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h
new file mode 100644
index 0000000..6012527
--- /dev/null
+++ b/src/machine/machine-dbus.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 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-bus.h"
+#include "machine.h"
+
+extern const sd_bus_vtable machine_vtable[];
+
+char *machine_bus_path(Machine *s);
+int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+
+int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
+
+int machine_send_signal(Machine *m, bool new_machine);
+int machine_send_create_reply(Machine *m, sd_bus_error *error);
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 0d84213..8dec716 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -32,9 +32,10 @@
 #include "fileio.h"
 #include "special.h"
 #include "unit-name.h"
-#include "machine.h"
 #include "bus-util.h"
 #include "bus-error.h"
+#include "machine.h"
+#include "machine-dbus.h"
 
 Machine* machine_new(Manager *manager, const char *name) {
         Machine *m;
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 3a78ab4..64accda 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -94,22 +94,6 @@ int machine_kill(Machine *m, KillWho who, int signo);
 
 MachineState machine_get_state(Machine *u);
 
-extern const sd_bus_vtable machine_vtable[];
-
-char *machine_bus_path(Machine *s);
-int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-
-int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error);
-
-int machine_send_signal(Machine *m, bool new_machine);
-int machine_send_create_reply(Machine *m, sd_bus_error *error);
-
 const char* machine_class_to_string(MachineClass t) _const_;
 MachineClass machine_class_from_string(const char *s) _pure_;
 
diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c
index 1ca2c95..66594ab 100644
--- a/src/machine/machined-dbus.c
+++ b/src/machine/machined-dbus.c
@@ -38,8 +38,10 @@
 #include "bus-common-errors.h"
 #include "time-util.h"
 #include "cgroup-util.h"
-#include "image.h"
+#include "machine-image.h"
+#include "image-dbus.h"
 #include "machined.h"
+#include "machine-dbus.h"
 
 static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_free_ char *p = NULL;
diff --git a/src/machine/machined.h b/src/machine/machined.h
index 5fc1bd1..695ba29 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -33,7 +33,8 @@
 typedef struct Manager Manager;
 
 #include "machine.h"
-#include "image.h"
+#include "machine-dbus.h"
+#include "image-dbus.h"
 
 struct Manager {
         sd_event *event;
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
new file mode 100644
index 0000000..51f89d5
--- /dev/null
+++ b/src/shared/machine-image.c
@@ -0,0 +1,325 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 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/statfs.h>
+
+#include "strv.h"
+#include "utf8.h"
+#include "btrfs-util.h"
+#include "path-util.h"
+#include "machine-image.h"
+
+static const char image_search_path[] =
+        "/var/lib/machines\0"
+        "/var/lib/container\0"
+        "/usr/local/lib/machines\0"
+        "/usr/lib/machines\0";
+
+Image *image_unref(Image *i) {
+        if (!i)
+                return NULL;
+
+        free(i->name);
+        free(i->path);
+        free(i);
+        return NULL;
+}
+
+static int image_new(
+                ImageType t,
+                const char *pretty,
+                const char *path,
+                const char *filename,
+                bool read_only,
+                usec_t crtime,
+                usec_t mtime,
+                Image **ret) {
+
+        _cleanup_(image_unrefp) Image *i = NULL;
+
+        assert(t >= 0);
+        assert(t < _IMAGE_TYPE_MAX);
+        assert(pretty);
+        assert(filename);
+        assert(ret);
+
+        i = new0(Image, 1);
+        if (!i)
+                return -ENOMEM;
+
+        i->type = t;
+        i->read_only = read_only;
+        i->crtime = crtime;
+        i->mtime = mtime;
+
+        i->name = strdup(pretty);
+        if (!i->name)
+                return -ENOMEM;
+
+        if (path)
+                i->path = strjoin(path, "/", filename, NULL);
+        else
+                i->path = strdup(filename);
+
+        if (!i->path)
+                return -ENOMEM;
+
+        path_kill_slashes(i->path);
+
+        *ret = i;
+        i = NULL;
+
+        return 0;
+}
+
+static int image_make(
+                const char *pretty,
+                int dfd,
+                const char *path,
+                const char *filename,
+                Image **ret) {
+
+        struct stat st;
+        bool read_only;
+        int r;
+
+        assert(filename);
+
+        /* We explicitly *do* follow symlinks here, since we want to
+         * allow symlinking trees into /var/lib/container/, and treat
+         * them normally. */
+
+        if (fstatat(dfd, filename, &st, 0) < 0)
+                return -errno;
+
+        read_only =
+                (path && path_startswith(path, "/usr")) ||
+                (faccessat(dfd, filename, W_OK, AT_EACCESS) < 0 && errno == EROFS);
+
+        if (S_ISDIR(st.st_mode)) {
+
+                if (!ret)
+                        return 1;
+
+                if (!pretty)
+                        pretty = filename;
+
+                /* btrfs subvolumes have inode 256 */
+                if (st.st_ino == 256) {
+                        _cleanup_close_ int fd = -1;
+                        struct statfs sfs;
+
+                        fd = openat(dfd, filename, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
+                        if (fd < 0)
+                                return -errno;
+
+                        if (fstatfs(fd, &sfs) < 0)
+                                return -errno;
+
+                        if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
+                                BtrfsSubvolInfo info;
+
+                                /* It's a btrfs subvolume */
+
+                                r = btrfs_subvol_get_info_fd(fd, &info);
+                                if (r < 0)
+                                        return r;
+
+                                r = image_new(IMAGE_SUBVOLUME,
+                                              pretty,
+                                              path,
+                                              filename,
+                                              info.read_only || read_only,
+                                              info.otime,
+                                              0,
+                                              ret);
+                                if (r < 0)
+                                        return r;
+
+                                return 1;
+                        }
+                }
+
+                /* It's just a normal directory. */
+
+                r = image_new(IMAGE_DIRECTORY,
+                              pretty,
+                              path,
+                              filename,
+                              read_only,
+                              0,
+                              0,
+                              ret);
+                if (r < 0)
+                        return r;
+
+                return 1;
+
+        } else if (S_ISREG(st.st_mode) && endswith(filename, ".gpt")) {
+                usec_t crtime = 0;
+
+                /* It's a GPT block device */
+
+                if (!ret)
+                        return 1;
+
+                fd_getcrtime_at(dfd, filename, &crtime, 0);
+
+                if (!pretty)
+                        pretty = strndupa(filename, strlen(filename) - 4);
+
+                r = image_new(IMAGE_GPT,
+                              pretty,
+                              path,
+                              filename,
+                              !(st.st_mode & 0222) || read_only,
+                              crtime,
+                              timespec_load(&st.st_mtim),
+                              ret);
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        return 0;
+}
+
+int image_find(const char *name, Image **ret) {
+        const char *path;
+        int r;
+
+        assert(name);
+
+        /* There are no images with invalid names */
+        if (!image_name_is_valid(name))
+                return 0;
+
+        NULSTR_FOREACH(path, image_search_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+
+                d = opendir(path);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                r = image_make(NULL, dirfd(d), path, name, ret);
+                if (r == 0 || r == -ENOENT) {
+                        _cleanup_free_ char *gpt = NULL;
+
+                        gpt = strappend(name, ".gpt");
+                        if (!gpt)
+                                return -ENOMEM;
+
+                        r = image_make(NULL, dirfd(d), path, gpt, ret);
+                        if (r == 0 || r == -ENOENT)
+                                continue;
+                }
+                if (r < 0)
+                        return r;
+
+                return 1;
+        }
+
+        if (streq(name, ".host"))
+                return image_make(".host", AT_FDCWD, NULL, "/", ret);
+
+        return 0;
+};
+
+int image_discover(Hashmap *h) {
+        const char *path;
+        int r;
+
+        assert(h);
+
+        NULSTR_FOREACH(path, image_search_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                struct dirent *de;
+
+                d = opendir(path);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                FOREACH_DIRENT_ALL(de, d, return -errno) {
+                        _cleanup_(image_unrefp) Image *image = NULL;
+
+                        if (!image_name_is_valid(de->d_name))
+                                continue;
+
+                        if (hashmap_contains(h, de->d_name))
+                                continue;
+
+                        r = image_make(NULL, dirfd(d), path, de->d_name, &image);
+                        if (r == 0 || r == -ENOENT)
+                                continue;
+                        if (r < 0)
+                                return r;
+
+                        r = hashmap_put(h, image->name, image);
+                        if (r < 0)
+                                return r;
+
+                        image = NULL;
+                }
+        }
+
+        if (!hashmap_contains(h, ".host")) {
+                _cleanup_(image_unrefp) Image *image = NULL;
+
+                r = image_make(".host", AT_FDCWD, NULL, "/", &image);
+                if (r < 0)
+                        return r;
+
+                r = hashmap_put(h, image->name, image);
+                if (r < 0)
+                        return r;
+
+                image = NULL;
+
+        }
+
+        return 0;
+}
+
+void image_hashmap_free(Hashmap *map) {
+        Image *i;
+
+        while ((i = hashmap_steal_first(map)))
+                image_unref(i);
+
+        hashmap_free(map);
+}
+
+static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
+        [IMAGE_DIRECTORY] = "directory",
+        [IMAGE_SUBVOLUME] = "subvolume",
+        [IMAGE_GPT] = "gpt",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(image_type, ImageType);
diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h
new file mode 100644
index 0000000..646598f
--- /dev/null
+++ b/src/shared/machine-image.h
@@ -0,0 +1,55 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#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 "time-util.h"
+#include "hashmap.h"
+
+typedef enum ImageType {
+        IMAGE_DIRECTORY,
+        IMAGE_SUBVOLUME,
+        IMAGE_GPT,
+        _IMAGE_TYPE_MAX,
+        _IMAGE_TYPE_INVALID = -1
+} ImageType;
+
+typedef struct Image {
+        ImageType type;
+        char *name;
+        char *path;
+        bool read_only;
+
+        usec_t crtime;
+        usec_t mtime;
+} Image;
+
+Image *image_unref(Image *i);
+void image_hashmap_free(Hashmap *map);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(Image*, image_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, image_hashmap_free);
+
+int image_find(const char *name, Image **ret);
+int image_discover(Hashmap *map);
+
+const char* image_type_to_string(ImageType t) _const_;
+ImageType image_type_from_string(const char *s) _pure_;



More information about the systemd-commits mailing list