2
0
mirror of git://github.com/lxc/lxc synced 2025-08-31 01:19:35 +00:00

zfs: rework zfs storage driver

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
Christian Brauner
2017-07-25 17:09:24 +02:00
parent f83dd99ebe
commit 3ef1df7c33
4 changed files with 653 additions and 215 deletions

View File

@@ -82,8 +82,8 @@ static const struct bdev_ops aufs_ops = {
.clone_paths = &aufs_clonepaths,
.destroy = &aufs_destroy,
.create = &aufs_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = true,
.can_backup = true,
};
@@ -96,8 +96,8 @@ static const struct bdev_ops btrfs_ops = {
.clone_paths = &btrfs_clonepaths,
.destroy = &btrfs_destroy,
.create = &btrfs_create,
.create_clone = &btrfs_create_clone,
.create_snapshot = &btrfs_create_snapshot,
.copy = &btrfs_create_clone,
.snapshot = &btrfs_create_snapshot,
.can_snapshot = true,
.can_backup = true,
};
@@ -110,8 +110,8 @@ static const struct bdev_ops dir_ops = {
.clone_paths = &dir_clonepaths,
.destroy = &dir_destroy,
.create = &dir_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = false,
.can_backup = true,
};
@@ -124,8 +124,8 @@ static const struct bdev_ops loop_ops = {
.clone_paths = &loop_clonepaths,
.destroy = &loop_destroy,
.create = &loop_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = false,
.can_backup = true,
};
@@ -138,8 +138,8 @@ static const struct bdev_ops lvm_ops = {
.clone_paths = &lvm_clonepaths,
.destroy = &lvm_destroy,
.create = &lvm_create,
.create_clone = &lvm_create_clone,
.create_snapshot = &lvm_create_snapshot,
.copy = &lvm_create_clone,
.snapshot = &lvm_create_snapshot,
.can_snapshot = true,
.can_backup = false,
};
@@ -152,8 +152,8 @@ const struct bdev_ops nbd_ops = {
.clone_paths = &nbd_clonepaths,
.destroy = &nbd_destroy,
.create = &nbd_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = true,
.can_backup = false,
};
@@ -166,8 +166,8 @@ static const struct bdev_ops ovl_ops = {
.clone_paths = &ovl_clonepaths,
.destroy = &ovl_destroy,
.create = &ovl_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = true,
.can_backup = true,
};
@@ -180,8 +180,8 @@ static const struct bdev_ops rbd_ops = {
.clone_paths = &rbd_clonepaths,
.destroy = &rbd_destroy,
.create = &rbd_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = NULL,
.snapshot = NULL,
.can_snapshot = false,
.can_backup = false,
};
@@ -194,8 +194,8 @@ static const struct bdev_ops zfs_ops = {
.clone_paths = &zfs_clonepaths,
.destroy = &zfs_destroy,
.create = &zfs_create,
.create_clone = NULL,
.create_snapshot = NULL,
.copy = &zfs_copy,
.snapshot = &zfs_snapshot,
.can_snapshot = true,
.can_backup = true,
};
@@ -436,9 +436,9 @@ struct bdev *bdev_copy(struct lxc_container *c0, const char *cname,
if (!strcmp(orig->type, "btrfs") && !strcmp(new->type, "btrfs")) {
bool bret = false;
if (snap || btrfs_same_fs(orig->dest, new->dest) == 0)
bret = new->ops->create_snapshot(c0->lxc_conf, orig, new, 0);
bret = new->ops->snapshot(c0->lxc_conf, orig, new, 0);
else
bret = new->ops->create_clone(c0->lxc_conf, orig, new, 0);
bret = new->ops->copy(c0->lxc_conf, orig, new, 0);
if (!bret)
return NULL;
return new;
@@ -448,11 +448,24 @@ struct bdev *bdev_copy(struct lxc_container *c0, const char *cname,
if (!strcmp(orig->type, "lvm") && !strcmp(new->type, "lvm")) {
bool bret = false;
if (snap)
bret = new->ops->create_snapshot(c0->lxc_conf, orig,
bret = new->ops->snapshot(c0->lxc_conf, orig,
new, newsize);
else
bret = new->ops->create_clone(c0->lxc_conf, orig, new,
newsize);
bret = new->ops->copy(c0->lxc_conf, orig, new, newsize);
if (!bret)
return NULL;
return new;
}
/* zfs */
if (!strcmp(orig->type, "zfs") && !strcmp(new->type, "zfs")) {
bool bret = false;
if (snap)
bret = new->ops->snapshot(c0->lxc_conf, orig, new,
newsize);
else
bret = new->ops->copy(c0->lxc_conf, orig, new, newsize);
if (!bret)
return NULL;
return new;

View File

@@ -73,10 +73,10 @@ struct bdev_ops {
const char *oldname, const char *cname,
const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf);
bool (*create_clone)(struct lxc_conf *conf, struct bdev *orig,
struct bdev *new, uint64_t newsize);
bool (*create_snapshot)(struct lxc_conf *conf, struct bdev *orig,
struct bdev *new, uint64_t newsize);
bool (*copy)(struct lxc_conf *conf, struct bdev *orig, struct bdev *new,
uint64_t newsize);
bool (*snapshot)(struct lxc_conf *conf, struct bdev *orig,
struct bdev *new, uint64_t newsize);
bool can_snapshot;
bool can_backup;
};

View File

@@ -22,6 +22,7 @@
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,17 +33,77 @@
#include "bdev.h"
#include "config.h"
#include "log.h"
#include "lxcrsync.h"
#include "lxczfs.h"
#include "parse.h"
#include "utils.h"
lxc_log_define(lxczfs, lxc);
/* 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.
*/
struct zfs_args {
const char *dataset;
const char *snapshot;
const char *options;
void *argv;
};
int zfs_detect_exec_wrapper(void *data)
{
struct zfs_args *args = data;
execlp("zfs", "zfs", "get", "type", "-H", "-o", "name", args->dataset,
(char *)NULL);
return -1;
}
int zfs_create_exec_wrapper(void *args)
{
struct zfs_args *zfs_args = args;
execvp("zfs", zfs_args->argv);
return -1;
}
int zfs_delete_exec_wrapper(void *args)
{
struct zfs_args *zfs_args = args;
execlp("zfs", "zfs", "destroy", "-r", zfs_args->dataset, (char *)NULL);
return -1;
}
int zfs_snapshot_exec_wrapper(void *args)
{
struct zfs_args *zfs_args = args;
execlp("zfs", "zfs", "snapshot", "-r", zfs_args->snapshot, (char *)NULL);
return -1;
}
int zfs_clone_exec_wrapper(void *args)
{
struct zfs_args *zfs_args = args;
execlp("zfs", "zfs", "clone", "-p", "-o", "canmount=noauto", "-o",
zfs_args->options, zfs_args->snapshot, zfs_args->dataset,
(char *)NULL);
return -1;
}
int zfs_get_parent_snapshot_exec_wrapper(void *args)
{
struct zfs_args *zfs_args = args;
execlp("zfs", "zfs", "get", "origin", "-o", "value", "-H",
zfs_args->dataset, (char *)NULL);
return -1;
}
static bool zfs_list_entry(const char *path, char *output, size_t inlen)
{
@@ -68,27 +129,60 @@ static bool zfs_list_entry(const char *path, char *output, size_t inlen)
bool zfs_detect(const char *path)
{
int ret;
char *dataset;
struct zfs_args cmd_args = {0};
char cmd_output[MAXPATHLEN] = {0};
if (!strncmp(path, "zfs:", 4))
return true;
char *output = malloc(LXC_LOG_BUFFER_SIZE);
/* This is a legacy zfs setup where the rootfs path
* "<lxcpath>/<lxcname>/rootfs" is given.
*/
if (*path == '/') {
bool found;
char *output = malloc(LXC_LOG_BUFFER_SIZE);
if (!output) {
ERROR("out of memory");
if (!output) {
ERROR("out of memory");
return false;
}
found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE);
free(output);
return found;
}
cmd_args.dataset = path;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_detect_exec_wrapper, (void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to detect zfs dataset \"%s\": %s", path, cmd_output);
return false;
}
bool found = zfs_list_entry(path, output, LXC_LOG_BUFFER_SIZE);
free(output);
if (cmd_output[0] == '\0')
return false;
return found;
/* remove any possible leading and trailing whitespace */
dataset = cmd_output;
dataset += lxc_char_left_gc(dataset, strlen(dataset));
dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0';
if (strcmp(dataset, path))
return false;
return true;
}
int zfs_mount(struct bdev *bdev)
{
int ret;
char *mntdata, *src;
size_t oldlen, newlen, totallen;
char *mntdata, *src, *tmp;
unsigned long mntflags;
char cmd_output[MAXPATHLEN] = {0};
if (strcmp(bdev->type, "zfs"))
return -22;
@@ -96,126 +190,266 @@ int zfs_mount(struct bdev *bdev)
if (!bdev->src || !bdev->dest)
return -22;
if (parse_mntopts(bdev->mntopts, &mntflags, &mntdata) < 0) {
ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata);
if (ret < 0) {
ERROR("Failed to parse mount options");
free(mntdata);
return -22;
}
/* This is a legacy zfs setup where the rootfs path
* "<lxcpath>/<lxcname>/rootfs" is given and we do a bind-mount.
*/
src = lxc_storage_get_path(bdev->src, bdev->type);
ret = mount(src, bdev->dest, "bind", MS_BIND | MS_REC | mntflags,
mntdata);
free(mntdata);
if (*src == '/') {
bool found;
return ret;
found = zfs_list_entry(src, cmd_output, sizeof(cmd_output));
if (!found) {
ERROR("Failed to find zfs entry \"%s\"", src);
return -1;
}
tmp = strchr(cmd_output, ' ');
if (!tmp) {
ERROR("Failed to detect zfs dataset associated with "
"\"%s\"", src);
return -1;
}
*tmp = '\0';
src = cmd_output;
}
/* ','
* +
* strlen("zfsutil")
* +
* ','
* +
* strlen(mntpoint=)
* +
* strlen(src)
* +
* '\0'
*/
newlen = 1 + 7 + 1 + 9 + strlen(src) + 1;
oldlen = mntdata ? strlen(mntdata) : 0;
totallen = (newlen + oldlen);
tmp = realloc(mntdata, totallen);
if (!tmp) {
ERROR("Failed to reallocate memory");
free(mntdata);
return -1;
}
mntdata = tmp;
ret = snprintf((mntdata + oldlen), newlen, ",zfsutil,mntpoint=%s", src);
if (ret < 0 || (size_t)ret >= newlen) {
ERROR("Failed to create string");
free(mntdata);
return -1;
}
ret = mount(src, bdev->dest, "zfs", mntflags, mntdata);
free(mntdata);
if (ret < 0 && errno != EBUSY) {
SYSERROR("Failed to mount \"%s\" on \"%s\"", src, bdev->dest);
return -1;
}
TRACE("Mounted \"%s\" on \"%s\"", src, bdev->dest);
return 0;
}
int zfs_umount(struct bdev *bdev)
{
int ret;
if (strcmp(bdev->type, "zfs"))
return -22;
if (!bdev->src || !bdev->dest)
return -22;
return umount(bdev->dest);
ret = umount(bdev->dest);
if (ret < 0)
SYSERROR("Failed to unmount \"%s\"", bdev->dest);
else
TRACE("Unmounted \"%s\"", bdev->dest);
return ret;
}
int zfs_clone(const char *opath, const char *npath, const char *oname,
const char *nname, const char *lxcpath, int snapshot)
bool zfs_copy(struct lxc_conf *conf, struct bdev *orig, struct bdev *new,
uint64_t newsize)
{
// use the 'zfs list | grep opath' entry to get the zfsroot
char output[MAXPATHLEN], option[MAXPATHLEN];
char *p;
const char *zfsroot = output;
int ret;
pid_t pid;
char cmd_output[MAXPATHLEN], option[MAXPATHLEN];
struct rsync_data data = {0, 0};
struct zfs_args cmd_args = {0};
char *argv[] = {"zfs", /* 0 */
"create", /* 1 */
"-o", "", /* 2, 3 */
"-o", "canmount=noauto", /* 4, 5 */
"-p", /* 6 */
"", /* 7 */
NULL};
if (zfs_list_entry(opath, output, MAXPATHLEN)) {
// zfsroot is output up to ' '
if ((p = strchr(output, ' ')) == NULL)
return -1;
*p = '\0';
/* mountpoint */
ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest);
if (ret < 0 || ret >= MAXPATHLEN) {
ERROR("Failed to create string");
return false;
}
argv[3] = option;
argv[7] = lxc_storage_get_path(new->src, new->type);
if ((p = strrchr(output, '/')) == NULL)
return -1;
*p = '\0';
cmd_args.argv = argv;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_create_exec_wrapper, (void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output);
return false;
} else if (cmd_output[0] != '\0') {
INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output);
} else {
zfsroot = lxc_global_config_value("lxc.bdev.zfs.root");
TRACE("Created zfs dataset \"%s\"", new->src);
}
ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s/%s/rootfs", lxcpath,
nname);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
ret = mkdir_p(new->dest, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", new->dest);
return false;
}
// zfs create -omountpoint=$lxcpath/$lxcname $zfsroot/$nname
data.orig = orig;
data.new = new;
ret = run_command(cmd_output, sizeof(cmd_output),
lxc_rsync_exec_wrapper, (void *)&data);
if (ret < 0) {
ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest,
new->dest, cmd_output);
return false;
}
TRACE("Rsynced from \"%s\" to \"%s\"", orig->dest, new->dest);
return true;
}
/* create read-only snapshot and create a clone from it */
bool zfs_snapshot(struct lxc_conf *conf, struct bdev *orig, struct bdev *new,
uint64_t newsize)
{
int ret;
size_t snapshot_len, len;
char *orig_src, *tmp, *snap_name, *snapshot;
struct zfs_args cmd_args = {0};
char cmd_output[MAXPATHLEN] = {0}, option[MAXPATHLEN];
orig_src = lxc_storage_get_path(orig->src, orig->type);
if (*orig_src == '/') {
bool found;
found = zfs_list_entry(orig_src, cmd_output, sizeof(cmd_output));
if (!found) {
ERROR("Failed to find zfs entry \"%s\"", orig_src);
return false;
}
tmp = strchr(cmd_output, ' ');
if (!tmp) {
ERROR("Failed to detect zfs dataset associated with "
"\"%s\"", orig_src);
return false;
}
*tmp = '\0';
orig_src = cmd_output;
}
snapshot = strdup(orig_src);
if (!snapshot) {
if ((pid = fork()) < 0)
return -1;
if (!pid) {
char dev[MAXPATHLEN];
ret =
snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, nname);
if (ret < 0 || ret >= MAXPATHLEN)
exit(EXIT_FAILURE);
execlp("zfs", "zfs", "create", option, dev,
(char *)NULL);
exit(EXIT_FAILURE);
}
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", zfsroot, oname,
nname);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
(void)snprintf(path2, MAXPATHLEN, "%s/%s", zfsroot, nname);
// if the snapshot exists, delete it
if ((pid = fork()) < 0)
return -1;
if (!pid) {
int dev0 = open("/dev/null", O_WRONLY);
if (dev0 >= 0)
dup2(dev0, STDERR_FILENO);
execlp("zfs", "zfs", "destroy", path1, (char *)NULL);
exit(EXIT_FAILURE);
}
// 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) {
execlp("zfs", "zfs", "snapshot", path1, (char *)NULL);
exit(EXIT_FAILURE);
}
if (wait_for_pid(pid) < 0)
return -1;
// run second (clone) command
if ((pid = fork()) < 0)
return -1;
if (!pid) {
execlp("zfs", "zfs", "clone", option, path1, path2,
(char *)NULL);
exit(EXIT_FAILURE);
}
return wait_for_pid(pid);
ERROR("Failed to duplicate string \"%s\"", orig_src);
return false;
}
snap_name = strrchr(new->src, '/');
if (!snap_name) {
ERROR("Failed to detect \"/\" in \"%s\"", new->src);
free(snapshot);
return false;
}
snap_name++;
/* strlen(snapshot)
* +
* @
* +
* strlen(cname)
* +
* \0
*/
snapshot_len = strlen(snapshot);
len = snapshot_len + 1 + strlen(snap_name) + 1;
tmp = realloc(snapshot, len);
if (!tmp) {
ERROR("Failed to reallocate memory");
free(snapshot);
return false;
}
snapshot = tmp;
len -= snapshot_len;
ret = snprintf(snapshot + snapshot_len, len, "@%s", snap_name);
if (ret < 0 || ret >= len) {
ERROR("Failed to create string");
free(snapshot);
return false;
}
cmd_args.snapshot = snapshot;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_snapshot_exec_wrapper, (void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to create zfs snapshot \"%s\": %s", snapshot, cmd_output);
free(snapshot);
return false;
} else if (cmd_output[0] != '\0') {
INFO("Created zfs snapshot \"%s\": %s", snapshot, cmd_output);
} else {
TRACE("Created zfs snapshot \"%s\"", snapshot);
}
ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", new->dest);
if (ret < 0 || ret >= MAXPATHLEN) {
ERROR("Failed to create string");
free(snapshot);
return -1;
}
cmd_args.dataset = lxc_storage_get_path(new->src, new->type);
cmd_args.snapshot = snapshot;
cmd_args.options = option;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_clone_exec_wrapper, (void *)&cmd_args);
if (ret < 0)
ERROR("Failed to create zfs dataset \"%s\": %s", new->src, cmd_output);
else if (cmd_output[0] != '\0')
INFO("Created zfs dataset \"%s\": %s", new->src, cmd_output);
else
TRACE("Created zfs dataset \"%s\"", new->src);
free(snapshot);
return true;
}
int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf)
{
char *origsrc, *newsrc;
int len, ret;
char *dataset, *orig_src, *tmp;
int ret;
size_t dataset_len, len;
char cmd_output[MAXPATHLEN] = {0};
if (!orig->src || !orig->dest)
return -1;
@@ -226,77 +460,261 @@ int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
return -1;
}
len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 4 + 3;
new->src = malloc(len);
if (!new->src)
orig_src = lxc_storage_get_path(orig->src, orig->type);
if (!strcmp(orig->type, "zfs")) {
size_t len;
if (*orig_src == '/') {
bool found;
found = zfs_list_entry(orig_src, cmd_output,
sizeof(cmd_output));
if (!found) {
ERROR("Failed to find zfs entry \"%s\"", orig_src);
return -1;
}
tmp = strchr(cmd_output, ' ');
if (!tmp) {
ERROR("Failed to detect zfs dataset associated "
"with \"%s\"", orig_src);
return -1;
}
*tmp = '\0';
orig_src = cmd_output;
}
tmp = strrchr(orig_src, '/');
if (!tmp) {
ERROR("Failed to detect \"/\" in \"%s\"", orig_src);
return -1;
}
len = tmp - orig_src;
dataset = strndup(orig_src, len);
if (!dataset) {
ERROR("Failed to duplicate string \"%zu\" "
"bytes of string \"%s\"", len, orig_src);
return -1;
}
} else {
tmp = (char *)lxc_global_config_value("lxc.bdev.zfs.root");
if (!tmp) {
ERROR("The \"lxc.bdev.zfs.root\" property is not set");
return -1;
}
dataset = strdup(tmp);
if (!dataset) {
ERROR("Failed to duplicate string \"%s\"", tmp);
return -1;
}
}
/* strlen("zfs:") = 4
* +
* strlen(dataset)
* +
* / = 1
* +
* strlen(cname)
* +
* \0
*/
dataset_len = strlen(dataset);
len = 4 + dataset_len + 1 + strlen(cname) + 1;
new->src = realloc(dataset, len);
if (!new->src) {
ERROR("Failed to reallocate memory");
free(dataset);
return -1;
}
memmove(new->src + 4, new->src, dataset_len);
memmove(new->src, "zfs:", 4);
ret = snprintf(new->src, len, "zfs:%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len)
return -1;
newsrc = lxc_storage_get_path(new->src, new->type);
new->dest = strdup(newsrc);
if (!new->dest)
return -1;
origsrc = lxc_storage_get_path(orig->src, orig->type);
return zfs_clone(origsrc, newsrc, oldname, cname, lxcpath, snap);
}
/*
* TODO: detect whether this was a clone, and if so then also delete the
* snapshot it was based on, so that we don't hold the original
* container busy.
*/
int zfs_destroy(struct bdev *orig)
{
pid_t pid;
char output[MAXPATHLEN];
char *p, *src;
if ((pid = fork()) < 0)
return -1;
if (pid)
return wait_for_pid(pid);
src = lxc_storage_get_path(orig->src, orig->type);
if (!zfs_list_entry(src, output, MAXPATHLEN)) {
ERROR("Error: zfs entry for %s not found", orig->src);
len -= dataset_len - 4;
ret = snprintf(new->src + dataset_len + 4, len, "/%s", cname);
if (ret < 0 || ret >= len) {
ERROR("Failed to create string");
return -1;
}
// zfs mount is output up to ' '
if ((p = strchr(output, ' ')) == NULL)
/* strlen(lxcpath)
* +
* /
* +
* strlen(cname)
* +
* /
* +
* strlen("rootfs")
* +
* \0
*/
len = strlen(lxcpath) + 1 + strlen(cname) + 1 + strlen("rootfs") + 1;
new->dest = malloc(len);
if (!new->dest) {
ERROR("Failed to allocate memory");
return -1;
*p = '\0';
}
execlp("zfs", "zfs", "destroy", "-r", output, (char *)NULL);
exit(EXIT_FAILURE);
ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
if (ret < 0 || ret >= len) {
ERROR("Failed to create string \"%s/%s/rootfs\"", lxcpath, cname);
return -1;
}
ret = mkdir_p(new->dest, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", new->dest);
return -1;
}
return 0;
}
struct zfs_exec_args {
char *dataset;
char *options;
};
int zfs_create_exec_wrapper(void *args)
int zfs_destroy(struct bdev *orig)
{
struct zfs_exec_args *zfs_args = args;
int ret;
char *dataset, *src, *tmp;
bool found;
char *parent_snapshot = NULL;
struct zfs_args cmd_args = {0};
char cmd_output[MAXPATHLEN] = {0};
execlp("zfs", "zfs", "create", zfs_args->options, zfs_args->dataset,
(char *)NULL);
return -1;
src = lxc_storage_get_path(orig->src, orig->type);
/* This is a legacy zfs setup where the rootfs path
* "<lxcpath>/<lxcname>/rootfs" is given.
*/
if (*src == '/') {
char *tmp;
found = zfs_list_entry(src, cmd_output, sizeof(cmd_output));
if (!found) {
ERROR("Failed to find zfs entry \"%s\"", orig->src);
return -1;
}
tmp = strchr(cmd_output, ' ');
if (!tmp) {
ERROR("Failed to detect zfs dataset associated with "
"\"%s\"", cmd_output);
return -1;
}
*tmp = '\0';
dataset = cmd_output;
} else {
cmd_args.dataset = src;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_detect_exec_wrapper, (void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to detect zfs dataset \"%s\": %s", src,
cmd_output);
return -1;
}
if (cmd_output[0] == '\0') {
ERROR("Failed to detect zfs dataset \"%s\"", src);
return -1;
}
/* remove any possible leading and trailing whitespace */
dataset = cmd_output;
dataset += lxc_char_left_gc(dataset, strlen(dataset));
dataset[lxc_char_right_gc(dataset, strlen(dataset))] = '\0';
if (strcmp(dataset, src)) {
ERROR("Detected dataset \"%s\" does not match expected "
"dataset \"%s\"", dataset, src);
return -1;
}
}
cmd_args.dataset = strdup(dataset);
if (!cmd_args.dataset) {
ERROR("Failed to duplicate string \"%s\"", dataset);
return -1;
}
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_get_parent_snapshot_exec_wrapper,
(void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to retrieve parent snapshot of zfs dataset "
"\"%s\": %s", dataset, cmd_output);
free((void *)cmd_args.dataset);
return -1;
} else {
INFO("Retrieved parent snapshot of zfs dataset \"%s\": %s", src,
cmd_output);
}
/* remove any possible leading and trailing whitespace */
tmp = cmd_output;
tmp += lxc_char_left_gc(tmp, strlen(tmp));
tmp[lxc_char_right_gc(tmp, strlen(tmp))] = '\0';
/* check whether the dataset has a parent snapshot */
if (*tmp != '-' && *(tmp + 1) != '\0') {
parent_snapshot = strdup(tmp);
if (!parent_snapshot) {
ERROR("Failed to duplicate string \"%s\"", tmp);
free((void *)cmd_args.dataset);
return -1;
}
}
/* delete dataset */
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_delete_exec_wrapper, (void *)&cmd_args);
if (ret < 0) {
ERROR("Failed to delete zfs dataset \"%s\": %s", dataset,
cmd_output);
free((void *)cmd_args.dataset);
free(parent_snapshot);
return -1;
} else if (cmd_output[0] != '\0') {
INFO("Deleted zfs dataset \"%s\": %s", src, cmd_output);
} else {
INFO("Deleted zfs dataset \"%s\"", src);
}
free((void *)cmd_args.dataset);
/* Not a clone so nothing more to do. */
if (!parent_snapshot)
return 0;
/* delete parent snapshot */
cmd_args.dataset = parent_snapshot;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_delete_exec_wrapper, (void *)&cmd_args);
if (ret < 0)
ERROR("Failed to delete zfs snapshot \"%s\": %s", dataset, cmd_output);
else if (cmd_output[0] != '\0')
INFO("Deleted zfs snapshot \"%s\": %s", src, cmd_output);
else
INFO("Deleted zfs snapshot \"%s\"", src);
free((void *)cmd_args.dataset);
return ret;
}
int zfs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs)
{
const char *zfsroot;
char cmd_output[MAXPATHLEN], dev[MAXPATHLEN], option[MAXPATHLEN];
int ret;
size_t len;
struct zfs_exec_args cmd_args;
struct zfs_args cmd_args = {0};
char cmd_output[MAXPATHLEN], option[MAXPATHLEN];
char *argv[] = {"zfs", /* 0 */
"create", /* 1 */
"-o", "", /* 2, 3 */
"-o", "canmount=noauto", /* 4, 5 */
"-p", /* 6 */
"", /* 7 */
NULL};
if (!specs || !specs->zfs.zfsroot)
zfsroot = lxc_global_config_value("lxc.bdev.zfs.root");
@@ -305,35 +723,48 @@ int zfs_create(struct bdev *bdev, const char *dest, const char *n,
bdev->dest = strdup(dest);
if (!bdev->dest) {
ERROR("No mount target specified or out of memory");
ERROR("Failed to duplicate string \"%s\"", dest);
return -1;
}
len = strlen(bdev->dest) + 1;
len = strlen(zfsroot) + 1 + strlen(n) + 1;
/* strlen("zfs:") */
len += 4;
bdev->src = malloc(len);
if (!bdev->src)
if (!bdev->src) {
ERROR("Failed to allocate memory");
return -1;
}
ret = snprintf(bdev->src, len, "zfs:%s", bdev->dest);
if (ret < 0 || (size_t)ret >= len)
ret = snprintf(bdev->src, len, "zfs:%s/%s", zfsroot, n);
if (ret < 0 || ret >= len) {
ERROR("Failed to create string");
return -1;
}
argv[7] = lxc_storage_get_path(bdev->src, bdev->type);
ret = snprintf(option, MAXPATHLEN, "-omountpoint=%s", bdev->dest);
if (ret < 0 || ret >= MAXPATHLEN)
ret = snprintf(option, MAXPATHLEN, "mountpoint=%s", bdev->dest);
if (ret < 0 || ret >= MAXPATHLEN) {
ERROR("Failed to create string");
return -1;
}
argv[3] = option;
ret = snprintf(dev, MAXPATHLEN, "%s/%s", zfsroot, n);
if (ret < 0 || ret >= MAXPATHLEN)
return -1;
cmd_args.options = option;
cmd_args.dataset = dev;
cmd_args.argv = argv;
ret = run_command(cmd_output, sizeof(cmd_output),
zfs_create_exec_wrapper, (void *)&cmd_args);
if (ret < 0)
ERROR("Failed to create zfs dataset \"%s\": %s", dev,
cmd_output);
ERROR("Failed to create zfs dataset \"%s\": %s", bdev->src, cmd_output);
else if (cmd_output[0] != '\0')
INFO("Created zfs dataset \"%s\": %s", bdev->src, cmd_output);
else
TRACE("Created zfs dataset \"%s\"", bdev->src);
ret = mkdir_p(bdev->dest, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", bdev->dest);
return -1;
}
return ret;
}

View File

@@ -25,36 +25,30 @@
#define __LXC_ZFS_H
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
/* defined in bdev.h */
struct bdev;
/* defined in lxccontainer.h */
struct bdev_specs;
/* defined conf.h */
struct lxc_conf;
/*
* Functions associated with an zfs bdev struct.
*/
int zfs_clone(const char *opath, const char *npath, const char *oname,
const char *nname, const char *lxcpath, int snapshot);
int zfs_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath,
int snap, uint64_t newsize, struct lxc_conf *conf);
int zfs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
/*
* TODO: detect whether this was a clone, and if so then also delete the
* snapshot it was based on, so that we don't hold the original
* container busy.
*/
int zfs_destroy(struct bdev *orig);
bool zfs_detect(const char *path);
int zfs_mount(struct bdev *bdev);
int zfs_umount(struct bdev *bdev);
extern int zfs_clonepaths(struct bdev *orig, struct bdev *new,
const char *oldname, const char *cname,
const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf);
extern int zfs_create(struct bdev *bdev, const char *dest, const char *n,
struct bdev_specs *specs);
extern int zfs_destroy(struct bdev *orig);
extern bool zfs_detect(const char *path);
extern int zfs_mount(struct bdev *bdev);
extern int zfs_umount(struct bdev *bdev);
extern bool zfs_copy(struct lxc_conf *conf, struct bdev *orig, struct bdev *new,
uint64_t newsize);
extern bool zfs_snapshot(struct lxc_conf *conf, struct bdev *orig,
struct bdev *new, uint64_t newsize);
#endif /* __LXC_ZFS_H */