bdev/lxcrbd.h \
bdev/lxcrsync.h \
bdev/lxczfs.h \
+ bdev/storage_utils.h \
cgroups/cgroup.h \
caps.h \
conf.h \
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 \
#include "lxczfs.h"
#include "namespace.h"
#include "parse.h"
+#include "storage_utils.h"
#include "utils.h"
#ifndef BLKGETSIZE64
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)
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)
{
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;
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;
-}
/* 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
#include "bdev.h"
#include "log.h"
#include "lxcloop.h"
+#include "storage_utils.h"
#include "utils.h"
lxc_log_define(lxcloop, lxc);
#include "config.h"
#include "log.h"
#include "lxclvm.h"
+#include "storage_utils.h"
#include "utils.h"
/* major()/minor() */
#include "bdev.h"
#include "log.h"
#include "lxcnbd.h"
+#include "storage_utils.h"
#include "utils.h"
lxc_log_define(lxcnbd, lxc);
#include "bdev.h"
#include "log.h"
+#include "storage_utils.h"
#include "utils.h"
lxc_log_define(lxcrbd, lxc);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
#include "console.h"
#include "error.h"
#include "log.h"
+#include "lxccontainer.h"
#include "lxclock.h"
#include "lxcseccomp.h"
#include "lxcutmp.h"
#include "monitor.h"
#include "namespace.h"
#include "start.h"
+#include "storage_utils.h"
#include "sync.h"
#include "utils.h"
#include "lsm/lsm.h"
#include "bdev.h"
#include "log.h"
#include "lxc.h"
+#include "storage_utils.h"
#include "utils.h"
lxc_log_define(lxc_create_ui, lxc);