chiark / gitweb /
Fix check_loopback()
[elogind.git] / src / machine / image.c
index 0ba96524977812b8f6f8090619d59403e771bc28..46a216bbb77a65fc50090c1f6a998c8bc0c0859d 100644 (file)
 #include "strv.h"
 #include "utf8.h"
 #include "btrfs-util.h"
+#include "path-util.h"
 #include "image.h"
-#include "bus-label.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)
@@ -37,173 +43,265 @@ Image *image_unref(Image *i) {
         return NULL;
 }
 
-static int add_image(
-                Hashmap *h,
+static int image_new(
                 ImageType t,
-                const char *name,
+                const char *pretty,
                 const char *path,
+                const char *filename,
                 bool read_only,
+                usec_t crtime,
                 usec_t mtime,
-                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(pretty);
+        assert(filename);
+        assert(ret);
 
-        i = new(Image, 1);
+        i = new0(Image, 1);
         if (!i)
                 return -ENOMEM;
 
         i->type = t;
         i->read_only = read_only;
+        i->crtime = crtime;
         i->mtime = mtime;
-        i->btime = btime;
 
-        i->name = strdup(name);
+        i->name = strdup(pretty);
         if (!i->name)
                 return -ENOMEM;
 
-        if (path) {
-                i->path = strdup(path);
-                if (!i->path)
-                        return -ENOMEM;
-        }
+        if (path)
+                i->path = strjoin(path, "/", filename, NULL);
+        else
+                i->path = strdup(filename);
+
+        if (!i->path)
+                return -ENOMEM;
 
-        r = hashmap_put(h, i->name, i);
-        if (r < 0)
-                return r;
+        path_kill_slashes(i->path);
 
+        *ret = i;
         i = NULL;
+
         return 0;
 }
 
-int image_discover(Hashmap *h) {
+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(h);
+        assert(name);
 
-        FOREACH_STRING(path, "/var/lib/container", "/var/lib/machine") {
+        /* 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;
-                struct dirent *de;
 
                 d = opendir(path);
                 if (!d) {
                         if (errno == ENOENT)
-                                return 0;
+                                continue;
 
                         return -errno;
                 }
 
-                FOREACH_DIRENT_ALL(de, d, return -errno) {
-                        struct stat st;
-
-                        if (STR_IN_SET(de->d_name, ".", ".."))
-                                continue;
-
-                        /* Temporary files for atomically creating new files */
-                        if (startswith(de->d_name, ".#"))
-                                continue;
+                r = image_make(NULL, dirfd(d), path, name, ret);
+                if (r == 0 || r == -ENOENT) {
+                        _cleanup_free_ char *gpt = NULL;
 
-                        if (string_has_cc(de->d_name, NULL))
-                                continue;
+                        gpt = strappend(name, ".gpt");
+                        if (!gpt)
+                                return -ENOMEM;
 
-                        if (!utf8_is_valid(de->d_name))
+                        r = image_make(NULL, dirfd(d), path, gpt, ret);
+                        if (r == 0 || r == -ENOENT)
                                 continue;
+                }
+                if (r < 0)
+                        return r;
 
-                        if (hashmap_contains(h, de->d_name))
-                                continue;
+                return 1;
+        }
 
-                        /* 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;
+        if (streq(name, ".host"))
+                return image_make(".host", AT_FDCWD, NULL, "/", ret);
 
-                                return -errno;
-                        }
-
-                        if (S_ISDIR(st.st_mode)) {
+        return 0;
+};
 
-                                /* btrfs subvolumes have inode 256 */
-                                if (st.st_ino == 256) {
-                                        _cleanup_close_ int fd = -1;
-                                        struct statfs sfs;
+int image_discover(Hashmap *h) {
+        const char *path;
+        int r;
 
-                                        fd = openat(dirfd(d), de->d_name, O_CLOEXEC|O_NOCTTY|O_DIRECTORY);
-                                        if (fd < 0) {
-                                                if (errno == ENOENT)
-                                                        continue;
+        assert(h);
 
-                                                return -errno;
-                                        }
+        NULSTR_FOREACH(path, image_search_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                struct dirent *de;
 
-                                        if (fstatfs(fd, &sfs) < 0)
-                                                return -errno;
+                d = opendir(path);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
 
-                                        if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) {
-                                                usec_t btime = 0;
-                                                int ro;
+                        return -errno;
+                }
 
-                                                /* It's a btrfs subvolume */
+                FOREACH_DIRENT_ALL(de, d, return -errno) {
+                        _cleanup_(image_unrefp) Image *image = NULL;
 
-                                                ro = btrfs_subvol_is_read_only_fd(fd);
-                                                if (ro < 0)
-                                                        return ro;
+                        if (!image_name_is_valid(de->d_name))
+                                continue;
 
-                                                /* r = btrfs_subvol_get_btime(fd, &btime); */
-                                                /* if (r < 0) */
-                                                /*         return r; */
+                        if (hashmap_contains(h, de->d_name))
+                                continue;
 
-                                                r = add_image(h,
-                                                              IMAGE_SUBVOLUME,
-                                                              de->d_name,
-                                                              path,
-                                                              ro,
-                                                              0,
-                                                              btime);
+                        r = image_make(NULL, dirfd(d), path, de->d_name, &image);
+                        if (r == 0 || r == -ENOENT)
+                                continue;
+                        if (r < 0)
+                                return r;
 
-                                                if (r < 0)
-                                                        return r;
+                        r = hashmap_put(h, image->name, image);
+                        if (r < 0)
+                                return r;
 
-                                                continue;
-                                        }
-                                }
+                        image = NULL;
+                }
+        }
 
-                                /* It's just a normal directory. */
+        if (!hashmap_contains(h, ".host")) {
+                _cleanup_(image_unrefp) Image *image = NULL;
 
-                                r = add_image(h,
-                                              IMAGE_DIRECTORY,
-                                              de->d_name,
-                                              path,
-                                              false,
-                                              0,
-                                              0);
-                                if (r < 0)
-                                        return r;
+                r = image_make(".host", AT_FDCWD, NULL, "/", &image);
+                if (r < 0)
+                        return r;
 
-                        } else if (S_ISREG(st.st_mode) &&
-                                   endswith(de->d_name, ".gpt")) {
+                r = hashmap_put(h, image->name, image);
+                if (r < 0)
+                        return r;
 
-                                /* It's a GPT block device */
+                image = NULL;
 
-                                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 0;
@@ -218,18 +316,6 @@ void image_hashmap_free(Hashmap *map) {
         hashmap_free(map);
 }
 
-char *image_bus_path(const char *name) {
-        _cleanup_free_ char *e = NULL;
-
-        assert(name);
-
-        e = bus_label_escape(name);
-        if (!e)
-                return NULL;
-
-        return strappend("/org/freedesktop/machine1/image/", e);
-}
-
 static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
         [IMAGE_DIRECTORY] = "directory",
         [IMAGE_SUBVOLUME] = "subvolume",