From 1f92162dc0432b6f7f8156d22348f22934cbea3f Mon Sep 17 00:00:00 2001 From: =?utf8?q?St=C3=A9phane=20Graber?= Date: Wed, 12 Feb 2014 14:01:02 -0500 Subject: [PATCH] bdev: Add aufs support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is pretty much copy/paste from overlayfs. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- doc/lxc-clone.sgml.in | 10 +- doc/lxc-snapshot.sgml.in | 2 +- src/lxc/bdev.c | 260 ++++++++++++++++++++++++++++++++++++++- src/lxc/bdev.h | 4 +- src/lxc/conf.c | 2 +- src/lxc/lxc_clone.c | 2 +- src/lxc/lxccontainer.c | 12 +- src/lxc/lxccontainer.h | 2 +- 8 files changed, 275 insertions(+), 19 deletions(-) diff --git a/doc/lxc-clone.sgml.in b/doc/lxc-clone.sgml.in index 7b50094f4..13a5cf89d 100644 --- a/doc/lxc-clone.sgml.in +++ b/doc/lxc-clone.sgml.in @@ -88,15 +88,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA snapshot filesystem uses the backing store's snapshot functionality to create a very small copy-on-write snapshot of the original container. Snapshot clones require the new container backing store to support snapshotting. Currently - this includes only btrfs, lvm, overlayfs and zfs. LVM devices do not support + this includes only aufs, btrfs, lvm, overlayfs and zfs. LVM devices do not support snapshots of snapshots. The backing store of the new container will be the same type as the original container, - with one exception: overlayfs snapshots can be created of directory backed - containers. This can be requested by using the -B overlayfs + with one exception: aufs and overlayfs snapshots can be created of directory backed + containers. This can be requested by using (for overlayfs) the -B overlayfs arguments. @@ -210,8 +210,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Select a different backing store for the new container. By 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, loop + aufs and overlayfs snapshots of directory backed containers. Valid + backing stores include dir (directory), aufs, btrfs, lvm, zfs, loop and overlayfs. diff --git a/doc/lxc-snapshot.sgml.in b/doc/lxc-snapshot.sgml.in index f66070b1a..4b70279fa 100644 --- a/doc/lxc-snapshot.sgml.in +++ b/doc/lxc-snapshot.sgml.in @@ -115,7 +115,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - When restoring a snapshot, the last optional argument is the name to use for the restored container. If no name is given, then the original container will be destroyed and the restored container will take its place. Note that deleting the original snapshot is not possible in the case of overlayfs or zfs backed snapshots. + When restoring a snapshot, the last optional argument is the name to use for the restored container. If no name is given, then the original container will be destroyed and the restored container will take its place. Note that deleting the original snapshot is not possible in the case of aufs, overlayfs or zfs backed snapshots. diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c index 7f8ab9cba..1df602c7f 100644 --- a/src/lxc/bdev.c +++ b/src/lxc/bdev.c @@ -447,7 +447,7 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna int len, ret; if (snap) { - ERROR("directories cannot be snapshotted. Try overlayfs."); + ERROR("directories cannot be snapshotted. Try aufs or overlayfs."); return -1; } @@ -1997,11 +1997,264 @@ static const struct bdev_ops overlayfs_ops = { .can_snapshot = true, }; +// +// aufs ops +// + +static int aufs_detect(const char *path) +{ + if (strncmp(path, "aufs:", 5) == 0) + return 1; // take their word for it + return 0; +} + +// +// XXXXXXX plain directory bind mount ops +// +static int aufs_mount(struct bdev *bdev) +{ + char *options, *dup, *lower, *upper; + int len; + unsigned long mntflags; + char *mntdata; + int ret; + + 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 = index(dup, ':'))) + return -22; + if (!(upper = index(++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 + + if (mntdata) { + len = strlen(lower) + strlen(upper) + strlen("br==rw:=ro,") + strlen(mntdata) + 1; + options = alloca(len); + ret = snprintf(options, len, "br=%s=rw:%s=ro,%s", upper, lower, mntdata); + } + else { + len = strlen(lower) + strlen(upper) + strlen("br==rw:=ro") + 1; + options = alloca(len); + ret = snprintf(options, len, "br=%s=rw:%s=ro", upper, lower); + } + 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; +} + +static int aufs_umount(struct bdev *bdev) +{ + if (strcmp(bdev->type, "aufs")) + return -22; + if (!bdev->src || !bdev->dest) + return -22; + return umount(bdev->dest); +} + +static 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) +{ + 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 (strcmp(orig->type, "dir") == 0) { + char *delta; + int ret, len; + + // if we have /var/lib/lxc/c2/rootfs, then delta will be + // /var/lib/lxc/c2/delta0 + delta = strdup(new->dest); + if (!delta) { + return -1; + } + if (strlen(delta) < 6) { + free(delta); + return -22; + } + strcpy(&delta[strlen(delta)-6], "delta0"); + if ((ret = mkdir(delta, 0755)) < 0) { + SYSERROR("error: mkdir %s", delta); + free(delta); + return -1; + } + + // 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 = index(osrc, ':') + 1; + if (nsrc != osrc + 5 || (odelta = index(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 (do_rsync(odelta, ndelta) < 0) { + 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; +} + +static int aufs_destroy(struct bdev *orig) +{ + char *upper; + + if (strncmp(orig->src, "aufs:", 5) != 0) + return -22; + upper = index(orig->src + 5, ':'); + if (!upper) + return -22; + upper++; + return lxc_rmdir_onedev(upper); +} + +/* + * 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 + */ +static 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; +} + +static const struct bdev_ops aufs_ops = { + .detect = &aufs_detect, + .mount = &aufs_mount, + .umount = &aufs_umount, + .clone_paths = &aufs_clonepaths, + .destroy = &aufs_destroy, + .create = &aufs_create, + .can_snapshot = true, +}; + + static const struct bdev_type bdevs[] = { {.name = "zfs", .ops = &zfs_ops,}, {.name = "lvm", .ops = &lvm_ops,}, {.name = "btrfs", .ops = &btrfs_ops,}, {.name = "dir", .ops = &dir_ops,}, + {.name = "aufs", .ops = &aufs_ops,}, {.name = "overlayfs", .ops = &overlayfs_ops,}, {.name = "loop", .ops = &loop_ops,}, }; @@ -2227,7 +2480,8 @@ struct bdev *bdev_copy(struct lxc_container *c0, const char *cname, *needs_rdep = 0; if (bdevtype && strcmp(orig->type, "dir") == 0 && - strcmp(bdevtype, "overlayfs") == 0) + (strcmp(bdevtype, "aufs") == 0 || + strcmp(bdevtype, "overlayfs") == 0)) *needs_rdep = 1; new = bdev_get(bdevtype ? bdevtype : orig->type); @@ -2339,7 +2593,7 @@ struct bdev *bdev_create(const char *dest, const char *type, return do_bdev_create(dest, type, cname, specs); } -char *overlayfs_getlower(char *p) +char *overlay_getlower(char *p) { char *p1 = index(p, ':'); if (p1) diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h index e5d852344..250e3209c 100644 --- a/src/lxc/bdev.h +++ b/src/lxc/bdev.h @@ -24,7 +24,7 @@ #ifndef __LXC_BDEV_H #define __LXC_BDEV_H /* blockdev operations for: - * dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs + * aufs, dir, raw, btrfs, overlayfs, aufs, lvm, loop, zfs * someday: qemu-nbd, qcow2, qed */ @@ -84,7 +84,7 @@ struct bdev { int lofd; }; -char *overlayfs_getlower(char *p); +char *overlay_getlower(char *p); bool bdev_is_dir(const char *path); diff --git a/src/lxc/conf.c b/src/lxc/conf.c index ecf2171fc..9e7563359 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -3346,7 +3346,7 @@ int chown_mapped_root(char *path, struct lxc_conf *conf) * In case of overlay, we want only the writeable layer * to be chowned */ - if (strncmp(path, "overlayfs:", 10) == 0) { + if (strncmp(path, "overlayfs:", 10) == 0 || strncmp(path, "aufs:", 5) == 0) { chownpath = strchr(path, ':'); if (!chownpath) { ERROR("Bad overlay path: %s", path); diff --git a/src/lxc/lxc_clone.c b/src/lxc/lxc_clone.c index d7e6bc93d..05579b84b 100644 --- a/src/lxc/lxc_clone.c +++ b/src/lxc/lxc_clone.c @@ -79,7 +79,7 @@ static void usage(const char *me) printf("\n"); 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(" the original. Options include aufs, btrfs, lvm, overlayfs, \n"); printf(" dir and loop\n"); printf(" -L: for blockdev-backed backingstore, use specified size * specified\n"); printf(" unit. Default size is the size of the source blockdev, default\n"); diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 09d287bd9..51e0ac575 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -891,13 +891,15 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet src = c->lxc_conf->rootfs.path; /* - * for an overlayfs create, what the user wants is the template to fill + * for an overlay create, what the user wants is the template to fill * in what will become the readonly lower layer. So don't mount for * the template */ - if (strncmp(src, "overlayfs:", 10) == 0) { - src = overlayfs_getlower(src+10); - } + if (strncmp(src, "overlayfs:", 10) == 0) + src = overlay_getlower(src+10); + if (strncmp(src, "aufs:", 5) == 0) + src = overlay_getlower(src+5); + bdev = bdev_init(src, c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Error opening rootfs"); @@ -2830,7 +2832,7 @@ static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile) if (bdev_is_dir(c->lxc_conf->rootfs.path)) { ERROR("Snapshot of directory-backed container requested."); ERROR("Making a copy-clone. If you do want snapshots, then"); - ERROR("please create an overlayfs clone first, snapshot that"); + ERROR("please create an aufs or overlayfs clone first, snapshot that"); ERROR("and keep the original container pristine."); flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; } diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 92c76b45b..b9873ebec 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -688,7 +688,7 @@ struct lxc_container { * \return \c true on success, else \c false. * \warning If \p newname is the same as the current container * name, the container will be destroyed. However, this will - * fail if the snapshot is overlayfs-based, since the snapshots + * fail if the snapshot is overlay-based, since the snapshots * will pin the original container. * \note As an example, if the container exists as \c /var/lib/lxc/c1, snapname might be \c 'snap0' * (representing \c /var/lib/lxcsnaps/c1/snap0). If \p newname is \p c2, -- 2.47.2