[lxc-devel] [PATCH] implement zfs bdev and clone
Serge Hallyn
serge.hallyn at ubuntu.com
Fri Apr 26 03:21:41 UTC 2013
Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
src/lxc/bdev.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 177 insertions(+)
diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c
index 990e8c5..3df53a5 100644
--- a/src/lxc/bdev.c
+++ b/src/lxc/bdev.c
@@ -427,6 +427,182 @@ struct bdev_ops dir_ops = {
.clone_paths = &dir_clonepaths,
};
+
+//
+// XXXXXXX 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.
+//
+
+static int zfs_list_entry(const char *path, char *output)
+{
+ FILE *f;
+ int found=0;
+
+ if ((f = popen("zfs list", "r")) == NULL) {
+ SYSERROR("popen failed");
+ return 0;
+ }
+ while (fgets(output, LXC_LOG_BUFFER_SIZE, f)) {
+ if (strstr(output, path)) {
+ found = 1;
+ break;
+ }
+ }
+ (void) pclose(f);
+
+ return found;
+}
+
+static int zfs_detect(const char *path)
+{
+ char *output = malloc(LXC_LOG_BUFFER_SIZE);
+ int found;
+
+ if (!output) {
+ ERROR("out of memory");
+ return 0;
+ }
+ found = zfs_list_entry(path, output);
+ free(output);
+ return found;
+}
+
+int zfs_mount(struct bdev *bdev)
+{
+ if (strcmp(bdev->type, "zfs"))
+ return -22;
+ if (!bdev->src || !bdev->dest)
+ return -22;
+ return mount(bdev->src, bdev->dest, "bind", MS_BIND | MS_REC, NULL);
+}
+
+int zfs_umount(struct bdev *bdev)
+{
+ if (strcmp(bdev->type, "zfs"))
+ return -22;
+ if (!bdev->src || !bdev->dest)
+ return -22;
+ return umount(bdev->dest);
+}
+
+static 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], *p;
+ int ret;
+ pid_t pid;
+
+ if (zfs_list_entry(opath, output) < 0)
+ return -1;
+
+ if ((p = index(output, ' ')) == NULL)
+ return -1;
+ *p = '\0';
+ if ((p = rindex(output, '/')) == NULL)
+ return -1;
+ *p = '\0';
+
+ ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs",
+ lxcpath, nname);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return -1;
+
+ // zfsroot is output up to ' '
+ // 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", output, nname);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ exit(1);
+ return execlp("zfs", "zfs", "create", option, dev, NULL);
+ }
+ return wait_for_pid(pid);
+ } else {
+ // if snapshot, do
+ // 'zfs snapshot zfsroot/oname at nname
+ // zfs clone zfsroot/oname at nname zfsroot/nname
+ char path1[MAXPATHLEN], path2[MAXPATHLEN];
+
+ ret = snprintf(path1, MAXPATHLEN, "%s/%s@%s", output,
+ oname, nname);
+ if (ret < 0 || ret >= MAXPATHLEN)
+ return -1;
+ (void) snprintf(path2, MAXPATHLEN, "%s/%s", output, nname);
+
+ // if the snapshot exists, delete it
+ if ((pid = fork()) < 0)
+ return -1;
+ if (!pid) {
+ return execlp("zfs", "zfs", "destroy", path1, NULL);
+ }
+ // 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) {
+ return execlp("zfs", "zfs", "snapshot", path1, NULL);
+ }
+ if (wait_for_pid(pid) < 0)
+ return -1;
+
+ // run second (clone) command
+ if ((pid = fork()) < 0)
+ return -1;
+ if (!pid) {
+ return execlp("zfs", "zfs", "clone", option, path1, path2, NULL);
+ }
+ return wait_for_pid(pid);
+ }
+}
+
+static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
+ const char *cname, const char *oldpath, const char *lxcpath, int snap,
+ unsigned long newsize)
+{
+ if (!orig->src || !orig->dest)
+ return -1;
+
+ if (strcmp(orig->type, "zfs")) {
+ ERROR("zfs clone from %s backing store is not supported",
+ orig->type);
+ return -1;
+ }
+
+ if (orig->data) {
+ new->data = strdup(orig->data);
+ if (!new->data)
+ return -1;
+ }
+ new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath);
+ if (!new->dest)
+ return -1;
+
+ new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
+ if (!new->src)
+ return -1;
+
+ return zfs_clone(orig->src, new->src, oldname, cname, lxcpath, snap);
+}
+
+struct bdev_ops zfs_ops = {
+ .detect = &zfs_detect,
+ .mount = &zfs_mount,
+ .umount = &zfs_umount,
+ .clone_paths = &zfs_clonepaths,
+};
+
//
// LVM ops
//
@@ -1021,6 +1197,7 @@ struct bdev_ops overlayfs_ops = {
};
struct bdev_type bdevs[] = {
+ {.name = "zfs", .ops = &zfs_ops,},
{.name = "lvm", .ops = &lvm_ops,},
{.name = "btrfs", .ops = &btrfs_ops,},
{.name = "dir", .ops = &dir_ops,},
--
1.7.9.5
More information about the lxc-devel
mailing list