]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
storage: add storage_utils.{c.h}
authorChristian Brauner <christian.brauner@ubuntu.com>
Sat, 1 Jul 2017 16:15:59 +0000 (18:15 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sun, 2 Jul 2017 12:40:04 +0000 (14:40 +0200)
non-functional changes

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/Makefile.am
src/lxc/bdev/bdev.c
src/lxc/bdev/bdev.h
src/lxc/bdev/lxcloop.c
src/lxc/bdev/lxclvm.c
src/lxc/bdev/lxcnbd.c
src/lxc/bdev/lxcrbd.c
src/lxc/bdev/storage_utils.c [new file with mode: 0644]
src/lxc/bdev/storage_utils.h [new file with mode: 0644]
src/lxc/start.c
src/lxc/tools/lxc_create.c

index ea1982ea7dbd1d8fadfccc7af219318dfbe32cbd..54fad1226b87a46a200572cac2f5df686f253e05 100644 (file)
@@ -17,6 +17,7 @@ noinst_HEADERS = \
        bdev/lxcrbd.h \
        bdev/lxcrsync.h \
        bdev/lxczfs.h \
+       bdev/storage_utils.h \
        cgroups/cgroup.h \
        caps.h \
        conf.h \
@@ -86,6 +87,7 @@ liblxc_la_SOURCES = \
        bdev/lxcrbd.c bdev/lxcrbd.h \
        bdev/lxcrsync.c bdev/lxcrsync.h \
        bdev/lxczfs.c bdev/lxczfs.h \
+       bdev/storage_utils.c bdev/storage_utils.h \
        cgroups/cgfs.c \
        cgroups/cgfsng.c \
        cgroups/cgroup.c cgroups/cgroup.h \
index 55abefa389d1e86e1cad5ceddf6f8c0eb54a4aad..ea1bca17a14d2eaabe52f04acf2478778710a474 100644 (file)
@@ -65,6 +65,7 @@
 #include "lxczfs.h"
 #include "namespace.h"
 #include "parse.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 #ifndef BLKGETSIZE64
@@ -200,86 +201,85 @@ static const struct bdev_type bdevs[] = {
 
 static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type);
 
-/* helpers */
-static const struct bdev_type *bdev_query(struct lxc_conf *conf,
-                                         const char *src);
-static struct bdev *bdev_get(const char *type);
-static struct bdev *do_bdev_create(const char *dest, const char *type,
-                                  const char *cname, struct bdev_specs *specs);
-static int find_fstype_cb(char *buffer, void *data);
-static char *linkderef(char *path, char *dest);
-static bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
-                               bool maybesnap);
-
-/* the bulk of this needs to become a common helper */
-char *dir_new_path(char *src, const char *oldname, const char *name,
-                  const char *oldpath, const char *lxcpath)
+static const struct bdev_type *get_bdev_by_name(const char *name)
 {
-       char *ret, *p, *p2;
-       int l1, l2, nlen;
-
-       nlen = strlen(src) + 1;
-       l1 = strlen(oldpath);
-       p = src;
-       /* if src starts with oldpath, look for oldname only after
-        * that path */
-       if (strncmp(src, oldpath, l1) == 0) {
-               p += l1;
-               nlen += (strlen(lxcpath) - l1);
-       }
-       l2 = strlen(oldname);
-       while ((p = strstr(p, oldname)) != NULL) {
-               p += l2;
-               nlen += strlen(name) - l2;
-       }
+       size_t i, cmplen;
 
-       ret = malloc(nlen);
-       if (!ret)
+       cmplen = strcspn(name, ":");
+       if (cmplen == 0)
                return NULL;
 
-       p = ret;
-       if (strncmp(src, oldpath, l1) == 0) {
-               p += sprintf(p, "%s", lxcpath);
-               src += l1;
-       }
+       for (i = 0; i < numbdevs; i++)
+               if (strncmp(bdevs[i].name, name, cmplen) == 0)
+                       break;
 
-       while ((p2 = strstr(src, oldname)) != NULL) {
-               strncpy(p, src, p2 - src); // copy text up to oldname
-               p += p2 - src;             // move target pointer (p)
-               p += sprintf(p, "%s",
-                            name); // print new name in place of oldname
-               src = p2 + l2;      // move src to end of oldname
-       }
-       sprintf(p, "%s", src); // copy the rest of src
-       return ret;
+       if (i == numbdevs)
+               return NULL;
+
+       DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
+       return &bdevs[i];
 }
 
-/*
- * attach_block_device returns true if all went well,
- * meaning either a block device was attached or was not
- * needed.  It returns false if something went wrong and
- * container startup should be stopped.
- */
-bool attach_block_device(struct lxc_conf *conf)
+const struct bdev_type *bdev_query(struct lxc_conf *conf, const char *src)
 {
-       char *path;
+       size_t i;
+       const struct bdev_type *bdev;
 
-       if (!conf->rootfs.path)
-               return true;
+       bdev = get_bdev_by_name(src);
+       if (bdev)
+               return bdev;
 
-       path = conf->rootfs.path;
-       if (!requires_nbd(path))
-               return true;
+       for (i = 0; i < numbdevs; i++)
+               if (bdevs[i].ops->detect(src))
+                       break;
 
-       path = strchr(path, ':');
-       if (!path)
-               return false;
+       if (i == numbdevs)
+               return NULL;
 
-       path++;
-       if (!attach_nbd(path, conf))
-               return false;
+       DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
+       return &bdevs[i];
+}
+
+struct bdev *bdev_get(const char *type)
+{
+       size_t i;
+       struct bdev *bdev;
+
+       for (i = 0; i < numbdevs; i++) {
+               if (strcmp(bdevs[i].name, type) == 0)
+                       break;
+       }
+
+       if (i == numbdevs)
+               return NULL;
+
+       bdev = malloc(sizeof(struct bdev));
+       if (!bdev)
+               return NULL;
+
+       memset(bdev, 0, sizeof(struct bdev));
+       bdev->ops = bdevs[i].ops;
+       bdev->type = bdevs[i].name;
+
+       return bdev;
+}
+
+static struct bdev *do_bdev_create(const char *dest, const char *type,
+                                  const char *cname, struct bdev_specs *specs)
+{
+
+       struct bdev *bdev;
+
+       bdev = bdev_get(type);
+       if (!bdev)
+               return NULL;
 
-       return true;
+       if (bdev->ops->create(bdev, dest, cname, specs) < 0) {
+               bdev_put(bdev);
+               return NULL;
+       }
+
+       return bdev;
 }
 
 bool bdev_can_backup(struct lxc_conf *conf)
@@ -529,29 +529,6 @@ bool bdev_destroy(struct lxc_conf *conf)
        return ret;
 }
 
-int bdev_destroy_wrapper(void *data)
-{
-       struct lxc_conf *conf = data;
-
-       if (setgid(0) < 0) {
-               ERROR("Failed to setgid to 0");
-               return -1;
-       }
-
-       if (setgroups(0, NULL) < 0)
-               WARN("Failed to clear groups");
-
-       if (setuid(0) < 0) {
-               ERROR("Failed to setuid to 0");
-               return -1;
-       }
-
-       if (!bdev_destroy(conf))
-               return -1;
-
-       return 0;
-}
-
 struct bdev *bdev_init(struct lxc_conf *conf, const char *src, const char *dst,
                       const char *mntopts)
 {
@@ -607,219 +584,6 @@ void bdev_put(struct bdev *bdev)
        free(bdev);
 }
 
-/*
- * return block size of dev->src in units of bytes
- */
-int blk_getsize(struct bdev *bdev, uint64_t *size)
-{
-       int fd, ret;
-       char *path = bdev->src;
-
-       if (strcmp(bdev->type, "loop") == 0)
-               path = bdev->src + 5;
-
-       fd = open(path, O_RDONLY);
-       if (fd < 0)
-               return -1;
-
-       ret = ioctl(fd, BLKGETSIZE64, size); // size of device in bytes
-       close(fd);
-       return ret;
-}
-
-void detach_block_device(struct lxc_conf *conf)
-{
-       if (conf->nbd_idx != -1)
-               detach_nbd_idx(conf->nbd_idx);
-}
-
-/*
- * Given a bdev (presumably blockdev-based), detect the fstype
- * by trying mounting (in a private mntns) it.
- * @bdev: bdev to investigate
- * @type: preallocated char* in which to write the fstype
- * @len: length of passed in char*
- * Returns length of fstype, of -1 on error
- */
-int detect_fs(struct bdev *bdev, char *type, int len)
-{
-       int p[2], ret;
-       size_t linelen;
-       pid_t pid;
-       FILE *f;
-       char *sp1, *sp2, *sp3, *line = NULL;
-       char *srcdev;
-
-       if (!bdev || !bdev->src || !bdev->dest)
-               return -1;
-
-       srcdev = bdev->src;
-       if (strcmp(bdev->type, "loop") == 0)
-               srcdev = bdev->src + 5;
-
-       ret = pipe(p);
-       if (ret < 0)
-               return -1;
-
-       if ((pid = fork()) < 0)
-               return -1;
-
-       if (pid > 0) {
-               int status;
-               close(p[1]);
-               memset(type, 0, len);
-               ret = read(p[0], type, len - 1);
-               close(p[0]);
-               if (ret < 0) {
-                       SYSERROR("error reading from pipe");
-                       wait(&status);
-                       return -1;
-               } else if (ret == 0) {
-                       ERROR("child exited early - fstype not found");
-                       wait(&status);
-                       return -1;
-               }
-               wait(&status);
-               type[len - 1] = '\0';
-               INFO("detected fstype %s for %s", type, srcdev);
-               return ret;
-       }
-
-       if (unshare(CLONE_NEWNS) < 0)
-               exit(1);
-
-       if (detect_shared_rootfs()) {
-               if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
-                       SYSERROR("Failed to make / rslave");
-                       ERROR("Continuing...");
-               }
-       }
-
-       ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts);
-       if (ret < 0) {
-               ERROR("failed mounting %s onto %s to detect fstype", srcdev,
-                     bdev->dest);
-               exit(1);
-       }
-
-       // if symlink, get the real dev name
-       char devpath[MAXPATHLEN];
-       char *l = linkderef(srcdev, devpath);
-       if (!l)
-               exit(1);
-       f = fopen("/proc/self/mounts", "r");
-       if (!f)
-               exit(1);
-
-       while (getline(&line, &linelen, f) != -1) {
-               sp1 = strchr(line, ' ');
-               if (!sp1)
-                       exit(1);
-               *sp1 = '\0';
-               if (strcmp(line, l))
-                       continue;
-               sp2 = strchr(sp1 + 1, ' ');
-               if (!sp2)
-                       exit(1);
-               *sp2 = '\0';
-               sp3 = strchr(sp2 + 1, ' ');
-               if (!sp3)
-                       exit(1);
-               *sp3 = '\0';
-               sp2++;
-               if (write(p[1], sp2, strlen(sp2)) != strlen(sp2))
-                       exit(1);
-
-               exit(0);
-       }
-
-       exit(1);
-}
-
-int do_mkfs_exec_wrapper(void *args)
-{
-       int ret;
-       char *mkfs;
-       char **data = args;
-       /* strlen("mkfs.")
-        * +
-        * strlen(data[0])
-        * +
-        * \0
-        */
-       size_t len = 5 + strlen(data[0]) + 1;
-
-       mkfs = malloc(len);
-       if (!mkfs)
-               return -1;
-
-       ret = snprintf(mkfs, len, "mkfs.%s", data[0]);
-       if (ret < 0 || (size_t)ret >= len) {
-               free(mkfs);
-               return -1;
-       }
-
-       TRACE("executing \"%s %s\"", mkfs, data[1]);
-       execlp(mkfs, mkfs, data[1], (char *)NULL);
-       SYSERROR("failed to run \"%s %s \"", mkfs, data[1]);
-       return -1;
-}
-
-/*
- * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm
- * is a block device.
- */
-int is_blktype(struct bdev *b)
-{
-       if (strcmp(b->type, "lvm") == 0)
-               return 1;
-
-       return 0;
-}
-
-int mount_unknown_fs(const char *rootfs, const char *target,
-                    const char *options)
-{
-       size_t i;
-       int ret;
-       struct cbarg {
-               const char *rootfs;
-               const char *target;
-               const char *options;
-       } cbarg = {
-           .rootfs = rootfs,
-           .target = target,
-           .options = options,
-       };
-
-       /*
-        * find the filesystem type with brute force:
-        * first we check with /etc/filesystems, in case the modules
-        * are auto-loaded and fall back to the supported kernel fs
-        */
-       char *fsfile[] = {
-           "/etc/filesystems",
-           "/proc/filesystems",
-       };
-
-       for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) {
-               if (access(fsfile[i], F_OK))
-                       continue;
-
-               ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
-               if (ret < 0) {
-                       ERROR("failed to parse '%s'", fsfile[i]);
-                       return -1;
-               }
-
-               if (ret)
-                       return 0;
-       }
-
-       ERROR("failed to determine fs type for '%s'", rootfs);
-       return -1;
-}
-
 bool rootfs_is_blockdev(struct lxc_conf *conf)
 {
        const struct bdev_type *q;
@@ -845,205 +609,3 @@ bool rootfs_is_blockdev(struct lxc_conf *conf)
 
        return false;
 }
-
-static struct bdev *do_bdev_create(const char *dest, const char *type,
-                                  const char *cname, struct bdev_specs *specs)
-{
-
-       struct bdev *bdev;
-
-       bdev = bdev_get(type);
-       if (!bdev)
-               return NULL;
-
-       if (bdev->ops->create(bdev, dest, cname, specs) < 0) {
-               bdev_put(bdev);
-               return NULL;
-       }
-
-       return bdev;
-}
-
-static struct bdev *bdev_get(const char *type)
-{
-       size_t i;
-       struct bdev *bdev;
-
-       for (i = 0; i < numbdevs; i++) {
-               if (strcmp(bdevs[i].name, type) == 0)
-                       break;
-       }
-
-       if (i == numbdevs)
-               return NULL;
-
-       bdev = malloc(sizeof(struct bdev));
-       if (!bdev)
-               return NULL;
-
-       memset(bdev, 0, sizeof(struct bdev));
-       bdev->ops = bdevs[i].ops;
-       bdev->type = bdevs[i].name;
-
-       return bdev;
-}
-
-static const struct bdev_type *get_bdev_by_name(const char *name)
-{
-       size_t i, cmplen;
-
-       cmplen = strcspn(name, ":");
-       if (cmplen == 0)
-               return NULL;
-
-       for (i = 0; i < numbdevs; i++)
-               if (strncmp(bdevs[i].name, name, cmplen) == 0)
-                       break;
-
-       if (i == numbdevs)
-               return NULL;
-
-       DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
-       return &bdevs[i];
-}
-
-static const struct bdev_type *bdev_query(struct lxc_conf *conf,
-                                         const char *src)
-{
-       size_t i;
-       const struct bdev_type *bdev;
-
-       bdev = get_bdev_by_name(src);
-       if (bdev)
-               return bdev;
-
-       for (i = 0; i < numbdevs; i++)
-               if (bdevs[i].ops->detect(src))
-                       break;
-
-       if (i == numbdevs)
-               return NULL;
-
-       DEBUG("Detected rootfs type \"%s\"", bdevs[i].name);
-       return &bdevs[i];
-}
-
-/*
- * These are copied from conf.c.  However as conf.c will be moved to using
- * the callback system, they can be pulled from there eventually, so we
- * don't need to pollute utils.c with these low level functions
- */
-static int find_fstype_cb(char *buffer, void *data)
-{
-       struct cbarg {
-               const char *rootfs;
-               const char *target;
-               const char *options;
-       } *cbarg = data;
-
-       unsigned long mntflags;
-       char *mntdata;
-       char *fstype;
-
-       /* we don't try 'nodev' entries */
-       if (strstr(buffer, "nodev"))
-               return 0;
-
-       fstype = buffer;
-       fstype += lxc_char_left_gc(fstype, strlen(fstype));
-       fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0';
-
-       DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs,
-             cbarg->target, fstype);
-
-       if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) {
-               free(mntdata);
-               return 0;
-       }
-
-       if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) {
-               DEBUG("mount failed with error: %s", strerror(errno));
-               free(mntdata);
-               return 0;
-       }
-
-       free(mntdata);
-
-       INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs,
-            cbarg->target, fstype);
-
-       return 1;
-}
-
-static char *linkderef(char *path, char *dest)
-{
-       struct stat sbuf;
-       ssize_t ret;
-
-       ret = stat(path, &sbuf);
-       if (ret < 0)
-               return NULL;
-
-       if (!S_ISLNK(sbuf.st_mode))
-               return path;
-
-       ret = readlink(path, dest, MAXPATHLEN);
-       if (ret < 0) {
-               SYSERROR("error reading link %s", path);
-               return NULL;
-       } else if (ret >= MAXPATHLEN) {
-               ERROR("link in %s too long", path);
-               return NULL;
-       }
-       dest[ret] = '\0';
-
-       return dest;
-}
-
-/*
- * is an unprivileged user allowed to make this kind of snapshot
- */
-static bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
-                               bool maybesnap)
-{
-       if (!t) {
-               // new type will be same as original
-               // (unless snap && b->type == dir, in which case it will be
-               // overlayfs -- which is also allowed)
-               if (strcmp(b->type, "dir") == 0 ||
-                   strcmp(b->type, "aufs") == 0 ||
-                   strcmp(b->type, "overlayfs") == 0 ||
-                   strcmp(b->type, "btrfs") == 0 ||
-                   strcmp(b->type, "loop") == 0)
-                       return true;
-
-               return false;
-       }
-
-       // unprivileged users can copy and snapshot dir, overlayfs,
-       // and loop.  In particular, not zfs, btrfs, or lvm.
-       if (strcmp(t, "dir") == 0 ||
-           strcmp(t, "aufs") == 0 ||
-           strcmp(t, "overlayfs") == 0 ||
-           strcmp(t, "btrfs") == 0 ||
-           strcmp(t, "loop") == 0)
-               return true;
-
-       return false;
-}
-
-bool is_valid_bdev_type(const char *type)
-{
-       if (strcmp(type, "dir") == 0 ||
-           strcmp(type, "btrfs") == 0 ||
-           strcmp(type, "aufs") == 0 ||
-           strcmp(type, "loop") == 0 ||
-           strcmp(type, "lvm") == 0 ||
-           strcmp(type, "nbd") == 0 ||
-           strcmp(type, "overlayfs") == 0 ||
-           strcmp(type, "rbd") == 0 ||
-           strcmp(type, "zfs") == 0)
-               return true;
-
-       return false;
-}
index 1caf3ae72de096042c9fec493732a0a8db75bca9..b723b5080887fe9ab8783c26bf4ea7aad4e30bd9 100644 (file)
@@ -124,26 +124,6 @@ bool bdev_destroy(struct lxc_conf *conf);
 
 /* callback function to be used with userns_exec_1() */
 int bdev_destroy_wrapper(void *data);
-
-/* Some helpers for lvm, rdb, and/or loop:
- * Maybe they should move to a separate implementation and header-file
- * (bdev_utils.{c,h}) which can be included in bdev.c?
- */
-int blk_getsize(struct bdev *bdev, uint64_t *size);
-int detect_fs(struct bdev *bdev, char *type, int len);
-int do_mkfs_exec_wrapper(void *args);
-int is_blktype(struct bdev *b);
-int mount_unknown_fs(const char *rootfs, const char *target,
-               const char *options);
-bool rootfs_is_blockdev(struct lxc_conf *conf);
-
-/*
- * these are really for qemu-nbd support, as container shutdown
- * must explicitly request device detach.
- */
-bool attach_block_device(struct lxc_conf *conf);
-void detach_block_device(struct lxc_conf *conf);
-
-bool is_valid_bdev_type(const char *type);
+extern bool rootfs_is_blockdev(struct lxc_conf *conf);
 
 #endif // __LXC_BDEV_H
index 9cd63b01aed5ce8d24329e2ca1f8ac55d0e7f278..017ead53a1240434a425297088ed81d50d89cf58 100644 (file)
@@ -34,6 +34,7 @@
 #include "bdev.h"
 #include "log.h"
 #include "lxcloop.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 lxc_log_define(lxcloop, lxc);
index 407c55ff81cbcdb187eed7635c0dfa8e39bf988f..9c329a2f30c754e0dd39a5e42d4bbb83a02b6072 100644 (file)
@@ -36,6 +36,7 @@
 #include "config.h"
 #include "log.h"
 #include "lxclvm.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 /* major()/minor() */
index a9bf4f186a5a529dec1391bfb99b3517a6cd9c0f..4a55e9fff37a75791a17878b7626ed097b918746 100644 (file)
@@ -34,6 +34,7 @@
 #include "bdev.h"
 #include "log.h"
 #include "lxcnbd.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 lxc_log_define(lxcnbd, lxc);
index 133b67e52ab530ddbb8d8161600ee69605f4233b..8e63c3fd07b081be7d1cce8a87aa634d2303f71a 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "bdev.h"
 #include "log.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 lxc_log_define(lxcrbd, lxc);
diff --git a/src/lxc/bdev/storage_utils.c b/src/lxc/bdev/storage_utils.c
new file mode 100644 (file)
index 0000000..6ca6b51
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * Authors:
+ * Christian Brauner <christian.brauner@ubuntu.com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <sched.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "bdev.h"
+#include "log.h"
+#include "lxcnbd.h"
+#include "parse.h"
+#include "storage_utils.h"
+#include "utils.h"
+
+#ifndef BLKGETSIZE64
+#define BLKGETSIZE64 _IOR(0x12, 114, size_t)
+#endif
+
+lxc_log_define(storage_utils, lxc);
+
+/* the bulk of this needs to become a common helper */
+char *dir_new_path(char *src, const char *oldname, const char *name,
+                  const char *oldpath, const char *lxcpath)
+{
+       char *ret, *p, *p2;
+       int l1, l2, nlen;
+
+       nlen = strlen(src) + 1;
+       l1 = strlen(oldpath);
+       p = src;
+       /* if src starts with oldpath, look for oldname only after
+        * that path */
+       if (strncmp(src, oldpath, l1) == 0) {
+               p += l1;
+               nlen += (strlen(lxcpath) - l1);
+       }
+       l2 = strlen(oldname);
+       while ((p = strstr(p, oldname)) != NULL) {
+               p += l2;
+               nlen += strlen(name) - l2;
+       }
+
+       ret = malloc(nlen);
+       if (!ret)
+               return NULL;
+
+       p = ret;
+       if (strncmp(src, oldpath, l1) == 0) {
+               p += sprintf(p, "%s", lxcpath);
+               src += l1;
+       }
+
+       while ((p2 = strstr(src, oldname)) != NULL) {
+               strncpy(p, src, p2 - src); // copy text up to oldname
+               p += p2 - src;             // move target pointer (p)
+               p += sprintf(p, "%s",
+                            name); // print new name in place of oldname
+               src = p2 + l2;      // move src to end of oldname
+       }
+       sprintf(p, "%s", src); // copy the rest of src
+       return ret;
+}
+
+/*
+ * attach_block_device returns true if all went well,
+ * meaning either a block device was attached or was not
+ * needed.  It returns false if something went wrong and
+ * container startup should be stopped.
+ */
+bool attach_block_device(struct lxc_conf *conf)
+{
+       char *path;
+
+       if (!conf->rootfs.path)
+               return true;
+
+       path = conf->rootfs.path;
+       if (!requires_nbd(path))
+               return true;
+
+       path = strchr(path, ':');
+       if (!path)
+               return false;
+
+       path++;
+       if (!attach_nbd(path, conf))
+               return false;
+
+       return true;
+}
+
+/*
+ * return block size of dev->src in units of bytes
+ */
+int blk_getsize(struct bdev *bdev, uint64_t *size)
+{
+       int fd, ret;
+       char *path = bdev->src;
+
+       if (strcmp(bdev->type, "loop") == 0)
+               path = bdev->src + 5;
+
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               return -1;
+
+       ret = ioctl(fd, BLKGETSIZE64, size); // size of device in bytes
+       close(fd);
+       return ret;
+}
+
+void detach_block_device(struct lxc_conf *conf)
+{
+       if (conf->nbd_idx != -1)
+               detach_nbd_idx(conf->nbd_idx);
+}
+
+/*
+ * Given a bdev (presumably blockdev-based), detect the fstype
+ * by trying mounting (in a private mntns) it.
+ * @bdev: bdev to investigate
+ * @type: preallocated char* in which to write the fstype
+ * @len: length of passed in char*
+ * Returns length of fstype, of -1 on error
+ */
+int detect_fs(struct bdev *bdev, char *type, int len)
+{
+       int p[2], ret;
+       size_t linelen;
+       pid_t pid;
+       FILE *f;
+       char *sp1, *sp2, *sp3, *line = NULL;
+       char *srcdev;
+
+       if (!bdev || !bdev->src || !bdev->dest)
+               return -1;
+
+       srcdev = bdev->src;
+       if (strcmp(bdev->type, "loop") == 0)
+               srcdev = bdev->src + 5;
+
+       ret = pipe(p);
+       if (ret < 0)
+               return -1;
+
+       if ((pid = fork()) < 0)
+               return -1;
+
+       if (pid > 0) {
+               int status;
+               close(p[1]);
+               memset(type, 0, len);
+               ret = read(p[0], type, len - 1);
+               close(p[0]);
+               if (ret < 0) {
+                       SYSERROR("error reading from pipe");
+                       wait(&status);
+                       return -1;
+               } else if (ret == 0) {
+                       ERROR("child exited early - fstype not found");
+                       wait(&status);
+                       return -1;
+               }
+               wait(&status);
+               type[len - 1] = '\0';
+               INFO("detected fstype %s for %s", type, srcdev);
+               return ret;
+       }
+
+       if (unshare(CLONE_NEWNS) < 0)
+               exit(1);
+
+       if (detect_shared_rootfs()) {
+               if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
+                       SYSERROR("Failed to make / rslave");
+                       ERROR("Continuing...");
+               }
+       }
+
+       ret = mount_unknown_fs(srcdev, bdev->dest, bdev->mntopts);
+       if (ret < 0) {
+               ERROR("failed mounting %s onto %s to detect fstype", srcdev,
+                     bdev->dest);
+               exit(1);
+       }
+
+       // if symlink, get the real dev name
+       char devpath[MAXPATHLEN];
+       char *l = linkderef(srcdev, devpath);
+       if (!l)
+               exit(1);
+       f = fopen("/proc/self/mounts", "r");
+       if (!f)
+               exit(1);
+
+       while (getline(&line, &linelen, f) != -1) {
+               sp1 = strchr(line, ' ');
+               if (!sp1)
+                       exit(1);
+               *sp1 = '\0';
+               if (strcmp(line, l))
+                       continue;
+               sp2 = strchr(sp1 + 1, ' ');
+               if (!sp2)
+                       exit(1);
+               *sp2 = '\0';
+               sp3 = strchr(sp2 + 1, ' ');
+               if (!sp3)
+                       exit(1);
+               *sp3 = '\0';
+               sp2++;
+               if (write(p[1], sp2, strlen(sp2)) != strlen(sp2))
+                       exit(1);
+
+               exit(0);
+       }
+
+       exit(1);
+}
+
+int do_mkfs_exec_wrapper(void *args)
+{
+       int ret;
+       char *mkfs;
+       char **data = args;
+       /* strlen("mkfs.")
+        * +
+        * strlen(data[0])
+        * +
+        * \0
+        */
+       size_t len = 5 + strlen(data[0]) + 1;
+
+       mkfs = malloc(len);
+       if (!mkfs)
+               return -1;
+
+       ret = snprintf(mkfs, len, "mkfs.%s", data[0]);
+       if (ret < 0 || (size_t)ret >= len) {
+               free(mkfs);
+               return -1;
+       }
+
+       TRACE("executing \"%s %s\"", mkfs, data[1]);
+       execlp(mkfs, mkfs, data[1], (char *)NULL);
+       SYSERROR("failed to run \"%s %s \"", mkfs, data[1]);
+       return -1;
+}
+
+/*
+ * This will return 1 for physical disks, qemu-nbd, loop, etc right now only lvm
+ * is a block device.
+ */
+int is_blktype(struct bdev *b)
+{
+       if (strcmp(b->type, "lvm") == 0)
+               return 1;
+
+       return 0;
+}
+
+int mount_unknown_fs(const char *rootfs, const char *target,
+                    const char *options)
+{
+       size_t i;
+       int ret;
+       struct cbarg {
+               const char *rootfs;
+               const char *target;
+               const char *options;
+       } cbarg = {
+           .rootfs = rootfs,
+           .target = target,
+           .options = options,
+       };
+
+       /*
+        * find the filesystem type with brute force:
+        * first we check with /etc/filesystems, in case the modules
+        * are auto-loaded and fall back to the supported kernel fs
+        */
+       char *fsfile[] = {
+           "/etc/filesystems",
+           "/proc/filesystems",
+       };
+
+       for (i = 0; i < sizeof(fsfile) / sizeof(fsfile[0]); i++) {
+               if (access(fsfile[i], F_OK))
+                       continue;
+
+               ret = lxc_file_for_each_line(fsfile[i], find_fstype_cb, &cbarg);
+               if (ret < 0) {
+                       ERROR("failed to parse '%s'", fsfile[i]);
+                       return -1;
+               }
+
+               if (ret)
+                       return 0;
+       }
+
+       ERROR("failed to determine fs type for '%s'", rootfs);
+       return -1;
+}
+
+/*
+ * These are copied from conf.c.  However as conf.c will be moved to using
+ * the callback system, they can be pulled from there eventually, so we
+ * don't need to pollute utils.c with these low level functions
+ */
+int find_fstype_cb(char *buffer, void *data)
+{
+       struct cbarg {
+               const char *rootfs;
+               const char *target;
+               const char *options;
+       } *cbarg = data;
+
+       unsigned long mntflags;
+       char *mntdata;
+       char *fstype;
+
+       /* we don't try 'nodev' entries */
+       if (strstr(buffer, "nodev"))
+               return 0;
+
+       fstype = buffer;
+       fstype += lxc_char_left_gc(fstype, strlen(fstype));
+       fstype[lxc_char_right_gc(fstype, strlen(fstype))] = '\0';
+
+       DEBUG("trying to mount '%s'->'%s' with fstype '%s'", cbarg->rootfs,
+             cbarg->target, fstype);
+
+       if (parse_mntopts(cbarg->options, &mntflags, &mntdata) < 0) {
+               free(mntdata);
+               return 0;
+       }
+
+       if (mount(cbarg->rootfs, cbarg->target, fstype, mntflags, mntdata)) {
+               DEBUG("mount failed with error: %s", strerror(errno));
+               free(mntdata);
+               return 0;
+       }
+
+       free(mntdata);
+
+       INFO("mounted '%s' on '%s', with fstype '%s'", cbarg->rootfs,
+            cbarg->target, fstype);
+
+       return 1;
+}
+
+char *linkderef(char *path, char *dest)
+{
+       struct stat sbuf;
+       ssize_t ret;
+
+       ret = stat(path, &sbuf);
+       if (ret < 0)
+               return NULL;
+
+       if (!S_ISLNK(sbuf.st_mode))
+               return path;
+
+       ret = readlink(path, dest, MAXPATHLEN);
+       if (ret < 0) {
+               SYSERROR("error reading link %s", path);
+               return NULL;
+       } else if (ret >= MAXPATHLEN) {
+               ERROR("link in %s too long", path);
+               return NULL;
+       }
+       dest[ret] = '\0';
+
+       return dest;
+}
+
+/*
+ * is an unprivileged user allowed to make this kind of snapshot
+ */
+bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
+                        bool maybesnap)
+{
+       if (!t) {
+               // new type will be same as original
+               // (unless snap && b->type == dir, in which case it will be
+               // overlayfs -- which is also allowed)
+               if (strcmp(b->type, "dir") == 0 ||
+                   strcmp(b->type, "aufs") == 0 ||
+                   strcmp(b->type, "overlayfs") == 0 ||
+                   strcmp(b->type, "btrfs") == 0 ||
+                   strcmp(b->type, "loop") == 0)
+                       return true;
+
+               return false;
+       }
+
+       // unprivileged users can copy and snapshot dir, overlayfs,
+       // and loop.  In particular, not zfs, btrfs, or lvm.
+       if (strcmp(t, "dir") == 0 ||
+           strcmp(t, "aufs") == 0 ||
+           strcmp(t, "overlayfs") == 0 ||
+           strcmp(t, "btrfs") == 0 ||
+           strcmp(t, "loop") == 0)
+               return true;
+
+       return false;
+}
+
+bool is_valid_bdev_type(const char *type)
+{
+       if (strcmp(type, "dir") == 0 ||
+           strcmp(type, "btrfs") == 0 ||
+           strcmp(type, "aufs") == 0 ||
+           strcmp(type, "loop") == 0 ||
+           strcmp(type, "lvm") == 0 ||
+           strcmp(type, "nbd") == 0 ||
+           strcmp(type, "overlayfs") == 0 ||
+           strcmp(type, "rbd") == 0 ||
+           strcmp(type, "zfs") == 0)
+               return true;
+
+       return false;
+}
+
+int bdev_destroy_wrapper(void *data)
+{
+       struct lxc_conf *conf = data;
+
+       if (setgid(0) < 0) {
+               ERROR("Failed to setgid to 0");
+               return -1;
+       }
+
+       if (setgroups(0, NULL) < 0)
+               WARN("Failed to clear groups");
+
+       if (setuid(0) < 0) {
+               ERROR("Failed to setuid to 0");
+               return -1;
+       }
+
+       if (!bdev_destroy(conf))
+               return -1;
+
+       return 0;
+}
diff --git a/src/lxc/bdev/storage_utils.h b/src/lxc/bdev/storage_utils.h
new file mode 100644 (file)
index 0000000..cfd6aa4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2017 Canonical Ltd.
+ *
+ * Authors:
+ * Christian Brauner <christian.brauner@ubuntu.com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LXC_STORAGE_UTILS_H
+#define __LXC_STORAGE_UTILS_H
+
+#include "config.h"
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "conf.h"
+
+struct bdev;
+struct lxc_conf;
+
+extern char *dir_new_path(char *src, const char *oldname, const char *name,
+                         const char *oldpath, const char *lxcpath);
+extern bool attach_block_device(struct lxc_conf *conf);
+extern void detach_block_device(struct lxc_conf *conf);
+extern int blk_getsize(struct bdev *bdev, uint64_t *size);
+extern int detect_fs(struct bdev *bdev, char *type, int len);
+extern int do_mkfs_exec_wrapper(void *args);
+extern int is_blktype(struct bdev *b);
+extern int mount_unknown_fs(const char *rootfs, const char *target,
+                           const char *options);
+extern int find_fstype_cb(char *buffer, void *data);
+extern char *linkderef(char *path, char *dest);
+extern bool unpriv_snap_allowed(struct bdev *b, const char *t, bool snap,
+                               bool maybesnap);
+extern bool is_valid_bdev_type(const char *type);
+extern int bdev_destroy_wrapper(void *data);
+
+#endif // __LXC_STORAGE_UTILS_H
index e1acc7c89bfa1bcf63df554d9275abdd9a4ec98a..2d121bfd5d89bfe1335628a2893950f926ff8718 100644 (file)
@@ -71,6 +71,7 @@
 #include "console.h"
 #include "error.h"
 #include "log.h"
+#include "lxccontainer.h"
 #include "lxclock.h"
 #include "lxcseccomp.h"
 #include "lxcutmp.h"
@@ -78,6 +79,7 @@
 #include "monitor.h"
 #include "namespace.h"
 #include "start.h"
+#include "storage_utils.h"
 #include "sync.h"
 #include "utils.h"
 #include "lsm/lsm.h"
index dda51a44d42aeed0e595f1b2ccdafeeedca3dd74..ad449dcada2ea8b333bb0672de46f5225bf485d9 100644 (file)
@@ -30,6 +30,7 @@
 #include "bdev.h"
 #include "log.h"
 #include "lxc.h"
+#include "storage_utils.h"
 #include "utils.h"
 
 lxc_log_define(lxc_create_ui, lxc);