]> git.proxmox.com Git - mirror_lxc.git/blobdiff - src/lxc/lxccontainer.c
Add support for checkpoint and restore via CRIU
[mirror_lxc.git] / src / lxc / lxccontainer.c
index 11e70cb71c480ee6344259b7fb384239f472faf9..ed6f8de977e84c075b770f7438bf793c4961cabe 100644 (file)
 #include <fcntl.h>
 #include <sched.h>
 #include <dirent.h>
+#include <sched.h>
+#include <arpa/inet.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <grp.h>
+#include <sys/syscall.h>
+
+#include <lxc/lxccontainer.h>
+#include <lxc/version.h>
+
 #include "config.h"
 #include "lxc.h"
 #include "state.h"
-#include <lxc/lxccontainer.h>
 #include "conf.h"
 #include "confile.h"
 #include "console.h"
 #include "cgroup.h"
 #include "commands.h"
-#include "version.h"
 #include "log.h"
 #include "bdev.h"
 #include "utils.h"
 #include "attach.h"
-#include <lxc/utils.h>
-#include <lxc/monitor.h>
-#include <lxc/namespace.h>
-#include <sched.h>
-#include <arpa/inet.h>
-#include <libgen.h>
+#include "monitor.h"
+#include "namespace.h"
+#include "lxclock.h"
+#include "sync.h"
 
 #if HAVE_IFADDRS_H
 #include <ifaddrs.h>
 #include <../include/ifaddrs.h>
 #endif
 
-#ifndef HAVE_GETLINE
-#ifdef HAVE_FGETLN
-#include <../include/getline.h>
-#endif
-#endif
-
 #define MAX_BUFFER 4096
 
-lxc_log_define(lxc_container, lxc);
+#define NOT_SUPPORTED_ERROR "the requested function %s is not currently supported with unprivileged containers"
 
-static bool file_exists(char *f)
+/* Define faccessat() if missing from the C library */
+#ifndef HAVE_FACCESSAT
+static int faccessat(int __fd, const char *__file, int __type, int __flag)
 {
-       struct stat statbuf;
-
-       return stat(f, &statbuf) == 0;
+#ifdef __NR_faccessat
+return syscall(__NR_faccessat, __fd, __file, __type, __flag);
+#else
+errno = ENOSYS;
+return -1;
+#endif
 }
+#endif
+
+
+lxc_log_define(lxc_container, lxc);
 
 static bool config_file_exists(const char *lxcpath, const char *cname)
 {
@@ -96,7 +105,7 @@ static bool config_file_exists(const char *lxcpath, const char *cname)
  * we remove that file.  When we load or try to start a container, if
  * we find that file, without a flock, we remove the container.
  */
-int ongoing_create(struct lxc_container *c)
+static int ongoing_create(struct lxc_container *c)
 {
        int len = strlen(c->config_path) + strlen(c->name) + 10;
        char *path = alloca(len);
@@ -111,9 +120,7 @@ int ongoing_create(struct lxc_container *c)
 
        if (!file_exists(path))
                return 0;
-       process_lock();
        fd = open(path, O_RDWR);
-       process_unlock();
        if (fd < 0) {
                // give benefit of the doubt
                SYSERROR("Error opening partial file");
@@ -126,19 +133,15 @@ int ongoing_create(struct lxc_container *c)
        lk.l_pid = -1;
        if (fcntl(fd, F_GETLK, &lk) == 0 && lk.l_pid != -1) {
                // create is still ongoing
-               process_lock();
                close(fd);
-               process_unlock();
                return 1;
        }
        // create completed but partial is still there.
-       process_lock();
        close(fd);
-       process_unlock();
        return 2;
 }
 
-int create_partial(struct lxc_container *c)
+static int create_partial(struct lxc_container *c)
 {
        // $lxcpath + '/' + $name + '/partial' + \0
        int len = strlen(c->config_path) + strlen(c->name) + 10;
@@ -151,10 +154,8 @@ int create_partial(struct lxc_container *c)
                ERROR("Error writing partial pathname");
                return -1;
        }
-       process_lock();
        if ((fd=open(path, O_RDWR | O_CREAT | O_EXCL, 0755)) < 0) {
                SYSERROR("Erorr creating partial file");
-               process_unlock();
                return -1;
        }
        lk.l_type = F_WRLCK;
@@ -164,24 +165,20 @@ int create_partial(struct lxc_container *c)
        if (fcntl(fd, F_SETLKW, &lk) < 0) {
                SYSERROR("Error locking partial file %s", path);
                close(fd);
-               process_unlock();
                return -1;
        }
-       process_unlock();
 
        return fd;
 }
 
-void remove_partial(struct lxc_container *c, int fd)
+static void remove_partial(struct lxc_container *c, int fd)
 {
        // $lxcpath + '/' + $name + '/partial' + \0
        int len = strlen(c->config_path) + strlen(c->name) + 10;
        char *path = alloca(len);
        int ret;
 
-       process_lock();
        close(fd);
-       process_unlock();
        ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name);
        if (ret < 0 || ret >= len) {
                ERROR("Error writing partial pathname");
@@ -246,6 +243,7 @@ static void lxc_container_free(struct lxc_container *c)
                free(c->config_path);
                c->config_path = NULL;
        }
+
        free(c);
 }
 
@@ -413,9 +411,13 @@ static bool load_config_locked(struct lxc_container *c, const char *fname)
 {
        if (!c->lxc_conf)
                c->lxc_conf = lxc_conf_init();
-       if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf))
-               return true;
-       return false;
+       if (!c->lxc_conf)
+               return false;
+       if (lxc_config_read(fname, c->lxc_conf, false) != 0)
+               return false;
+       if (!clone_update_unexp_network(c->lxc_conf))
+               return false;
+       return true;
 }
 
 static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file)
@@ -455,21 +457,23 @@ static bool lxcapi_load_config(struct lxc_container *c, const char *alt_file)
        return ret;
 }
 
-static void lxcapi_want_daemonize(struct lxc_container *c)
+static bool lxcapi_want_daemonize(struct lxc_container *c, bool state)
 {
        if (!c || !c->lxc_conf)
-               return;
+               return false;
        if (container_mem_lock(c)) {
                ERROR("Error getting mem lock");
-               return;
+               return false;
        }
-       c->daemonize = 1;
+       c->daemonize = state;
        /* daemonize implies close_all_fds so set it */
-       c->lxc_conf->close_all_fds = 1;
+       if (state == 1)
+               c->lxc_conf->close_all_fds = 1;
        container_mem_unlock(c);
+       return true;
 }
 
-static bool lxcapi_want_close_all_fds(struct lxc_container *c)
+static bool lxcapi_want_close_all_fds(struct lxc_container *c, bool state)
 {
        if (!c || !c->lxc_conf)
                return false;
@@ -477,7 +481,7 @@ static bool lxcapi_want_close_all_fds(struct lxc_container *c)
                ERROR("Error getting mem lock");
                return false;
        }
-       c->lxc_conf->close_all_fds = 1;
+       c->lxc_conf->close_all_fds = state;
        container_mem_unlock(c);
        return true;
 }
@@ -515,9 +519,7 @@ static bool am_single_threaded(void)
        DIR *dir;
        int count=0;
 
-       process_lock();
        dir = opendir("/proc/self/task");
-       process_unlock();
        if (!dir) {
                INFO("failed to open /proc/self/task");
                return false;
@@ -535,9 +537,7 @@ static bool am_single_threaded(void)
                if (++count > 1)
                        break;
        }
-       process_lock();
        closedir(dir);
-       process_unlock();
        return count == 1;
 }
 
@@ -549,10 +549,11 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
 {
        int ret;
        struct lxc_conf *conf;
-       int daemonize = 0;
+       bool daemonize = false;
+       FILE *pid_fp = NULL;
        char *default_args[] = {
                "/sbin/init",
-               '\0',
+               NULL,
        };
 
        /* container exists */
@@ -600,19 +601,20 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
        * while container is running...
        */
        if (daemonize) {
-               if (!lxc_container_get(c))
-                       return false;
                lxc_monitord_spawn(c->config_path);
 
                pid_t pid = fork();
-               if (pid < 0) {
-                       lxc_container_put(c);
+               if (pid < 0)
                        return false;
-               }
-               if (pid != 0)
+
+               if (pid != 0) {
+                       /* Set to NULL because we don't want father unlink
+                        * the PID file, child will do the free and unlink.
+                        */
+                       c->pidfile = NULL;
                        return wait_on_daemonized_start(c, pid);
+               }
 
-               process_unlock(); // we're no longer sharing
                /* second fork to be reparented by init */
                pid = fork();
                if (pid < 0) {
@@ -626,6 +628,7 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                        SYSERROR("Error chdir()ing to /.");
                        return false;
                }
+               lxc_check_inherited(conf, -1);
                close(0);
                close(1);
                close(2);
@@ -640,9 +643,32 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
                }
        }
 
+       /* We need to write PID file after daeminize, so we always
+        * write the right PID.
+        */
+       if (c->pidfile) {
+               pid_fp = fopen(c->pidfile, "w");
+               if (pid_fp == NULL) {
+                       SYSERROR("Failed to create pidfile '%s' for '%s'",
+                                c->pidfile, c->name);
+                       return false;
+               }
+
+               if (fprintf(pid_fp, "%d\n", getpid()) < 0) {
+                       SYSERROR("Failed to write '%s'", c->pidfile);
+                       fclose(pid_fp);
+                       pid_fp = NULL;
+                       return false;
+               }
+
+               fclose(pid_fp);
+               pid_fp = NULL;
+       }
+
 reboot:
        conf->reboot = 0;
        ret = lxc_start(c->name, argv, conf, c->config_path);
+       c->error_num = ret;
 
        if (conf->reboot) {
                INFO("container requested reboot");
@@ -650,12 +676,16 @@ reboot:
                goto reboot;
        }
 
-       if (daemonize) {
-               lxc_container_put(c);
+       if (c->pidfile) {
+               unlink(c->pidfile);
+               free(c->pidfile);
+               c->pidfile = NULL;
+       }
+
+       if (daemonize)
                exit (ret == 0 ? true : false);
-       } else {
+       else
                return (ret == 0 ? true : false);
-       }
 }
 
 /*
@@ -706,6 +736,31 @@ static bool lxcapi_stop(struct lxc_container *c)
        return ret == 0;
 }
 
+static int do_create_container_dir(const char *path, struct lxc_conf *conf)
+{
+       int ret = -1, lasterr;
+       char *p = alloca(strlen(path)+1);
+       mode_t mask = umask(0002);
+       ret = mkdir(path, 0770);
+       lasterr = errno;
+       umask(mask);
+       errno = lasterr;
+       if (ret) {
+               if (errno == EEXIST)
+                       ret = 0;
+               else {
+                       SYSERROR("failed to create container path %s", path);
+                       return -1;
+               }
+       }
+       strcpy(p, path);
+       if (!lxc_list_empty(&conf->id_map) && chown_mapped_root(p, conf) != 0) {
+               ERROR("Failed to chown container dir");
+               ret = -1;
+       }
+       return ret;
+}
+
 /*
  * create the standard expected container dir
  */
@@ -723,13 +778,7 @@ static bool create_container_dir(struct lxc_container *c)
                free(s);
                return false;
        }
-       ret = mkdir(s, 0755);
-       if (ret) {
-               if (errno == EEXIST)
-                       ret = 0;
-               else
-                       SYSERROR("failed to create container path for %s\n", c->name);
-       }
+       ret = do_create_container_dir(s, c->lxc_conf);
        free(s);
        return ret == 0;
 }
@@ -766,7 +815,7 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
 
        bdev = bdev_create(dest, type, c->name, specs);
        if (!bdev) {
-               ERROR("Failed to create backing store type %s\n", type);
+               ERROR("Failed to create backing store type %s", type);
                return NULL;
        }
 
@@ -775,9 +824,10 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
        /* if we are not root, chown the rootfs dir to root in the
         * target uidmap */
 
-       if (geteuid() != 0) {
+       if (geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) {
                if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) {
-                       ERROR("Error chowning %s to container root\n", bdev->dest);
+                       ERROR("Error chowning %s to container root", bdev->dest);
+                       suggest_default_idmap();
                        bdev_put(bdev);
                        return NULL;
                }
@@ -812,7 +862,7 @@ static char *get_template_path(const char *t)
                return NULL;
        }
        if (access(tpath, X_OK) < 0) {
-               SYSERROR("bad template: %s\n", t);
+               SYSERROR("bad template: %s", t);
                free(tpath);
                return NULL;
        }
@@ -838,7 +888,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 
        pid = fork();
        if (pid < 0) {
-               SYSERROR("failed to fork task for container creation template\n");
+               SYSERROR("failed to fork task for container creation template");
                return false;
        }
 
@@ -850,7 +900,6 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                char **newargv;
                struct lxc_conf *conf = c->lxc_conf;
 
-               process_unlock(); // we're no longer sharing
                if (quiet) {
                        close(0);
                        close(1);
@@ -862,14 +911,16 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
 
                src = c->lxc_conf->rootfs.path;
                /*
-                * for an overlayfs create, what the user wants is the template to fill
+                * for an overlay create, what the user wants is the template to fill
                 * in what will become the readonly lower layer.  So don't mount for
                 * the template
                 */
-               if (strncmp(src, "overlayfs:", 10) == 0) {
-                       src = overlayfs_getlower(src+10);
-               }
-               bdev = bdev_init(src, c->lxc_conf->rootfs.mount, NULL);
+               if (strncmp(src, "overlayfs:", 10) == 0)
+                       src = overlay_getlower(src+10);
+               if (strncmp(src, "aufs:", 5) == 0)
+                       src = overlay_getlower(src+5);
+
+               bdev = bdev_init(c->lxc_conf, src, c->lxc_conf->rootfs.mount, NULL);
                if (!bdev) {
                        ERROR("Error opening rootfs");
                        exit(1);
@@ -881,15 +932,15 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                                exit(1);
                        }
                        if (detect_shared_rootfs()) {
-                               if (mount("", "", NULL, MS_SLAVE|MS_REC, 0)) {
+                               if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
                                        SYSERROR("Failed to make / rslave to run template");
                                        ERROR("Continuing...");
                                }
                        }
                }
-               if (strcmp(bdev->type, "dir") != 0) {
+               if (strcmp(bdev->type, "dir") && strcmp(bdev->type, "btrfs")) {
                        if (geteuid() != 0) {
-                               ERROR("non-root users can only create directory-backed containers");
+                               ERROR("non-root users can only create btrfs and directory-backed containers");
                                exit(1);
                        }
                        if (bdev->ops->mount(bdev) < 0) {
@@ -908,7 +959,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                 */
                if (argv)
                        for (nargs = 0; argv[nargs]; nargs++) ;
-               nargs += 4;  // template, path, rootfs and name args
+               nargs += 4; // template, path, rootfs and name args
 
                newargv = malloc(nargs * sizeof(*newargv));
                if (!newargv)
@@ -960,9 +1011,10 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                 * and we append "--mapped-uid x", where x is the mapped uid
                 * for our geteuid()
                 */
-               if (geteuid() != 0 && !lxc_list_empty(&conf->id_map)) {
+               if (!lxc_list_empty(&conf->id_map)) {
                        int n2args = 1;
                        char txtuid[20];
+                       char txtgid[20];
                        char **n2 = malloc(n2args * sizeof(*n2));
                        struct lxc_list *it;
                        struct id_map *map;
@@ -990,13 +1042,13 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                                if (ret < 0 || ret >= 200)
                                        exit(1);
                        }
-                       int hostid_mapped = mapped_hostid(geteuid(), conf);
-                       int extraargs = hostid_mapped >= 0 ?  1 : 3;
+                       int hostid_mapped = mapped_hostid(geteuid(), conf, ID_TYPE_UID);
+                       int extraargs = hostid_mapped >= 0 ? 1 : 3;
                        n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *));
                        if (!n2)
                                exit(1);
                        if (hostid_mapped < 0) {
-                               hostid_mapped = find_unmapped_nsuid(conf);
+                               hostid_mapped = find_unmapped_nsuid(conf, ID_TYPE_UID);
                                n2[n2args++] = "-m";
                                if (hostid_mapped < 0) {
                                        ERROR("Could not find free uid to map");
@@ -1014,22 +1066,49 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                                        exit(1);
                                }
                        }
+                       int hostgid_mapped = mapped_hostid(getegid(), conf, ID_TYPE_GID);
+                       extraargs = hostgid_mapped >= 0 ? 1 : 3;
+                       n2 = realloc(n2, (nargs + n2args + extraargs) * sizeof(char *));
+                       if (!n2)
+                               exit(1);
+                       if (hostgid_mapped < 0) {
+                               hostgid_mapped = find_unmapped_nsuid(conf, ID_TYPE_GID);
+                               n2[n2args++] = "-m";
+                               if (hostgid_mapped < 0) {
+                                       ERROR("Could not find free uid to map");
+                                       exit(1);
+                               }
+                               n2[n2args++] = malloc(200);
+                               if (!n2[n2args-1]) {
+                                       SYSERROR("out of memory");
+                                       exit(1);
+                               }
+                               ret = snprintf(n2[n2args-1], 200, "g:%d:%d:1",
+                                       hostgid_mapped, getegid());
+                               if (ret < 0 || ret >= 200) {
+                                       ERROR("string too long");
+                                       exit(1);
+                               }
+                       }
                        n2[n2args++] = "--";
                        for (i = 0; i < nargs; i++)
                                n2[i + n2args] = newargv[i];
                        n2args += nargs;
                        // Finally add "--mapped-uid $uid" to tell template what to chown
                        // cached images to
-                       n2args += 2;
+                       n2args += 4;
                        n2 = realloc(n2, n2args * sizeof(char *));
                        if (!n2) {
                                SYSERROR("out of memory");
                                exit(1);
                        }
                        // note n2[n2args-1] is NULL
-                       n2[n2args-3] = "--mapped-uid";
+                       n2[n2args-5] = "--mapped-uid";
                        snprintf(txtuid, 20, "%d", hostid_mapped);
-                       n2[n2args-2] = txtuid;
+                       n2[n2args-4] = txtuid;
+                       n2[n2args-3] = "--mapped-gid";
+                       snprintf(txtgid, 20, "%d", hostgid_mapped);
+                       n2[n2args-2] = txtgid;
                        n2[n2args-1] = NULL;
                        free(newargv);
                        newargv = n2;
@@ -1041,14 +1120,14 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
        }
 
        if (wait_for_pid(pid) != 0) {
-               ERROR("container creation template for %s failed\n", c->name);
+               ERROR("container creation template for %s failed", c->name);
                return false;
        }
 
        return true;
 }
 
-bool prepend_lxc_header(char *path, const char *t, char *const argv[])
+static bool prepend_lxc_header(char *path, const char *t, char *const argv[])
 {
        long flen;
        char *contents;
@@ -1060,9 +1139,7 @@ bool prepend_lxc_header(char *path, const char *t, char *const argv[])
        char *tpath;
 #endif
 
-       process_lock();
        f = fopen(path, "r");
-       process_unlock();
        if (f == NULL)
                return false;
 
@@ -1078,9 +1155,7 @@ bool prepend_lxc_header(char *path, const char *t, char *const argv[])
                goto out_free_contents;
 
        contents[flen] = '\0';
-       process_lock();
        ret = fclose(f);
-       process_unlock();
        f = NULL;
        if (ret < 0)
                goto out_free_contents;
@@ -1088,7 +1163,7 @@ bool prepend_lxc_header(char *path, const char *t, char *const argv[])
 #if HAVE_LIBGNUTLS
        tpath = get_template_path(t);
        if (!tpath) {
-               ERROR("bad template: %s\n", t);
+               ERROR("bad template: %s", t);
                goto out_free_contents;
        }
 
@@ -1101,9 +1176,7 @@ bool prepend_lxc_header(char *path, const char *t, char *const argv[])
        free(tpath);
 #endif
 
-       process_lock();
        f = fopen(path, "w");
-       process_unlock();
        if (f == NULL) {
                SYSERROR("reopening config for writing");
                free(contents);
@@ -1124,12 +1197,11 @@ bool prepend_lxc_header(char *path, const char *t, char *const argv[])
                fprintf(f, "%02x", md_value[i]);
        fprintf(f, "\n");
 #endif
+       fprintf(f, "# For additional config options, please look at lxc.container.conf(5)\n");
        if (fwrite(contents, 1, flen, f) != flen) {
                SYSERROR("Writing original contents");
                free(contents);
-               process_lock();
                fclose(f);
-               process_unlock();
                return false;
        }
        ret = 0;
@@ -1138,9 +1210,7 @@ out_free_contents:
 out_error:
        if (f) {
                int newret;
-               process_lock();
                newret = fclose(f);
-               process_unlock();
                if (ret == 0)
                        ret = newret;
        }
@@ -1153,13 +1223,17 @@ out_error:
 
 static void lxcapi_clear_config(struct lxc_container *c)
 {
-       if (c && c->lxc_conf) {
-               lxc_conf_free(c->lxc_conf);
-               c->lxc_conf = NULL;
+       if (c) {
+               if (c->lxc_conf) {
+                       lxc_conf_free(c->lxc_conf);
+                       c->lxc_conf = NULL;
+               }
        }
 }
 
 static bool lxcapi_destroy(struct lxc_container *c);
+static bool container_destroy(struct lxc_container *c);
+static bool get_snappath_dir(struct lxc_container *c, char *snappath);
 /*
  * lxcapi_create:
  * create a container with the given parameters.
@@ -1189,7 +1263,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
        if (t) {
                tpath = get_template_path(t);
                if (!tpath) {
-                       ERROR("bad template: %s\n", t);
+                       ERROR("bad template: %s", t);
                        goto out;
                }
        }
@@ -1207,8 +1281,8 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
        }
 
        if (!c->lxc_conf) {
-               if (!c->load_config(c, LXC_DEFAULT_CONFIG)) {
-                       ERROR("Error loading default configuration file %s\n", LXC_DEFAULT_CONFIG);
+               if (!c->load_config(c, lxc_global_config_value("lxc.default_config"))) {
+                       ERROR("Error loading default configuration file %s", lxc_global_config_value("lxc.default_config"));
                        goto free_tpath;
                }
        }
@@ -1249,14 +1323,13 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
         */
        pid = fork();
        if (pid < 0) {
-               SYSERROR("failed to fork task for container creation template\n");
+               SYSERROR("failed to fork task for container creation template");
                goto out_unlock;
        }
 
        if (pid == 0) { // child
                struct bdev *bdev = NULL;
 
-               process_unlock(); // we're no longer sharing
                if (!(bdev = do_bdev_create(c, bdevtype, specs))) {
                        ERROR("Error creating backing store type %s for %s",
                                bdevtype ? bdevtype : "(none)", c->name);
@@ -1265,7 +1338,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
 
                /* save config file again to store the new rootfs location */
                if (!c->save_config(c, NULL)) {
-                       ERROR("failed to save starting configuration for %s\n", c->name);
+                       ERROR("failed to save starting configuration for %s", c->name);
                        // parent task won't see bdev in config so we delete it
                        bdev->ops->umount(bdev);
                        bdev->ops->destroy(bdev);
@@ -1277,8 +1350,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t,
                goto out_unlock;
 
        /* reload config to get the rootfs */
-       if (c->lxc_conf)
-               lxc_conf_free(c->lxc_conf);
+       lxc_conf_free(c->lxc_conf);
        c->lxc_conf = NULL;
        if (!load_config_locked(c, c->configfile))
                goto out_unlock;
@@ -1303,7 +1375,7 @@ out_unlock:
                remove_partial(c, partial_fd);
 out:
        if (!ret && c)
-               lxcapi_destroy(c);
+               container_destroy(c);
 free_tpath:
        if (tpath)
                free(tpath);
@@ -1331,23 +1403,20 @@ static bool lxcapi_shutdown(struct lxc_container *c, int timeout)
 {
        bool retv;
        pid_t pid;
+       int haltsignal = SIGPWR;
 
        if (!c)
                return false;
 
-       if (!timeout)
-               timeout = -1;
        if (!c->is_running(c))
                return true;
        pid = c->init_pid(c);
        if (pid <= 0)
                return true;
-       kill(pid, SIGPWR);
+       if (c->lxc_conf && c->lxc_conf->haltsignal)
+               haltsignal = c->lxc_conf->haltsignal;
+       kill(pid, haltsignal);
        retv = c->wait(c, "STOPPED", timeout);
-       if (!retv && timeout > 0) {
-               c->stop(c);
-               retv = c->wait(c, "STOPPED", 0); // 0 means don't wait
-       }
        return retv;
 }
 
@@ -1380,6 +1449,20 @@ out:
        return bret;
 }
 
+static void do_clear_unexp_config_line(struct lxc_conf *conf, const char *key)
+{
+       if (strcmp(key, "lxc.cgroup") == 0)
+               clear_unexp_config_line(conf, key, true);
+       else if (strcmp(key, "lxc.network") == 0)
+               clear_unexp_config_line(conf, key, true);
+       else if (strcmp(key, "lxc.hook") == 0)
+               clear_unexp_config_line(conf, key, true);
+       else
+               clear_unexp_config_line(conf, key, false);
+       if (!do_append_unexp_config_line(conf, key, ""))
+               WARN("Error clearing configuration for %s", key);
+}
+
 static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
 {
        int ret;
@@ -1389,58 +1472,61 @@ static bool lxcapi_clear_config_item(struct lxc_container *c, const char *key)
        if (container_mem_lock(c))
                return false;
        ret = lxc_clear_config_item(c->lxc_conf, key);
+       if (!ret)
+               do_clear_unexp_config_line(c->lxc_conf, key);
        container_mem_unlock(c);
        return ret == 0;
 }
 
-static inline void exit_from_ns(struct lxc_container *c, int *old_netns, int *new_netns) {
-       /* Switch back to original netns */
-       if (*old_netns >= 0 && setns(*old_netns, CLONE_NEWNET))
-               SYSERROR("failed to setns");
-       process_lock();
-       if (*new_netns >= 0)
-               close(*new_netns);
-       if (*old_netns >= 0)
-               close(*old_netns);
-       process_unlock();
-}
-
-static inline bool enter_to_ns(struct lxc_container *c, int *old_netns, int *new_netns) {
-       int ret = 0;
+static inline bool enter_to_ns(struct lxc_container *c) {
+       int netns, userns, ret = 0, init_pid = 0;;
        char new_netns_path[MAXPATHLEN];
+       char new_userns_path[MAXPATHLEN];
 
        if (!c->is_running(c))
                goto out;
 
-       /* Save reference to old netns */
-       process_lock();
-       *old_netns = open("/proc/self/ns/net", O_RDONLY);
-       process_unlock();
-       if (*old_netns < 0) {
-               SYSERROR("failed to open /proc/self/ns/net");
-               goto out;
+       init_pid = c->init_pid(c);
+
+       /* Switch to new userns */
+       if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) {
+               ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid);
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       goto out;
+
+               userns = open(new_userns_path, O_RDONLY);
+               if (userns < 0) {
+                       SYSERROR("failed to open %s", new_userns_path);
+                       goto out;
+               }
+
+               if (setns(userns, CLONE_NEWUSER)) {
+                       SYSERROR("failed to setns for CLONE_NEWUSER");
+                       close(userns);
+                       goto out;
+               }
+               close(userns);
        }
 
        /* Switch to new netns */
-       ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", c->init_pid(c));
+       ret = snprintf(new_netns_path, MAXPATHLEN, "/proc/%d/ns/net", init_pid);
        if (ret < 0 || ret >= MAXPATHLEN)
                goto out;
 
-       process_lock();
-       *new_netns = open(new_netns_path, O_RDONLY);
-       process_unlock();
-       if (*new_netns < 0) {
+       netns = open(new_netns_path, O_RDONLY);
+       if (netns < 0) {
                SYSERROR("failed to open %s", new_netns_path);
                goto out;
        }
 
-       if (setns(*new_netns, CLONE_NEWNET)) {
-               SYSERROR("failed to setns");
+       if (setns(netns, CLONE_NEWNET)) {
+               SYSERROR("failed to setns for CLONE_NEWNET");
+               close(netns);
                goto out;
        }
+       close(netns);
        return true;
 out:
-       exit_from_ns(c, old_netns, new_netns);
        return false;
 }
 
@@ -1517,125 +1603,206 @@ static bool remove_from_array(char ***names, char *cname, int size)
 
 static char** lxcapi_get_interfaces(struct lxc_container *c)
 {
-       int i, count = 0;
-       struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+       pid_t pid;
+       int i, count = 0, pipefd[2];
        char **interfaces = NULL;
-       int old_netns = -1, new_netns = -1;
+       char interface[IFNAMSIZ];
 
-       if (!enter_to_ns(c, &old_netns, &new_netns))
-               goto out;
+       if(pipe(pipefd) < 0) {
+               SYSERROR("pipe failed");
+               return NULL;
+       }
 
-       /* Grab the list of interfaces */
-       if (getifaddrs(&interfaceArray)) {
-               SYSERROR("failed to get interfaces list");
-               goto out;
+       pid = fork();
+       if (pid < 0) {
+               SYSERROR("failed to fork task to get interfaces information");
+               close(pipefd[0]);
+               close(pipefd[1]);
+               return NULL;
        }
 
-       /* Iterate through the interfaces */
-       for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
-               if (array_contains(&interfaces, tempIfAddr->ifa_name, count))
-                       continue;
+       if (pid == 0) { // child
+               int ret = 1, nbytes;
+               struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
 
-               if(!add_to_array(&interfaces, tempIfAddr->ifa_name, count))
-                       goto err;
+               /* close the read-end of the pipe */
+               close(pipefd[0]);
+
+               if (!enter_to_ns(c)) {
+                       SYSERROR("failed to enter namespace");
+                       goto out;
+               }
+
+               /* Grab the list of interfaces */
+               if (getifaddrs(&interfaceArray)) {
+                       SYSERROR("failed to get interfaces list");
+                       goto out;
+               }
+
+               /* Iterate through the interfaces */
+               for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+                       nbytes = write(pipefd[1], tempIfAddr->ifa_name, IFNAMSIZ);
+                       if (nbytes < 0) {
+                               ERROR("write failed");
+                               goto out;
+                       }
+                       count++;
+               }
+               ret = 0;
+
+       out:
+               if (interfaceArray)
+                       freeifaddrs(interfaceArray);
+
+               /* close the write-end of the pipe, thus sending EOF to the reader */
+               close(pipefd[1]);
+               exit(ret);
+       }
+
+       /* close the write-end of the pipe */
+       close(pipefd[1]);
+
+       while (read(pipefd[0], &interface, IFNAMSIZ) == IFNAMSIZ) {
+               if (array_contains(&interfaces, interface, count))
+                               continue;
+
+               if(!add_to_array(&interfaces, interface, count))
+                       ERROR("PARENT: add_to_array failed");
                count++;
        }
 
-out:
-       if (interfaceArray)
-               freeifaddrs(interfaceArray);
+       if (wait_for_pid(pid) != 0) {
+               for(i=0;i<count;i++)
+                       free(interfaces[i]);
+               free(interfaces);
+               interfaces = NULL;
+       }
 
-       exit_from_ns(c, &old_netns, &new_netns);
+       /* close the read-end of the pipe */
+       close(pipefd[0]);
 
        /* Append NULL to the array */
        if(interfaces)
                interfaces = (char **)lxc_append_null_to_array((void **)interfaces, count);
 
        return interfaces;
-
-err:
-       for(i=0;i<count;i++)
-               free(interfaces[i]);
-       free(interfaces);
-       interfaces = NULL;
-       goto out;
 }
 
-static char** lxcapi_get_ips(struct lxc_container *c, char* interface, char* family, int scope)
+static char** lxcapi_get_ips(struct lxc_container *c, const char* interface, const char* family, int scope)
 {
-       int i, count = 0;
-       struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
-       char addressOutputBuffer[INET6_ADDRSTRLEN];
-       void *tempAddrPtr = NULL;
+       pid_t pid;
+       int i, count = 0, pipefd[2];
        char **addresses = NULL;
-       char *address = NULL;
-       int old_netns = -1, new_netns = -1;
+       char address[INET6_ADDRSTRLEN];
 
-       if (!enter_to_ns(c, &old_netns, &new_netns))
-               goto out;
+       if(pipe(pipefd) < 0) {
+               SYSERROR("pipe failed");
+               return NULL;
+       }
 
-       /* Grab the list of interfaces */
-       if (getifaddrs(&interfaceArray)) {
-               SYSERROR("failed to get interfaces list");
-               goto out;
+       pid = fork();
+       if (pid < 0) {
+               SYSERROR("failed to fork task to get container ips");
+               close(pipefd[0]);
+               close(pipefd[1]);
+               return NULL;
        }
 
-       /* Iterate through the interfaces */
-       for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
-               if (tempIfAddr->ifa_addr == NULL)
-                       continue;
+       if (pid == 0) { // child
+               int ret = 1, nbytes;
+               struct ifaddrs *interfaceArray = NULL, *tempIfAddr = NULL;
+               char addressOutputBuffer[INET6_ADDRSTRLEN];
+               void *tempAddrPtr = NULL;
+               char *address = NULL;
 
-               if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
-                       if (family && strcmp(family, "inet"))
-                               continue;
-                       tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
+               /* close the read-end of the pipe */
+               close(pipefd[0]);
+
+               if (!enter_to_ns(c)) {
+                       SYSERROR("failed to enter namespace");
+                       goto out;
                }
-               else {
-                       if (family && strcmp(family, "inet6"))
+
+               /* Grab the list of interfaces */
+               if (getifaddrs(&interfaceArray)) {
+                       SYSERROR("failed to get interfaces list");
+                       goto out;
+               }
+
+               /* Iterate through the interfaces */
+               for (tempIfAddr = interfaceArray; tempIfAddr != NULL; tempIfAddr = tempIfAddr->ifa_next) {
+                       if (tempIfAddr->ifa_addr == NULL)
                                continue;
 
-                       if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
+                       if(tempIfAddr->ifa_addr->sa_family == AF_INET) {
+                               if (family && strcmp(family, "inet"))
+                                       continue;
+                               tempAddrPtr = &((struct sockaddr_in *)tempIfAddr->ifa_addr)->sin_addr;
+                       }
+                       else {
+                               if (family && strcmp(family, "inet6"))
+                                       continue;
+
+                               if (((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_scope_id != scope)
+                                       continue;
+
+                               tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
+                       }
+
+                       if (interface && strcmp(interface, tempIfAddr->ifa_name))
+                               continue;
+                       else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
                                continue;
 
-                       tempAddrPtr = &((struct sockaddr_in6 *)tempIfAddr->ifa_addr)->sin6_addr;
+                       address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
+                                               tempAddrPtr,
+                                               addressOutputBuffer,
+                                               sizeof(addressOutputBuffer));
+                       if (!address)
+                                       continue;
+
+                       nbytes = write(pipefd[1], address, INET6_ADDRSTRLEN);
+                       if (nbytes < 0) {
+                               ERROR("write failed");
+                               goto out;
+                       }
+                       count++;
                }
+               ret = 0;
 
-               if (interface && strcmp(interface, tempIfAddr->ifa_name))
-                       continue;
-               else if (!interface && strcmp("lo", tempIfAddr->ifa_name) == 0)
-                       continue;
+       out:
+               if(interfaceArray)
+                       freeifaddrs(interfaceArray);
 
-               address = (char *)inet_ntop(tempIfAddr->ifa_addr->sa_family,
-                                          tempAddrPtr,
-                                          addressOutputBuffer,
-                                          sizeof(addressOutputBuffer));
-               if (!address)
-                       continue;
+               /* close the write-end of the pipe, thus sending EOF to the reader */
+               close(pipefd[1]);
+               exit(ret);
+       }
 
+       /* close the write-end of the pipe */
+       close(pipefd[1]);
+
+       while (read(pipefd[0], &address, INET6_ADDRSTRLEN) == INET6_ADDRSTRLEN) {
                if(!add_to_array(&addresses, address, count))
-                       goto err;
+                       ERROR("PARENT: add_to_array failed");
                count++;
        }
 
-out:
-       if(interfaceArray)
-               freeifaddrs(interfaceArray);
+       if (wait_for_pid(pid) != 0) {
+               for(i=0;i<count;i++)
+                       free(addresses[i]);
+               free(addresses);
+               addresses = NULL;
+       }
 
-       exit_from_ns(c, &old_netns, &new_netns);
+       /* close the read-end of the pipe */
+       close(pipefd[0]);
 
        /* Append NULL to the array */
        if(addresses)
                addresses = (char **)lxc_append_null_to_array((void **)addresses, count);
 
        return addresses;
-
-err:
-       for(i=0;i<count;i++)
-               free(addresses[i]);
-       free(addresses);
-       addresses = NULL;
-
-       goto out;
 }
 
 static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char *retv, int inlen)
@@ -1651,6 +1818,19 @@ static int lxcapi_get_config_item(struct lxc_container *c, const char *key, char
        return ret;
 }
 
+static char* lxcapi_get_running_config_item(struct lxc_container *c, const char *key)
+{
+       char *ret;
+
+       if (!c || !c->lxc_conf)
+               return NULL;
+       if (container_mem_lock(c))
+               return NULL;
+       ret = lxc_cmd_get_config_item(c->name, key, c->get_config_path(c));
+       container_mem_unlock(c);
+       return ret;
+}
+
 static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv, int inlen)
 {
        if (!key)
@@ -1666,7 +1846,7 @@ static int lxcapi_get_keys(struct lxc_container *c, const char *key, char *retv,
                return -1;
        int ret = -1;
        if (strncmp(key, "lxc.network.", 12) == 0)
-               ret =  lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen);
+               ret = lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen);
        container_mem_unlock(c);
        return ret;
 }
@@ -1680,12 +1860,12 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file)
        if (!alt_file)
                alt_file = c->configfile;
        if (!alt_file)
-               return false;  // should we write to stdout if no file is specified?
+               return false; // should we write to stdout if no file is specified?
 
        // If we haven't yet loaded a config, load the stock config
        if (!c->lxc_conf) {
-               if (!c->load_config(c, LXC_DEFAULT_CONFIG)) {
-                       ERROR("Error loading default configuration file %s while saving %s\n", LXC_DEFAULT_CONFIG, c->name);
+               if (!c->load_config(c, lxc_global_config_value("lxc.default_config"))) {
+                       ERROR("Error loading default configuration file %s while saving %s", lxc_global_config_value("lxc.default_config"), c->name);
                        return false;
                }
        }
@@ -1709,15 +1889,11 @@ static bool lxcapi_save_config(struct lxc_container *c, const char *alt_file)
        if (lret)
                return false;
 
-       process_lock();
        fout = fopen(alt_file, "w");
-       process_unlock();
        if (!fout)
                goto out;
        write_config(fout, c->lxc_conf);
-       process_lock();
        fclose(fout);
-       process_unlock();
        ret = true;
 
 out:
@@ -1741,35 +1917,25 @@ static bool mod_rdep(struct lxc_container *c, bool inc)
                        c->name);
        if (ret < 0 || ret > MAXPATHLEN)
                goto out;
-       process_lock();
        f = fopen(path, "r");
-       process_unlock();
        if (f) {
                ret = fscanf(f, "%d", &v);
-               process_lock();
                fclose(f);
-               process_unlock();
                if (ret != 1) {
                        ERROR("Corrupted file %s", path);
                        goto out;
                }
        }
        v += inc ? 1 : -1;
-       process_lock();
        f = fopen(path, "w");
-       process_unlock();
        if (!f)
                goto out;
        if (fprintf(f, "%d\n", v) < 0) {
                ERROR("Error writing new snapshots value");
-               process_lock();
                fclose(f);
-               process_unlock();
                goto out;
        }
-       process_lock();
        ret = fclose(f);
-       process_unlock();
        if (ret != 0) {
                SYSERROR("Error writing to or closing snapshots file");
                goto out;
@@ -1805,14 +1971,12 @@ static void mod_all_rdeps(struct lxc_container *c, bool inc)
                ERROR("Path name too long");
                return;
        }
-       process_lock();
        f = fopen(path, "r");
-       process_unlock();
        if (f == NULL)
                return;
        while (getline(&lxcpath, &pathlen, f) != -1) {
                if (getline(&lxcname, &namelen, f) == -1) {
-                       ERROR("badly formatted file %s\n", path);
+                       ERROR("badly formatted file %s", path);
                        goto out;
                }
                strip_newline(lxcpath);
@@ -1830,12 +1994,10 @@ static void mod_all_rdeps(struct lxc_container *c, bool inc)
 out:
        if (lxcpath) free(lxcpath);
        if (lxcname) free(lxcname);
-       process_lock();
        fclose(f);
-       process_unlock();
 }
 
-static bool has_snapshots(struct lxc_container *c)
+static bool has_fs_snapshots(struct lxc_container *c)
 {
        char path[MAXPATHLEN];
        int ret, v;
@@ -1846,15 +2008,11 @@ static bool has_snapshots(struct lxc_container *c)
                        c->name);
        if (ret < 0 || ret > MAXPATHLEN)
                goto out;
-       process_lock();
        f = fopen(path, "r");
-       process_unlock();
        if (!f)
                goto out;
        ret = fscanf(f, "%d", &v);
-       process_lock();
        fclose(f);
-       process_unlock();
        if (ret != 1)
                goto out;
        bret = v != 0;
@@ -1863,11 +2021,76 @@ out:
        return bret;
 }
 
-// do we want the api to support --force, or leave that to the caller?
-static bool lxcapi_destroy(struct lxc_container *c)
+static bool has_snapshots(struct lxc_container *c)
 {
-       struct bdev *r = NULL;
-       bool ret = false;
+       char path[MAXPATHLEN];
+       struct dirent dirent, *direntp;
+       int count=0;
+       DIR *dir;
+
+       if (!get_snappath_dir(c, path))
+               return false;
+       dir = opendir(path);
+       if (!dir)
+               return false;
+       while (!readdir_r(dir, &dirent, &direntp)) {
+               if (!direntp)
+                       break;
+
+               if (!strcmp(direntp->d_name, "."))
+                       continue;
+
+               if (!strcmp(direntp->d_name, ".."))
+                       continue;
+               count++;
+               break;
+       }
+       closedir(dir);
+       return count > 0;
+}
+
+static int lxc_rmdir_onedev_wrapper(void *data)
+{
+       char *arg = (char *) data;
+       return lxc_rmdir_onedev(arg, "snaps");
+}
+
+static int do_bdev_destroy(struct lxc_conf *conf)
+{
+       struct bdev *r;
+       int ret = 0;
+
+       r = bdev_init(conf, conf->rootfs.path, conf->rootfs.mount, NULL);
+       if (!r)
+               return -1;
+
+       if (r->ops->destroy(r) < 0)
+               ret = -1;
+       bdev_put(r);
+       return ret;
+}
+
+static int bdev_destroy_wrapper(void *data)
+{
+       struct lxc_conf *conf = data;
+
+       if (setgid(0) < 0) {
+               ERROR("Failed to setgid to 0");
+               return -1;
+       }
+       if (setgroups(0, NULL) < 0)
+               WARN("Failed to clear groups");
+       if (setuid(0) < 0) {
+               ERROR("Failed to setuid to 0");
+               return -1;
+       }
+       return do_bdev_destroy(conf);
+}
+
+static bool container_destroy(struct lxc_container *c)
+{
+       bool bret = false;
+       int ret;
 
        if (!c || !lxcapi_is_defined(c))
                return false;
@@ -1881,20 +2104,15 @@ static bool lxcapi_destroy(struct lxc_container *c)
                goto out;
        }
 
-       if (c->lxc_conf && has_snapshots(c)) {
-               ERROR("container %s has dependent snapshots", c->name);
-               goto out;
-       }
-
-       if (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount)
-               r = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
-       if (r) {
-               if (r->ops->destroy(r) < 0) {
-                       bdev_put(r);
+       if (c->lxc_conf && c->lxc_conf->rootfs.path && c->lxc_conf->rootfs.mount) {
+               if (am_unpriv())
+                       ret = userns_exec_1(c->lxc_conf, bdev_destroy_wrapper, c->lxc_conf);
+               else
+                       ret = do_bdev_destroy(c->lxc_conf);
+               if (ret < 0) {
                        ERROR("Error destroying rootfs for %s", c->name);
                        goto out;
                }
-               bdev_put(r);
        }
 
        mod_all_rdeps(c, false);
@@ -1902,20 +2120,54 @@ static bool lxcapi_destroy(struct lxc_container *c)
        const char *p1 = lxcapi_get_config_path(c);
        char *path = alloca(strlen(p1) + strlen(c->name) + 2);
        sprintf(path, "%s/%s", p1, c->name);
-       if (lxc_rmdir_onedev(path) < 0) {
+       if (am_unpriv())
+               ret = userns_exec_1(c->lxc_conf, lxc_rmdir_onedev_wrapper, path);
+       else
+               ret = lxc_rmdir_onedev(path, "snaps");
+       if (ret < 0) {
                ERROR("Error destroying container directory for %s", c->name);
                goto out;
        }
-       ret = true;
+       bret = true;
 
 out:
        container_disk_unlock(c);
-       return ret;
+       return bret;
 }
 
-static bool set_config_item_locked(struct lxc_container *c, const char *key, const char *v)
+static bool lxcapi_destroy(struct lxc_container *c)
 {
-       struct lxc_config_t *config;
+       if (!c || !lxcapi_is_defined(c))
+               return false;
+       if (has_snapshots(c)) {
+               ERROR("Container %s has snapshots;  not removing", c->name);
+               return false;
+       }
+
+       if (has_fs_snapshots(c)) {
+               ERROR("container %s has snapshots on its rootfs", c->name);
+               return false;
+       }
+
+       return container_destroy(c);
+}
+
+static bool lxcapi_snapshot_destroy_all(struct lxc_container *c);
+
+static bool lxcapi_destroy_with_snapshots(struct lxc_container *c)
+{
+       if (!c || !lxcapi_is_defined(c))
+               return false;
+       if (!lxcapi_snapshot_destroy_all(c)) {
+               ERROR("Error deleting all snapshots");
+               return false;
+       }
+       return lxcapi_destroy(c);
+}
+
+static bool set_config_item_locked(struct lxc_container *c, const char *key, const char *v)
+{
+       struct lxc_config_t *config;
 
        if (!c->lxc_conf)
                c->lxc_conf = lxc_conf_init();
@@ -1924,7 +2176,9 @@ static bool set_config_item_locked(struct lxc_container *c, const char *key, con
        config = lxc_getconfig(key);
        if (!config)
                return false;
-       return (0 == config->cb(key, v, c->lxc_conf));
+       if (config->cb(key, v, c->lxc_conf) != 0)
+               return false;
+       return do_append_unexp_config_line(c->lxc_conf, key, v);
 }
 
 static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v)
@@ -2069,32 +2323,17 @@ static int lxcapi_get_cgroup_item(struct lxc_container *c, const char *subsys, c
        return ret;
 }
 
-const char *lxc_get_default_config_path(void)
-{
-       return default_lxc_path();
-}
-
-const char *lxc_get_default_lvm_vg(void)
-{
-       return default_lvm_vg();
-}
-
-const char *lxc_get_default_lvm_thin_pool(void)
+const char *lxc_get_global_config_item(const char *key)
 {
-       return default_lvm_thin_pool();
-}
-
-const char *lxc_get_default_zfs_root(void)
-{
-       return default_zfs_root();
+       return lxc_global_config_value(key);
 }
 
 const char *lxc_get_version(void)
 {
-       return lxc_version();
+       return LXC_VERSION;
 }
 
-static int copy_file(char *old, char *new)
+static int copy_file(const char *old, const char *new)
 {
        int in, out;
        ssize_t len, ret;
@@ -2111,21 +2350,15 @@ static int copy_file(char *old, char *new)
                return -1;
        }
 
-       process_lock();
        in = open(old, O_RDONLY);
-       process_unlock();
        if (in < 0) {
                SYSERROR("Error opening original file %s", old);
                return -1;
        }
-       process_lock();
        out = open(new, O_CREAT | O_EXCL | O_WRONLY, 0644);
-       process_unlock();
        if (out < 0) {
                SYSERROR("Error opening new file %s", new);
-               process_lock();
                close(in);
-               process_unlock();
                return -1;
        }
 
@@ -2138,15 +2371,13 @@ static int copy_file(char *old, char *new)
                if (len == 0)
                        break;
                ret = write(out, buf, len);
-               if (ret < len) {  // should we retry?
+               if (ret < len) { // should we retry?
                        SYSERROR("Error: write to new file %s was interrupted", new);
                        goto err;
                }
        }
-       process_lock();
        close(in);
        close(out);
-       process_unlock();
 
        // we set mode, but not owner/group
        ret = chmod(new, sbuf.st_mode);
@@ -2158,18 +2389,22 @@ static int copy_file(char *old, char *new)
        return 0;
 
 err:
-       process_lock();
        close(in);
        close(out);
-       process_unlock();
        return -1;
 }
 
 static int copyhooks(struct lxc_container *oldc, struct lxc_container *c)
 {
-       int i;
-       int ret;
+       int i, len, ret;
        struct lxc_list *it;
+       char *cpath;
+
+       len = strlen(oldc->config_path) + strlen(oldc->name) + 3;
+       cpath = alloca(len);
+       ret = snprintf(cpath, len, "%s/%s/", oldc->config_path, oldc->name);
+       if (ret < 0 || ret >= len)
+               return -1;
 
        for (i=0; i<NUM_LXC_HOOKS; i++) {
                lxc_list_for_each(it, &c->lxc_conf->hooks[i]) {
@@ -2178,6 +2413,10 @@ static int copyhooks(struct lxc_container *oldc, struct lxc_container *c)
                        char tmppath[MAXPATHLEN];
                        if (!fname) // relative path - we don't support, but maybe we should
                                return 0;
+                       if (strncmp(hookname, cpath, len - 1) != 0) {
+                               // this hook is public - ignore
+                               continue;
+                       }
                        // copy the script, and change the entry in confile
                        ret = snprintf(tmppath, MAXPATHLEN, "%s/%s/%s",
                                        c->config_path, c->name, fname+1);
@@ -2195,6 +2434,10 @@ static int copyhooks(struct lxc_container *oldc, struct lxc_container *c)
                }
        }
 
+       if (!clone_update_unexp_hooks(c->lxc_conf)) {
+               ERROR("Error saving new hooks in clone");
+               return -1;
+       }
        c->save_config(c, NULL);
        return 0;
 }
@@ -2202,17 +2445,13 @@ static int copyhooks(struct lxc_container *oldc, struct lxc_container *c)
 static void new_hwaddr(char *hwaddr)
 {
        FILE *f;
-       process_lock();
        f = fopen("/dev/urandom", "r");
-       process_unlock();
        if (f) {
                unsigned int seed;
                int ret = fread(&seed, sizeof(seed), 1, f);
                if (ret != 1)
                        seed = time(NULL);
-               process_lock();
                fclose(f);
-               process_unlock();
                srand(seed);
        } else
                srand(time(NULL));
@@ -2240,6 +2479,8 @@ static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c)
        if (!oldpath)
                return 0;
 
+       clear_unexp_config_line(c->lxc_conf, "lxc.mount", false);
+
        char *p = strrchr(oldpath, '/');
        if (!p)
                return -1;
@@ -2264,6 +2505,10 @@ static int copy_fstab(struct lxc_container *oldc, struct lxc_container *c)
                ERROR("error: allocating pathname");
                return -1;
        }
+       if (!do_append_unexp_config_line(c->lxc_conf, "lxc.mount", newpath)) {
+               ERROR("error saving new lxctab");
+               return -1;
+       }
 
        return 0;
 }
@@ -2302,30 +2547,25 @@ static bool add_rdepends(struct lxc_container *c, struct lxc_container *c0)
                c->name);
        if (ret < 0 || ret >= MAXPATHLEN)
                return false;
-       process_lock();
        f = fopen(path, "a");
-       process_unlock();
        if (!f)
                return false;
        bret = true;
        // if anything goes wrong, just return an error
        if (fprintf(f, "%s\n%s\n", c0->config_path, c0->name) < 0)
                bret = false;
-       process_lock();
        if (fclose(f) != 0)
                bret = false;
-       process_unlock();
        return bret;
 }
 
 static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
-               const char *newtype, int flags, const char *bdevdata, unsigned long newsize)
+               const char *newtype, int flags, const char *bdevdata, uint64_t newsize)
 {
        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 & LXC_CLONE_SNAPSHOT),
+       bdev = bdev_copy(c0, c->name, c->config_path, newtype, flags,
                        bdevdata, newsize, &need_rdep);
        if (!bdev) {
                ERROR("Error copying storage");
@@ -2338,6 +2578,12 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
                ERROR("Out of memory while setting storage path");
                return -1;
        }
+       // We will simply append a new lxc.rootfs entry to the unexpanded config
+       clear_unexp_config_line(c->lxc_conf, "lxc.rootfs", false);
+       if (!do_append_unexp_config_line(c->lxc_conf, "lxc.rootfs", c->lxc_conf->rootfs.path)) {
+               ERROR("Error saving new rootfs to cloend config");
+               return -1;
+       }
        if (flags & LXC_CLONE_SNAPSHOT)
                copy_rdepends(c, c0);
        if (need_rdep) {
@@ -2351,37 +2597,60 @@ static int copy_storage(struct lxc_container *c0, struct lxc_container *c,
        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 (setuid(0) < 0) {
+               ERROR("Failed to setuid to 0");
                return -1;
-       if (pid > 0)
-               return wait_for_pid(pid);
+       }
+       if (setgroups(0, NULL) < 0)
+               WARN("Failed to clear groups");
 
-       process_unlock(); // we're no longer sharing
-       bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
+       if (unshare(CLONE_NEWNS) < 0)
+               return -1;
+       bdev = bdev_init(c->lxc_conf, 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);
+                       bdev_put(bdev);
+                       return -1;
+               }
+               if (detect_shared_rootfs()) {
+                       if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL)) {
+                               SYSERROR("Failed to make / rslave");
+                               ERROR("Continuing...");
+                       }
+               }
+               if (bdev->ops->mount(bdev) < 0) {
+                       bdev_put(bdev);
+                       return -1;
                }
-               if (bdev->ops->mount(bdev) < 0)
-                       exit(1);
        } else { // TODO come up with a better way
                if (bdev->dest)
                        free(bdev->dest);
@@ -2399,7 +2668,7 @@ static int clone_update_rootfs(struct lxc_container *c0,
                if (setenv("LXC_CONFIG_FILE", conf->rcfile, 1)) {
                        SYSERROR("failed to set environment variable for config path");
                }
-               if (setenv("LXC_ROOTFS_MOUNT", conf->rootfs.mount, 1)) {
+               if (setenv("LXC_ROOTFS_MOUNT", bdev->dest, 1)) {
                        SYSERROR("failed to set environment variable for rootfs mount");
                }
                if (setenv("LXC_ROOTFS_PATH", conf->rootfs.path, 1)) {
@@ -2408,26 +2677,40 @@ static int clone_update_rootfs(struct lxc_container *c0,
 
                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);
+                       bdev_put(bdev);
+                       return -1;
                }
        }
 
        if (!(flags & LXC_CLONE_KEEPNAME)) {
                ret = snprintf(path, MAXPATHLEN, "%s/etc/hostname", bdev->dest);
+               bdev_put(bdev);
+
                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);
+                       SYSERROR("unable to open %s: ignoring", path);
+                       return 0;
+               }
+               if (fprintf(fout, "%s", c->name) < 0) {
+                       fclose(fout);
+                       return -1;
                }
-               if (fprintf(fout, "%s", c->name) < 0)
-                       exit(1);
                if (fclose(fout) < 0)
-                       exit(1);
+                       return -1;
        }
-       exit(0);
+       else
+               bdev_put(bdev);
+
+       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);
 }
 
 /*
@@ -2442,31 +2725,31 @@ sudo lxc-clone -o o1 -n n1 -s -L|-fssize fssize -v|--vgname vgname \
 only rootfs gets converted (copied/snapshotted) on clone.
 */
 
-static int create_file_dirname(char *path)
+static int create_file_dirname(char *path, struct lxc_conf *conf)
 {
        char *p = strrchr(path, '/');
-       int ret;
+       int ret = -1;
 
        if (!p)
                return -1;
        *p = '\0';
-       ret = mkdir(path, 0755);
-       if (ret && errno != EEXIST)
-               SYSERROR("creating container path %s\n", path);
+        ret = do_create_container_dir(path, conf);
        *p = '/';
        return ret;
 }
 
-struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
+static struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
                const char *lxcpath, int flags,
-               const char *bdevtype, const char *bdevdata, unsigned long newsize,
+               const char *bdevtype, const char *bdevdata, uint64_t newsize,
                char **hookargs)
 {
        struct lxc_container *c2 = NULL;
        char newpath[MAXPATHLEN];
        int ret, storage_copied = 0;
-       const char *n, *l;
+       char *origroot = NULL;
+       struct clone_update_data data;
        FILE *fout;
+       pid_t pid;
 
        if (!c || !c->is_defined(c))
                return NULL;
@@ -2480,10 +2763,12 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
        }
 
        // Make sure the container doesn't yet exist.
-       n = newname ? newname : c->name;
-       l = lxcpath ? lxcpath : c->get_config_path(c);
-       ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", l, n);
-       if (ret < 0  || ret >= MAXPATHLEN) {
+       if (!newname)
+               newname = c->name;
+       if (!lxcpath)
+               lxcpath = c->get_config_path(c);
+       ret = snprintf(newpath, MAXPATHLEN, "%s/%s/config", lxcpath, newname);
+       if (ret < 0 || ret >= MAXPATHLEN) {
                SYSERROR("clone: failed making config pathname");
                goto out;
        }
@@ -2492,51 +2777,64 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
                goto out;
        }
 
-       ret = create_file_dirname(newpath);
+       ret = create_file_dirname(newpath, c->lxc_conf);
        if (ret < 0 && errno != EEXIST) {
                ERROR("Error creating container dir for %s", newpath);
                goto out;
        }
 
        // copy the configuration, tweak it as needed,
-       process_lock();
+       if (c->lxc_conf->rootfs.path) {
+               origroot = c->lxc_conf->rootfs.path;
+               c->lxc_conf->rootfs.path = NULL;
+       }
        fout = fopen(newpath, "w");
-       process_unlock();
        if (!fout) {
                SYSERROR("open %s", newpath);
                goto out;
        }
        write_config(fout, c->lxc_conf);
-       process_lock();
        fclose(fout);
-       process_unlock();
+       c->lxc_conf->rootfs.path = origroot;
 
-       sprintf(newpath, "%s/%s/rootfs", l, n);
+       sprintf(newpath, "%s/%s/rootfs", lxcpath, newname);
        if (mkdir(newpath, 0755) < 0) {
                SYSERROR("error creating %s", newpath);
                goto out;
        }
 
-       c2 = lxc_container_new(n, l);
+       if (am_unpriv()) {
+               if (chown_mapped_root(newpath, c->lxc_conf) < 0) {
+                       ERROR("Error chowning %s to container root", newpath);
+                       goto out;
+               }
+       }
+
+       c2 = lxc_container_new(newname, lxcpath);
        if (!c2) {
-               ERROR("clone: failed to create new container (%s %s)", n, l);
+               ERROR("clone: failed to create new container (%s %s)", newname,
+                               lxcpath);
                goto out;
        }
 
+       // copy/snapshot rootfs's
+       ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize);
+       if (ret < 0)
+               goto out;
+
+       clear_unexp_config_line(c2->lxc_conf, "lxc.utsname", false);
+
        // update utsname
        if (!set_config_item_locked(c2, "lxc.utsname", newname)) {
                ERROR("Error setting new hostname");
                goto out;
        }
 
-
-       // copy hooks if requested
-       if (flags & LXC_CLONE_COPYHOOKS) {
-               ret = copyhooks(c, c2);
-               if (ret < 0) {
-                       ERROR("error copying hooks");
-                       goto out;
-               }
+       // copy hooks
+       ret = copyhooks(c, c2);
+       if (ret < 0) {
+               ERROR("error copying hooks");
+               goto out;
        }
 
        if (copy_fstab(c, c2) < 0) {
@@ -2545,13 +2843,13 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
        }
 
        // update macaddrs
-       if (!(flags & LXC_CLONE_KEEPMACADDR))
+       if (!(flags & LXC_CLONE_KEEPMACADDR)) {
                network_new_hwaddrs(c2);
-
-       // copy/snapshot rootfs's
-       ret = copy_storage(c, c2, bdevtype, flags, bdevdata, newsize);
-       if (ret < 0)
-               goto out;
+               if (!clone_update_unexp_network(c2->lxc_conf)) {
+                       ERROR("Error updating network for clone");
+                       goto out;
+               }
+       }
 
        // We've now successfully created c2's storage, so clear it out if we
        // fail after this
@@ -2560,12 +2858,31 @@ struct lxc_container *lxcapi_clone(struct lxc_container *c, const char *newname,
        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);
@@ -2579,6 +2896,41 @@ out:
        return NULL;
 }
 
+static bool lxcapi_rename(struct lxc_container *c, const char *newname)
+{
+       struct bdev *bdev;
+       struct lxc_container *newc;
+
+       if (!c || !c->name || !c->config_path || !c->lxc_conf)
+               return false;
+
+       if (has_fs_snapshots(c) || has_snapshots(c)) {
+               ERROR("Renaming a container with snapshots is not supported");
+               return false;
+       }
+       bdev = bdev_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;
+       }
+
+       newc = lxcapi_clone(c, newname, c->config_path, LXC_CLONE_KEEPMACADDR, NULL, bdev->type, 0, NULL);
+       bdev_put(bdev);
+       if (!newc) {
+               lxc_container_put(newc);
+               return false;
+       }
+
+       if (newc && lxcapi_is_defined(newc))
+               lxc_container_put(newc);
+
+       if (!container_destroy(c)) {
+               ERROR("Could not destroy existing container %s", c->name);
+               return false;
+       }
+       return true;
+}
+
 static int lxcapi_attach(struct lxc_container *c, lxc_attach_exec_t exec_function, void *exec_payload, lxc_attach_options_t *options, pid_t *attached_process)
 {
        if (!c)
@@ -2606,7 +2958,7 @@ static int lxcapi_attach_run_wait(struct lxc_container *c, lxc_attach_options_t
        return lxc_wait_for_pid_status(pid);
 }
 
-int get_next_index(const char *lxcpath, char *cname)
+static int get_next_index(const char *lxcpath, char *cname)
 {
        char *fname;
        struct stat sb;
@@ -2622,16 +2974,51 @@ int get_next_index(const char *lxcpath, char *cname)
        }
 }
 
-static int lxcapi_snapshot(struct lxc_container *c, char *commentfile)
+static bool get_snappath_dir(struct lxc_container *c, char *snappath)
+{
+       int ret;
+       /*
+        * If the old style snapshot path exists, use it
+        * /var/lib/lxc -> /var/lib/lxcsnaps
+        */
+       ret = snprintf(snappath, MAXPATHLEN, "%ssnaps", c->config_path);
+       if (ret < 0 || ret >= MAXPATHLEN)
+               return false;
+       if (dir_exists(snappath)) {
+               ret = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       return false;
+               return true;
+       }
+
+       /*
+        * Use the new style path
+        * /var/lib/lxc -> /var/lib/lxc + c->name + /snaps + \0
+        */
+       ret = snprintf(snappath, MAXPATHLEN, "%s/%s/snaps", c->config_path, c->name);
+       if (ret < 0 || ret >= MAXPATHLEN)
+               return false;
+       return true;
+}
+
+static int lxcapi_snapshot(struct lxc_container *c, const char *commentfile)
 {
        int i, flags, ret;
        struct lxc_container *c2;
        char snappath[MAXPATHLEN], newname[20];
 
-       // /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 (!bdev_can_backup(c->lxc_conf)) {
+               ERROR("%s's backing store cannot be backed up.", c->name);
+               ERROR("Your container must use another backing store type.");
+               return -1;
+       }
+
+       if (!get_snappath_dir(c, snappath))
                return -1;
+
        i = get_next_index(snappath, c->name);
 
        if (mkdir_p(snappath, 0755) < 0) {
@@ -2643,10 +3030,22 @@ static int lxcapi_snapshot(struct lxc_container *c, char *commentfile)
        if (ret < 0 || ret >= 20)
                return -1;
 
-       flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME;
+       /*
+        * We pass LXC_CLONE_SNAPSHOT to make sure that a rdepends file entry is
+        * created in the original container
+        */
+       flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_KEEPMACADDR | LXC_CLONE_KEEPNAME |
+               LXC_CLONE_KEEPBDEVTYPE | LXC_CLONE_MAYBE_SNAPSHOT;
+       if (bdev_is_dir(c->lxc_conf, c->lxc_conf->rootfs.path)) {
+               ERROR("Snapshot of directory-backed container requested.");
+               ERROR("Making a copy-clone.  If you do want snapshots, then");
+               ERROR("please create an aufs or overlayfs clone first, snapshot that");
+               ERROR("and keep the original container pristine.");
+               flags &= ~LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT;
+       }
        c2 = c->clone(c, newname, snappath, flags, NULL, NULL, 0, NULL);
        if (!c2) {
-               ERROR("clone of %s:%s failed\n", c->config_path, c->name);
+               ERROR("clone of %s:%s failed", c->config_path, c->name);
                return -1;
        }
 
@@ -2665,11 +3064,9 @@ static int lxcapi_snapshot(struct lxc_container *c, char *commentfile)
 
        char *dfnam = alloca(strlen(snappath) + strlen(newname) + 5);
        sprintf(dfnam, "%s/%s/ts", snappath, newname);
-       process_lock();
        f = fopen(dfnam, "w");
-       process_unlock();
        if (!f) {
-               ERROR("Failed to open %s\n", dfnam);
+               ERROR("Failed to open %s", dfnam);
                return -1;
        }
        if (fprintf(f, "%s", buffer) < 0) {
@@ -2677,9 +3074,7 @@ static int lxcapi_snapshot(struct lxc_container *c, char *commentfile)
                fclose(f);
                return -1;
        }
-       process_lock();
        ret = fclose(f);
-       process_unlock();
        if (ret != 0) {
                SYSERROR("Writing timestamp");
                return -1;
@@ -2733,9 +3128,7 @@ static char *get_timestamp(char* snappath, char *name)
        ret = snprintf(path, MAXPATHLEN, "%s/%s/ts", snappath, name);
        if (ret < 0 || ret >= MAXPATHLEN)
                return NULL;
-       process_lock();
        fin = fopen(path, "r");
-       process_unlock();
        if (!fin)
                return NULL;
        (void) fseek(fin, 0, SEEK_END);
@@ -2752,31 +3145,26 @@ static char *get_timestamp(char* snappath, char *name)
                        }
                }
        }
-       process_lock();
        fclose(fin);
-       process_unlock();
        return s;
 }
 
 static int lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret_snaps)
 {
        char snappath[MAXPATHLEN], path2[MAXPATHLEN];
-       int dirlen, count = 0, ret;
+       int count = 0, ret;
        struct dirent dirent, *direntp;
        struct lxc_snapshot *snaps =NULL, *nsnaps;
        DIR *dir;
 
        if (!c || !lxcapi_is_defined(c))
                return -1;
-       // snappath is ${lxcpath}snaps/${lxcname}/
-       dirlen = snprintf(snappath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (dirlen < 0 || dirlen >= MAXPATHLEN) {
+
+       if (!get_snappath_dir(c, snappath)) {
                ERROR("path name too long");
                return -1;
        }
-       process_lock();
        dir = opendir(snappath);
-       process_unlock();
        if (!dir) {
                INFO("failed to open %s - assuming no snapshots", snappath);
                return 0;
@@ -2819,10 +3207,8 @@ static int lxcapi_snapshot_list(struct lxc_container *c, struct lxc_snapshot **r
                count++;
        }
 
-       process_lock();
        if (closedir(dir))
                WARN("failed to close directory");
-       process_unlock();
 
        *ret_snaps = snaps;
        return count;
@@ -2834,17 +3220,15 @@ out_free:
                        lxcsnap_free(&snaps[i]);
                free(snaps);
        }
-       process_lock();
        if (closedir(dir))
                WARN("failed to close directory");
-       process_unlock();
        return -1;
 }
 
-static bool lxcapi_snapshot_restore(struct lxc_container *c, char *snapname, char *newname)
+static bool lxcapi_snapshot_restore(struct lxc_container *c, const char *snapname, const char *newname)
 {
        char clonelxcpath[MAXPATHLEN];
-       int ret;
+       int flags = 0;
        struct lxc_container *snap, *rest;
        struct bdev *bdev;
        bool b = false;
@@ -2852,7 +3236,12 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, char *snapname, cha
        if (!c || !c->name || !c->config_path)
                return false;
 
-       bdev = bdev_init(c->lxc_conf->rootfs.path, c->lxc_conf->rootfs.mount, NULL);
+       if (has_fs_snapshots(c)) {
+               ERROR("container rootfs has dependent snapshots");
+               return false;
+       }
+
+       bdev = bdev_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;
@@ -2860,15 +3249,8 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, char *snapname, cha
 
        if (!newname)
                newname = c->name;
-       if (strcmp(c->name, newname) == 0) {
-               if (!lxcapi_destroy(c)) {
-                       ERROR("Could not destroy existing container %s", newname);
-                       bdev_put(bdev);
-                       return false;
-               }
-       }
-       ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (ret < 0 || ret >= MAXPATHLEN) {
+
+       if (!get_snappath_dir(c, clonelxcpath)) {
                bdev_put(bdev);
                return false;
        }
@@ -2882,7 +3264,19 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, char *snapname, cha
                return false;
        }
 
-       rest = lxcapi_clone(snap, newname, c->config_path, 0, bdev->type, NULL, 0, NULL);
+       if (strcmp(c->name, newname) == 0) {
+               if (!container_destroy(c)) {
+                       ERROR("Could not destroy existing container %s", newname);
+                       lxc_container_put(snap);
+                       bdev_put(bdev);
+                       return false;
+               }
+       }
+
+       if (strcmp(bdev->type, "dir") != 0 && strcmp(bdev->type, "loop") != 0)
+               flags = LXC_CLONE_SNAPSHOT | LXC_CLONE_MAYBE_SNAPSHOT;
+       rest = lxcapi_clone(snap, newname, c->config_path, flags,
+                       bdev->type, NULL, 0, NULL);
        bdev_put(bdev);
        if (rest && lxcapi_is_defined(rest))
                b = true;
@@ -2892,21 +3286,13 @@ static bool lxcapi_snapshot_restore(struct lxc_container *c, char *snapname, cha
        return b;
 }
 
-static bool lxcapi_snapshot_destroy(struct lxc_container *c, char *snapname)
+static bool do_snapshot_destroy(const char *snapname, const char *clonelxcpath)
 {
-       int ret;
-       char clonelxcpath[MAXPATHLEN];
        struct lxc_container *snap = NULL;
-
-       if (!c || !c->name || !c->config_path)
-               return false;
-
-       ret = snprintf(clonelxcpath, MAXPATHLEN, "%ssnaps/%s", c->config_path, c->name);
-       if (ret < 0 || ret >= MAXPATHLEN)
-               goto err;
+       bool bret = false;
 
        snap = lxc_container_new(snapname, clonelxcpath);
-       if (!snap || !lxcapi_is_defined(snap)) {
+       if (!snap) {
                ERROR("Could not find snapshot %s", snapname);
                goto err;
        }
@@ -2915,115 +3301,664 @@ static bool lxcapi_snapshot_destroy(struct lxc_container *c, char *snapname)
                ERROR("Could not destroy snapshot %s", snapname);
                goto err;
        }
-       lxc_container_put(snap);
+       bret = true;
 
-       return true;
 err:
        if (snap)
                lxc_container_put(snap);
-       return false;
-}
-
-static bool lxcapi_may_control(struct lxc_container *c)
-{
-       return lxc_try_cmd(c->name, c->config_path) == 0;
+       return bret;
 }
 
-static bool add_remove_device_node(struct lxc_container *c, char *src_path, char *dest_path, bool add)
+static bool remove_all_snapshots(const char *path)
 {
-       int ret;
-       struct stat st;
-       char path[MAXPATHLEN];
-       char value[MAX_BUFFER];
-       char *directory_path = NULL, *p;
+       DIR *dir;
+       struct dirent dirent, *direntp;
+       bool bret = true;
 
-       /* make sure container is running */
-       if (!c->is_running(c)) {
-               ERROR("container is not running");
-               goto out;
+       dir = opendir(path);
+       if (!dir) {
+               SYSERROR("opendir on snapshot path %s", path);
+               return false;
+       }
+       while (!readdir_r(dir, &dirent, &direntp)) {
+               if (!direntp)
+                       break;
+               if (!strcmp(direntp->d_name, "."))
+                       continue;
+               if (!strcmp(direntp->d_name, ".."))
+                       continue;
+               if (!do_snapshot_destroy(direntp->d_name, path)) {
+                       bret = false;
+                       continue;
+               }
        }
 
-       /* use src_path if dest_path is NULL otherwise use dest_path */
-       p = dest_path ? dest_path : src_path;
+       closedir(dir);
 
-       /* prepare the path */
-       ret = snprintf(path, MAXPATHLEN, "/proc/%d/root/%s", c->init_pid(c), p);
-       if (ret < 0 || ret >= MAXPATHLEN)
-               goto out;
-       remove_trailing_slashes(path);
+       if (rmdir(path))
+               SYSERROR("Error removing directory %s", path);
 
-       p = add ? src_path : path;
-       /* make sure we can access p */
-       if(access(p, F_OK) < 0 || stat(p, &st) < 0)
-               goto out;
+       return bret;
+}
 
-       /* continue if path is character device or block device */
-       if (S_ISCHR(st.st_mode))
-               ret = snprintf(value, MAX_BUFFER, "c %d:%d rwm", major(st.st_rdev), minor(st.st_rdev));
-       else if (S_ISBLK(st.st_mode))
-               ret = snprintf(value, MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev));
-       else
-               goto out;
+static bool lxcapi_snapshot_destroy(struct lxc_container *c, const char *snapname)
+{
+       char clonelxcpath[MAXPATHLEN];
 
-       /* check snprintf return code */
-       if (ret < 0 || ret >= MAX_BUFFER)
-               goto out;
+       if (!c || !c->name || !c->config_path || !snapname)
+               return false;
 
-       directory_path = dirname(strdup(path));
-       /* remove path and directory_path (if empty) */
-       if(access(path, F_OK) == 0) {
+       if (!get_snappath_dir(c, clonelxcpath))
+               return false;
+
+       return do_snapshot_destroy(snapname, clonelxcpath);
+}
+
+static bool lxcapi_snapshot_destroy_all(struct lxc_container *c)
+{
+       char clonelxcpath[MAXPATHLEN];
+
+       if (!c || !c->name || !c->config_path)
+               return false;
+
+       if (!get_snappath_dir(c, clonelxcpath))
+               return false;
+
+       return remove_all_snapshots(clonelxcpath);
+}
+
+static bool lxcapi_may_control(struct lxc_container *c)
+{
+       return lxc_try_cmd(c->name, c->config_path) == 0;
+}
+
+static bool do_add_remove_node(pid_t init_pid, const char *path, bool add,
+               struct stat *st)
+{
+       char chrootpath[MAXPATHLEN];
+       char *directory_path = NULL;
+       pid_t pid;
+       int ret;
+
+       if ((pid = fork()) < 0) {
+               SYSERROR("failed to fork a child helper");
+               return false;
+       }
+       if (pid) {
+               if (wait_for_pid(pid) != 0) {
+                       ERROR("Failed to create note in guest");
+                       return false;
+               }
+               return true;
+       }
+
+       /* prepare the path */
+       ret = snprintf(chrootpath, MAXPATHLEN, "/proc/%d/root", init_pid);
+       if (ret < 0 || ret >= MAXPATHLEN)
+               return false;
+
+       if (chroot(chrootpath) < 0)
+               exit(1);
+       if (chdir("/") < 0)
+               exit(1);
+       /* remove path if it exists */
+       if(faccessat(AT_FDCWD, path, F_OK, AT_SYMLINK_NOFOLLOW) == 0) {
                if (unlink(path) < 0) {
                        ERROR("unlink failed");
-                       goto out;
-               }
-               if (rmdir(directory_path) < 0 && errno != ENOTEMPTY) {
-                       ERROR("rmdir failed");
-                       goto out;
+                       exit(1);
                }
        }
+       if (!add)
+               exit(0);
 
-       if (add) {
-               /* create the missing directories */
-               if (mkdir_p(directory_path, 0755) < 0) {
-                       ERROR("failed to create directory");
-                       goto out;
-               }
+       /* create any missing directories */
+       directory_path = dirname(strdup(path));
+       if (mkdir_p(directory_path, 0755) < 0 && errno != EEXIST) {
+               ERROR("failed to create directory");
+               exit(1);
+       }
 
-               /* create the device node */
-               if (mknod(path, st.st_mode, st.st_rdev) < 0) {
-                       ERROR("mknod failed");
-            goto out;
-               }
+       /* create the device node */
+       if (mknod(path, st->st_mode, st->st_rdev) < 0) {
+               ERROR("mknod failed");
+               exit(1);
+       }
+
+       exit(0);
+}
+
+static bool add_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path, bool add)
+{
+       int ret;
+       struct stat st;
+       char value[MAX_BUFFER];
+       const char *p;
+
+       /* make sure container is running */
+       if (!c->is_running(c)) {
+               ERROR("container is not running");
+               return false;
+       }
+
+       /* use src_path if dest_path is NULL otherwise use dest_path */
+       p = dest_path ? dest_path : src_path;
+
+       /* make sure we can access p */
+       if(access(p, F_OK) < 0 || stat(p, &st) < 0)
+               return false;
+
+       /* continue if path is character device or block device */
+       if (S_ISCHR(st.st_mode))
+               ret = snprintf(value, MAX_BUFFER, "c %d:%d rwm", major(st.st_rdev), minor(st.st_rdev));
+       else if (S_ISBLK(st.st_mode))
+               ret = snprintf(value, MAX_BUFFER, "b %d:%d rwm", major(st.st_rdev), minor(st.st_rdev));
+       else
+               return false;
 
-               /* add device node to device list */
+       /* check snprintf return code */
+       if (ret < 0 || ret >= MAX_BUFFER)
+               return false;
+
+       if (!do_add_remove_node(c->init_pid(c), p, add, &st))
+               return false;
+
+       /* add or remove device to/from cgroup access list */
+       if (add) {
                if (!c->set_cgroup_item(c, "devices.allow", value)) {
                        ERROR("set_cgroup_item failed while adding the device node");
-                       goto out;
+                       return false;
                }
        } else {
-               /* remove device node from device list */
                if (!c->set_cgroup_item(c, "devices.deny", value)) {
                        ERROR("set_cgroup_item failed while removing the device node");
-                       goto out;
+                       return false;
                }
        }
+
        return true;
-out:
-       if (directory_path)
-               free(directory_path);
-       return false;
 }
 
-static bool lxcapi_add_device_node(struct lxc_container *c, char *src_path, char *dest_path)
+static bool lxcapi_add_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
 {
+       if (am_unpriv()) {
+               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
+               return false;
+       }
        return add_remove_device_node(c, src_path, dest_path, true);
 }
 
-static bool lxcapi_remove_device_node(struct lxc_container *c, char *src_path, char *dest_path)
+static bool lxcapi_remove_device_node(struct lxc_container *c, const char *src_path, const char *dest_path)
 {
+       if (am_unpriv()) {
+               ERROR(NOT_SUPPORTED_ERROR, __FUNCTION__);
+               return false;
+       }
        return add_remove_device_node(c, src_path, dest_path, false);
 }
 
+struct criu_opts {
+       /* The type of criu invocation, one of "dump" or "restore" */
+       char *action;
+
+       /* The directory to pass to criu */
+       char *directory;
+
+       /* The container to dump */
+       struct lxc_container *c;
+
+       /* Enable criu verbose mode? */
+       bool verbose;
+
+       /* dump: stop the container or not after dumping? */
+       bool stop;
+
+       /* restore: the file to write the init process' pid into */
+       char *pidfile;
+};
+
+/*
+ * @out must be 128 bytes long
+ */
+static int read_criu_file(const char *directory, const char *file, int netnr, char *out)
+{
+       char path[PATH_MAX];
+       int ret;
+       FILE *f;
+
+       ret = snprintf(path, PATH_MAX,  "%s/%s%d", directory, file, netnr);
+       if (ret < 0 || ret >= PATH_MAX) {
+               ERROR("%s: path too long", __func__);
+               return -1;
+       }
+
+       f = fopen(path, "r");
+       if (!f)
+               return -1;
+
+       ret = fscanf(f, "%127s", out);
+       fclose(f);
+       if (ret <= 0)
+               return -1;
+
+       return 0;
+}
+
+static void exec_criu(struct criu_opts *opts)
+{
+       char **argv, log[PATH_MAX];
+       int static_args = 13, argc = 0, i, ret;
+
+       /* The command line always looks like:
+        * criu $(action) --tcp-established --file-locks --link-remap --manage-cgroups \
+        *     --action-script foo.sh -D $(directory) -o $(directory)/$(action).log
+        * +1 for final NULL */
+
+       if (strcmp(opts->action, "dump") == 0) {
+               /* -t pid */
+               static_args += 2;
+
+               /* --leave-running */
+               if (!opts->stop)
+                       static_args++;
+       } else if (strcmp(opts->action, "restore") == 0) {
+               /* --root $(lxc_mount_point) --restore-detached --pidfile $foo */
+               static_args += 5;
+       } else {
+               return;
+       }
+
+       if (opts->verbose)
+               static_args++;
+
+       ret = snprintf(log, PATH_MAX, "%s/%s.log", opts->directory, opts->action);
+       if (ret < 0 || ret >= PATH_MAX) {
+               ERROR("logfile name too long\n");
+               return;
+       }
+
+       argv = malloc(static_args * sizeof(*argv));
+       if (!argv)
+               return;
+
+       memset(argv, 0, static_args * sizeof(*argv));
+
+#define DECLARE_ARG(arg)                       \
+       do {                                    \
+               argv[argc++] = strdup(arg);     \
+               if (!argv[argc-1])              \
+                       goto err;               \
+       } while (0)
+
+       argv[argc++] = on_path("criu", NULL);
+       if (!argv[argc-1]) {
+               ERROR("Couldn't find criu binary\n");
+               goto err;
+       }
+
+       DECLARE_ARG(opts->action);
+       DECLARE_ARG("--tcp-established");
+       DECLARE_ARG("--file-locks");
+       DECLARE_ARG("--link-remap");
+       DECLARE_ARG("--manage-cgroups");
+       DECLARE_ARG("--action-script");
+       DECLARE_ARG(LIBEXECDIR "/lxc/lxc-restore-net");
+       DECLARE_ARG("-D");
+       DECLARE_ARG(opts->directory);
+       DECLARE_ARG("-o");
+       DECLARE_ARG(log);
+
+       if (opts->verbose)
+               DECLARE_ARG("-vvvvvv");
+
+       if (strcmp(opts->action, "dump") == 0) {
+               char pid[32];
+
+               if (sprintf(pid, "%d", lxcapi_init_pid(opts->c)) < 0)
+                       goto err;
+
+               DECLARE_ARG("-t");
+               DECLARE_ARG(pid);
+               if (!opts->stop)
+                       DECLARE_ARG("--leave-running");
+       } else if (strcmp(opts->action, "restore") == 0) {
+               int netnr = 0;
+               struct lxc_list *it;
+
+               DECLARE_ARG("--root");
+               DECLARE_ARG(opts->c->lxc_conf->rootfs.mount);
+               DECLARE_ARG("--restore-detached");
+               DECLARE_ARG("--pidfile");
+               DECLARE_ARG(opts->pidfile);
+
+               lxc_list_for_each(it, &opts->c->lxc_conf->network) {
+                       char eth[128], veth[128], buf[257];
+                       void *m;
+
+                       if (read_criu_file(opts->directory, "veth", netnr, veth))
+                               goto err;
+                       if (read_criu_file(opts->directory, "eth", netnr, eth))
+                               goto err;
+                       ret = snprintf(buf, 257, "%s=%s", eth, veth);
+                       if (ret < 0 || ret >= 257)
+                               goto err;
+
+                       /* final NULL and --veth-pair eth0:vethASDF */
+                       m = realloc(argv, (argc + 1 + 2) * sizeof(*argv));
+                       if (!m)
+                               goto err;
+                       argv = m;
+
+                       DECLARE_ARG("--veth-pair");
+                       DECLARE_ARG(buf);
+                       argv[argc] = NULL;
+
+                       netnr++;
+               }
+       }
+
+#undef DECLARE_ARG
+
+       execv(argv[0], argv);
+err:
+       for (i = 0; argv[i]; i++)
+               free(argv[i]);
+       free(argv);
+}
+
+/* Check and make sure the container has a configuration that we know CRIU can
+ * dump. */
+static bool criu_ok(struct lxc_container *c)
+{
+       struct lxc_list *it;
+       bool found_deny_rule = false;
+
+       if (geteuid()) {
+               ERROR("Must be root to checkpoint\n");
+               return false;
+       }
+
+       /* We only know how to restore containers with veth networks. */
+       lxc_list_for_each(it, &c->lxc_conf->network) {
+               struct lxc_netdev *n = it->elem;
+               if (n->type != LXC_NET_VETH && n->type != LXC_NET_NONE) {
+                       ERROR("Found network that is not VETH or NONE\n");
+                       return false;
+               }
+       }
+
+       // These requirements come from http://criu.org/LXC
+       if (c->lxc_conf->console.path &&
+                       strcmp(c->lxc_conf->console.path, "none") != 0) {
+               ERROR("lxc.console must be none\n");
+               return false;
+       }
+
+       if (c->lxc_conf->tty != 0) {
+               ERROR("lxc.tty must be 0\n");
+               return false;
+       }
+
+       lxc_list_for_each(it, &c->lxc_conf->cgroup) {
+               struct lxc_cgroup *cg = it->elem;
+               if (strcmp(cg->subsystem, "devices.deny") == 0 &&
+                               strcmp(cg->value, "c 5:1 rwm") == 0) {
+
+                       found_deny_rule = true;
+                       break;
+               }
+       }
+
+       if (!found_deny_rule) {
+               ERROR("couldn't find devices.deny = c 5:1 rwm");
+               return false;
+       }
+
+       return true;
+}
+
+static bool lxcapi_checkpoint(struct lxc_container *c, char *directory, bool stop, bool verbose)
+{
+       int netnr, status;
+       struct lxc_list *it;
+       bool error = false;
+       pid_t pid;
+
+       if (!criu_ok(c))
+               return false;
+
+       if (mkdir(directory, 0700) < 0 && errno != EEXIST)
+               return false;
+
+       netnr = 0;
+       lxc_list_for_each(it, &c->lxc_conf->network) {
+               char *veth = NULL, *bridge = NULL, veth_path[PATH_MAX], eth[128];
+               struct lxc_netdev *n = it->elem;
+               int pret;
+
+               pret = snprintf(veth_path, PATH_MAX, "lxc.network.%d.veth.pair", netnr);
+               if (pret < 0 || pret >= PATH_MAX) {
+                       error = true;
+                       goto out;
+               }
+
+               veth = lxcapi_get_running_config_item(c, veth_path);
+               if (!veth) {
+                       /* criu_ok() checks that all interfaces are
+                        * LXC_NET{VETH,NONE}, and VETHs should have this
+                        * config */
+                       assert(n->type == LXC_NET_NONE);
+                       break;
+               }
+
+               pret = snprintf(veth_path, PATH_MAX, "lxc.network.%d.link", netnr);
+               if (pret < 0 || pret >= PATH_MAX) {
+                       error = true;
+                       goto out;
+               }
+
+               bridge = lxcapi_get_running_config_item(c, veth_path);
+               if (!bridge) {
+                       error = true;
+                       goto out;
+               }
+
+               pret = snprintf(veth_path, PATH_MAX, "%s/veth%d", directory, netnr);
+               if (pret < 0 || pret >= PATH_MAX || print_to_file(veth_path, veth) < 0) {
+                       error = true;
+                       goto out;
+               }
+
+               pret = snprintf(veth_path, PATH_MAX, "%s/bridge%d", directory, netnr);
+               if (pret < 0 || pret >= PATH_MAX || print_to_file(veth_path, bridge) < 0) {
+                       error = true;
+                       goto out;
+               }
+
+               if (n->name) {
+                       if (strlen(n->name) >= 128) {
+                               error = true;
+                               goto out;
+                       }
+                       strncpy(eth, n->name, 128);
+               } else
+                       sprintf(eth, "eth%d", netnr);
+
+               pret = snprintf(veth_path, PATH_MAX, "%s/eth%d", directory, netnr);
+               if (pret < 0 || pret >= PATH_MAX || print_to_file(veth_path, eth) < 0)
+                       error = true;
+
+out:
+               free(veth);
+               free(bridge);
+               if (error)
+                       return false;
+       }
+
+       pid = fork();
+       if (pid < 0)
+               return false;
+
+       if (pid == 0) {
+               struct criu_opts os;
+
+               os.action = "dump";
+               os.directory = directory;
+               os.c = c;
+               os.stop = stop;
+               os.verbose = verbose;
+
+               /* exec_criu() returning is an error */
+               exec_criu(&os);
+               exit(1);
+       } else {
+               pid_t w = waitpid(pid, &status, 0);
+               if (w == -1) {
+                       perror("waitpid");
+                       return false;
+               }
+
+               if (WIFEXITED(status)) {
+                       return !WEXITSTATUS(status);
+               }
+
+               return false;
+       }
+}
+
+static bool lxcapi_restore(struct lxc_container *c, char *directory, bool verbose)
+{
+       pid_t pid;
+       struct lxc_list *it;
+       struct lxc_rootfs *rootfs;
+       char pidfile[L_tmpnam];
+
+       if (!criu_ok(c))
+               return false;
+
+       if (geteuid()) {
+               ERROR("Must be root to restore\n");
+               return false;
+       }
+
+       if (!tmpnam(pidfile))
+               return false;
+
+       struct lxc_handler *handler;
+
+       handler = lxc_init(c->name, c->lxc_conf, c->config_path);
+       if (!handler)
+               return false;
+
+       pid = fork();
+       if (pid < 0)
+               return false;
+
+       if (pid == 0) {
+               struct criu_opts os;
+
+               if (unshare(CLONE_NEWNS))
+                       return false;
+
+               /* CRIU needs the lxc root bind mounted so that it is the root of some
+                * mount. */
+               rootfs = &c->lxc_conf->rootfs;
+
+               if (rootfs_is_blockdev(c->lxc_conf)) {
+                       if (do_rootfs_setup(c->lxc_conf, c->name, c->config_path) < 0)
+                               return false;
+               }
+               else {
+                       if (mkdir(rootfs->mount, 0755) < 0 && errno != EEXIST)
+                               return false;
+
+                       if (mount(rootfs->path, rootfs->mount, NULL, MS_BIND, NULL) < 0) {
+                               rmdir(rootfs->mount);
+                               return false;
+                       }
+               }
+
+               os.action = "restore";
+               os.directory = directory;
+               os.c = c;
+               os.pidfile = pidfile;
+               os.verbose = verbose;
+
+               /* exec_criu() returning is an error */
+               exec_criu(&os);
+               umount(rootfs->mount);
+               rmdir(rootfs->mount);
+               exit(1);
+       } else {
+               int status;
+               pid_t w = waitpid(pid, &status, 0);
+
+               if (w == -1) {
+                       perror("waitpid");
+                       return false;
+               }
+
+               if (WIFEXITED(status)) {
+                       if (WEXITSTATUS(status)) {
+                               return false;
+                       }
+                       else {
+                               int netnr = 0, ret;
+                               bool error = false;
+                               FILE *f = fopen(pidfile, "r");
+                               if (!f) {
+                                       perror("reading pidfile");
+                                       ERROR("couldn't read restore's init pidfile %s\n", pidfile);
+                                       return false;
+                               }
+
+                               ret = fscanf(f, "%d", (int*) &handler->pid);
+                               fclose(f);
+                               if (ret != 1) {
+                                       ERROR("reading restore pid failed");
+                                       return false;
+                               }
+
+                               if (container_mem_lock(c))
+                                       return false;
+
+                               lxc_list_for_each(it, &c->lxc_conf->network) {
+                                       char eth[128], veth[128];
+                                       struct lxc_netdev *netdev = it->elem;
+
+                                       if (read_criu_file(directory, "veth", netnr, veth)) {
+                                               error = true;
+                                               goto out_unlock;
+                                       }
+                                       if (read_criu_file(directory, "eth", netnr, eth)) {
+                                               error = true;
+                                               goto out_unlock;
+                                       }
+                                       netdev->priv.veth_attr.pair = strdup(veth);
+                                       if (!netdev->priv.veth_attr.pair) {
+                                               error = true;
+                                               goto out_unlock;
+                                       }
+                                       netnr++;
+                               }
+out_unlock:
+                               container_mem_unlock(c);
+                               if (error)
+                                       return false;
+
+                               if (lxc_set_state(c->name, handler, RUNNING))
+                                       return false;
+                       }
+               }
+
+               if (lxc_poll(c->name, handler)) {
+                       lxc_abort(c->name, handler);
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 static int lxcapi_attach_run_waitl(struct lxc_container *c, lxc_attach_options_t *options, const char *program, const char *arg, ...)
 {
        va_list ap;
@@ -3052,6 +3987,9 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
 {
        struct lxc_container *c;
 
+       if (!name)
+               return NULL;
+
        c = malloc(sizeof(*c));
        if (!c) {
                fprintf(stderr, "failed to malloc lxc_container\n");
@@ -3062,10 +4000,10 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        if (configpath)
                c->config_path = strdup(configpath);
        else
-               c->config_path = strdup(default_lxc_path());
+               c->config_path = strdup(lxc_global_config_value("lxc.lxcpath"));
 
        if (!c->config_path) {
-               fprintf(stderr, "Out of memory");
+               fprintf(stderr, "Out of memory\n");
                goto err;
        }
 
@@ -3093,14 +4031,16 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
                goto err;
        }
 
-       if (file_exists(c->configfile))
-               lxcapi_load_config(c, NULL);
+       if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL))
+               goto err;
 
        if (ongoing_create(c) == 2) {
                ERROR("Error: %s creation was not completed", c->name);
-               lxcapi_destroy(c);
+               container_destroy(c);
                lxcapi_clear_config(c);
        }
+       c->daemonize = true;
+       c->pidfile = NULL;
 
        // assign the member functions
        c->is_defined = lxcapi_is_defined;
@@ -3121,6 +4061,8 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->wait = lxcapi_wait;
        c->set_config_item = lxcapi_set_config_item;
        c->destroy = lxcapi_destroy;
+       c->destroy_with_snapshots = lxcapi_destroy_with_snapshots;
+       c->rename = lxcapi_rename;
        c->save_config = lxcapi_save_config;
        c->get_keys = lxcapi_get_keys;
        c->create = lxcapi_create;
@@ -3130,6 +4072,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->clear_config = lxcapi_clear_config;
        c->clear_config_item = lxcapi_clear_config_item;
        c->get_config_item = lxcapi_get_config_item;
+       c->get_running_config_item = lxcapi_get_running_config_item;
        c->get_cgroup_item = lxcapi_get_cgroup_item;
        c->set_cgroup_item = lxcapi_set_cgroup_item;
        c->get_config_path = lxcapi_get_config_path;
@@ -3144,9 +4087,12 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
        c->snapshot_list = lxcapi_snapshot_list;
        c->snapshot_restore = lxcapi_snapshot_restore;
        c->snapshot_destroy = lxcapi_snapshot_destroy;
+       c->snapshot_destroy_all = lxcapi_snapshot_destroy_all;
        c->may_control = lxcapi_may_control;
        c->add_device_node = lxcapi_add_device_node;
        c->remove_device_node = lxcapi_remove_device_node;
+       c->checkpoint = lxcapi_checkpoint;
+       c->restore = lxcapi_restore;
 
        /* we'll allow the caller to update these later */
        if (lxc_log_init(NULL, "none", NULL, "lxc_container", 0, c->config_path)) {
@@ -3183,12 +4129,9 @@ int list_defined_containers(const char *lxcpath, char ***names, struct lxc_conta
        struct lxc_container *c;
 
        if (!lxcpath)
-               lxcpath = default_lxc_path();
+               lxcpath = lxc_global_config_value("lxc.lxcpath");
 
-       process_lock();
        dir = opendir(lxcpath);
-       process_unlock();
-
        if (!dir) {
                SYSERROR("opendir on lxcpath");
                return -1;
@@ -3247,9 +4190,7 @@ int list_defined_containers(const char *lxcpath, char ***names, struct lxc_conta
                nfound++;
        }
 
-       process_lock();
        closedir(dir);
-       process_unlock();
        return nfound;
 
 free_bad:
@@ -3263,9 +4204,7 @@ free_bad:
                        lxc_container_put((*cret)[i]);
                free(*cret);
        }
-       process_lock();
        closedir(dir);
-       process_unlock();
        return -1;
 }
 
@@ -3280,7 +4219,7 @@ int list_active_containers(const char *lxcpath, char ***nret,
        struct lxc_container *c;
 
        if (!lxcpath)
-               lxcpath = default_lxc_path();
+               lxcpath = lxc_global_config_value("lxc.lxcpath");
        lxcpath_len = strlen(lxcpath);
 
        if (cret)
@@ -3288,9 +4227,7 @@ int list_active_containers(const char *lxcpath, char ***nret,
        if (nret)
                *nret = NULL;
 
-       process_lock();
        FILE *f = fopen("/proc/net/unix", "r");
-       process_unlock();
        if (!f)
                return -1;
 
@@ -3372,9 +4309,7 @@ out:
        if (line)
                free(line);
 
-       process_lock();
        fclose(f);
-       process_unlock();
        return ret;
 }