Commit 3b117b85 by Serge Hallyn

Merge pull request #742 from brauner/2015-12-12/split_bdev_into_modules

Split bdev into modules
parents 4dbfaf30 cdb4e53a
......@@ -7,9 +7,16 @@ noinst_HEADERS = \
arguments.h \
attach.h \
bdev/bdev.h \
bdev/lxcaufs.h \
bdev/lxcbtrfs.h \
bdev/lxcdir.h \
bdev/lxcloop.h \
bdev/lxclvm.h \
bdev/lxcnbd.h \
bdev/lxcoverlay.h \
bdev/lxcrbd.h \
bdev/lxcrsync.h \
bdev/lxczfs.h \
caps.h \
cgroup.h \
conf.h \
......@@ -63,9 +70,16 @@ endif
liblxc_so_SOURCES = \
arguments.c arguments.h \
bdev/bdev.c bdev/bdev.h \
bdev/lxcaufs.c bdev/lxcaufs.h \
bdev/lxcbtrfs.c bdev/lxcbtrfs.h \
bdev/lxcdir.c bdev/lxcdir.h \
bdev/lxcloop.c bdev/lxcloop.h \
bdev/lxclvm.c bdev/lxclvm.h \
bdev/lxcnbd.c bdev/lxcnbd.h \
bdev/lxcoverlay.c bdev/lxcoverlay.h \
bdev/lxcrbd.c bdev/lxcrbd.h \
bdev/lxcrsync.c bdev/lxcrsync.h \
bdev/lxczfs.c bdev/lxczfs.h \
commands.c commands.h \
start.c start.h \
execute.c \
......
......@@ -58,6 +58,9 @@
#define MS_STRICTATIME (1 << 24)
#endif
#define DEFAULT_FS_SIZE 1073741824
#define DEFAULT_FSTYPE "ext3"
struct bdev;
struct bdev_ops {
......@@ -125,6 +128,17 @@ 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(const char *path, const char *fstype);
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.
......@@ -132,6 +146,4 @@ int bdev_destroy_wrapper(void *data);
bool attach_block_device(struct lxc_conf *conf);
void detach_block_device(struct lxc_conf *conf);
bool rootfs_is_blockdev(struct lxc_conf *conf);
#endif // __LXC_BDEV_H
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "bdev.h"
#include "log.h"
#include "lxcrsync.h"
#include "utils.h"
lxc_log_define(lxcaufs, lxc);
/* the bulk of this needs to become a common helper */
extern char *dir_new_path(char *src, const char *oldname, const char *name,
const char *oldpath, const char *lxcpath);
int aufs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
if (!snap) {
ERROR("aufs is only for snapshot clones");
return -22;
}
if (!orig->src || !orig->dest)
return -1;
new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath);
if (!new->dest)
return -1;
if (mkdir_p(new->dest, 0755) < 0)
return -1;
if (am_unpriv() && chown_mapped_root(new->dest, conf) < 0)
WARN("Failed to update ownership of %s", new->dest);
if (strcmp(orig->type, "dir") == 0) {
char *delta, *lastslash;
int ret, len, lastslashidx;
// if we have /var/lib/lxc/c2/rootfs, then delta will be
// /var/lib/lxc/c2/delta0
lastslash = strrchr(new->dest, '/');
if (!lastslash)
return -22;
if (strlen(lastslash) < 7)
return -22;
lastslash++;
lastslashidx = lastslash - new->dest;
delta = malloc(lastslashidx + 7);
if (!delta)
return -1;
strncpy(delta, new->dest, lastslashidx+1);
strcpy(delta+lastslashidx, "delta0");
if ((ret = mkdir(delta, 0755)) < 0) {
SYSERROR("error: mkdir %s", delta);
free(delta);
return -1;
}
if (am_unpriv() && chown_mapped_root(delta, conf) < 0)
WARN("Failed to update ownership of %s", delta);
// the src will be 'aufs:lowerdir:upperdir'
len = strlen(delta) + strlen(orig->src) + 12;
new->src = malloc(len);
if (!new->src) {
free(delta);
return -ENOMEM;
}
ret = snprintf(new->src, len, "aufs:%s:%s", orig->src, delta);
free(delta);
if (ret < 0 || ret >= len)
return -ENOMEM;
} else if (strcmp(orig->type, "aufs") == 0) {
// What exactly do we want to do here?
// I think we want to use the original lowerdir, with a
// private delta which is originally rsynced from the
// original delta
char *osrc, *odelta, *nsrc, *ndelta;
int len, ret;
if (!(osrc = strdup(orig->src)))
return -22;
nsrc = strchr(osrc, ':') + 1;
if (nsrc != osrc + 5 || (odelta = strchr(nsrc, ':')) == NULL) {
free(osrc);
return -22;
}
*odelta = '\0';
odelta++;
ndelta = dir_new_path(odelta, oldname, cname, oldpath, lxcpath);
if (!ndelta) {
free(osrc);
return -ENOMEM;
}
if ((ret = mkdir(ndelta, 0755)) < 0 && errno != EEXIST) {
SYSERROR("error: mkdir %s", ndelta);
free(osrc);
free(ndelta);
return -1;
}
if (am_unpriv() && chown_mapped_root(ndelta, conf) < 0)
WARN("Failed to update ownership of %s", ndelta);
struct rsync_data_char rdata;
rdata.src = odelta;
rdata.dest = ndelta;
if (am_unpriv())
ret = userns_exec_1(conf, rsync_delta_wrapper, &rdata);
else
ret = rsync_delta(&rdata);
if (ret) {
free(osrc);
free(ndelta);
ERROR("copying aufs delta");
return -1;
}
len = strlen(nsrc) + strlen(ndelta) + 12;
new->src = malloc(len);
if (!new->src) {
free(osrc);
free(ndelta);
return -ENOMEM;
}
ret = snprintf(new->src, len, "aufs:%s:%s", nsrc, ndelta);
free(osrc);
free(ndelta);
if (ret < 0 || ret >= len)
return -ENOMEM;
} else {
ERROR("aufs clone of %s container is not yet supported",
orig->type);
// Note, supporting this will require aufs_mount supporting
// mounting of the underlay. No big deal, just needs to be done.
return -1;
}
return 0;
}
/*
* to say 'lxc-create -t ubuntu -n o1 -B aufs' means you want
* $lxcpath/$lxcname/rootfs to have the created container, while all
* changes after starting the container are written to
* $lxcpath/$lxcname/delta0
*/
int aufs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
char *delta;
int ret, len = strlen(dest), newlen;
if (len < 8 || strcmp(dest+len-7, "/rootfs") != 0)
return -1;
if (!(bdev->dest = strdup(dest))) {
ERROR("Out of memory");
return -1;
}
delta = alloca(strlen(dest)+1);
strcpy(delta, dest);
strcpy(delta+len-6, "delta0");
if (mkdir_p(delta, 0755) < 0) {
ERROR("Error creating %s", delta);
return -1;
}
/* aufs:lower:upper */
newlen = (2 * len) + strlen("aufs:") + 2;
bdev->src = malloc(newlen);
if (!bdev->src) {
ERROR("Out of memory");
return -1;
}
ret = snprintf(bdev->src, newlen, "aufs:%s:%s", dest, delta);
if (ret < 0 || ret >= newlen)
return -1;
if (mkdir_p(bdev->dest, 0755) < 0) {
ERROR("Error creating %s", bdev->dest);
return -1;
}
return 0;
}
int aufs_destroy(struct bdev *orig)
{
char *upper;
if (strncmp(orig->src, "aufs:", 5) != 0)
return -22;
upper = strchr(orig->src + 5, ':');
if (!upper)
return -22;
upper++;
return lxc_rmdir_onedev(upper, NULL);
}
int aufs_detect(const char *path)
{
if (strncmp(path, "aufs:", 5) == 0)
return 1; // take their word for it
return 0;
}
int aufs_mount(struct bdev *bdev)
{
char *options, *dup, *lower, *upper;
int len;
unsigned long mntflags;
char *mntdata;
int ret;
const char *xinopath = "/dev/shm/aufs.xino";
if (strcmp(bdev->type, "aufs"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
// separately mount it first
// mount -t aufs -obr=${upper}=rw:${lower}=ro lower dest
dup = alloca(strlen(bdev->src)+1);
strcpy(dup, bdev->src);
if (!(lower = strchr(dup, ':')))
return -22;
if (!(upper = strchr(++lower, ':')))
return -22;
*upper = '\0';
upper++;
if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) {
free(mntdata);
return -22;
}
// TODO We should check whether bdev->src is a blockdev, and if so
// but for now, only support aufs of a basic directory
// AUFS does not work on top of certain filesystems like (XFS or Btrfs)
// so add xino=/dev/shm/aufs.xino parameter to mount options.
// The same xino option can be specified to multiple aufs mounts, and
// a xino file is not shared among multiple aufs mounts.
//
// see http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg02587.html
// http://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05126.html
if (mntdata) {
len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,,xino=") + strlen(mntdata) + 1;
options = alloca(len);
ret = snprintf(options, len, "br=%s=rw:%s=ro,%s,xino=%s", upper, lower, mntdata, xinopath);
}
else {
len = strlen(lower) + strlen(upper) + strlen(xinopath) + strlen("br==rw:=ro,xino=") + 1;
options = alloca(len);
ret = snprintf(options, len, "br=%s=rw:%s=ro,xino=%s", upper, lower, xinopath);
}
if (ret < 0 || ret >= len) {
free(mntdata);
return -1;
}
ret = mount(lower, bdev->dest, "aufs", MS_MGC_VAL | mntflags, options);
if (ret < 0)
SYSERROR("aufs: error mounting %s onto %s options %s",
lower, bdev->dest, options);
else
INFO("aufs: mounted %s onto %s options %s",
lower, bdev->dest, options);
return ret;
}
int aufs_umount(struct bdev *bdev)
{
if (strcmp(bdev->type, "aufs"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_AUFS_H
#define __LXC_AUFS_H
#define _GNU_SOURCE
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an aufs bdev struct.
*/
int aufs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int aufs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
int aufs_destroy(struct bdev *orig);
int aufs_detect(const char *path);
int aufs_mount(struct bdev *bdev);
int aufs_umount(struct bdev *bdev);
#endif /* __LXC_AUFS_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <string.h>
#include "bdev.h"
#include "log.h"
#include "utils.h"
lxc_log_define(lxcdir, lxc);
/*
* for a simple directory bind mount, we substitute the old container
* name and paths for the new
*/
int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
int len, ret;
if (snap) {
ERROR("directories cannot be snapshotted. Try aufs or overlayfs.");
return -1;
}
if (!orig->dest || !orig->src)
return -1;
len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
new->src = malloc(len);
if (!new->src)
return -1;
ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
if ((new->dest = strdup(new->src)) == NULL)
return -1;
return 0;
}
int dir_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
if (specs && specs->dir)
bdev->src = strdup(specs->dir);
else
bdev->src = strdup(dest);
bdev->dest = strdup(dest);
if (!bdev->src || !bdev->dest) {
ERROR("Out of memory");
return -1;
}
if (mkdir_p(bdev->src, 0755) < 0) {
ERROR("Error creating %s", bdev->src);
return -1;
}
if (mkdir_p(bdev->dest, 0755) < 0) {
ERROR("Error creating %s", bdev->dest);
return -1;
}
return 0;
}
int dir_destroy(struct bdev *orig)
{
if (lxc_rmdir_onedev(orig->src, NULL) < 0)
return -1;
return 0;
}
int dir_detect(const char *path)
{
if (strncmp(path, "dir:", 4) == 0)
return 1; // take their word for it
if (is_dir(path))
return 1;
return 0;
}
int dir_mount(struct bdev *bdev)
{
unsigned long mntflags;
char *mntdata;
int ret;
if (strcmp(bdev->type, "dir"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) {
free(mntdata);
return -22;
}
ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata);
free(mntdata);
return ret;
}
int dir_umount(struct bdev *bdev)
{
if (strcmp(bdev->type, "dir"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_DIR_H
#define __LXC_DIR_H
#define _GNU_SOURCE
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with a dir bdev struct.
*/
int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int dir_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
int dir_destroy(struct bdev *orig);
int dir_detect(const char *path);
int dir_mount(struct bdev *bdev);
int dir_umount(struct bdev *bdev);
#endif /* __LXC_DIR_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/loop.h>
#include <sys/types.h>
#include "bdev.h"
#include "log.h"
#include "lxcloop.h"
#include "utils.h"
lxc_log_define(lxcloop, lxc);
static int do_loop_create(const char *path, uint64_t size, const char *fstype);
static int find_free_loopdev_no_control(int *retfd, char *namep);
static int find_free_loopdev(int *retfd, char *namep);
/*
* No idea what the original blockdev will be called, but the copy will be
* called $lxcpath/$lxcname/rootdev
*/
int loop_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
char fstype[100];
uint64_t size = newsize;
int len, ret;
char *srcdev;
if (snap) {
ERROR("loop devices cannot be snapshotted.");
return -1;
}
if (!orig->dest || !orig->src)
return -1;
len = strlen(lxcpath) + strlen(cname) + strlen("rootdev") + 3;
srcdev = alloca(len);
ret = snprintf(srcdev, len, "%s/%s/rootdev", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
new->src = malloc(len + 5);
if (!new->src)
return -1;
ret = snprintf(new->src, len + 5, "loop:%s", srcdev);
if (ret < 0 || ret >= len + 5)
return -1;
new->dest = malloc(len);
if (!new->dest)
return -1;
ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
// it's tempting to say: if orig->src == loopback and !newsize, then
// copy the loopback file. However, we'd have to make sure to
// correctly keep holes! So punt for now.
if (is_blktype(orig)) {
if (!newsize && blk_getsize(orig, &size) < 0) {
ERROR("Error getting size of %s", orig->src);
return -1;
}
if (detect_fs(orig, fstype, 100) < 0) {
INFO("could not find fstype for %s, using %s", orig->src,
DEFAULT_FSTYPE);
return -1;
}
} else {
sprintf(fstype, "%s", DEFAULT_FSTYPE);
if (!newsize)
size = DEFAULT_FS_SIZE;
}
return do_loop_create(srcdev, size, fstype);
}
int loop_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
const char *fstype;
uint64_t sz;
int ret, len;
char *srcdev;
if (!specs)
return -1;
// dest is passed in as $lxcpath / $lxcname / rootfs
// srcdev will be: $lxcpath / $lxcname / rootdev
// src will be 'loop:$srcdev'
len = strlen(dest) + 2;
srcdev = alloca(len);
ret = snprintf(srcdev, len, "%s", dest);
if (ret < 0 || ret >= len)
return -1;
sprintf(srcdev + len - 4, "dev");
bdev->src = malloc(len + 5);
if (!bdev->src)
return -1;
ret = snprintf(bdev->src, len + 5, "loop:%s", srcdev);
if (ret < 0 || ret >= len + 5)
return -1;
sz = specs->fssize;
if (!sz)
sz = DEFAULT_FS_SIZE;
fstype = specs->fstype;
if (!fstype)
fstype = DEFAULT_FSTYPE;
if (!(bdev->dest = strdup(dest)))
return -1;
if (mkdir_p(bdev->dest, 0755) < 0) {
ERROR("Error creating %s", bdev->dest);
return -1;
}
return do_loop_create(srcdev, sz, fstype);
}
int loop_destroy(struct bdev *orig)
{
return unlink(orig->src + 5);
}
int loop_detect(const char *path)
{
if (strncmp(path, "loop:", 5) == 0)
return 1;
return 0;
}
int loop_mount(struct bdev *bdev)
{
int lfd, ffd = -1, ret = -1;
struct loop_info64 lo;
char loname[100];
if (strcmp(bdev->type, "loop"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
if (find_free_loopdev(&lfd, loname) < 0)
return -22;
ffd = open(bdev->src + 5, O_RDWR);
if (ffd < 0) {
SYSERROR("Error opening backing file %s", bdev->src);
goto out;
}
if (ioctl(lfd, LOOP_SET_FD, ffd) < 0) {
SYSERROR("Error attaching backing file to loop dev");
goto out;
}
memset(&lo, 0, sizeof(lo));
lo.lo_flags = LO_FLAGS_AUTOCLEAR;
if (ioctl(lfd, LOOP_SET_STATUS64, &lo) < 0) {
SYSERROR("Error setting autoclear on loop dev");
goto out;
}
ret = mount_unknown_fs(loname, bdev->dest, bdev->mntopts);
if (ret < 0)
ERROR("Error mounting %s", bdev->src);
else
bdev->lofd = lfd;
out:
if (ffd > -1)
close(ffd);
if (ret < 0) {
close(lfd);
bdev->lofd = -1;
}
return ret;
}
int loop_umount(struct bdev *bdev)
{
int ret;
if (strcmp(bdev->type, "loop"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
ret = umount(bdev->dest);
if (bdev->lofd >= 0) {
close(bdev->lofd);
bdev->lofd = -1;
}
return ret;
}
static int do_loop_create(const char *path, uint64_t size, const char *fstype)
{
int fd, ret;
// create the new loopback file.
fd = creat(path, S_IRUSR|S_IWUSR);
if (fd < 0)
return -1;
if (lseek(fd, size, SEEK_SET) < 0) {
SYSERROR("Error seeking to set new loop file size");
close(fd);
return -1;
}
if (write(fd, "1", 1) != 1) {
SYSERROR("Error creating new loop file");
close(fd);
return -1;
}
ret = close(fd);
if (ret < 0) {
SYSERROR("Error closing new loop file");
return -1;
}
// create an fs in the loopback file
if (do_mkfs(path, fstype) < 0) {
ERROR("Error creating filesystem type %s on %s", fstype,
path);
return -1;
}
return 0;
}
static int find_free_loopdev_no_control(int *retfd, char *namep)
{
struct dirent dirent, *direntp;
struct loop_info64 lo;
DIR *dir;
int fd = -1;
dir = opendir("/dev");
if (!dir) {
SYSERROR("Error opening /dev");
return -1;
}
while (!readdir_r(dir, &dirent, &direntp)) {
if (!direntp)
break;
if (strncmp(direntp->d_name, "loop", 4) != 0)
continue;
fd = openat(dirfd(dir), direntp->d_name, O_RDWR);
if (fd < 0)
continue;
if (ioctl(fd, LOOP_GET_STATUS64, &lo) == 0 || errno != ENXIO) {
close(fd);
fd = -1;
continue;
}
// We can use this fd
snprintf(namep, 100, "/dev/%s", direntp->d_name);
break;
}
closedir(dir);
if (fd == -1) {
ERROR("No loop device found");
return -1;
}
*retfd = fd;
return 0;
}
static int find_free_loopdev(int *retfd, char *namep)
{
int rc, fd = -1;
int ctl = open("/dev/loop-control", O_RDWR);
if (ctl < 0)
return find_free_loopdev_no_control(retfd, namep);
rc = ioctl(ctl, LOOP_CTL_GET_FREE);
if (rc >= 0) {
snprintf(namep, 100, "/dev/loop%d", rc);
fd = open(namep, O_RDWR);
}
close(ctl);
if (fd == -1) {
ERROR("No loop device found");
return -1;
}
*retfd = fd;
return 0;
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_LOOP_H
#define __LXC_LOOP_H
#define _GNU_SOURCE
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* functions associated with a loop bdev struct
*/
int loop_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int loop_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
int loop_destroy(struct bdev *orig);
int loop_detect(const char *path);
int loop_mount(struct bdev *bdev);
int loop_umount(struct bdev *bdev);
#endif /* __LXC_LOOP_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
#include <inttypes.h> /* Required for PRIu64 to work. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bdev.h"
#include "log.h"
#include "lxclvm.h"
#include "utils.h"
lxc_log_define(lxclvm, lxc);
extern char *dir_new_path(char *src, const char *oldname, const char *name,
const char *oldpath, const char *lxcpath);
/*
* LVM ops
*/
/*
* path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not
* yet exist. This function will attempt to create /dev/$vg/$lv of size
* $size. If thinpool is specified, we'll check for it's existence and if
* it's
* a valid thin pool, and if so, we'll create the requested lv from that
* thin
* pool.
*/
static int do_lvm_create(const char *path, uint64_t size,
const char *thinpool)
{
int ret, pid, len;
char sz[24], *pathdup, *vg, *lv, *tp = NULL;
if ((pid = fork()) < 0) {
SYSERROR("failed fork");
return -1;
}
if (pid > 0)
return wait_for_pid(pid);
// specify bytes to lvcreate
ret = snprintf(sz, 24, "%"PRIu64"b", size);
if (ret < 0 || ret >= 24)
exit(EXIT_FAILURE);
pathdup = strdup(path);
if (!pathdup)
exit(EXIT_FAILURE);
lv = strrchr(pathdup, '/');
if (!lv)
exit(EXIT_FAILURE);
*lv = '\0';
lv++;
vg = strrchr(pathdup, '/');
if (!vg)
exit(EXIT_FAILURE);
vg++;
if (thinpool) {
len = strlen(pathdup) + strlen(thinpool) + 2;
tp = alloca(len);
ret = snprintf(tp, len, "%s/%s", pathdup, thinpool);
if (ret < 0 || ret >= len)
exit(EXIT_FAILURE);
ret = lvm_is_thin_pool(tp);
INFO("got %d for thin pool at path: %s", ret, tp);
if (ret < 0)
exit(EXIT_FAILURE);
if (!ret)
tp = NULL;
}
if (!tp)
execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
else
execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL);
SYSERROR("execlp");
exit(EXIT_FAILURE);
}
/*
* Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM
* prefix "LVM-", then this is an lvm2 LV
*/
int lvm_detect(const char *path)
{
char devp[MAXPATHLEN], buf[4];
FILE *fout;
int ret;
struct stat statbuf;
if (strncmp(path, "lvm:", 4) == 0)
return 1; // take their word for it
ret = stat(path, &statbuf);
if (ret != 0)
return 0;
if (!S_ISBLK(statbuf.st_mode))
return 0;
ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid",
major(statbuf.st_rdev), minor(statbuf.st_rdev));
if (ret < 0 || ret >= MAXPATHLEN) {
ERROR("lvm uuid pathname too long");
return 0;
}
fout = fopen(devp, "r");
if (!fout)
return 0;
ret = fread(buf, 1, 4, fout);
fclose(fout);
if (ret != 4 || strncmp(buf, "LVM-", 4) != 0)
return 0;
return 1;
}
int lvm_mount(struct bdev *bdev)
{
if (strcmp(bdev->type, "lvm"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
/* if we might pass in data sometime, then we'll have to enrich
* mount_unknown_fs */
return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts);
}
int lvm_umount(struct bdev *bdev)
{
if (strcmp(bdev->type, "lvm"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
}
int lvm_compare_lv_attr(const char *path, int pos, const char expected)
{
struct lxc_popen_FILE *f;
int ret, len, status, start=0;
char *cmd, output[12];
const char *lvscmd = "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null";
len = strlen(lvscmd) + strlen(path) - 1;
cmd = alloca(len);
ret = snprintf(cmd, len, lvscmd, path);
if (ret < 0 || ret >= len)
return -1;
f = lxc_popen(cmd);
if (f == NULL) {
SYSERROR("popen failed");
return -1;
}
ret = fgets(output, 12, f->f) == NULL;
status = lxc_pclose(f);
if (ret || WEXITSTATUS(status))
// Assume either vg or lvs do not exist, default
// comparison to false.
return 0;
len = strlen(output);
while(start < len && output[start] == ' ') start++;
if (start + pos < len && output[start + pos] == expected)
return 1;
return 0;
}
int lvm_is_thin_volume(const char *path)
{
return lvm_compare_lv_attr(path, 6, 't');
}
int lvm_is_thin_pool(const char *path)
{
return lvm_compare_lv_attr(path, 0, 't');
}
int lvm_snapshot(const char *orig, const char *path, uint64_t size)
{
int ret, pid;
char sz[24], *pathdup, *lv;
if ((pid = fork()) < 0) {
SYSERROR("failed fork");
return -1;
}
if (pid > 0)
return wait_for_pid(pid);
// specify bytes to lvcreate
ret = snprintf(sz, 24, "%"PRIu64"b", size);
if (ret < 0 || ret >= 24)
exit(EXIT_FAILURE);
pathdup = strdup(path);
if (!pathdup)
exit(EXIT_FAILURE);
lv = strrchr(pathdup, '/');
if (!lv) {
free(pathdup);
exit(EXIT_FAILURE);
}
*lv = '\0';
lv++;
// check if the original lv is backed by a thin pool, in which case we
// cannot specify a size that's different from the original size.
ret = lvm_is_thin_volume(orig);
if (ret == -1) {
free(pathdup);
return -1;
}
if (!ret) {
ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL);
} else {
ret = execlp("lvcreate", "lvcreate", "-s", "-n", lv, orig, (char *)NULL);
}
free(pathdup);
exit(EXIT_FAILURE);
}
int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf)
{
char fstype[100];
uint64_t size = newsize;
int len, ret;
if (!orig->src || !orig->dest)
return -1;
if (strcmp(orig->type, "lvm")) {
const char *vg;
if (snap) {
ERROR("LVM snapshot from %s backing store is not supported",
orig->type);
return -1;
}
vg = lxc_global_config_value("lxc.bdev.lvm.vg");
len = strlen("/dev/") + strlen(vg) + strlen(cname) + 2;
if ((new->src = malloc(len)) == NULL)
return -1;
ret = snprintf(new->src, len, "/dev/%s/%s", vg, cname);
if (ret < 0 || ret >= len)
return -1;
} else {
new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
if (!new->src)
return -1;
}
if (orig->mntopts) {
new->mntopts = strdup(orig->mntopts);
if (!new->mntopts)
return -1;
}
len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
new->dest = malloc(len);
if (!new->dest)
return -1;
ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
if (mkdir_p(new->dest, 0755) < 0)
return -1;
if (is_blktype(orig)) {
if (!newsize && blk_getsize(orig, &size) < 0) {
ERROR("Error getting size of %s", orig->src);
return -1;
}
if (detect_fs(orig, fstype, 100) < 0) {
INFO("could not find fstype for %s, using ext3", orig->src);
return -1;
}
} else {
sprintf(fstype, "ext3");
if (!newsize)
size = DEFAULT_FS_SIZE;
}
if (snap) {
if (lvm_snapshot(orig->src, new->src, size) < 0) {
ERROR("could not create %s snapshot of %s", new->src, orig->src);
return -1;
}
} else {
if (do_lvm_create(new->src, size, lxc_global_config_value("lxc.bdev.lvm.thin_pool")) < 0) {
ERROR("Error creating new lvm blockdev");
return -1;
}
if (do_mkfs(new->src, fstype) < 0) {
ERROR("Error creating filesystem type %s on %s", fstype,
new->src);
return -1;
}
}
return 0;
}
int lvm_destroy(struct bdev *orig)
{
pid_t pid;
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("lvremove", "lvremove", "-f", orig->src, NULL);
exit(EXIT_FAILURE);
}
return wait_for_pid(pid);
}
int lvm_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
const char *vg, *thinpool, *fstype, *lv = n;
uint64_t sz;
int ret, len;
if (!specs)
return -1;
vg = specs->lvm.vg;
if (!vg)
vg = lxc_global_config_value("lxc.bdev.lvm.vg");
thinpool = specs->lvm.thinpool;
if (!thinpool)
thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
/* /dev/$vg/$lv */
if (specs->lvm.lv)
lv = specs->lvm.lv;
len = strlen(vg) + strlen(lv) + 7;
bdev->src = malloc(len);
if (!bdev->src)
return -1;
ret = snprintf(bdev->src, len, "/dev/%s/%s", vg, lv);
if (ret < 0 || ret >= len)
return -1;
// fssize is in bytes.
sz = specs->fssize;
if (!sz)
sz = DEFAULT_FS_SIZE;
if (do_lvm_create(bdev->src, sz, thinpool) < 0) {
ERROR("Error creating new lvm blockdev %s size %"PRIu64" bytes", bdev->src, sz);
return -1;
}
fstype = specs->fstype;
if (!fstype)
fstype = DEFAULT_FSTYPE;
if (do_mkfs(bdev->src, fstype) < 0) {
ERROR("Error creating filesystem type %s on %s", fstype,
bdev->src);
return -1;
}
if (!(bdev->dest = strdup(dest)))
return -1;
if (mkdir_p(bdev->dest, 0755) < 0) {
ERROR("Error creating %s", bdev->dest);
return -1;
}
return 0;
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_LVM_H
#define __LXC_LVM_H
#define _GNU_SOURCE
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an lvm bdev struct.
*/
int lvm_detect(const char *path);
int lvm_mount(struct bdev *bdev);
int lvm_umount(struct bdev *bdev);
int lvm_compare_lv_attr(const char *path, int pos, const char expected);
int lvm_is_thin_volume(const char *path);
int lvm_is_thin_pool(const char *path);
int lvm_snapshot(const char *orig, const char *path, uint64_t size);
int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf);
int lvm_destroy(struct bdev *orig);
int lvm_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
#endif /* __LXC_LVM_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include "bdev.h"
#include "log.h"
#include "lxcnbd.h"
#include "utils.h"
lxc_log_define(lxcnbd, lxc);
struct nbd_attach_data {
const char *nbd;
const char *path;
};
static bool clone_attach_nbd(const char *nbd, const char *path);
static int do_attach_nbd(void *d);
static bool nbd_busy(int idx);
static void nbd_detach(const char *path);
static int nbd_get_partition(const char *src);
static bool wait_for_partition(const char *path);
bool attach_nbd(char *src, struct lxc_conf *conf)
{
char *orig = alloca(strlen(src)+1), *p, path[50];
int i = 0;
strcpy(orig, src);
/* if path is followed by a partition, drop that for now */
p = strchr(orig, ':');
if (p)
*p = '\0';
while (1) {
sprintf(path, "/dev/nbd%d", i);
if (!file_exists(path))
return false;
if (nbd_busy(i)) {
i++;
continue;
}
if (!clone_attach_nbd(path, orig))
return false;
conf->nbd_idx = i;
return true;
}
}
void detach_nbd_idx(int idx)
{
int ret;
char path[50];
ret = snprintf(path, 50, "/dev/nbd%d", idx);
if (ret < 0 || ret >= 50)
return;
nbd_detach(path);
}
int nbd_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
return -ENOSYS;
}
int nbd_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
return -ENOSYS;
}
int nbd_destroy(struct bdev *orig)
{
return -ENOSYS;
}
int nbd_detect(const char *path)
{
if (strncmp(path, "nbd:", 4) == 0)
return 1;
return 0;
}
int nbd_mount(struct bdev *bdev)
{
int ret = -1, partition;
char path[50];
if (strcmp(bdev->type, "nbd"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
/* nbd_idx should have been copied by bdev_init from the lxc_conf */
if (bdev->nbd_idx < 0)
return -22;
partition = nbd_get_partition(bdev->src);
if (partition)
ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx,
partition);
else
ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx);
if (ret < 0 || ret >= 50) {
ERROR("Error setting up nbd device path");
return ret;
}
/* It might take awhile for the partition files to show up */
if (partition) {
if (!wait_for_partition(path))
return -2;
}
ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts);
if (ret < 0)
ERROR("Error mounting %s", bdev->src);
return ret;
}
int nbd_umount(struct bdev *bdev)
{
int ret;
if (strcmp(bdev->type, "nbd"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
ret = umount(bdev->dest);
return ret;
}
bool requires_nbd(const char *path)
{
if (strncmp(path, "nbd:", 4) == 0)
return true;
return false;
}
static int do_attach_nbd(void *d)
{
struct nbd_attach_data *data = d;
const char *nbd, *path;
pid_t pid;
sigset_t mask;
int sfd;
ssize_t s;
struct signalfd_siginfo fdsi;
sigemptyset(&mask);
sigaddset(&mask, SIGHUP);
sigaddset(&mask, SIGCHLD);
nbd = data->nbd;
path = data->path;
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
SYSERROR("Error blocking signals for nbd watcher");
exit(1);
}
sfd = signalfd(-1, &mask, 0);
if (sfd == -1) {
SYSERROR("Error opening signalfd for nbd task");
exit(1);
}
if (prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) < 0)
SYSERROR("Error setting parent death signal for nbd watcher");
pid = fork();
if (pid) {
for (;;) {
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
if (s != sizeof(struct signalfd_siginfo))
SYSERROR("Error reading from signalfd");
if (fdsi.ssi_signo == SIGHUP) {
/* container has exited */
nbd_detach(nbd);
exit(0);
} else if (fdsi.ssi_signo == SIGCHLD) {
int status;
/* If qemu-nbd fails, or is killed by a signal,
* then exit */
while (waitpid(-1, &status, WNOHANG) > 0) {
if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) ||
WIFSIGNALED(status)) {
nbd_detach(nbd);
exit(1);
}
}
}
}
}
close(sfd);
if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
WARN("Warning: unblocking signals for nbd watcher");
execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, NULL);
SYSERROR("Error executing qemu-nbd");
exit(1);
}
static bool clone_attach_nbd(const char *nbd, const char *path)
{
pid_t pid;
struct nbd_attach_data data;
data.nbd = nbd;
data.path = path;
pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID);
if (pid < 0)
return false;
return true;
}
static bool nbd_busy(int idx)
{
char path[100];
int ret;
ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx);
if (ret < 0 || ret >= 100)
return true;
return file_exists(path);
}
static void nbd_detach(const char *path)
{
int ret;
pid_t pid = fork();
if (pid < 0) {
SYSERROR("Error forking to detach nbd");
return;
}
if (pid) {
ret = wait_for_pid(pid);
if (ret < 0)
ERROR("nbd disconnect returned an error");
return;
}
execlp("qemu-nbd", "qemu-nbd", "-d", path, NULL);
SYSERROR("Error executing qemu-nbd");
exit(1);
}
/*
* Pick the partition # off the end of a nbd:file:p
* description. Return 1-9 for the partition id, or 0
* for no partition.
*/
static int nbd_get_partition(const char *src)
{
char *p = strchr(src, ':');
if (!p)
return 0;
p = strchr(p+1, ':');
if (!p)
return 0;
p++;
if (*p < '1' || *p > '9')
return 0;
return *p - '0';
}
static bool wait_for_partition(const char *path)
{
int count = 0;
while (count < 5) {
if (file_exists(path))
return true;
sleep(1);
count++;
}
ERROR("Device %s did not show up after 5 seconds", path);
return false;
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_NBD_H
#define __LXC_NBD_H
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an nbd bdev struct.
*/
int nbd_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int nbd_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
int nbd_destroy(struct bdev *orig);
int nbd_detect(const char *path);
int nbd_mount(struct bdev *bdev);
int nbd_umount(struct bdev *bdev);
/* helpers */
bool attach_nbd(char *src, struct lxc_conf *conf);
void detach_nbd_idx(int idx);
bool requires_nbd(const char *path);
#endif /* __LXC_NBD_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
#include <inttypes.h> /* Required for PRIu64 to work. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bdev.h"
#include "log.h"
#include "utils.h"
lxc_log_define(lxcrbd, lxc);
int rbd_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
ERROR("rbd clonepaths not implemented");
return -1;
}
int rbd_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
const char *rbdpool, *rbdname = n, *fstype;
uint64_t size;
int ret, len;
char sz[24];
pid_t pid;
if (!specs)
return -1;
rbdpool = specs->rbd.rbdpool;
if (!rbdpool)
rbdpool = lxc_global_config_value("lxc.bdev.rbd.rbdpool");
if (specs->rbd.rbdname)
rbdname = specs->rbd.rbdname;
/* source device /dev/rbd/lxc/ctn */
len = strlen(rbdpool) + strlen(rbdname) + 11;
bdev->src = malloc(len);
if (!bdev->src)
return -1;
ret = snprintf(bdev->src, len, "/dev/rbd/%s/%s", rbdpool, rbdname);
if (ret < 0 || ret >= len)
return -1;
// fssize is in bytes.
size = specs->fssize;
if (!size)
size = DEFAULT_FS_SIZE;
// in megabytes for rbd tool
ret = snprintf(sz, 24, "%"PRIu64, size / 1024 / 1024 );
if (ret < 0 || ret >= 24)
exit(1);
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("rbd", "rbd", "create" , "--pool", rbdpool, rbdname, "--size", sz, NULL);
exit(1);
}
if (wait_for_pid(pid) < 0)
return -1;
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("rbd", "rbd", "map", "--pool", rbdpool, rbdname, NULL);
exit(1);
}
if (wait_for_pid(pid) < 0)
return -1;
fstype = specs->fstype;
if (!fstype)
fstype = DEFAULT_FSTYPE;
if (do_mkfs(bdev->src, fstype) < 0) {
ERROR("Error creating filesystem type %s on %s", fstype,
bdev->src);
return -1;
}
if (!(bdev->dest = strdup(dest)))
return -1;
if (mkdir_p(bdev->dest, 0755) < 0 && errno != EEXIST) {
ERROR("Error creating %s", bdev->dest);
return -1;
}
return 0;
}
int rbd_destroy(struct bdev *orig)
{
pid_t pid;
char *rbdfullname;
if ( file_exists(orig->src) ) {
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("rbd", "rbd", "unmap" , orig->src, NULL);
exit(1);
}
if (wait_for_pid(pid) < 0)
return -1;
}
if ((pid = fork()) < 0)
return -1;
if (!pid) {
rbdfullname = alloca(strlen(orig->src) - 8);
strcpy( rbdfullname, &orig->src[9] );
execlp("rbd", "rbd", "rm" , rbdfullname, NULL);
exit(1);
}
return wait_for_pid(pid);
}
int rbd_detect(const char *path)
{
if ( memcmp(path, "/dev/rbd/", 9) == 0)
return 1;
return 0;
}
int rbd_mount(struct bdev *bdev)
{
if (strcmp(bdev->type, "rbd"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
if ( !file_exists(bdev->src) ) {
// if blkdev does not exist it should be mapped, because it is not persistent on reboot
ERROR("Block device %s is not mapped.", bdev->src);
return -1;
}
return mount_unknown_fs(bdev->src, bdev->dest, bdev->mntopts);
}
int rbd_umount(struct bdev *bdev)
{
if (strcmp(bdev->type, "rbd"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_RDB_H
#define __LXC_RDB_H
#define _GNU_SOURCE
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an rdb bdev struct.
*/
int rbd_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int rbd_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
int rbd_destroy(struct bdev *orig);
int rbd_detect(const char *path);
int rbd_mount(struct bdev *bdev);
int rbd_umount(struct bdev *bdev);
#endif /* __LXC_RDB_H */
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include "bdev.h"
#include "config.h"
#include "log.h"
#include "lxczfs.h"
#include "utils.h"
lxc_log_define(lxczfs, lxc);
/*
* zfs ops:
* There are two ways we could do this. We could always specify the 'zfs device'
* (i.e. tank/lxc lxc/container) as rootfs. But instead (at least right now) we
* have lxc-create specify $lxcpath/$lxcname/rootfs as the mountpoint, so that
* it is always mounted. That means 'mount' is really never needed and could be
* noop, but for the sake of flexibility let's always bind-mount.
*/
int zfs_list_entry(const char *path, char *output, size_t inlen)
{
struct lxc_popen_FILE *f;
int found=0;
f = lxc_popen("zfs list 2> /dev/null");
if (f == NULL) {
SYSERROR("popen failed");
return 0;
}
while (fgets(output, inlen, f->f)) {
if (strstr(output, path)) {
found = 1;
break;
}
}
(void) lxc_pclose(f);
return found;
}
int zfs_detect(const char *path)
{
char *output = malloc(LXC_LOG_BUFFER_SIZE);
if (!output) {
ERROR("out of memory");
return 0;
}
int found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE);
free(output);
return found;
}
int zfs_mount(struct bdev *bdev)
{
if (strcmp(bdev->type, "zfs"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
char *mntdata;
unsigned long mntflags;
if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) {
free(mntdata);
return -22;
}
int ret = mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags, mntdata);
free(mntdata);
return ret;
}
int zfs_umount(struct bdev *bdev)
{
if (strcmp(bdev->type, "zfs"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
}
int zfs_clone(const char *opath, const char *npath, const char *oname,
const char *nname, const char *lxcpath, int snapshot)
{
// use the 'zfs list | grep opath' entry to get the zfsroot
char output[MAXPATHLEN], option[MAXPATHLEN];
char *p;
const char *zfsroot = output;
int ret;
pid_t pid;
if (zfs_list_entry(opath, output, MAXPATHLEN)) {
// zfsroot is output up to ' '
if ((p = strchr(output, ' ')) == NULL)
return -1;
*p = '\0';
if ((p = strrchr(output, '/')) == NULL)
return -1;
*p = '\0';
} else {
zfsroot = lxc_global_config_value("lxc.bdev.zfs.root");
}
ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", lxcpath, nname);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
// zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname
if (!snapshot) {
if ((pid = fork()) < 0)
return -1;
if (!pid) {
char dev[MAXPATHLEN];
ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, nname);
if (ret < 0 || ret >= MAXPATHLEN)
exit(EXIT_FAILURE);
execlp("zfs", "zfs", "create", option, dev, NULL);
exit(EXIT_FAILURE);
}
return wait_for_pid(pid);
} else {
// if snapshot, do
// 'zfs snapshot zfsroot/oname@nname
// zfs clone zfsroot/oname@nname zfsroot/nname
char path1[MAXPATHLEN], path2[MAXPATHLEN];
ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", zfsroot,
oname, nname);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
(void) snprintf(path2, MAXPATHLEN, "%s/%s", zfsroot, nname);
// if the snapshot exists, delete it
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("zfs", "zfs", "destroy", path1, NULL);
exit(EXIT_FAILURE);
}
// it probably doesn't exist so destroy probably will fail.
(void) wait_for_pid(pid);
// run first (snapshot) command
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("zfs", "zfs", "snapshot", path1, NULL);
exit(EXIT_FAILURE);
}
if (wait_for_pid(pid) < 0)
return -1;
// run second (clone) command
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("zfs", "zfs", "clone", option, path1, path2, NULL);
exit(EXIT_FAILURE);
}
return wait_for_pid(pid);
}
}
int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf)
{
int len, ret;
if (!orig->src || !orig->dest)
return -1;
if (snap && strcmp(orig->type, "zfs")) {
ERROR("zfs snapshot from %s backing store is not supported", orig->type);
return -1;
}
len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
new->src = malloc(len);
if (!new->src)
return -1;
ret = snprintf(new->src, len, "%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
if ((new->dest = strdup(new->src)) == NULL)
return -1;
return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap);
}
/*
* TODO: detect whether this was a clone, and if so then also delete the
* snapshot it was based on, so that we don't hold the original
* container busy.
*/
int zfs_destroy(struct bdev *orig)
{
pid_t pid;
char output[MAXPATHLEN];
char *p;
if ((pid = fork()) < 0)
return -1;
if (pid)
return wait_for_pid(pid);
if (!zfs_list_entry(orig->src, output, MAXPATHLEN)) {
ERROR("Error: zfs entry for %s not found", orig->src);
return -1;
}
// zfs mount is output up to ' '
if ((p = strchr(output, ' ')) == NULL)
return -1;
*p = '\0';
execlp("zfs", "zfs", "destroy", output, NULL);
exit(EXIT_FAILURE);
}
int zfs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
const char *zfsroot;
char option[MAXPATHLEN];
int ret;
pid_t pid;
if (!specs || !specs->zfs.zfsroot)
zfsroot = lxc_global_config_value("lxc.bdev.zfs.root");
else
zfsroot = specs->zfs.zfsroot;
if (!(bdev->dest = strdup(dest))) {
ERROR("No mount target specified or out of memory");
return -1;
}
if (!(bdev->src = strdup(bdev->dest))) {
ERROR("out of memory");
return -1;
}
ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s", bdev->dest);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
if ((pid = fork()) < 0)
return -1;
if (pid)
return wait_for_pid(pid);
char dev[MAXPATHLEN];
ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, n);
if (ret < 0 || ret >= MAXPATHLEN)
exit(EXIT_FAILURE);
execlp("zfs", "zfs", "create", option, dev, NULL);
exit(EXIT_FAILURE);
}
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <daniel.lezcano at free.fr>
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LXC_ZFS_H
#define __LXC_ZFS_H
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an zfs bdev struct.
*/
int zfs_clone(const char *opath, const char *npath, const char *oname,
const char *nname, const char *lxcpath, int snapshot);
int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int zfs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
/*
* TODO: detect whether this was a clone, and if so then also delete the
* snapshot it was based on, so that we don't hold the original
* container busy.
*/
int zfs_destroy(struct bdev *orig);
int zfs_detect(const char *path);
int zfs_list_entry(const char *path, char *output, size_t inlen);
int zfs_mount(struct bdev *bdev);
int zfs_umount(struct bdev *bdev);
#endif /* __LXC_ZFS_H */
......@@ -453,97 +453,6 @@ static int run_script(const char *name, const char *section,
return run_buffer(buffer);
}
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';
/* ignore blank line and comment */
if (fstype[0] == '\0' || fstype[0] == '#')
return 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 -1;
}
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 int mount_unknown_fs(const char *rootfs, const char *target,
const char *options)
{
int i;
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++) {
int ret;
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;
}
static int mount_rootfs_dir(const char *rootfs, const char *target,
const char *options)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment