From: Christian Brauner Date: Tue, 1 Aug 2017 17:54:24 +0000 (+0200) Subject: overlay: correctly restore from snapshot X-Git-Tag: lxc-2.1.0~32^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=17a367d84154a248ba2d56aecde24f675399d54c;p=thirdparty%2Flxc.git overlay: correctly restore from snapshot Signed-off-by: Christian Brauner --- diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index b81f11916..3639d6f28 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -106,7 +106,8 @@ static bool do_lxcapi_destroy(struct lxc_container *c); static const char *lxcapi_get_config_path(struct lxc_container *c); #define do_lxcapi_get_config_path(c) lxcapi_get_config_path(c) static bool do_lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); -static bool container_destroy(struct lxc_container *c); +static bool container_destroy(struct lxc_container *c, + struct lxc_storage *storage); static bool get_snappath_dir(struct lxc_container *c, char *snappath); static bool lxcapi_snapshot_destroy_all(struct lxc_container *c); static bool do_lxcapi_save_config(struct lxc_container *c, const char *alt_file); @@ -1742,7 +1743,7 @@ out_unlock: remove_partial(c, partial_fd); out: if (!ret) - container_destroy(c); + container_destroy(c, NULL); free_tpath: free(tpath); return ret; @@ -2580,11 +2581,21 @@ static int lxc_rmdir_onedev_wrapper(void *data) return lxc_rmdir_onedev(arg, "snaps"); } -static bool container_destroy(struct lxc_container *c) +static int lxc_unlink_exec_wrapper(void *data) { + char *arg = data; + return unlink(arg); +} + +static bool container_destroy(struct lxc_container *c, + struct lxc_storage *storage) +{ + const char *p1; + size_t len; + struct lxc_conf *conf; + char *path = NULL; bool bret = false; int ret = 0; - struct lxc_conf *conf; if (!c || !do_lxcapi_is_defined(c)) return false; @@ -2601,28 +2612,27 @@ static bool container_destroy(struct lxc_container *c) if (conf && !lxc_list_empty(&conf->hooks[LXCHOOK_DESTROY])) { /* Start of environment variable setup for hooks */ - if (c->name && setenv("LXC_NAME", c->name, 1)) { - SYSERROR("failed to set environment variable for container name"); - } - if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) { - SYSERROR("failed to set environment variable for config path"); - } - if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) { - SYSERROR("failed to set environment variable for rootfs mount"); - } - if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) { - SYSERROR("failed to set environment variable for rootfs mount"); - } - if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) { - SYSERROR("failed to set environment variable for console path"); - } - if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) { - SYSERROR("failed to set environment variable for console log"); - } + if (c->name && setenv("LXC_NAME", c->name, 1)) + SYSERROR("Failed to set environment variable for container name"); + + if (conf->rcfile && setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) + SYSERROR("Failed to set environment variable for config path"); + + if (conf->rootfs.mount && setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) + SYSERROR("Failed to set environment variable for rootfs mount"); + + if (conf->rootfs.path && setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) + SYSERROR("Failed to set environment variable for rootfs mount"); + + if (conf->console.path && setenv("LXC_CONSOLE", conf->console.path, 1)) + SYSERROR("Failed to set environment variable for console path"); + + if (conf->console.log_path && setenv("LXC_CONSOLE_LOGPATH", conf->console.log_path, 1)) + SYSERROR("Failed to set environment variable for console log"); /* End of environment variable setup for hooks */ if (run_lxc_hooks(c->name, "destroy", conf, c->get_config_path(c), NULL)) { - ERROR("Error executing clone hook for %s", c->name); + ERROR("Failed to execute clone hook for \"%s\"", c->name); goto out; } } @@ -2645,23 +2655,72 @@ static bool container_destroy(struct lxc_container *c) mod_all_rdeps(c, false); - const char *p1 = do_lxcapi_get_config_path(c); - char *path = alloca(strlen(p1) + strlen(c->name) + 2); - sprintf(path, "%s/%s", p1, c->name); + p1 = do_lxcapi_get_config_path(c); + /* strlen(p1) + * + + * / + * + + * strlen(c->name) + * + + * / + * + + * strlen("config") = 6 + * + + * \0 + */ + len = strlen(p1) + 1 + strlen(c->name) + 1 + 6 + 1; + path = malloc(len); + if (!path) { + ERROR("Failed to allocate memory"); + goto out; + } + + /* For an overlay container the rootfs is considered immutable and + * cannot be removed when restoring from a snapshot. + */ + if (storage && (!strcmp(storage->type, "overlay") || + !strcmp(storage->type, "overlayfs")) && + (storage->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { + ret = snprintf(path, len, "%s/%s/config", p1, c->name); + if (ret < 0 || (size_t)ret >= len) + goto out; + + if (am_unpriv()) + ret = userns_exec_1(conf, lxc_unlink_exec_wrapper, path, + "lxc_unlink_exec_wrapper"); + else + ret = unlink(path); + if (ret < 0) { + SYSERROR("Failed to destroy config file \"%s\" for \"%s\"", + path, c->name); + goto out; + } + INFO("Destroyed config file \"%s\" for \"%s\"", path, c->name); + + bret = true; + goto out; + } + + ret = snprintf(path, len, "%s/%s", p1, c->name); + if (ret < 0 || (size_t)ret >= len) + goto out; if (am_unpriv()) ret = userns_exec_1(conf, lxc_rmdir_onedev_wrapper, path, "lxc_rmdir_onedev_wrapper"); else ret = lxc_rmdir_onedev(path, "snaps"); if (ret < 0) { - ERROR("Error destroying container directory for %s", c->name); + ERROR("Failed to destroy directory \"%s\" for \"%s\"", path, + c->name); goto out; } - INFO("Destroyed directory for %s", c->name); + INFO("Destroyed directory \"%s\" for \"%s\"", path, c->name); bret = true; out: + if (path) + free(path); container_disk_unlock(c); return bret; } @@ -2680,7 +2739,7 @@ static bool do_lxcapi_destroy(struct lxc_container *c) return false; } - return container_destroy(c); + return container_destroy(c, NULL); } WRAP_API(bool, lxcapi_destroy) @@ -3314,14 +3373,15 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char const char *bdevtype, const char *bdevdata, uint64_t newsize, char **hookargs) { - struct lxc_container *c2 = NULL; char newpath[MAXPATHLEN]; - int ret, storage_copied = 0; - char *origroot = NULL, *saved_unexp_conf = NULL; + int ret; struct clone_update_data data; size_t saved_unexp_len; FILE *fout; pid_t pid; + int storage_copied = 0; + char *origroot = NULL, *saved_unexp_conf = NULL; + struct lxc_container *c2 = NULL; if (!c || !do_lxcapi_is_defined(c)) return NULL; @@ -3344,6 +3404,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char SYSERROR("clone: failed making config pathname"); goto out; } + if (file_exists(newpath)) { ERROR("error: clone: %s exists", newpath); goto out; @@ -3394,9 +3455,18 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char SYSERROR("clone: failed making rootfs pathname"); goto out; } - if (mkdir(newpath, 0755) < 0) { - SYSERROR("error creating %s", newpath); - goto out; + + ret = mkdir(newpath, 0755); + if (ret < 0) { + /* For an overlay container the rootfs is considered immutable + * and will not have been removed when restoring from a + * snapshot. + */ + if (errno != ENOENT && + !(flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE)) { + SYSERROR("Failed to create directory \"%s\"", newpath); + goto out; + } } if (am_unpriv()) { @@ -3539,7 +3609,7 @@ static bool do_lxcapi_rename(struct lxc_container *c, const char *newname) if (newc && lxcapi_is_defined(newc)) lxc_container_put(newc); - if (!container_destroy(c)) { + if (!container_destroy(c, NULL)) { ERROR("Could not destroy existing container %s", c->name); return false; } @@ -3873,12 +3943,21 @@ static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snap return false; } - bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL); + bdev = storage_init(c->lxc_conf, c->lxc_conf->rootfs.path, + c->lxc_conf->rootfs.mount, NULL); if (!bdev) { ERROR("Failed to find original backing store type"); return false; } + /* For an overlay container the rootfs is considered immutable + * and cannot be removed when restoring from a snapshot. We pass this + * internal flag along to communicate this to various parts of the + * codebase. + */ + if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) + bdev->flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; + if (!newname) newname = c->name; @@ -3891,13 +3970,14 @@ static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snap snap = lxc_container_new(snapname, clonelxcpath); if (!snap || !lxcapi_is_defined(snap)) { ERROR("Could not open snapshot %s", snapname); - if (snap) lxc_container_put(snap); + if (snap) + lxc_container_put(snap); storage_put(bdev); return false; } - if (strcmp(c->name, newname) == 0) { - if (!container_destroy(c)) { + if (!strcmp(c->name, newname)) { + if (!container_destroy(c, bdev)) { ERROR("Could not destroy existing container %s", newname); lxc_container_put(snap); storage_put(bdev); @@ -3907,6 +3987,9 @@ static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snap if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0) flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT; + + if (!strcmp(bdev->type, "overlay") || !strcmp(bdev->type, "overlayfs")) + flags |= LXC_STORAGE_INTERNAL_OVERLAY_RESTORE; rest = lxcapi_clone(snap, newname, c->config_path, flags, bdev->type, NULL, 0, NULL); storage_put(bdev); @@ -3914,6 +3997,7 @@ static bool do_lxcapi_snapshot_restore(struct lxc_container *c, const char *snap b = true; if (rest) lxc_container_put(rest); + lxc_container_put(snap); return b; } @@ -4414,7 +4498,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath if (ongoing_create(c) == 2) { ERROR("Error: %s creation was not completed", c->name); - container_destroy(c); + container_destroy(c, NULL); lxcapi_clear_config(c); } c->daemonize = true; diff --git a/src/lxc/storage/btrfs.c b/src/lxc/storage/btrfs.c index 18755aa01..3057cf546 100644 --- a/src/lxc/storage/btrfs.c +++ b/src/lxc/storage/btrfs.c @@ -438,8 +438,8 @@ bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, data.orig = orig; data.new = new; if (am_unpriv()) { - ret = userns_exec_1(conf, lxc_rsync_exec_wrapper, &data, - "lxc_rsync_exec_wrapper"); + ret = userns_exec_1(conf, lxc_storage_rsync_exec_wrapper, &data, + "lxc_storage_rsync_exec_wrapper"); if (ret < 0) { ERROR("Failed to rsync from \"%s\" into \"%s\"", orig->dest, new->dest); @@ -450,7 +450,7 @@ bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, } ret = run_command(cmd_output, sizeof(cmd_output), - lxc_rsync_exec_wrapper, (void *)&data); + lxc_storage_rsync_exec_wrapper, (void *)&data); if (ret < 0) { ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest, new->dest, cmd_output); diff --git a/src/lxc/storage/lvm.c b/src/lxc/storage/lvm.c index fc3a2c485..6ceab1dea 100644 --- a/src/lxc/storage/lvm.c +++ b/src/lxc/storage/lvm.c @@ -507,7 +507,7 @@ bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, data.orig = orig; data.new = new; ret = run_command(cmd_output, sizeof(cmd_output), - lxc_rsync_exec_wrapper, (void *)&data); + lxc_storage_rsync_exec_wrapper, (void *)&data); if (ret < 0) { ERROR("Failed to rsync from \"%s\" to \"%s\"", orig->dest, new->dest); diff --git a/src/lxc/storage/overlay.c b/src/lxc/storage/overlay.c index 1753dc1b0..e63a6ba56 100644 --- a/src/lxc/storage/overlay.c +++ b/src/lxc/storage/overlay.c @@ -43,7 +43,7 @@ static char *ovl_name; static char *ovl_version[] = {"overlay", "overlayfs"}; static char *ovl_detect_name(void); -static int ovl_do_rsync(struct lxc_storage *orig, struct lxc_storage *new, +static int ovl_do_rsync(const char *src, const char *dest, struct lxc_conf *conf); static int ovl_remount_on_enodev(const char *lower, const char *target, const char *name, unsigned long mountflags, @@ -292,14 +292,16 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char return -ENOMEM; } ret = snprintf(new->src, len, "overlay:%s:%s", nsrc, ndelta); - free(osrc); - free(ndelta); if (ret < 0 || (size_t)ret >= len) { ERROR("Failed to create string"); + free(osrc); + free(ndelta); return -1; } - ret = ovl_do_rsync(orig, new, conf); + ret = ovl_do_rsync(odelta, ndelta, conf); + free(osrc); + free(ndelta); if (ret < 0) return -1; @@ -462,6 +464,12 @@ int ovl_destroy(struct lxc_storage *orig) if (!ovl && strncmp(upper, "overlayfs:", 10)) return -22; + /* For an overlay container the rootfs is considered immutable + * and cannot be removed when restoring from a snapshot. + */ + if (orig->flags & LXC_STORAGE_INTERNAL_OVERLAY_RESTORE) + return 0; + if (ovl) upper += 8; else @@ -953,28 +961,25 @@ static char *ovl_detect_name(void) return v; } -static int ovl_do_rsync(struct lxc_storage *orig, struct lxc_storage *new, +static int ovl_do_rsync(const char *src, const char *dest, struct lxc_conf *conf) { int ret = -1; - struct rsync_data rdata = {0, 0}; + struct rsync_data_char rdata = {0}; char cmd_output[MAXPATHLEN] = {0}; - rdata.orig = orig; - rdata.new = new; - if (am_unpriv()) { + rdata.src = (char *)src; + rdata.dest = (char *)dest; + if (am_unpriv()) ret = userns_exec_1(conf, lxc_rsync_exec_wrapper, &rdata, "lxc_rsync_exec_wrapper"); - if (ret < 0) - ERROR("Failed to rsync from \"%s\" into \"%s\"", - orig->dest, new->dest); - } else { + else ret = run_command(cmd_output, sizeof(cmd_output), lxc_rsync_exec_wrapper, (void *)&rdata); - if (ret < 0) - ERROR("Failed to rsync from \"%s\" into \"%s\": %s", - orig->dest, new->dest, cmd_output); - } + if (ret < 0) + ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", src, dest, + cmd_output[0] != '\0' ? ": " : "", + cmd_output[0] != '\0' ? cmd_output : ""); return ret; } diff --git a/src/lxc/storage/rsync.c b/src/lxc/storage/rsync.c index e50f4152b..55c9504e7 100644 --- a/src/lxc/storage/rsync.c +++ b/src/lxc/storage/rsync.c @@ -39,12 +39,28 @@ lxc_log_define(rsync, lxc); -int lxc_rsync_exec_wrapper(void *data) +int lxc_storage_rsync_exec_wrapper(void *data) { struct rsync_data *arg = data; return lxc_rsync(arg); } +int lxc_rsync_exec_wrapper(void *data) +{ + int ret; + struct rsync_data_char *args = data; + + ret = lxc_switch_uid_gid(0, 0); + if (ret < 0) + return -1; + + ret = lxc_setgroups(0, NULL); + if (ret < 0) + return -1; + + return lxc_rsync_exec(args->src, args->dest); +} + int lxc_rsync_exec(const char *src, const char *dest) { int ret; @@ -70,8 +86,8 @@ int lxc_rsync_exec(const char *src, const char *dest) int lxc_rsync(struct rsync_data *data) { int ret; - struct lxc_storage *orig = data->orig, *new = data->new; char *dest, *src; + struct lxc_storage *orig = data->orig, *new = data->new; ret = unshare(CLONE_NEWNS); if (ret < 0) { @@ -101,6 +117,7 @@ int lxc_rsync(struct rsync_data *data) ret = lxc_switch_uid_gid(0, 0); if (ret < 0) return -1; + ret = lxc_setgroups(0, NULL); if (ret < 0) return -1; diff --git a/src/lxc/storage/rsync.h b/src/lxc/storage/rsync.h index d8581fe40..9984bc98b 100644 --- a/src/lxc/storage/rsync.h +++ b/src/lxc/storage/rsync.h @@ -39,6 +39,7 @@ struct rsync_data_char { /* new helpers */ extern int lxc_rsync_exec_wrapper(void *data); +extern int lxc_storage_rsync_exec_wrapper(void *data); extern int lxc_rsync_exec(const char *src, const char *dest); extern int lxc_rsync(struct rsync_data *data); diff --git a/src/lxc/storage/storage.c b/src/lxc/storage/storage.c index 468563b8c..fee3d8df1 100644 --- a/src/lxc/storage/storage.c +++ b/src/lxc/storage/storage.c @@ -502,11 +502,11 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname, data.orig = orig; data.new = new; if (am_unpriv()) - ret = userns_exec_1(c->lxc_conf, lxc_rsync_exec_wrapper, &data, - "lxc_rsync_exec_wrapper"); + ret = userns_exec_1(c->lxc_conf, lxc_storage_rsync_exec_wrapper, + &data, "lxc_storage_rsync_exec_wrapper"); else ret = run_command(cmd_output, sizeof(cmd_output), - lxc_rsync_exec_wrapper, (void *)&data); + lxc_storage_rsync_exec_wrapper, (void *)&data); if (ret < 0) { ERROR("Failed to rsync from \"%s\" into \"%s\"%s%s", orig->dest, new->dest, @@ -598,6 +598,8 @@ struct lxc_storage *storage_init(struct lxc_conf *conf, const char *src, struct lxc_storage *bdev; const struct lxc_storage_type *q; + BUILD_BUG_ON(LXC_STORAGE_INTERNAL_OVERLAY_RESTORE <= LXC_CLONE_MAXFLAGS); + if (!src) src = conf->rootfs.path; diff --git a/src/lxc/storage/storage.h b/src/lxc/storage/storage.h index 735886cd2..082b59e05 100644 --- a/src/lxc/storage/storage.h +++ b/src/lxc/storage/storage.h @@ -57,6 +57,8 @@ #define DEFAULT_FS_SIZE 1073741824 #define DEFAULT_FSTYPE "ext3" +#define LXC_STORAGE_INTERNAL_OVERLAY_RESTORE (1 << 6) + struct lxc_storage; struct lxc_storage_ops { @@ -97,6 +99,7 @@ struct lxc_storage { int lofd; /* index for the connected nbd device. */ int nbd_idx; + int flags; }; extern bool storage_is_dir(struct lxc_conf *conf, const char *path); diff --git a/src/lxc/storage/zfs.c b/src/lxc/storage/zfs.c index a9f560fc3..1198fd52b 100644 --- a/src/lxc/storage/zfs.c +++ b/src/lxc/storage/zfs.c @@ -325,7 +325,7 @@ bool zfs_copy(struct lxc_conf *conf, struct lxc_storage *orig, data.orig = orig; data.new = new; ret = run_command(cmd_output, sizeof(cmd_output), - lxc_rsync_exec_wrapper, (void *)&data); + lxc_storage_rsync_exec_wrapper, (void *)&data); if (ret < 0) { ERROR("Failed to rsync from \"%s\" into \"%s\": %s", orig->dest, new->dest, cmd_output);