[lxc-devel] [PATCH] Several backing store improvements

Serge Hallyn serge.hallyn at ubuntu.com
Fri Apr 26 22:00:47 UTC 2013


(on top of the clone patch)

1. allow copy clones from other bdevs

2. for lvm and zfs, as we don't yet support passing options, only default
VG of 'lxc' and default zfsroot of 'tank' are supported when converting
another backing store type.

3. refuse deletion of container which has lvm or zfs snapshots.

	Note that since a zfs clone must be made from a zfs snapshot,
	which is made from the original zfs fs, even after we
	lxc-destroy the snapshotted container we still must manually
	remove the snapshot.  This can be handled automatically, by
	looking for snapshots where c1 is the original, c2 is the clone,
	tank/c2 no longer exists, but tank/c1 at c2 does.  We can then
	remove tank/c1 at c2 and feel free to remove tank/c1.  This patch
	does NOT do that yet.

4. Make sure not to return when we're a forked child.

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 src/lxc/bdev.c         | 167 +++++++++++++++++++++++++++++++------------------
 src/lxc/bdev.h         |   7 +++
 src/lxc/lxc-destroy.in |  52 ++++++++++++---
 3 files changed, 158 insertions(+), 68 deletions(-)

diff --git a/src/lxc/bdev.c b/src/lxc/bdev.c
index 3df53a5..4ba550c 100644
--- a/src/lxc/bdev.c
+++ b/src/lxc/bdev.c
@@ -81,7 +81,8 @@ static int do_rsync(const char *src, const char *dest)
 	s[l-2] = '/';
 	s[l-1] = '\0';
 
-	return execlp("rsync", "rsync", "-a", s, dest, (char *)NULL);
+	execlp("rsync", "rsync", "-a", s, dest, (char *)NULL);
+	exit(1);
 }
 
 static int blk_getsize(const char *path, unsigned long *size)
@@ -189,7 +190,8 @@ static int do_mkfs(const char *path, const char *fstype)
 	if (pid > 0)
 		return wait_for_pid(pid);
 
-	return execlp("mkfs", "mkfs", "-t", fstype, path, NULL);
+	execlp("mkfs", "mkfs", "-t", fstype, path, NULL);
+	exit(1);
 }
 
 static char *linkderef(char *path, char *dest)
@@ -391,31 +393,25 @@ static int dir_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
 		const char *cname, const char *oldpath, const char *lxcpath, int snap,
 		unsigned long newsize)
 {
+	int len, ret;
+
 	if (snap) {
 		ERROR("directories cannot be snapshotted.  Try overlayfs.");
 		return -1;
 	}
 
-	if (strcmp(orig->type, "dir")) {
-		ERROR("Directory clone from %s backing store is not supported",
-			orig->type);
-		return -1;
-	}
-
 	if (!orig->dest || !orig->src)
 		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);
+	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;
 }
@@ -499,8 +495,10 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
 	int ret;
 	pid_t pid;
 
-	if (zfs_list_entry(opath, output) < 0)
-		return -1;
+	if (!zfs_list_entry(opath, output))
+		// default is tank.  I'd prefer lxc, but apparently this is
+		// tradition.
+		sprintf(output, "tank");
 
 	if ((p = index(output, ' ')) == NULL)
 		return -1;
@@ -524,7 +522,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
 			ret = snprintf(dev, MAXPATHLEN, "%s/%s", output, nname);
 			if (ret < 0  || ret >= MAXPATHLEN)
 				exit(1);
-			return execlp("zfs", "zfs", "create", option, dev, NULL);
+			execlp("zfs", "zfs", "create", option, dev, NULL);
+			exit(1);
 		}
 		return wait_for_pid(pid);
 	} else {
@@ -543,7 +542,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
 		if ((pid = fork()) < 0)
 			return -1;
 		if (!pid) {
-			return execlp("zfs", "zfs", "destroy", path1, NULL);
+			execlp("zfs", "zfs", "destroy", path1, NULL);
+			exit(1);
 		}
 		// it probably doesn't exist so destroy probably will fail.
 		(void) wait_for_pid(pid);
@@ -552,7 +552,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
 		if ((pid = fork()) < 0)
 			return -1;
 		if (!pid) {
-			return execlp("zfs", "zfs", "snapshot", path1, NULL);
+			execlp("zfs", "zfs", "snapshot", path1, NULL);
+			exit(1);
 		}
 		if (wait_for_pid(pid) < 0)
 			return -1;
@@ -561,7 +562,8 @@ static int zfs_clone(const char *opath, const char *npath, const char *oname,
 		if ((pid = fork()) < 0)
 			return -1;
 		if (!pid) {
-			return execlp("zfs", "zfs", "clone", option, path1, path2, NULL);
+			execlp("zfs", "zfs", "clone", option, path1, path2, NULL);
+			exit(1);
 		}
 		return wait_for_pid(pid);
 	}
@@ -571,27 +573,26 @@ static int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
 		const char *cname, const char *oldpath, const char *lxcpath, int snap,
 		unsigned long newsize)
 {
+	int len, ret;
+
 	if (!orig->src || !orig->dest)
 		return -1;
 
-	if (strcmp(orig->type, "zfs")) {
-		ERROR("zfs clone from %s backing store is not supported",
+	if (snap && strcmp(orig->type, "zfs")) {
+		ERROR("zfs snapshot 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);
+	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);
 }
@@ -699,9 +700,9 @@ static int lvm_create(const char *path, unsigned long size)
 	if (!vg)
 		exit(1);
 	vg++;
-	ret = execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
+	execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
 	free(pathdup);
-	return ret;
+	exit(1);
 }
 
 static int lvm_snapshot(const char *orig, const char *path, unsigned long size)
@@ -733,7 +734,16 @@ static int lvm_snapshot(const char *orig, const char *path, unsigned long size)
 
 	ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL);
 	free(pathdup);
-	return ret;
+	exit(1);
+}
+
+// this will return 1 for physical disks, qemu-nbd, loop, etc
+// right now only lvm is a block device
+static int is_blktype(struct bdev *b)
+{
+	if (strcmp(b->type, "lvm") == 0)
+		return 1;
+	return 0;
 }
 
 static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
@@ -742,14 +752,30 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
 {
 	char fstype[100];
 	unsigned long size = newsize;
+	int len, ret;
 
 	if (!orig->src || !orig->dest)
 		return -1;
 
 	if (strcmp(orig->type, "lvm")) {
-		ERROR("LVM clone from %s backing store is not supported",
-			orig->type);
-		return -1;
+		if (snap) {
+			ERROR("LVM snapshot from %s backing store is not supported",
+				orig->type);
+			return -1;
+		}
+		// Use VG 'lxc' by default
+		// We will want to support custom VGs, at least as specified through
+		// /etc/lxc/lxc.conf, preferably also over cmdline
+		len = strlen("/dev/lxc/") + strlen(cname) + 1;
+		if ((new->src = malloc(len)) == NULL)
+			return -1;
+		ret = snprintf(new->src, len, "/dev/lxc/%s", 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->data) {
@@ -757,21 +783,32 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
 		if (!new->data)
 			return -1;
 	}
-	new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath);
+
+	len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
+	new->dest = malloc(len);
 	if (!new->dest)
 		return -1;
-	if (mkdir_p(new->dest, 0755) < 0)
+	ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
+	if (ret < 0 || ret >= len)
 		return -1;
-
-
-	new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
-	if (!new->src)
+	if (mkdir_p(new->dest, 0755) < 0)
 		return -1;
 
-	if (!newsize && blk_getsize(orig->src, &size) < 0) {
-		ERROR("Error getting size of %s", orig->src);
-		return -1;
+	if (is_blktype(orig)) {
+		if (!newsize && blk_getsize(orig->src, &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 = 1000000000; // default to 1G
 	}
+
 	if (snap) {
 		if (lvm_snapshot(orig->src, new->src, size) < 0) {
 			ERROR("could not create %s snapshot of %s", new->src, orig->src);
@@ -782,10 +819,6 @@ static int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldna
 			ERROR("Error creating new lvm blockdev");
 			return -1;
 		}
-		if (detect_fs(orig, fstype, 100) < 0) {
-			ERROR("could not find fstype for %s", orig->src);
-			return -1;
-		}
 		if (do_mkfs(new->src, fstype) < 0) {
 			ERROR("Error creating filesystem type %s on %s", fstype,
 				new->src);
@@ -997,15 +1030,27 @@ static int btrfs_clonepaths(struct bdev *orig, struct bdev *new, const char *old
 		return -1;
 
 	if (strcmp(orig->type, "btrfs")) {
-		ERROR("btrfs cloen from %s backing store is not supported",
-			orig->type);
-		return -1;
-	}
+		int len, ret;
+		if (snap) {
+			ERROR("btrfs 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;
+	} else {
+		// in case rootfs is in custom path, reuse it
+		if ((new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath)) == NULL)
+			return -1;
 
-	if ((new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath)) == NULL)
-		return -1;
+	}
 
-	if ((new->src = strdup(new->dest)) == NULL)
+	if ((new->dest = strdup(new->src)) == NULL)
 		return -1;
 
 	if (orig->data && (new->data = strdup(orig->data)) == NULL)
diff --git a/src/lxc/bdev.h b/src/lxc/bdev.h
index 131f158..cc03592 100644
--- a/src/lxc/bdev.h
+++ b/src/lxc/bdev.h
@@ -22,6 +22,13 @@ struct bdev_ops {
 			int snap, unsigned long newsize);
 };
 
+/*
+ * When lxc-start (conf.c) is mounting a rootfs, then src will be the
+ * 'lxc.rootfs' value, dest will be mount dir (i.e. $libdir/lxc)  When clone
+ * or create is doing so, then dest will be $lxcpath/$lxcname/rootfs, since
+ * we may need to rsync from one to the other.
+ * data is so far unused.
+ */
 struct bdev {
 	struct bdev_ops *ops;
 	char *type;
diff --git a/src/lxc/lxc-destroy.in b/src/lxc/lxc-destroy.in
index 2e7a486..de29909 100644
--- a/src/lxc/lxc-destroy.in
+++ b/src/lxc/lxc-destroy.in
@@ -40,6 +40,8 @@ help() {
     echo "  -P lxcpath container is in specified lxcpath" >&2
 }
 
+. @DATADIR@/lxc/lxc.functions
+
 usage_err() {
     [ -n "$1" ] && echo "$1" >&2
     usage
@@ -47,21 +49,46 @@ usage_err() {
 }
 
 verify_zfs() {
-    path=$1
-    if which zfs >/dev/null 2>&1 && zfs list | grep -q $path; then
+    local path=$1
+    which zfs > /dev/null 2>&1 || { echo no; return; }
+    if zfs list -H $path >/dev/null 2>&1; then
         echo zfs
     else
         echo no
     fi
 }
 
+busy_zfs() {
+    local path=$1
+    local dev
+    dev=`zfs list -H $path 2>/dev/null | awk '{ print $1 }'`
+    if zfs list -t snapshot | grep -q "$dev"; then
+        echo busy
+    else
+        echo zfs
+    fi
+}
+
+verify_lvm() {
+    local path=$1
+    if [ -b $path -o -h $path ]; then
+        lvdisplay $path > /dev/null 2>&1 && { echo lvm; return; }
+    fi
+    echo no
+}
+
+busy_lvm() {
+    local path=$1
+    lvdisplay $path | grep -q "LV snapshot status.*source of" && { echo busy; return; }
+    echo lvm
+}
+
 optarg_check() {
     if [ -z "$2" ]; then
         usage_err "option '$1' requires an argument"
     fi
 }
 
-. @DATADIR@/lxc/lxc.functions
 force=0
 
 while [ $# -gt 0 ]; do
@@ -134,14 +161,25 @@ fi
 # else, ignore it. We'll support deletion of others later.
 rootdev=`grep lxc.rootfs $lxc_path/$lxc_name/config 2>/dev/null | sed -e 's/^[^/]*//'`
 if [ -n "$rootdev" ]; then
-    if [ -b "$rootdev" -o -h "$rootdev" ]; then
-        lvdisplay $rootdev > /dev/null 2>&1
-        if [ $? -eq 0 ]; then
+    if [ `verify_lvm $rootdev` = "lvm" ]; then
+        if [ `busy_lvm $rootdev` = "busy" ]; then
+            echo "$rootdev has lvm snapshots - not deleting"
+            exit 1
+        else
             echo "removing backing store: $rootdev"
             lvremove -f $rootdev
         fi
     elif [ `verify_zfs $rootdev` = "zfs" ]; then
-        zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }')
+        if [ `busy_zfs $rootdev` = "busy" ]; then
+            echo "$rootdev has zfs snapshots - not deleting"
+            exit 1
+        else
+            zfs destroy $(zfs list | grep $rootdev | awk '{ print $1 }')
+            if [ $? -ne 0 ]; then
+                echo "zfs destroy failed - please wait a bit and try again"
+                exit 1
+            fi
+        fi
     elif [ -h "$rootdev" -o -d "$rootdev" ]; then
         if which btrfs >/dev/null 2>&1 &&
            btrfs subvolume list "$rootdev" >/dev/null 2>&1; then
-- 
1.8.1.2





More information about the lxc-devel mailing list