Create a loopfile backed container by doing: lxc-create -B loop -t template -n name
or lxc-clone -B loop -o dir1 -n loop1 The rootfs in the configuration file will be loop:/var/lib/lxc/loop1/rootdev Signed-off-by: Serge Hallyn <serge.hal...@ubuntu.com> --- doc/lxc-clone.sgml.in | 2 +- doc/lxc-create.sgml.in | 2 +- src/lxc/bdev.c | 309 +++++++++++++++++++++++++++++++++++++++++++++++-- src/lxc/bdev.h | 7 ++ src/lxc/lxc_clone.c | 2 +- src/lxc/lxc_create.c | 12 +- src/lxc/lxccontainer.c | 2 +- 7 files changed, 320 insertions(+), 16 deletions(-) diff --git a/doc/lxc-clone.sgml.in b/doc/lxc-clone.sgml.in index 2d2fda3..6885ff7 100644 --- a/doc/lxc-clone.sgml.in +++ b/doc/lxc-clone.sgml.in @@ -211,7 +211,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA default the same as the original container's is used. Note that currently changing the backingstore is only supported for overlayfs snapshots of directory backed containers. Valid - backing stores include dir (directory), btrfs, lvm, zfs + backing stores include dir (directory), btrfs, lvm, zfs, loop and overlayfs. </para> </listitem> diff --git a/doc/lxc-create.sgml.in b/doc/lxc-create.sgml.in index 3969246..f9b0228 100644 --- a/doc/lxc-create.sgml.in +++ b/doc/lxc-create.sgml.in @@ -125,7 +125,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA </term> <listitem> <para> - 'backingstore' is one of 'none', 'dir', 'lvm', or 'btrfs'. The + 'backingstore' is one of 'none', 'dir', 'lvm', 'loop', or 'btrfs'. The default is 'none', meaning that the container root filesystem will be a directory under <filename>@LXCPATH@/container/rootfs</filename>. 'dir' has the same meaning as 'none', but also allows the optional diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 5b89ba4..9dea57b 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -35,6 +35,8 @@ #include <sys/mount.h> #include <sys/wait.h> #include <libgen.h> +#include <linux/loop.h> +#include <dirent.h> #include "lxc.h" #include "config.h" #include "conf.h" @@ -72,9 +74,16 @@ static int do_rsync(const char *src, const char *dest) exit(1); } -static int blk_getsize(const char *path, unsigned long *size) +/* + * return block size of dev->src + */ +static int blk_getsize(struct bdev *bdev, unsigned long *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) @@ -177,6 +186,14 @@ static int do_mkfs(const char *path, const char *fstype) if (pid > 0) return wait_for_pid(pid); + // If the file is not a block device, we don't want mkfs to ask + // us about whether to proceed. + close(0); + close(1); + close(2); + open("/dev/zero", O_RDONLY); + open("/dev/null", O_RDWR); + open("/dev/null", O_RDWR); execlp("mkfs", "mkfs", "-t", fstype, path, NULL); exit(1); } @@ -218,10 +235,14 @@ static int detect_fs(struct bdev *bdev, char *type, int len) pid_t pid; FILE *f; char *sp1, *sp2, *sp3, *line = NULL; + char *srcdev = bdev->src; if (!bdev || !bdev->src || !bdev->dest) return -1; + if (strcmp(bdev->type, "loop") == 0) + srcdev = bdev->src + 5; + if (pipe(p) < 0) return -1; if ((pid = fork()) < 0) @@ -243,21 +264,21 @@ static int detect_fs(struct bdev *bdev, char *type, int len) } wait(&status); type[len-1] = '\0'; - INFO("detected fstype %s for %s", type, bdev->src); + INFO("detected fstype %s for %s", type, srcdev); return ret; } if (unshare(CLONE_NEWNS) < 0) exit(1); - ret = mount_unknow_fs(bdev->src, bdev->dest, 0); + ret = mount_unknow_fs(srcdev, bdev->dest, 0); if (ret < 0) { - ERROR("failed mounting %s onto %s to detect fstype", bdev->src, bdev->dest); + 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(bdev->src, devpath); + char *l = linkderef(srcdev, devpath); if (!l) exit(1); f = fopen("/proc/self/mounts", "r"); @@ -881,7 +902,7 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna return -1; if (is_blktype(orig)) { - if (!newsize && blk_getsize(orig->src, &size) < 0) { + if (!newsize && blk_getsize(orig, &size) < 0) { ERROR("Error getting size of %s", orig->src); return -1; } @@ -928,8 +949,8 @@ static int lvm_destroy(struct bdev *orig) return wait_for_pid(pid); } -#define DEFAULT_LVM_SZ 1024000000 -#define DEFAULT_LVM_FSTYPE "ext3" +#define DEFAULT_FS_SIZE 1024000000 +#define DEFAULT_FSTYPE "ext3" static int lvm_create(struct bdev *bdev, const char *dest, const char *n, struct bdev_specs *specs) { @@ -959,7 +980,7 @@ static int lvm_create(struct bdev *bdev, const char *dest, const char *n, // lvm.fssize is in bytes. sz = specs->u.lvm.fssize; if (!sz) - sz = DEFAULT_LVM_SZ; + sz = DEFAULT_FS_SIZE; INFO("Error creating new lvm blockdev %s size %lu", bdev->src, sz); if (do_lvm_create(bdev->src, sz) < 0) { @@ -969,7 +990,7 @@ static int lvm_create(struct bdev *bdev, const char *dest, const char *n, fstype = specs->u.lvm.fstype; if (!fstype) - fstype = DEFAULT_LVM_FSTYPE; + fstype = DEFAULT_FSTYPE; if (do_mkfs(bdev->src, fstype) < 0) { ERROR("Error creating filesystem type %s on %s", fstype, bdev->src); @@ -1289,6 +1310,272 @@ struct bdev_ops btrfs_ops = { }; // +// loopback dev ops +// +static int loop_detect(const char *path) +{ + if (strncmp(path, "loop:", 5) == 0) + return 1; + return 0; +} + +static int find_free_loopdev(int *retfd, char *namep) +{ + struct dirent dirent, *direntp; + struct loop_info64 lo; + DIR *dir; + int fd = -1; + + if (!(dir = opendir("/dev"))) { + SYSERROR("Error opening /dev"); + return -1; + } + while (!readdir_r(dir, &dirent, &direntp)) { + + if (!direntp) + break; + if (strncmp(direntp->d_name, "loop", 4) != 0) + continue; + if ((fd = openat(dirfd(dir), direntp->d_name, O_RDWR)) < 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; + } + if (fd == -1) { + ERROR("No loop device found"); + return -1; + } + closedir(dir); + + *retfd = fd; + return 0; +} + +static 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; + + if ((ffd = open(bdev->src + 5, O_RDWR)) < 0) { + SYSERROR("Error opening backing file %s\n", 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\n"); + goto out; + } + + ret = mount_unknow_fs(loname, bdev->dest, 0); + if (ret < 0) + ERROR("Error mounting %s\n", bdev->src); + else + bdev->lofd = lfd; + +out: + if (ffd > -1) + close(ffd); + if (ret < 0) { + close(lfd); + bdev->lofd = -1; + } + return ret; +} + +static 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, unsigned long size, const char *fstype) +{ + int fd; + // 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; + } + if (close(fd) < 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; +} + +/* + * No idea what the original blockdev will be called, but the copy will be + * called $lxcpath/$lxcname/rootdev + */ +static int loop_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname, + const char *cname, const char *oldpath, const char *lxcpath, int snap, + unsigned long newsize) +{ + char fstype[100]; + unsigned long 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; // default to 1G + } + return do_loop_create(srcdev, size, fstype); +} + +static int loop_create(struct bdev *bdev, const char *dest, const char *n, + struct bdev_specs *specs) +{ + const char *fstype; + unsigned long 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->u.loop.fssize; + if (!sz) + sz = DEFAULT_FS_SIZE; + + fstype = specs->u.loop.fstype; + if (!fstype) + fstype = DEFAULT_FSTYPE; + + if (!(bdev->dest = strdup(dest))) + return -1; + + if (mkdir_p(bdev->dest, 0755) < 0) { + ERROR("Error creating %s\n", bdev->dest); + return -1; + } + + return do_loop_create(srcdev, sz, fstype); +} + +static int loop_destroy(struct bdev *orig) +{ + return unlink(orig->src + 5); +} + +struct bdev_ops loop_ops = { + .detect = &loop_detect, + .mount = &loop_mount, + .umount = &loop_umount, + .clone_paths = &loop_clonepaths, + .destroy = &loop_destroy, + .create = &loop_create, +}; + +// // overlayfs ops // @@ -1525,6 +1812,7 @@ struct bdev_type bdevs[] = { {.name = "btrfs", .ops = &btrfs_ops,}, {.name = "dir", .ops = &dir_ops,}, {.name = "overlayfs", .ops = &overlayfs_ops,}, + {.name = "loop", .ops = &loop_ops,}, }; static const size_t numbdevs = sizeof(bdevs) / sizeof(struct bdev_type); @@ -1571,6 +1859,7 @@ struct bdev *bdev_init(const char *src, const char *dst, const char *data) if (r) break; } + if (i == numbdevs) return NULL; bdev = malloc(sizeof(struct bdev)); diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index 4f27ea9..1d79bb2 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -24,6 +24,10 @@ struct bdev_specs { char *fstype; unsigned long fssize; // fs size in bytes } lvm; + struct { + char *fstype; + unsigned long fssize; // fs size in bytes + } loop; } u; }; @@ -55,6 +59,9 @@ struct bdev { char *src; char *dest; char *data; + // turn the following into a union if need be + // lofd is the open fd for the mounted loopback file + int lofd; }; char *overlayfs_getlower(char *p); diff --git a/src/lxc/lxc_clone.c b/src/lxc/lxc_clone.c index 2b0ee43..b29b621 100644 --- a/src/lxc/lxc_clone.c +++ b/src/lxc/lxc_clone.c @@ -24,7 +24,7 @@ void usage(const char *me) printf(" -s: snapshot rather than copy\n"); printf(" -B: use specified new backingstore. Default is the same as\n"); printf(" the original. Options include btrfs, lvm, overlayfs, \n"); - printf(" dir\n"); + printf(" dir and loop\n"); printf(" -L: for blockdev-backed backingstore, use specified size\n"); printf(" -K: Keep name - do not change the container name\n"); printf(" -M: Keep macaddr - do not choose a random new mac address\n"); diff --git a/src/lxc/lxc_create.c b/src/lxc/lxc_create.c index c9231ef..6d8ca01 100644 --- a/src/lxc/lxc_create.c +++ b/src/lxc/lxc_create.c @@ -143,11 +143,14 @@ Options :\n\ bool validate_bdev_args(struct lxc_arguments *a) { - if (strcmp(a->bdevtype, "lvm") != 0) { - if (a->fstype || a->fssize) { + if (a->fstype || a->fssize) { + if (strcmp(a->bdevtype, "lvm") != 0 && + strcmp(a->bdevtype, "loop") != 0) { fprintf(stderr, "filesystem type and size are only valid with block devices\n"); return false; } + } + if (strcmp(a->bdevtype, "lvm") != 0) { if (a->lvname || a->vgname) { fprintf(stderr, "--lvname and --vgname are only valid with -B lvm\n"); return false; @@ -213,6 +216,11 @@ int main(int argc, char *argv[]) spec.u.lvm.fstype = my_args.fstype; if (my_args.fssize) spec.u.lvm.fssize = my_args.fssize; + } else if (strcmp(my_args.bdevtype, "loop") == 0) { + if (my_args.fstype) + spec.u.lvm.fstype = my_args.fstype; + if (my_args.fssize) + spec.u.lvm.fssize = my_args.fssize; } else if (my_args.dir) { ERROR("--dir is not yet supported"); exit(1); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 3764923..2edf749 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -534,7 +534,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv close(0); close(1); close(2); - open("/dev/null", O_RDONLY); + open("/dev/zero", O_RDONLY); open("/dev/null", O_RDWR); open("/dev/null", O_RDWR); setsid(); -- 1.8.1.2 ------------------------------------------------------------------------------ How ServiceNow helps IT people transform IT departments: 1. A cloud service to automate IT design, transition and operations 2. Dashboards that offer high-level views of enterprise services 3. A single system of record for all IT processes http://p.sf.net/sfu/servicenow-d2d-j _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel