From f2d5a09dcb5794a89d2a9eb6e91c27d9cc87104c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Sat, 1 Jul 2017 18:15:59 +0200 Subject: [PATCH] storage: add storage_utils.{c.h} non-functional changes Signed-off-by: Christian Brauner --- src/lxc/Makefile.am | 2 + src/lxc/bdev/bdev.c | 572 ++++------------------------------- src/lxc/bdev/bdev.h | 22 +- src/lxc/bdev/lxcloop.c | 1 + src/lxc/bdev/lxclvm.c | 1 + src/lxc/bdev/lxcnbd.c | 1 + src/lxc/bdev/lxcrbd.c | 1 + src/lxc/bdev/storage_utils.c | 480 +++++++++++++++++++++++++++++ src/lxc/bdev/storage_utils.h | 54 ++++ src/lxc/start.c | 2 + src/lxc/tools/lxc_create.c | 1 + 11 files changed, 611 insertions(+), 526 deletions(-) create mode 100644 src/lxc/bdev/storage_utils.c create mode 100644 src/lxc/bdev/storage_utils.h diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index ea1982ea7..54fad1226 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -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 \ diff --git a/src/lxc/bdev/bdev.c b/src/lxc/bdev/bdev.c index 55abefa38..ea1bca17a 100644 --- a/src/lxc/bdev/bdev.c +++ b/src/lxc/bdev/bdev.c @@ -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; -} diff --git a/src/lxc/bdev/bdev.h b/src/lxc/bdev/bdev.h index 1caf3ae72..b723b5080 100644 --- a/src/lxc/bdev/bdev.h +++ b/src/lxc/bdev/bdev.h @@ -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 diff --git a/src/lxc/bdev/lxcloop.c b/src/lxc/bdev/lxcloop.c index 9cd63b01a..017ead53a 100644 --- a/src/lxc/bdev/lxcloop.c +++ b/src/lxc/bdev/lxcloop.c @@ -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); diff --git a/src/lxc/bdev/lxclvm.c b/src/lxc/bdev/lxclvm.c index 407c55ff8..9c329a2f3 100644 --- a/src/lxc/bdev/lxclvm.c +++ b/src/lxc/bdev/lxclvm.c @@ -36,6 +36,7 @@ #include "config.h" #include "log.h" #include "lxclvm.h" +#include "storage_utils.h" #include "utils.h" /* major()/minor() */ diff --git a/src/lxc/bdev/lxcnbd.c b/src/lxc/bdev/lxcnbd.c index a9bf4f186..4a55e9fff 100644 --- a/src/lxc/bdev/lxcnbd.c +++ b/src/lxc/bdev/lxcnbd.c @@ -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); diff --git a/src/lxc/bdev/lxcrbd.c b/src/lxc/bdev/lxcrbd.c index 133b67e52..8e63c3fd0 100644 --- a/src/lxc/bdev/lxcrbd.c +++ b/src/lxc/bdev/lxcrbd.c @@ -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 index 000000000..6ca6b51ec --- /dev/null +++ b/src/lxc/bdev/storage_utils.c @@ -0,0 +1,480 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Christian Brauner + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..cfd6aa47c --- /dev/null +++ b/src/lxc/bdev/storage_utils.h @@ -0,0 +1,54 @@ +/* + * lxc: linux Container library + * + * Copyright © 2017 Canonical Ltd. + * + * Authors: + * Christian Brauner + * + * 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 +#include +#include + +#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 diff --git a/src/lxc/start.c b/src/lxc/start.c index e1acc7c89..2d121bfd5 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -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" diff --git a/src/lxc/tools/lxc_create.c b/src/lxc/tools/lxc_create.c index dda51a44d..ad449dcad 100644 --- a/src/lxc/tools/lxc_create.c +++ b/src/lxc/tools/lxc_create.c @@ -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); -- 2.47.3