return bdev;
}
+struct rsync_data {
+ struct bdev *orig;
+ struct bdev *new;
+};
+
+static int rsync_rootfs(struct rsync_data *data)
+{
+ struct bdev *orig = data->orig,
+ *new = data->new;
+
+ if (unshare(CLONE_NEWNS) < 0) {
+ SYSERROR("unshare CLONE_NEWNS");
+ return -1;
+ }
+
+ // If not a snapshot, copy the fs.
+ if (orig->ops->mount(orig) < 0) {
+ ERROR("failed mounting %s onto %s\n", orig->src, orig->dest);
+ return -1;
+ }
+ if (new->ops->mount(new) < 0) {
+ ERROR("failed mounting %s onto %s\n", new->src, new->dest);
+ return -1;
+ }
+ if (setgid(0) < 0) {
+ ERROR("Failed to setgid to 0");
+ return -1;
+ }
+ if (setuid(0) < 0) {
+ ERROR("Failed to setuid to 0");
+ return -1;
+ }
+ if (do_rsync(orig->dest, new->dest) < 0) {
+ ERROR("rsyncing %s to %s\n", orig->src, new->src);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rsync_rootfs_wrapper(void *data)
+{
+ struct rsync_data *arg = data;
+ return rsync_rootfs(arg);
+}
/*
* If we're not snaphotting, then bdev_copy becomes a simple case of mount
* the original, mount the new, and rsync the contents.
*/
-struct bdev *bdev_copy(const char *src, const char *oldname, const char *cname,
- const char *oldpath, const char *lxcpath, const char *bdevtype,
+struct bdev *bdev_copy(struct lxc_container *c0, const char *cname,
+ const char *lxcpath, const char *bdevtype,
int flags, const char *bdevdata, uint64_t newsize,
int *needs_rdep)
{
struct bdev *orig, *new;
pid_t pid;
+ int ret;
bool snap = flags & LXC_CLONE_SNAPSHOT;
bool maybe_snap = flags & LXC_CLONE_MAYBE_SNAPSHOT;
bool keepbdevtype = flags & LXC_CLONE_KEEPBDEVTYPE;
+ const char *src = c0->lxc_conf->rootfs.path;
+ const char *oldname = c0->name;
+ const char *oldpath = c0->config_path;
+ struct rsync_data data;
/* if the container name doesn't show up in the rootfs path, then
* we don't know how to come up with a new name
}
}
+ /* check for privilege */
+ if (am_unpriv()) {
+ if (bdevtype && strcmp(bdevtype, "dir") != 0) {
+ ERROR("Unprivileged users can only make dir copy-clones");
+ bdev_put(orig);
+ return NULL;
+ }
+ if (strcmp(orig->type, "dir") != 0) {
+ ERROR("Unprivileged users can only make dir copy-clones");
+ bdev_put(orig);
+ return NULL;
+ }
+ }
+
+
/*
* special case for snapshot - if caller requested maybe_snapshot and
* keepbdevtype and backing store is directory, then proceed with a copy
bdev_put(new);
return NULL;
}
+ if (snap)
+ return new;
pid = fork();
if (pid < 0) {
return new;
}
- if (unshare(CLONE_NEWNS) < 0) {
- SYSERROR("unshare CLONE_NEWNS");
- exit(1);
- }
- if (snap)
- exit(0);
-
- // If not a snapshot, copy the fs.
- if (orig->ops->mount(orig) < 0) {
- ERROR("failed mounting %s onto %s\n", src, orig->dest);
- exit(1);
- }
- if (new->ops->mount(new) < 0) {
- ERROR("failed mounting %s onto %s\n", new->src, new->dest);
- exit(1);
- }
- if (do_rsync(orig->dest, new->dest) < 0) {
- ERROR("rsyncing %s to %s\n", orig->src, new->src);
- exit(1);
- }
- // don't bother umounting, ns exit will do that
+ data.orig = orig;
+ data.new = new;
+ if (am_unpriv())
+ ret = userns_exec_1(c0->lxc_conf, rsync_rootfs_wrapper, &data);
+ else
+ ret = rsync_rootfs(&data);
- exit(0);
+ exit(ret == 0 ? 0 : 1);
}
static struct bdev * do_bdev_create(const char *dest, const char *type,
lxc_log_define(lxc_container, lxc);
-inline static bool am_unpriv() {
- return geteuid() != 0;
-}
-
static bool file_exists(const char *f)
{
struct stat statbuf;
struct bdev *bdev;
int need_rdep;
- bdev = bdev_copy(c0->lxc_conf->rootfs.path, c0->name, c->name,
- c0->config_path, c->config_path, newtype, flags,
+ bdev = bdev_copy(c0, c->name, c->config_path, newtype, flags,
bdevdata, newsize, &need_rdep);
if (!bdev) {
ERROR("Error copying storage");
return 0;
}
-static int clone_update_rootfs(struct lxc_container *c0,
- struct lxc_container *c, int flags,
- char **hookargs)
+struct clone_update_data {
+ struct lxc_container *c0;
+ struct lxc_container *c1;
+ int flags;
+ char **hookargs;
+};
+
+static int clone_update_rootfs(struct clone_update_data *data)
{
+ struct lxc_container *c0 = data->c0;
+ struct lxc_container *c = data->c1;
+ int flags = data->flags;
+ char **hookargs = data->hookargs;
int ret = -1;
char path[MAXPATHLEN];
struct bdev *bdev;
FILE *fout;
- pid_t pid;
struct lxc_conf *conf = c->lxc_conf;
/* update hostname in rootfs */
/* we're going to mount, so run in a clean namespace to simplify cleanup */
- pid = fork();
- if (pid < 0)
+ if (setgid(0) < 0) {
+ ERROR("Failed to setgid to 0");
return -1;
- if (pid > 0)
- return wait_for_pid(pid);
+ }
+ if (setuid(0) < 0) {
+ ERROR("Failed to setuid to 0");
+ return -1;
+ }
+ if (unshare(CLONE_NEWNS) < 0)
+ return -1;
bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
if (!bdev)
- exit(1);
+ return -1;
if (strcmp(bdev->type, "dir") != 0) {
if (unshare(CLONE_NEWNS) < 0) {
ERROR("error unsharing mounts");
- exit(1);
+ return -1;
}
if (bdev->ops->mount(bdev) < 0)
- exit(1);
+ return -1;
} else { // TODO come up with a better way
if (bdev->dest)
free(bdev->dest);
if (run_lxc_hooks(c->name, "clone", conf, c->get_config_path(c), hookargs)) {
ERROR("Error executing clone hook for %s", c->name);
- exit(1);
+ return -1;
}
}
if (!(flags & LXC_CLONE_KEEPNAME)) {
ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest);
if (ret < 0 || ret >= MAXPATHLEN)
- exit(1);
+ return -1;
if (!file_exists(path))
- exit(0);
+ return 0;
if (!(fout = fopen(path, "w"))) {
SYSERROR("unable to open %s: ignoring\n", path);
- exit(0);
+ return 0;
}
if (fprintf(fout, "%s", c->name) < 0)
- exit(1);
+ return -1;
if (fclose(fout) < 0)
- exit(1);
+ return -1;
}
- exit(0);
+ return 0;
+}
+
+static int clone_update_rootfs_wrapper(void *data)
+{
+ struct clone_update_data *arg = (struct clone_update_data *) data;
+ return clone_update_rootfs(arg);
}
/*
char newpath[MAXPATHLEN];
int ret, storage_copied = 0;
const char *n, *l;
+ struct clone_update_data data;
FILE *fout;
+ pid_t pid;
if (!c || !c->is_defined(c))
return NULL;
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return NULL;
- }
-
if (container_mem_lock(c))
return NULL;
goto out;
}
+ if (am_unpriv()) {
+ if (chown_mapped_root(newpath, c->lxc_conf) < 0) {
+ ERROR("Error chowning %s to container root\n", newpath);
+ goto out;
+ }
+ }
+
c2 = lxc_container_new(n, l);
if (!c2) {
ERROR("clone: failed to create new container (%s %s)", n, l);
if (!c2->save_config(c2, NULL))
goto out;
- if (clone_update_rootfs(c, c2, flags, hookargs) < 0)
+ if ((pid = fork()) < 0) {
+ SYSERROR("fork");
goto out;
+ }
+ if (pid > 0) {
+ ret = wait_for_pid(pid);
+ if (ret)
+ goto out;
+ container_mem_unlock(c);
+ return c2;
+ }
+ data.c0 = c;
+ data.c1 = c2;
+ data.flags = flags;
+ data.hookargs = hookargs;
+ if (am_unpriv())
+ ret = userns_exec_1(c->lxc_conf, clone_update_rootfs_wrapper,
+ &data);
+ else
+ ret = clone_update_rootfs(&data);
+ if (ret < 0)
+ exit(1);
- // TODO: update c's lxc.snapshot = count
container_mem_unlock(c);
- return c2;
+ exit(0);
out:
container_mem_unlock(c);
if (!c || !c->name || !c->config_path)
return false;
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return false;
- }
-
bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
if (!bdev) {
ERROR("Failed to find original backing store type");
struct lxc_container *c2;
char snappath[MAXPATHLEN], newname[20];
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return -1;
- }
-
// /var/lib/lxc -> /var/lib/lxcsnaps \0
ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
if (ret < 0 || ret >= MAXPATHLEN)
if (!c || !lxcapi_is_defined(c))
return -1;
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return -1;
- }
-
// snappath is ${lxcpath}snaps/${lxcname}/
dirlen = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
if (dirlen < 0 || dirlen >= MAXPATHLEN) {
if (!c || !c->name || !c->config_path)
return false;
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return false;
- }
-
bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
if (!bdev) {
ERROR("Failed to find original backing store type");
if (!c || !c->name || !c->config_path)
return false;
- if (am_unpriv()) {
- ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
- return false;
- }
-
ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
if (ret < 0 || ret >= MAXPATHLEN)
goto err;