From c2ce6a3d82b717c4c1e6245ad8c6ce1173f502d0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 19 Dec 2014 20:07:23 +0100 Subject: [PATCH 1/1] machined: add new GetImage() bus call for retrieving the bus path for an image --- src/libsystemd/sd-bus/bus-common-errors.c | 1 + src/libsystemd/sd-bus/bus-common-errors.h | 1 + src/machine/image.c | 248 +++++++++++++--------- src/machine/image.h | 1 + src/machine/machined-dbus.c | 28 +++ src/machine/org.freedesktop.machine1.conf | 4 + src/shared/util.c | 17 ++ src/shared/util.h | 1 + 8 files changed, 200 insertions(+), 101 deletions(-) diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 3dc00b5e4..8e9073879 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -45,6 +45,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 5b7f41ef1..9007b85c1 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -43,6 +43,7 @@ #define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" +#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" #define BUS_ERROR_NO_MACHINE_FOR_PID "org.freedesktop.machine1.NoMachineForPID" #define BUS_ERROR_MACHINE_EXISTS "org.freedesktop.machine1.MachineExists" #define BUS_ERROR_NO_PRIVATE_NETWORKING "org.freedesktop.machine1.NoPrivateNetworking" diff --git a/src/machine/image.c b/src/machine/image.c index 0ba965249..8f577adb5 100644 --- a/src/machine/image.c +++ b/src/machine/image.c @@ -27,6 +27,10 @@ #include "image.h" #include "bus-label.h" +static const char image_search_path[] = + "/var/lib/container\0" + "/var/lib/machine\0"; + Image *image_unref(Image *i) { if (!i) return NULL; @@ -37,24 +41,23 @@ Image *image_unref(Image *i) { return NULL; } -static int add_image( - Hashmap *h, +static int image_new( ImageType t, const char *name, const char *path, bool read_only, usec_t mtime, - usec_t btime) { + usec_t btime, + Image **ret) { _cleanup_(image_unrefp) Image *i = NULL; - int r; - assert(h); assert(t >= 0); assert(t < _IMAGE_TYPE_MAX); assert(name); + assert(ret); - i = new(Image, 1); + i = new0(Image, 1); if (!i) return -ENOMEM; @@ -73,136 +76,179 @@ static int add_image( return -ENOMEM; } - r = hashmap_put(h, i->name, i); - if (r < 0) - return r; - + *ret = i; i = NULL; + return 0; } -int image_discover(Hashmap *h) { - const char *path; +static int image_make(int dfd, const char *name, const char *path, Image **ret) { + struct stat st; int r; - assert(h); + assert(dfd >= 0); + assert(name); - FOREACH_STRING(path, "/var/lib/container", "/var/lib/machine") { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; + /* We explicitly *do* follow symlinks here, since we want to + * allow symlinking trees into /var/lib/container/, and treat + * them normally. */ - d = opendir(path); - if (!d) { - if (errno == ENOENT) - return 0; + if (fstatat(dfd, name, &st, 0) < 0) + return -errno; - return -errno; - } + if (S_ISDIR(st.st_mode)) { - FOREACH_DIRENT_ALL(de, d, return -errno) { - struct stat st; + if (!ret) + return 1; - if (STR_IN_SET(de->d_name, ".", "..")) - continue; + /* btrfs subvolumes have inode 256 */ + if (st.st_ino == 256) { + _cleanup_close_ int fd = -1; + struct statfs sfs; - /* Temporary files for atomically creating new files */ - if (startswith(de->d_name, ".#")) - continue; + fd = openat(dfd, name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); + if (fd < 0) + return -errno; - if (string_has_cc(de->d_name, NULL)) - continue; + if (fstatfs(fd, &sfs) < 0) + return -errno; - if (!utf8_is_valid(de->d_name)) - continue; + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) { + usec_t btime = 0; + int ro; - if (hashmap_contains(h, de->d_name)) - continue; + /* It's a btrfs subvolume */ - /* We explicitly *do* follow symlinks here, - * since we want to allow symlinking trees - * into /var/lib/container/, and treat them - * normally. */ - if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { - if (errno == ENOENT) - continue; + ro = btrfs_subvol_is_read_only_fd(fd); + if (ro < 0) + return ro; - return -errno; + /* r = btrfs_subvol_get_btime(fd, &btime); */ + /* if (r < 0) */ + /* return r; */ + + r = image_new(IMAGE_SUBVOLUME, + name, + path, + ro, + 0, + btime, + ret); + if (r < 0) + return r; + + return 1; } + } - if (S_ISDIR(st.st_mode)) { + /* It's just a normal directory. */ - /* btrfs subvolumes have inode 256 */ - if (st.st_ino == 256) { - _cleanup_close_ int fd = -1; - struct statfs sfs; + r = image_new(IMAGE_DIRECTORY, + name, + path, + false, + 0, + 0, + ret); + if (r < 0) + return r; - fd = openat(dirfd(d), de->d_name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (fd < 0) { - if (errno == ENOENT) - continue; + return 1; - return -errno; - } + } else if (S_ISREG(st.st_mode) && endswith(name, ".gpt")) { - if (fstatfs(fd, &sfs) < 0) - return -errno; + /* It's a GPT block device */ - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) { - usec_t btime = 0; - int ro; + if (!ret) + return 1; - /* It's a btrfs subvolume */ + r = image_new(IMAGE_GPT, + name, + path, + !!(st.st_mode & 0111), + timespec_load(&st.st_mtim), + 0, + ret); + if (r < 0) + return r; - ro = btrfs_subvol_is_read_only_fd(fd); - if (ro < 0) - return ro; + return 1; + } - /* r = btrfs_subvol_get_btime(fd, &btime); */ - /* if (r < 0) */ - /* return r; */ + return 0; +} - r = add_image(h, - IMAGE_SUBVOLUME, - de->d_name, - path, - ro, - 0, - btime); +int image_find(const char *name, Image **ret) { + const char *path; + int r; - if (r < 0) - return r; + assert(name); - continue; - } - } + /* There are no images with invalid names */ + if (!image_name_is_valid(name)) + return 0; - /* It's just a normal directory. */ + NULSTR_FOREACH(path, image_search_path) { + _cleanup_closedir_ DIR *d = NULL; - r = add_image(h, - IMAGE_DIRECTORY, - de->d_name, - path, - false, - 0, - 0); - if (r < 0) - return r; + d = opendir(path); + if (!d) { + if (errno == ENOENT) + continue; - } else if (S_ISREG(st.st_mode) && - endswith(de->d_name, ".gpt")) { + return -errno; + } - /* It's a GPT block device */ + r = image_make(dirfd(d), name, path, ret); + if (r == 0 || r == -ENOENT) + continue; + if (r < 0) + return r; - r = add_image(h, - IMAGE_GPT, - de->d_name, - path, - !!(st.st_mode & 0111), - timespec_load(&st.st_mtim), - 0); - if (r < 0) - return r; - } + return 1; + } + + 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) + return 0; + + 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(dirfd(d), de->d_name, path, &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; } } diff --git a/src/machine/image.h b/src/machine/image.h index c77fd19d8..f298fc304 100644 --- a/src/machine/image.h +++ b/src/machine/image.h @@ -46,6 +46,7 @@ Image *image_unref(Image *i); void image_hashmap_free(Hashmap *map); +int image_find(const char *name, Image **ret); int image_discover(Hashmap *map); char *image_bus_path(const char *name); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 949c7d6b2..022956423 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -68,6 +68,33 @@ static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userda return sd_bus_reply_method_return(message, "o", p); } +static int method_get_image(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_free_ char *p = NULL; + Manager *m = userdata; + const char *name; + int r; + + assert(bus); + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = image_find(name, NULL); + if (r == 0) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_IMAGE, "No image '%s' known", name); + if (r < 0) + return r; + + p = image_bus_path(name); + if (!p) + return -ENOMEM; + + return sd_bus_reply_method_return(message, "o", p); +} + static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *p = NULL; Manager *m = userdata; @@ -491,6 +518,7 @@ static int method_list_images(sd_bus *bus, sd_bus_message *message, void *userda const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED), + 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(ssbo)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index 3745c527f..bd8fbeff4 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -52,6 +52,10 @@ send_interface="org.freedesktop.machine1.Manager" send_member="GetMachineByPID"/> + + diff --git a/src/shared/util.c b/src/shared/util.c index 1ad82b27d..06b607784 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -4257,6 +4257,23 @@ bool machine_name_is_valid(const char *s) { return true; } +bool image_name_is_valid(const char *s) { + if (!filename_is_valid(s)) + return false; + + if (string_has_cc(s, NULL)) + return false; + + if (!utf8_is_valid(s)) + return false; + + /* Temporary files for atomically creating new files */ + if (startswith(s, ".#")) + return false; + + return true; +} + int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, diff --git a/src/shared/util.h b/src/shared/util.h index 712f65a95..1804b8c3a 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -545,6 +545,7 @@ bool hostname_is_valid(const char *s) _pure_; char* hostname_cleanup(char *s, bool lowercase); bool machine_name_is_valid(const char *s) _pure_; +bool image_name_is_valid(const char *s) _pure_; char* strshorten(char *s, size_t l); -- 2.30.2