Signed-off-by: Serge Hallyn <serge.hal...@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@nname
+               // zfs clone zfsroot/oname@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


------------------------------------------------------------------------------
Try New Relic Now & We'll Send You this Cool Shirt
New Relic is the only SaaS-based application performance monitoring service 
that delivers powerful full stack analytics. Optimize and monitor your
browser, app, & servers with just a few lines of code. Try New Relic
and get this awesome Nerd Life shirt! http://p.sf.net/sfu/newrelic_d2d_apr
_______________________________________________
Lxc-devel mailing list
Lxc-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lxc-devel

Reply via email to