Unverified Commit cb47a5af by Stéphane Graber Committed by GitHub

Merge pull request #3320 from brauner/2020-03-22/fixes

overlay: rewrite
parents 7d2b8a6f 27d79d0a
...@@ -2795,8 +2795,8 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid) ...@@ -2795,8 +2795,8 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
/* Return the host uid/gid to which the container root is mapped in val. /* Return the host uid/gid to which the container root is mapped in val.
* Return true if id was found, false otherwise. * Return true if id was found, false otherwise.
*/ */
bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype, static bool get_mapped_rootid(const struct lxc_conf *conf, enum idtype idtype,
unsigned long *val) unsigned long *val)
{ {
unsigned nsid; unsigned nsid;
struct id_map *map; struct id_map *map;
...@@ -2820,7 +2820,7 @@ bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype, ...@@ -2820,7 +2820,7 @@ bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype,
return false; return false;
} }
int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype) int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype)
{ {
struct id_map *map; struct id_map *map;
struct lxc_list *it; struct lxc_list *it;
...@@ -2871,7 +2871,7 @@ int chown_mapped_root_exec_wrapper(void *args) ...@@ -2871,7 +2871,7 @@ int chown_mapped_root_exec_wrapper(void *args)
* root is privileged with respect to hostuid/hostgid X, allowing * root is privileged with respect to hostuid/hostgid X, allowing
* him to do the chown. * him to do the chown.
*/ */
int chown_mapped_root(const char *path, struct lxc_conf *conf) int chown_mapped_root(const char *path, const struct lxc_conf *conf)
{ {
uid_t rootuid, rootgid; uid_t rootuid, rootgid;
unsigned long val; unsigned long val;
......
...@@ -437,9 +437,9 @@ extern int lxc_setup(struct lxc_handler *handler); ...@@ -437,9 +437,9 @@ extern int lxc_setup(struct lxc_handler *handler);
extern int lxc_setup_parent(struct lxc_handler *handler); extern int lxc_setup_parent(struct lxc_handler *handler);
extern int setup_resource_limits(struct lxc_list *limits, pid_t pid); extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype); extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
extern int mapped_hostid(unsigned id, struct lxc_conf *conf, extern int mapped_hostid(unsigned id, const struct lxc_conf *conf,
enum idtype idtype); enum idtype idtype);
extern int chown_mapped_root(const char *path, struct lxc_conf *conf); extern int chown_mapped_root(const char *path, const struct lxc_conf *conf);
extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data, extern int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data,
const char *fn_name); const char *fn_name);
extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *),
......
...@@ -58,19 +58,19 @@ static inline void lxc_list_add_elem(struct lxc_list *list, void *elem) ...@@ -58,19 +58,19 @@ static inline void lxc_list_add_elem(struct lxc_list *list, void *elem)
} }
/* Retrieve first element of list. */ /* Retrieve first element of list. */
static inline void *lxc_list_first_elem(struct lxc_list *list) static inline void *lxc_list_first_elem(const struct lxc_list *list)
{ {
return list->next->elem; return list->next->elem;
} }
/* Retrieve last element of list. */ /* Retrieve last element of list. */
static inline void *lxc_list_last_elem(struct lxc_list *list) static inline void *lxc_list_last_elem(const struct lxc_list *list)
{ {
return list->prev->elem; return list->prev->elem;
} }
/* Determine if list is empty. */ /* Determine if list is empty. */
static inline int lxc_list_empty(struct lxc_list *list) static inline int lxc_list_empty(const struct lxc_list *list)
{ {
return list == list->next; return list == list->next;
} }
......
...@@ -1275,7 +1275,7 @@ static struct lxc_storage *do_storage_create(struct lxc_container *c, ...@@ -1275,7 +1275,7 @@ static struct lxc_storage *do_storage_create(struct lxc_container *c,
if (ret < 0 || (size_t)ret >= len) if (ret < 0 || (size_t)ret >= len)
return NULL; return NULL;
bdev = storage_create(dest, type, c->name, specs); bdev = storage_create(dest, type, c->name, specs, c->lxc_conf);
if (!bdev) { if (!bdev) {
ERROR("Failed to create \"%s\" storage", type); ERROR("Failed to create \"%s\" storage", type);
return NULL; return NULL;
...@@ -1290,8 +1290,7 @@ static struct lxc_storage *do_storage_create(struct lxc_container *c, ...@@ -1290,8 +1290,7 @@ static struct lxc_storage *do_storage_create(struct lxc_container *c,
/* If we are not root, chown the rootfs dir to root in the target user /* If we are not root, chown the rootfs dir to root in the target user
* namespace. * namespace.
*/ */
ret = geteuid(); if (am_guest_unpriv() || !lxc_list_empty(&c->lxc_conf->id_map)) {
if (ret != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) {
ret = chown_mapped_root(bdev->dest, c->lxc_conf); ret = chown_mapped_root(bdev->dest, c->lxc_conf);
if (ret < 0) { if (ret < 0) {
ERROR("Error chowning \"%s\" to container root", bdev->dest); ERROR("Error chowning \"%s\" to container root", bdev->dest);
......
...@@ -906,7 +906,7 @@ int btrfs_destroy(struct lxc_storage *orig) ...@@ -906,7 +906,7 @@ int btrfs_destroy(struct lxc_storage *orig)
} }
int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n, int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
int ret; int ret;
size_t len; size_t len;
......
...@@ -366,8 +366,8 @@ extern int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -366,8 +366,8 @@ extern int btrfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldname, const char *cname, const char *oldname, const char *cname,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int btrfs_create(struct lxc_storage *bdev, const char *dest, extern int btrfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
const char *n, struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int btrfs_destroy(struct lxc_storage *orig); extern int btrfs_destroy(struct lxc_storage *orig);
extern bool btrfs_detect(const char *path); extern bool btrfs_detect(const char *path);
extern int btrfs_mount(struct lxc_storage *bdev); extern int btrfs_mount(struct lxc_storage *bdev);
......
...@@ -58,7 +58,7 @@ int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -58,7 +58,7 @@ int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
} }
int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, int dir_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
int ret; int ret;
const char *src; const char *src;
......
...@@ -17,7 +17,7 @@ extern int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -17,7 +17,7 @@ extern int dir_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int dir_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int dir_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int dir_destroy(struct lxc_storage *orig); extern int dir_destroy(struct lxc_storage *orig);
extern bool dir_detect(const char *path); extern bool dir_detect(const char *path);
extern int dir_mount(struct lxc_storage *bdev); extern int dir_mount(struct lxc_storage *bdev);
......
...@@ -114,7 +114,7 @@ int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -114,7 +114,7 @@ int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
} }
int loop_create(struct lxc_storage *bdev, const char *dest, const char *n, int loop_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
__do_free char *srcdev = NULL; __do_free char *srcdev = NULL;
const char *fstype; const char *fstype;
......
...@@ -17,7 +17,7 @@ extern int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -17,7 +17,7 @@ extern int loop_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int loop_create(struct lxc_storage *bdev, const char *dest, extern int loop_create(struct lxc_storage *bdev, const char *dest,
const char *n, struct bdev_specs *specs); const char *n, struct bdev_specs *specs, const struct lxc_conf *conf);
extern int loop_destroy(struct lxc_storage *orig); extern int loop_destroy(struct lxc_storage *orig);
extern bool loop_detect(const char *path); extern bool loop_detect(const char *path);
extern int loop_mount(struct lxc_storage *bdev); extern int loop_mount(struct lxc_storage *bdev);
......
...@@ -609,7 +609,7 @@ int lvm_destroy(struct lxc_storage *orig) ...@@ -609,7 +609,7 @@ int lvm_destroy(struct lxc_storage *orig)
} }
int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
const char *vg, *thinpool, *fstype, *lv = n; const char *vg, *thinpool, *fstype, *lv = n;
uint64_t sz; uint64_t sz;
......
...@@ -24,7 +24,7 @@ extern int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -24,7 +24,7 @@ extern int lvm_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int lvm_destroy(struct lxc_storage *orig); extern int lvm_destroy(struct lxc_storage *orig);
extern int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int lvm_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig, extern bool lvm_create_clone(struct lxc_conf *conf, struct lxc_storage *orig,
struct lxc_storage *new, uint64_t newsize); struct lxc_storage *new, uint64_t newsize);
extern bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig, extern bool lvm_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
......
...@@ -91,7 +91,7 @@ int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -91,7 +91,7 @@ int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
} }
int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
return -ENOSYS; return -ENOSYS;
} }
......
...@@ -17,7 +17,7 @@ extern int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -17,7 +17,7 @@ extern int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int nbd_destroy(struct lxc_storage *orig); extern int nbd_destroy(struct lxc_storage *orig);
extern bool nbd_detect(const char *path); extern bool nbd_detect(const char *path);
extern int nbd_mount(struct lxc_storage *bdev); extern int nbd_mount(struct lxc_storage *bdev);
......
...@@ -57,234 +57,133 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char ...@@ -57,234 +57,133 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return -1; return -1;
} }
if (am_guest_unpriv()) { if (am_guest_unpriv() || !lxc_list_empty(&conf->id_map)) {
ret = chown_mapped_root(new->dest, conf); ret = chown_mapped_root(new->dest, conf);
if (ret < 0) if (ret < 0)
WARN("Failed to update ownership of %s", new->dest); WARN("Failed to update ownership of %s", new->dest);
} }
if (strcmp(orig->type, "dir") == 0) { if (strcmp(orig->type, "dir") == 0) {
char *delta, *lastslash; __do_free char *delta = NULL, *work = NULL;
char *work; int len;
int len, lastslashidx;
/* If we have "/var/lib/lxc/c2/rootfs" then delta will be delta = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL);
* "/var/lib/lxc/c2/delta0".
*/
lastslash = strrchr(new->dest, '/');
if (!lastslash) {
ERROR("Failed to detect \"/\" in string \"%s\"",
new->dest);
return -22;
}
if (strlen(lastslash) < STRLITERALLEN("/rootfs")) {
ERROR("Failed to detect \"/rootfs\" in string \"%s\"",
new->dest);
return -22;
}
lastslash++;
lastslashidx = lastslash - new->dest;
delta = malloc(lastslashidx + 7);
if (!delta) {
ERROR("Failed to allocate memory");
return -1;
}
memcpy(delta, new->dest, lastslashidx + 1);
memcpy(delta + lastslashidx, "delta0", STRLITERALLEN("delta0"));
delta[lastslashidx + STRLITERALLEN("delta0")] = '\0';
ret = mkdir(delta, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", delta);
free(delta);
return -1;
}
if (am_guest_unpriv()) { ret = mkdir_p(delta, 0755);
ret = chown_mapped_root(delta, conf); if (ret < 0 && errno != EEXIST)
if (ret < 0) return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", delta);
WARN("Failed to update ownership of %s", delta);
}
/* Make workdir for overlayfs.v22 or higher: /*
* Make workdir for overlayfs.v22 or higher:
* The workdir will be * The workdir will be
* /var/lib/lxc/c2/olwork * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH
* and is used to prepare files before they are atomically * and is used to prepare files before they are atomically
* switched to the overlay destination. Workdirs need to be on * switched to the overlay destination. Workdirs need to be on
* the same filesystem as the upperdir so it's OK for it to be * the same filesystem as the upperdir so it's OK for it to be
* empty. * empty.
*/ */
work = malloc(lastslashidx + 7); work = must_make_path(lxcpath, cname, LXC_OVERLAY_WORK_PATH, NULL);
if (!work) {
ERROR("Failed to allocate memory");
free(delta);
return -1;
}
memcpy(work, new->dest, lastslashidx + 1); ret = mkdir_p(work, 0755);
memcpy(work + lastslashidx, "olwork", STRLITERALLEN("olwork")); if (ret < 0 && errno != EEXIST)
work[lastslashidx + STRLITERALLEN("olwork")] = '\0'; return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", work);
ret = mkdir(work, 0755); if (am_guest_unpriv() || !lxc_list_empty(&conf->id_map)) {
if (ret < 0) { __do_free char *lxc_overlay_delta_dir = NULL,
SYSERROR("Failed to create directory \"%s\"", work); *lxc_overlay_private_dir = NULL;
free(delta);
free(work); lxc_overlay_private_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_PRIVATE_DIR, NULL);
return -1; ret = chown_mapped_root(lxc_overlay_private_dir, conf);
} if (ret < 0)
WARN("Failed to update ownership of %s", lxc_overlay_private_dir);
lxc_overlay_delta_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL);
ret = chown_mapped_root(lxc_overlay_delta_dir, conf);
if (ret < 0)
WARN("Failed to update ownership of %s", lxc_overlay_delta_dir);
if (am_guest_unpriv()) {
ret = chown_mapped_root(work, conf); ret = chown_mapped_root(work, conf);
if (ret < 0) if (ret < 0)
WARN("Failed to update ownership of %s", work); WARN("Failed to update ownership of %s", work);
} }
free(work);
/* strlen("overlay:") = 8
* +
* strlen(delta)
* +
* :
* +
* strlen(src)
* +
* \0
*/
src = lxc_storage_get_path(orig->src, orig->type); src = lxc_storage_get_path(orig->src, orig->type);
len = 8 + strlen(delta) + 1 + strlen(src) + 1; len = STRLITERALLEN("overlay") + STRLITERALLEN(":") +
strlen(src) + STRLITERALLEN(":") + strlen(delta) + 1;
new->src = malloc(len); new->src = malloc(len);
if (!new->src) { if (!new->src)
ERROR("Failed to allocate memory"); return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory");
free(delta);
return -ENOMEM;
}
ret = snprintf(new->src, len, "overlay:%s:%s", src, delta); ret = snprintf(new->src, len, "overlay:%s:%s", src, delta);
free(delta); if (ret < 0 || (size_t)ret >= len)
if (ret < 0 || (size_t)ret >= len) { return log_error_errno(-EIO, EIO, "Failed to create string");
ERROR("Failed to create string");
return -1;
}
} else if (!strcmp(orig->type, "overlayfs") || } else if (!strcmp(orig->type, "overlayfs") ||
!strcmp(orig->type, "overlay")) { !strcmp(orig->type, "overlay")) {
char *clean_old_path, *clean_new_path; __do_free char *clean_old_path = NULL, *clean_new_path = NULL,
char *lastslash, *ndelta, *nsrc, *odelta, *osrc, *s1, *s2, *s3, *ndelta = NULL, *osrc = NULL, *work = NULL;
*work; char *nsrc, *odelta, *s1, *s2, *s3;
int lastslashidx;
size_t len, name_len; size_t len, name_len;
osrc = strdup(orig->src); osrc = strdup(orig->src);
if (!osrc) { if (!osrc)
ERROR("Failed to duplicate string \"%s\"", orig->src); return log_error_errno(-22, ENOMEM, "Failed to duplicate string \"%s\"", orig->src);
return -22;
}
nsrc = osrc; nsrc = osrc;
if (strncmp(osrc, "overlay:", 8) == 0) if (strncmp(osrc, "overlay:", STRLITERALLEN("overlay:")) == 0)
nsrc += 8; nsrc += STRLITERALLEN("overlay:");
else if (strncmp(osrc, "overlayfs:", 10) == 0) else if (strncmp(osrc, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0)
nsrc += 10; nsrc += STRLITERALLEN("overlayfs:");
odelta = strchr(nsrc, ':'); odelta = strchr(nsrc, ':');
if (!odelta) { if (!odelta)
ERROR("Failed to find \":\" in \"%s\"", nsrc); return log_error_errno(-22, ENOENT, "Failed to find \":\" in \"%s\"", nsrc);
free(osrc);
return -22;
}
*odelta = '\0'; *odelta = '\0';
odelta++; odelta++;
ndelta = must_make_path(lxcpath, cname, "delta0", NULL); ndelta = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL);
ret = mkdir(ndelta, 0755);
if (ret < 0 && errno != EEXIST) {
SYSERROR("Failed to create directory \"%s\"", ndelta);
free(osrc);
free(ndelta);
return -1;
}
if (am_guest_unpriv()) { ret = mkdir_p(ndelta, 0755);
ret = chown_mapped_root(ndelta, conf); if (ret < 0 && errno != EEXIST)
if (ret < 0) return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", ndelta);
WARN("Failed to update ownership of %s",
ndelta);
}
/* Make workdir for overlayfs.v22 or higher (See the comment /* Make workdir for overlayfs.v22 or higher (See the comment
* further up.). * further up.).
*/ */
lastslash = strrchr(ndelta, '/'); work = must_make_path(lxcpath, cname, LXC_OVERLAY_WORK_PATH, NULL);
if (!lastslash) { ret = mkdir_p(work, 0755);
ERROR("Failed to detect \"/\" in \"%s\"", ndelta); if (ret < 0 && errno != EEXIST)
free(osrc); return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", ndelta);
free(ndelta);
return -1;
}
lastslash++;
lastslashidx = lastslash - ndelta;
work = malloc(lastslashidx + 7);
if (!work) {
free(osrc);
free(ndelta);
ERROR("Failed to allocate memory");
return -1;
}
memcpy(work, ndelta, lastslashidx + 1); if (am_guest_unpriv() || !lxc_list_empty(&conf->id_map)) {
memcpy(work + lastslashidx, "olwork", STRLITERALLEN("olwork")); __do_free char *lxc_overlay_delta_dir = NULL,
work[lastslashidx + STRLITERALLEN("olwork")] = '\0'; *lxc_overlay_private_dir = NULL;
ret = mkdir(work, 0755); lxc_overlay_private_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_PRIVATE_DIR, NULL);
if (ret < 0 && errno != EEXIST) { ret = chown_mapped_root(lxc_overlay_private_dir, conf);
SYSERROR("Failed to create directory \"%s\"", ndelta); if (ret < 0)
free(osrc); WARN("Failed to update ownership of %s", lxc_overlay_private_dir);
free(ndelta);
free(work); lxc_overlay_delta_dir = must_make_path(lxcpath, cname, LXC_OVERLAY_DELTA_PATH, NULL);
return -1; ret = chown_mapped_root(lxc_overlay_delta_dir, conf);
} if (ret < 0)
WARN("Failed to update ownership of %s", lxc_overlay_delta_dir);
if (am_guest_unpriv()) {
ret = chown_mapped_root(work, conf); ret = chown_mapped_root(work, conf);
if (ret < 0) if (ret < 0)
WARN("Failed to update ownership of %s", work); WARN("Failed to update ownership of %s", work);
} }
free(work);
/* strlen("overlay:") = 8 len = STRLITERALLEN("overlay") + STRLITERALLEN(":") + strlen(nsrc) + STRLITERALLEN(":") + strlen(ndelta) + 1;
* +
* strlen(delta)
* +
* :
* +
* strlen(src)
* +
* \0
*/
len = 8 + strlen(ndelta) + 1 + strlen(nsrc) + 1;
new->src = malloc(len); new->src = malloc(len);
if (!new->src) { if (!new->src)
free(osrc); return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory");
free(ndelta);
ERROR("Failed to allocate memory");
return -ENOMEM;
}
ret = snprintf(new->src, len, "overlay:%s:%s", nsrc, ndelta); ret = snprintf(new->src, len, "overlay:%s:%s", nsrc, ndelta);
if (ret < 0 || (size_t)ret >= len) { if (ret < 0 || (size_t)ret >= len)
ERROR("Failed to create string"); return log_error_errno(-EIO, EIO, "Failed to create string");
free(osrc);
free(ndelta);
return -1;
}
ret = ovl_do_rsync(odelta, ndelta, conf); ret = ovl_do_rsync(odelta, ndelta, conf);
free(osrc);
free(ndelta);
if (ret < 0) if (ret < 0)
return -1; return -1;
...@@ -295,29 +194,19 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char ...@@ -295,29 +194,19 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
*/ */
clean_old_path = lxc_deslashify(oldpath); clean_old_path = lxc_deslashify(oldpath);
if (!clean_old_path) if (!clean_old_path)
return -1; return log_error_errno(-ENOMEM, ENOMEM, "Failed to create clean path for \"%s\"", oldpath);
clean_new_path = lxc_deslashify(lxcpath); clean_new_path = lxc_deslashify(lxcpath);
if (!clean_new_path) { if (!clean_new_path)
free(clean_old_path); return log_error_errno(-ENOMEM, ENOMEM, "Failed to create clean path for \"%s\"", lxcpath);
return -1;
}
s1 = strrchr(clean_old_path, '/'); s1 = strrchr(clean_old_path, '/');
if (!s1) { if (!s1)
ERROR("Failed to detect \"/\" in string \"%s\"", clean_old_path); return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/\" in string \"%s\"", clean_old_path);
free(clean_old_path);
free(clean_new_path);
return -1;
}
s2 = strrchr(clean_new_path, '/'); s2 = strrchr(clean_new_path, '/');
if (!s2) { if (!s2)
ERROR("Failed to detect \"/\" in string \"%s\"", clean_new_path); return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/\" in string \"%s\"", clean_new_path);
free(clean_old_path);
free(clean_new_path);
return -1;
}
if (!strncmp(s1, "/snaps", STRLITERALLEN("/snaps"))) { if (!strncmp(s1, "/snaps", STRLITERALLEN("/snaps"))) {
s1 = clean_new_path; s1 = clean_new_path;
...@@ -328,8 +217,6 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char ...@@ -328,8 +217,6 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
s2 = clean_new_path; s2 = clean_new_path;
s3 = (char *)oldname; s3 = (char *)oldname;
} else { } else {
free(clean_old_path);
free(clean_new_path);
return 0; return 0;
} }
...@@ -338,34 +225,23 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char ...@@ -338,34 +225,23 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
char *tmp; char *tmp;
tmp = (char *)(s2 + len + 1); tmp = (char *)(s2 + len + 1);
if (*tmp == '\0') { if (*tmp == '\0')
free(clean_old_path);
free(clean_new_path);
return 0; return 0;
}
name_len = strlen(s3); name_len = strlen(s3);
if (strncmp(s3, tmp, name_len)) { if (strncmp(s3, tmp, name_len))
free(clean_old_path);
free(clean_new_path);
return 0; return 0;
}
free(clean_old_path);
free(clean_new_path);
return LXC_CLONE_SNAPSHOT; return LXC_CLONE_SNAPSHOT;
} }
free(clean_old_path);
free(clean_new_path);
return 0; return 0;
} else { } else {
ERROR("overlay clone of %s container is not yet supported", /*
orig->type); * Note, supporting this will require ovl_mount supporting
/* Note, supporting this will require ovl_mount supporting
* mounting of the underlay. No big deal, just needs to be done. * mounting of the underlay. No big deal, just needs to be done.
*/ */
return -1; return log_error_errno(-EINVAL, EINVAL, "overlay clone of %s container is not yet supported", orig->type);
} }
return 0; return 0;
...@@ -373,65 +249,60 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char ...@@ -373,65 +249,60 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
/* To say "lxc-create -t ubuntu -n o1 -B overlay" means you want /* To say "lxc-create -t ubuntu -n o1 -B overlay" means you want
* "<lxcpath>/<lxcname>/rootfs" to have the created container, while all changes * "<lxcpath>/<lxcname>/rootfs" to have the created container, while all changes
* after starting the container are written to "<lxcpath>/<lxcname>/delta0". * after starting the container are written to "<lxcpath>/<lxcname>/LXC_OVERLAY_DELTA_PATH".
*/ */
int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
char *delta; __do_free char *delta = NULL, *tmp = NULL;
int ret; int ret;
size_t len, newlen; size_t len;
len = strlen(dest); len = strlen(dest);
if (len < 8 || strcmp(dest + len - 7, "/rootfs")) { if (len < 8 || strcmp(dest + len - STRLITERALLEN("/rootfs"), "/rootfs"))
ERROR("Failed to detect \"/rootfs\" in \"%s\"", dest); return log_error_errno(-ENOENT, ENOENT, "Failed to detect \"/rootfs\" in \"%s\"", dest);
return -1;
}
bdev->dest = strdup(dest); bdev->dest = strdup(dest);
if (!bdev->dest) { if (!bdev->dest)
ERROR("Failed to duplicate string \"%s\"", dest); return log_error_errno(-ENOMEM, ENOMEM, "Failed to duplicate string \"%s\"", dest);
return -1;
}
delta = strdup(dest); tmp = strndup(dest, len - STRLITERALLEN("/rootfs"));
if (!delta) { if (!tmp)
ERROR("Failed to allocate memory"); return log_error_errno(-ENOMEM, ENOMEM, "Failed to duplicate string \"%s\"", dest);
return -1;
} delta = must_make_path(tmp, LXC_OVERLAY_DELTA_PATH, NULL);
memcpy(delta + len - 6, "delta0", STRLITERALLEN("delta0"));
ret = mkdir_p(delta, 0755); ret = mkdir_p(delta, 0755);
if (ret < 0) { if (ret < 0 && errno != EEXIST)
SYSERROR("Failed to create directory \"%s\"", delta); return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", delta);
free(delta);
return -1; if (am_guest_unpriv() || !lxc_list_empty(&conf->id_map)) {
__do_free char *lxc_overlay_private_dir = NULL;
lxc_overlay_private_dir = must_make_path(tmp, LXC_OVERLAY_PRIVATE_DIR, NULL);
ret = chown_mapped_root(lxc_overlay_private_dir, conf);
if (ret < 0)
WARN("Failed to update ownership of %s", lxc_overlay_private_dir);
ret = chown_mapped_root(delta, conf);
if (ret < 0)
WARN("Failed to update ownership of %s", delta);
} }
/* overlay:lower:upper */ /* overlay:lower:upper */
newlen = (2 * len) + strlen("overlay:") + 2; len = STRLITERALLEN("overlay") + STRLITERALLEN(":") + len + STRLITERALLEN(":") + strlen(delta) + 1;
bdev->src = malloc(newlen); bdev->src = malloc(len);
if (!bdev->src) { if (!bdev->src)
ERROR("Failed to allocate memory"); return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory");
free(delta);
return -1;
}
ret = snprintf(bdev->src, newlen, "overlay:%s:%s", dest, delta); ret = snprintf(bdev->src, len, "overlay:%s:%s", dest, delta);
if (ret < 0 || (size_t)ret >= newlen) { if (ret < 0 || (size_t)ret >= len)
ERROR("Failed to create string"); return log_error_errno(-EIO, EIO, "Failed to create rootfs path");
free(delta);
return -1;
}
ret = mkdir_p(bdev->dest, 0755); ret = mkdir_p(bdev->dest, 0755);
if (ret < 0) { if (ret < 0 && errno != EEXIST)
SYSERROR("Failed to create directory \"%s\"", bdev->dest); return log_error_errno(-errno, errno, "Failed to create directory \"%s\"", bdev->dest);
free(delta);
return -1;
}
free(delta);
return 0; return 0;
} }
...@@ -475,7 +346,6 @@ int ovl_mount(struct lxc_storage *bdev) ...@@ -475,7 +346,6 @@ int ovl_mount(struct lxc_storage *bdev)
*options_work = NULL; *options_work = NULL;
char *tmp, *dup, *lower, *upper; char *tmp, *dup, *lower, *upper;
char *work, *lastslash; char *work, *lastslash;
int lastslashidx;
size_t len, len2; size_t len, len2;
unsigned long mntflags; unsigned long mntflags;
char *mntdata; char *mntdata;
...@@ -494,17 +364,15 @@ int ovl_mount(struct lxc_storage *bdev) ...@@ -494,17 +364,15 @@ int ovl_mount(struct lxc_storage *bdev)
* mount -t overlay * -o upperdir=${upper},lowerdir=${lower} lower dest * mount -t overlay * -o upperdir=${upper},lowerdir=${lower} lower dest
*/ */
dup = strdup(bdev->src); dup = strdup(bdev->src);
if (!dup) { if (!dup)
ERROR("Failed to allocate memory"); return log_error_errno(-ENOMEM, ENOMEM, "Failed to allocate memory");
return -1;
}
upper = dup; upper = dup;
lower = dup; lower = dup;
if (strncmp(dup, "overlay:", 8) == 0) if (strncmp(dup, "overlay:", STRLITERALLEN("overlay:")) == 0)
lower += 8; lower += STRLITERALLEN("overlay:");
else if (strncmp(dup, "overlayfs:", 10) == 0) else if (strncmp(dup, "overlayfs:", STRLITERALLEN("overlayfs:")) == 0)
lower += 10; lower += STRLITERALLEN("overlayfs:");
if (upper != lower) if (upper != lower)
upper = lower; upper = lower;
...@@ -532,9 +400,9 @@ int ovl_mount(struct lxc_storage *bdev) ...@@ -532,9 +400,9 @@ int ovl_mount(struct lxc_storage *bdev)
/* overlayfs.v22 or higher needs workdir option: /* overlayfs.v22 or higher needs workdir option:
* if upper is * if upper is
* /var/lib/lxc/c2/delta0 * /var/lib/lxc/c2/LXC_OVERLAY_DELTA_PATH
* then workdir is * then workdir is
* /var/lib/lxc/c2/olwork * /var/lib/lxc/c2/LXC_OVERLAY_WORK_PATH
*/ */
lastslash = strrchr(upper, '/'); lastslash = strrchr(upper, '/');
if (!lastslash) { if (!lastslash) {
...@@ -543,19 +411,9 @@ int ovl_mount(struct lxc_storage *bdev) ...@@ -543,19 +411,9 @@ int ovl_mount(struct lxc_storage *bdev)
return -22; return -22;
} }
lastslash++; upper[lastslash - upper] = '\0';
lastslashidx = lastslash - upper; work = must_make_path(upper, LXC_OVERLAY_WORK_DIR, NULL);
upper[lastslash - upper] = '/';
work = malloc(lastslashidx + 7);
if (!work) {
ERROR("Failed to allocate memory");
free(dup);
return -22;
}
memcpy(work, upper, lastslashidx + 1);
memcpy(work + lastslashidx, "olwork", STRLITERALLEN("olwork"));
work[lastslashidx + STRLITERALLEN("olwork")] = '\0';
ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata); ret = parse_mntopts(bdev->mntopts, &mntflags, &mntdata);
if (ret < 0) { if (ret < 0) {
......
...@@ -25,7 +25,7 @@ extern int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -25,7 +25,7 @@ extern int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int ovl_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int ovl_destroy(struct lxc_storage *orig); extern int ovl_destroy(struct lxc_storage *orig);
extern bool ovl_detect(const char *path); extern bool ovl_detect(const char *path);
extern int ovl_mount(struct lxc_storage *bdev); extern int ovl_mount(struct lxc_storage *bdev);
......
...@@ -77,7 +77,7 @@ int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -77,7 +77,7 @@ int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
} }
int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
const char *rbdpool, *fstype; const char *rbdpool, *fstype;
uint64_t size; uint64_t size;
......
...@@ -17,7 +17,7 @@ extern int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -17,7 +17,7 @@ extern int rbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int rbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int rbd_destroy(struct lxc_storage *orig); extern int rbd_destroy(struct lxc_storage *orig);
extern bool rbd_detect(const char *path); extern bool rbd_detect(const char *path);
extern int rbd_mount(struct lxc_storage *bdev); extern int rbd_mount(struct lxc_storage *bdev);
......
...@@ -259,7 +259,8 @@ struct lxc_storage *storage_get(const char *type) ...@@ -259,7 +259,8 @@ struct lxc_storage *storage_get(const char *type)
static struct lxc_storage *do_storage_create(const char *dest, const char *type, static struct lxc_storage *do_storage_create(const char *dest, const char *type,
const char *cname, const char *cname,
struct bdev_specs *specs) struct bdev_specs *specs,
const struct lxc_conf *conf)
{ {
int ret; int ret;
struct lxc_storage *bdev; struct lxc_storage *bdev;
...@@ -271,7 +272,7 @@ static struct lxc_storage *do_storage_create(const char *dest, const char *type, ...@@ -271,7 +272,7 @@ static struct lxc_storage *do_storage_create(const char *dest, const char *type,
if (!bdev) if (!bdev)
return NULL; return NULL;
ret = bdev->ops->create(bdev, dest, cname, specs); ret = bdev->ops->create(bdev, dest, cname, specs, conf);
if (ret < 0) { if (ret < 0) {
storage_put(bdev); storage_put(bdev);
return NULL; return NULL;
...@@ -521,14 +522,15 @@ on_error_put_orig: ...@@ -521,14 +522,15 @@ on_error_put_orig:
* @specs: details about the backing store to create, like fstype * @specs: details about the backing store to create, like fstype
*/ */
struct lxc_storage *storage_create(const char *dest, const char *type, struct lxc_storage *storage_create(const char *dest, const char *type,
const char *cname, struct bdev_specs *specs) const char *cname, struct bdev_specs *specs,
const struct lxc_conf *conf)
{ {
int ret; int ret;
struct lxc_storage *bdev; struct lxc_storage *bdev;
char *best_options[] = {"btrfs", "zfs", "lvm", "dir", "rbd", NULL}; char *best_options[] = {"btrfs", "zfs", "lvm", "dir", "rbd", NULL};
if (!type) if (!type)
return do_storage_create(dest, "dir", cname, specs); return do_storage_create(dest, "dir", cname, specs, conf);
ret = strcmp(type, "best"); ret = strcmp(type, "best");
if (ret == 0) { if (ret == 0) {
...@@ -537,7 +539,7 @@ struct lxc_storage *storage_create(const char *dest, const char *type, ...@@ -537,7 +539,7 @@ struct lxc_storage *storage_create(const char *dest, const char *type,
* opinionated preferences. * opinionated preferences.
*/ */
for (i = 0; best_options[i]; i++) { for (i = 0; best_options[i]; i++) {
bdev = do_storage_create(dest, best_options[i], cname, specs); bdev = do_storage_create(dest, best_options[i], cname, specs, conf);
if (bdev) if (bdev)
return bdev; return bdev;
} }
...@@ -552,13 +554,13 @@ struct lxc_storage *storage_create(const char *dest, const char *type, ...@@ -552,13 +554,13 @@ struct lxc_storage *storage_create(const char *dest, const char *type,
dup = must_copy_string(type); dup = must_copy_string(type);
lxc_iterate_parts(token, dup, ",") { lxc_iterate_parts(token, dup, ",") {
bdev = do_storage_create(dest, token, cname, specs); bdev = do_storage_create(dest, token, cname, specs, conf);
if (bdev) if (bdev)
return bdev; return bdev;
} }
} }
return do_storage_create(dest, type, cname, specs); return do_storage_create(dest, type, cname, specs, conf);
} }
bool storage_destroy(struct lxc_conf *conf) bool storage_destroy(struct lxc_conf *conf)
......
...@@ -54,7 +54,7 @@ struct lxc_storage_ops { ...@@ -54,7 +54,7 @@ struct lxc_storage_ops {
int (*umount)(struct lxc_storage *bdev); int (*umount)(struct lxc_storage *bdev);
int (*destroy)(struct lxc_storage *bdev); int (*destroy)(struct lxc_storage *bdev);
int (*create)(struct lxc_storage *bdev, const char *dest, const char *n, int (*create)(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
/* given original mount, rename the paths for cloned container */ /* given original mount, rename the paths for cloned container */
int (*clone_paths)(struct lxc_storage *orig, struct lxc_storage *new, int (*clone_paths)(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldname, const char *cname, const char *oldname, const char *cname,
...@@ -111,7 +111,8 @@ extern struct lxc_storage *storage_copy(struct lxc_container *c, ...@@ -111,7 +111,8 @@ extern struct lxc_storage *storage_copy(struct lxc_container *c,
bool *needs_rdep); bool *needs_rdep);
extern struct lxc_storage *storage_create(const char *dest, const char *type, extern struct lxc_storage *storage_create(const char *dest, const char *type,
const char *cname, const char *cname,
struct bdev_specs *specs); struct bdev_specs *specs,
const struct lxc_conf *conf);
extern void storage_put(struct lxc_storage *bdev); extern void storage_put(struct lxc_storage *bdev);
extern bool storage_destroy(struct lxc_conf *conf); extern bool storage_destroy(struct lxc_conf *conf);
extern bool rootfs_is_blockdev(struct lxc_conf *conf); extern bool rootfs_is_blockdev(struct lxc_conf *conf);
......
...@@ -5,9 +5,19 @@ ...@@ -5,9 +5,19 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include <stdio.h> #include <stdio.h>
#include "conf.h" #include "conf.h"
#include "macro.h"
#define LXC_OVERLAY_PRIVATE_DIR "overlay"
#define LXC_OVERLAY_DELTA_DIR "delta"
#define LXC_OVERLAY_WORK_DIR "work"
#define LXC_OVERLAY_DELTA_PATH LXC_OVERLAY_PRIVATE_DIR "/" LXC_OVERLAY_DELTA_DIR
#define LXC_OVERLAY_WORK_PATH LXC_OVERLAY_PRIVATE_DIR "/" LXC_OVERLAY_WORK_DIR
#define LXC_OVERLAY_PATH_LEN \
(STRLITERALLEN(LXC_OVERLAY_PRIVATE_DIR) + STRLITERALLEN("/") + 256 + 1)
struct lxc_storage; struct lxc_storage;
struct lxc_conf; struct lxc_conf;
......
...@@ -684,7 +684,7 @@ int zfs_destroy(struct lxc_storage *orig) ...@@ -684,7 +684,7 @@ int zfs_destroy(struct lxc_storage *orig)
} }
int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs) struct bdev_specs *specs, const struct lxc_conf *conf)
{ {
const char *zfsroot; const char *zfsroot;
int ret; int ret;
......
...@@ -18,7 +18,7 @@ extern int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, ...@@ -18,7 +18,7 @@ extern int zfs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
const char *oldpath, const char *lxcpath, int snap, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf); uint64_t newsize, struct lxc_conf *conf);
extern int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n, extern int zfs_create(struct lxc_storage *bdev, const char *dest, const char *n,
struct bdev_specs *specs); struct bdev_specs *specs, const struct lxc_conf *conf);
extern int zfs_destroy(struct lxc_storage *orig); extern int zfs_destroy(struct lxc_storage *orig);
extern bool zfs_detect(const char *path); extern bool zfs_detect(const char *path);
extern int zfs_mount(struct lxc_storage *bdev); extern int zfs_mount(struct lxc_storage *bdev);
......
...@@ -766,15 +766,17 @@ static char *mount_tmpfs(const char *oldname, const char *newname, ...@@ -766,15 +766,17 @@ static char *mount_tmpfs(const char *oldname, const char *newname,
fd = -1; fd = -1;
ret = fprintf(fp, "#! /bin/sh\n" ret = fprintf(fp, "#! /bin/sh\n"
"mount -n -t tmpfs -o mode=0755 none %s/%s\n", "mount -n -t tmpfs -o mode=0755 none %s/%s/overlay\n",
path, newname); path, newname);
if (ret < 0) if (ret < 0)
goto err_close; goto err_close;
if (!arg->keepname) { if (!arg->keepname) {
ret = fprintf(fp, "mkdir -p %s/%s/delta0/etc\n" ret = fprintf(fp,
"echo %s > %s/%s/delta0/etc/hostname\n", "mkdir -p %s/%s/%s/etc\n"
path, newname, newname, path, newname); "echo %s > %s/%s/%s/etc/hostname\n",
path, newname, LXC_OVERLAY_DELTA_PATH, newname,
path, newname, LXC_OVERLAY_DELTA_PATH);
if (ret < 0) if (ret < 0)
goto err_close; goto err_close;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment