mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-13 19:26:15 +00:00
Several backing store improvements
allow copy clones from other bdevs 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. 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@c2 does. We can then remove tank/c1@c2 and feel free to remove tank/c1. This patch does NOT do that yet. Make sure not to return when we're a forked child. Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
3baa76fe36
commit
ca52dcb559
151
src/lxc/bdev.c
151
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,36 +752,63 @@ 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",
|
||||
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) {
|
||||
new->data = strdup(orig->data);
|
||||
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;
|
||||
ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
|
||||
if (ret < 0 || ret >= len)
|
||||
return -1;
|
||||
if (mkdir_p(new->dest, 0755) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
|
||||
if (!new->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",
|
||||
int len, ret;
|
||||
if (snap) {
|
||||
ERROR("btrfs snapshot from %s backing store is not supported",
|
||||
orig->type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((new->dest = dir_new_path(orig->dest, oldname, cname, oldpath, lxcpath)) == NULL)
|
||||
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->src = strdup(new->dest)) == NULL)
|
||||
}
|
||||
|
||||
if ((new->dest = strdup(new->src)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (orig->data && (new->data = strdup(orig->data)) == NULL)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user