*/
#define _GNU_SOURCE
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
+#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
+#include <inttypes.h> /* Required for PRIu64 to work. */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "log.h"
#include "lxclvm.h"
-#include "lxcrsync.h"
#include "storage_utils.h"
#include "utils.h"
+/* major()/minor() */
#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
+# include <sys/mkdev.h>
#endif
lxc_log_define(lxclvm, lxc);
-struct lvcreate_args {
- const char *size;
- const char *vg;
- const char *lv;
- const char *thinpool;
+extern char *dir_new_path(char *src, const char *oldname, const char *name,
+ const char *oldpath, const char *lxcpath);
- /* snapshot specific arguments */
- const char *source_lv;
-};
-
-static int lvm_destroy_exec_wrapper(void *data)
-{
- struct lvcreate_args *args = data;
-
- (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
- execlp("lvremove", "lvremove", "-f", args->lv, (char *)NULL);
-
- return -1;
-}
-
-static int lvm_create_exec_wrapper(void *data)
-{
- struct lvcreate_args *args = data;
-
- (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
- if (args->thinpool)
- execlp("lvcreate", "lvcreate", "--thinpool", args->thinpool,
- "-V", args->size, args->vg, "-n", args->lv,
- (char *)NULL);
- else
- execlp("lvcreate", "lvcreate", "-L", args->size, args->vg, "-n",
- args->lv, (char *)NULL);
-
- return -1;
-}
-
-static int lvm_snapshot_exec_wrapper(void *data)
-{
- struct lvcreate_args *args = data;
-
- (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
- if (args->thinpool)
- execlp("lvcreate", "lvcreate", "-s", "-n", args->lv,
- args->source_lv, (char *)NULL);
- else
- execlp("lvcreate", "lvcreate", "-s", "-L", args->size, "-n",
- args->lv, args->source_lv, (char *)NULL);
-
- return -1;
-}
-
-/* The path must be "/dev/<vg>/<lv>". The volume group <vg> must be an existing
- * volume group, and the logical volume <lv> must not yet exist.
- * This function will attempt to create "/dev/<vg>/<lv> of size <size>. If
+/* Path must be '/dev/$vg/$lv', $vg must be an existing VG, and $lv must not yet
+ * exist. This function will attempt to create /dev/$vg/$lv of size $size. If
* thinpool is specified, we'll check for it's existence and if it's a valid
- * thin pool, and if so, we'll create the requested logical volume from that
- * thin pool.
+ * thin pool, and if so, we'll create the requested lv from that thin pool.
*/
static int do_lvm_create(const char *path, uint64_t size, const char *thinpool)
{
- int len, ret;
- char *pathdup, *vg, *lv;
- char cmd_output[MAXPATHLEN];
- char sz[24];
- char *tp = NULL;
- struct lvcreate_args cmd_args = {0};
+ int ret, pid, len;
+ char sz[24], *pathdup, *vg, *lv, *tp = NULL;
- ret = snprintf(sz, 24, "%" PRIu64 "b", size);
- if (ret < 0 || ret >= 24) {
- ERROR("Failed to create string: %d", ret);
+ if ((pid = fork()) < 0) {
+ SYSERROR("failed fork");
return -1;
}
+ if (pid > 0)
+ return wait_for_pid(pid);
+
+ // specify bytes to lvcreate
+ ret = snprintf(sz, 24, "%"PRIu64"b", size);
+ if (ret < 0 || ret >= 24)
+ exit(EXIT_FAILURE);
pathdup = strdup(path);
- if (!pathdup) {
- ERROR("Failed to duplicate string \"%s\"", path);
- return -1;
- }
+ if (!pathdup)
+ exit(EXIT_FAILURE);
lv = strrchr(pathdup, '/');
- if (!lv) {
- ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
- free(pathdup);
- return -1;
- }
+ if (!lv)
+ exit(EXIT_FAILURE);
+
*lv = '\0';
lv++;
- TRACE("Parsed logical volume \"%s\"", lv);
vg = strrchr(pathdup, '/');
- if (!vg) {
- ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
- free(pathdup);
- return -1;
- }
+ if (!vg)
+ exit(EXIT_FAILURE);
vg++;
- TRACE("Parsed volume group \"%s\"", vg);
if (thinpool) {
len = strlen(pathdup) + strlen(thinpool) + 2;
tp = alloca(len);
ret = snprintf(tp, len, "%s/%s", pathdup, thinpool);
- if (ret < 0 || ret >= len) {
- ERROR("Failed to create string: %d", ret);
- free(pathdup);
- return -1;
- }
+ if (ret < 0 || ret >= len)
+ exit(EXIT_FAILURE);
ret = lvm_is_thin_pool(tp);
- TRACE("got %d for thin pool at path: %s", ret, tp);
- if (ret < 0) {
- ERROR("Failed to detect whether \"%s\" is a thinpool", tp);
- free(pathdup);
- return -1;
- } else if (!ret) {
- TRACE("Detected that \"%s\" is not a thinpool", tp);
+ INFO("got %d for thin pool at path: %s", ret, tp);
+ if (ret < 0)
+ exit(EXIT_FAILURE);
+
+ if (!ret)
tp = NULL;
- } else {
- TRACE("Detected \"%s\" is a thinpool", tp);
- }
}
- cmd_args.thinpool = tp;
- cmd_args.vg = vg;
- cmd_args.lv = lv;
- cmd_args.size = sz;
- TRACE("Creating new lvm storage volume \"%s\" on volume group \"%s\" "
- "of size \"%s\"", lv, vg, sz);
- ret = run_command(cmd_output, sizeof(cmd_output),
- lvm_create_exec_wrapper, (void *)&cmd_args);
- if (ret < 0) {
- ERROR("Failed to create logical volume \"%s\": %s", lv,
- cmd_output);
- free(pathdup);
- return -1;
- }
- TRACE("Created new lvm storage volume \"%s\" on volume group \"%s\" "
- "of size \"%s\"", lv, vg, sz);
+ (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
+ if (!tp)
+ execlp("lvcreate", "lvcreate", "-L", sz, vg, "-n", lv, (char *)NULL);
+ else
+ execlp("lvcreate", "lvcreate", "--thinpool", tp, "-V", sz, vg, "-n", lv, (char *)NULL);
- free(pathdup);
- return ret;
+ SYSERROR("execlp");
+ exit(EXIT_FAILURE);
}
-/* Look at "/sys/dev/block/maj:min/dm/uuid". If it contains the hardcoded LVM
- * prefix "LVM-" then this is an lvm2 LV.
+
+/*
+ * Look at /sys/dev/block/maj:min/dm/uuid. If it contains the hardcoded LVM
+ * prefix "LVM-", then this is an lvm2 LV
*/
int lvm_detect(const char *path)
{
- int fd;
- ssize_t ret;
- struct stat statbuf;
char devp[MAXPATHLEN], buf[4];
+ FILE *fout;
+ int ret;
+ struct stat statbuf;
if (strncmp(path, "lvm:", 4) == 0)
return 1; // take their word for it
ret = stat(path, &statbuf);
- if (ret < 0)
+ if (ret != 0)
return 0;
-
if (!S_ISBLK(statbuf.st_mode))
return 0;
ret = snprintf(devp, MAXPATHLEN, "/sys/dev/block/%d:%d/dm/uuid",
- major(statbuf.st_rdev), minor(statbuf.st_rdev));
+ major(statbuf.st_rdev), minor(statbuf.st_rdev));
if (ret < 0 || ret >= MAXPATHLEN) {
- ERROR("Failed to create string");
+ ERROR("lvm uuid pathname too long");
return 0;
}
-
- fd = open(devp, O_RDONLY);
- if (fd < 0)
- return 0;
-
- ret = read(fd, buf, sizeof(buf));
- close(fd);
- if (ret != sizeof(buf))
+ fout = fopen(devp, "r");
+ if (!fout)
return 0;
-
- if (strncmp(buf, "LVM-", 4))
+ ret = fread(buf, 1, 4, fout);
+ fclose(fout);
+ if (ret != 4 || strncmp(buf, "LVM-", 4) != 0)
return 0;
-
return 1;
}
int lvm_compare_lv_attr(const char *path, int pos, const char expected)
{
struct lxc_popen_FILE *f;
- int ret, len, status;
- char *cmd;
- char output[12];
- int start=0;
+ int ret, len, status, start=0;
+ char *cmd, output[12];
const char *lvscmd = "lvs --unbuffered --noheadings -o lv_attr %s 2>/dev/null";
len = strlen(lvscmd) + strlen(path) - 1;
return -1;
f = lxc_popen(cmd);
- if (!f) {
+
+ if (f == NULL) {
SYSERROR("popen failed");
return -1;
}
- if (!fgets(output, 12, f->f))
- ret = 1;
+ ret = fgets(output, 12, f->f) == NULL;
status = lxc_pclose(f);
- /* Assume either vg or lvs do not exist, default comparison to false. */
+
if (ret || WEXITSTATUS(status))
+ // Assume either vg or lvs do not exist, default
+ // comparison to false.
return 0;
len = strlen(output);
- while (start < len && output[start] == ' ')
- start++;
+ while(start < len && output[start] == ' ') start++;
if (start + pos < len && output[start + pos] == expected)
return 1;
int lvm_snapshot(const char *orig, const char *path, uint64_t size)
{
- int ret;
- char *pathdup, *lv;
- char sz[24];
- char cmd_output[MAXPATHLEN];
- struct lvcreate_args cmd_args = {0};
+ int ret, pid;
+ char sz[24], *pathdup, *lv;
- ret = snprintf(sz, 24, "%" PRIu64 "b", size);
- if (ret < 0 || ret >= 24) {
- ERROR("Failed to create string");
+ if ((pid = fork()) < 0) {
+ SYSERROR("failed fork");
return -1;
}
+ if (pid > 0)
+ return wait_for_pid(pid);
- pathdup = strdup(path);
- if (!pathdup) {
- ERROR("Failed to duplicate string \"%s\"", path);
- return -1;
- }
+ // specify bytes to lvcreate
+ ret = snprintf(sz, 24, "%"PRIu64"b", size);
+ if (ret < 0 || ret >= 24)
+ exit(EXIT_FAILURE);
+ pathdup = strdup(path);
+ if (!pathdup)
+ exit(EXIT_FAILURE);
lv = strrchr(pathdup, '/');
if (!lv) {
- ERROR("Failed to detect \"/\" in string \"%s\"", pathdup);
free(pathdup);
- return -1;
+ exit(EXIT_FAILURE);
}
*lv = '\0';
lv++;
- TRACE("Parsed logical volume \"%s\"", lv);
- /* Check if the original logical volume is backed by a thinpool, in
- * which case we cannot specify a size that's different from the
- * original size.
- */
+ // check if the original lv is backed by a thin pool, in which case we
+ // cannot specify a size that's different from the original size.
ret = lvm_is_thin_volume(orig);
- if (ret < 0) {
+ if (ret == -1) {
free(pathdup);
return -1;
- } else if (ret) {
- cmd_args.thinpool = orig;
}
- cmd_args.lv = lv;
- cmd_args.source_lv = orig;
- cmd_args.size = sz;
- TRACE("Creating new lvm snapshot \"%s\" of \"%s\" with size \"%s\"", lv,
- orig, sz);
- ret = run_command(cmd_output, sizeof(cmd_output),
- lvm_snapshot_exec_wrapper, (void *)&cmd_args);
- if (ret < 0) {
- ERROR("Failed to create logical volume \"%s\": %s", orig,
- cmd_output);
- free(pathdup);
- return -1;
+ (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
+ if (!ret) {
+ ret = execlp("lvcreate", "lvcreate", "-s", "-L", sz, "-n", lv, orig, (char *)NULL);
+ } else {
+ ret = execlp("lvcreate", "lvcreate", "-s", "-n", lv, orig, (char *)NULL);
}
free(pathdup);
- return 0;
+ exit(EXIT_FAILURE);
}
int lvm_clonepaths(struct bdev *orig, struct bdev *new, const char *oldname,
const char *cname, const char *oldpath, const char *lxcpath, int snap,
uint64_t newsize, struct lxc_conf *conf)
{
+ char fstype[100];
+ uint64_t size = newsize;
int len, ret;
- const char *vg;
+ const char *cmd_args[2];
+ char cmd_output[MAXPATHLEN];
if (!orig->src || !orig->dest)
return -1;
- if (strcmp(orig->type, "lvm") && snap) {
- ERROR("LVM snapshot from \"%s\" storage driver is not supported",
- orig->type);
- return -1;
- }
-
if (strcmp(orig->type, "lvm")) {
- vg = lxc_global_config_value("lxc.bdev.lvm.vg");
- new->src = lxc_string_join(
- "/",
- (const char *[]){"lvm:", "dev", vg, cname, NULL},
- false);
- } else {
- char *dup, *slider, *src;
+ const char *vg;
- src = lxc_storage_get_path(orig->src, orig->type);
-
- dup = strdup(src);
- if (!dup) {
- ERROR("Failed to duplicate string \"%s\"", src);
+ if (snap) {
+ ERROR("LVM snapshot from %s backing store is not supported",
+ orig->type);
return -1;
}
-
- slider = strrchr(dup, '/');
- if (!slider) {
- ERROR("Failed to detect \"/\" in string \"%s\"", dup);
- free(dup);
+ vg = lxc_global_config_value("lxc.bdev.lvm.vg");
+ if (!vg) {
+ ERROR("The \"lxc.bdev.lvm.vg\" key is not set");
return -1;
}
- *slider = '\0';
- slider = dup;
-
- new->src = lxc_string_join(
- "/",
- (const char *[]){"lvm:", *slider == '/' ? ++slider : slider,
- cname, NULL},
- false);
- free(dup);
- }
- if (!new->src) {
- ERROR("Failed to create string");
- return -1;
+
+ len = strlen("/dev/") + strlen(vg) + strlen(cname) + 4 + 2;
+ new->src = malloc(len);
+ if (!new->src)
+ return -1;
+
+ ret = snprintf(new->src, len, "lvm:/dev/%s/%s", vg, cname);
+ if (ret < 0 || ret >= len)
+ return -1;
+ } else {
+ new->src = dir_new_path(orig->src, oldname, cname, oldpath, lxcpath);
+ if (!new->src)
+ return -1;
}
if (orig->mntopts) {
new->mntopts = strdup(orig->mntopts);
- if (!new->mntopts) {
- ERROR("Failed to duplicate string \"%s\"", orig->mntopts);
+ if (!new->mntopts)
return -1;
- }
}
len = strlen(lxcpath) + strlen(cname) + strlen("rootfs") + 3;
new->dest = malloc(len);
- if (!new->dest) {
- ERROR("Failed to allocate memory");
+ if (!new->dest)
return -1;
- }
-
ret = snprintf(new->dest, len, "%s/%s/rootfs", lxcpath, cname);
- if (ret < 0 || ret >= len) {
- ERROR("Failed to create string");
+ if (ret < 0 || ret >= len)
return -1;
- }
-
- ret = mkdir_p(new->dest, 0755);
- if (ret < 0) {
- SYSERROR("Failed to create directory \"%s\"", new->dest);
+ if (mkdir_p(new->dest, 0755) < 0)
return -1;
- }
-
- return 0;
-}
-
-bool lvm_create_clone(struct lxc_conf *conf, struct bdev *orig,
- struct bdev *new, uint64_t newsize)
-{
- char *src;
- const char *thinpool;
- int ret;
- struct rsync_data data;
- char *cmd_args[2];
- char cmd_output[MAXPATHLEN];
- char fstype[100] = "ext4";
- uint64_t size = newsize;
if (is_blktype(orig)) {
- /* detect size */
if (!newsize && blk_getsize(orig, &size) < 0) {
- ERROR("Failed to detect size of logical volume \"%s\"",
- orig->src);
+ ERROR("Error getting size of %s", orig->src);
return -1;
}
-
- /* detect filesystem */
if (detect_fs(orig, fstype, 100) < 0) {
- INFO("Failed to detect filesystem type for \"%s\"", orig->src);
+ INFO("could not find fstype for %s, using ext3", orig->src);
return -1;
}
- } else if (!newsize) {
+ } else {
+ sprintf(fstype, "ext3");
+ if (!newsize)
size = DEFAULT_FS_SIZE;
}
- src = lxc_storage_get_path(new->src, "lvm");
- thinpool = lxc_global_config_value("lxc.bdev.lvm.thin_pool");
+ if (snap) {
+ char *newsrc, *origsrc;
- ret = do_lvm_create(src, size, thinpool);
- if (ret < 0) {
- ERROR("Failed to create lvm storage volume \"%s\"", src);
- return -1;
- }
-
- cmd_args[0] = fstype;
- cmd_args[1] = src;
- ret = run_command(cmd_output, sizeof(cmd_output),
- do_mkfs_exec_wrapper, (void *)cmd_args);
- if (ret < 0) {
- ERROR("Failed to create new filesystem \"%s\" for lvm storage "
- "volume \"%s\": %s", fstype, src, cmd_output);
- return -1;
- }
-
- data.orig = orig;
- data.new = new;
- ret = rsync_rootfs(&data);
- if (ret < 0) {
- ERROR("Failed to rsync from \"%s\" to \"%s\"", orig->dest,
- new->dest);
- return false;
- }
-
- TRACE("Created lvm storage volume \"%s\"", new->dest);
- return true;
-}
-
-bool lvm_create_snapshot(struct lxc_conf *conf, struct bdev *orig,
- struct bdev *new, uint64_t newsize)
-{
- int ret;
- char *newsrc, *origsrc;
- uint64_t size = newsize;
+ origsrc = lxc_storage_get_path(orig->src, "lvm");
+ newsrc = lxc_storage_get_path(new->src, "lvm");
- if (is_blktype(orig)) {
- if (!newsize && blk_getsize(orig, &size) < 0) {
- ERROR("Failed to detect size of logical volume \"%s\"",
- orig->src);
+ if (lvm_snapshot(origsrc, newsrc, size) < 0) {
+ ERROR("could not create %s snapshot of %s", new->src, orig->src);
return -1;
}
- } else if (!newsize) {
- size = DEFAULT_FS_SIZE;
- }
+ } else {
+ char *src;
- origsrc = lxc_storage_get_path(orig->src, "lvm");
- newsrc = lxc_storage_get_path(new->src, "lvm");
+ src = lxc_storage_get_path(new->src, "lvm");
+ if (do_lvm_create(src, size, lxc_global_config_value("lxc.bdev.lvm.thin_pool")) < 0) {
+ ERROR("Error creating new lvm blockdev");
+ return -1;
+ }
- ret = lvm_snapshot(origsrc, newsrc, size);
- if (ret < 0) {
- ERROR("Failed to create lvm \"%s\" snapshot of \"%s\"",
- new->src, orig->src);
- return false;
+ cmd_args[0] = fstype;
+ cmd_args[1] = src;
+ // create an fs in the loopback file
+ ret = run_command(cmd_output, sizeof(cmd_output),
+ do_mkfs_exec_wrapper, (void *)cmd_args);
+ if (ret < 0)
+ return -1;
}
- TRACE("Created lvm snapshot \"%s\" from \"%s\"", new->dest, orig->dest);
- return true;
+ return 0;
}
int lvm_destroy(struct bdev *orig)
{
- int ret;
- char cmd_output[MAXPATHLEN];
- struct lvcreate_args cmd_args = {0};
-
- cmd_args.lv = lxc_storage_get_path(orig->src, "lvm");
- ret = run_command(cmd_output, sizeof(cmd_output),
- lvm_destroy_exec_wrapper, (void *)&cmd_args);
- if (ret < 0) {
- ERROR("Failed to destroy logical volume \"%s\": %s", orig->src,
- cmd_output);
+ char *src;
+
+ pid_t pid;
+
+ if ((pid = fork()) < 0)
return -1;
+
+ if (!pid) {
+ (void)setenv("LVM_SUPPRESS_FD_WARNINGS", "1", 1);
+ src = lxc_storage_get_path(orig->src, "lvm");
+ execlp("lvremove", "lvremove", "-f", src, (char *)NULL);
+ exit(EXIT_FAILURE);
}
- TRACE("Destroyed logical volume \"%s\"", orig->src);
- return 0;
+ return wait_for_pid(pid);
}
int lvm_create(struct bdev *bdev, const char *dest, const char *n,
- struct bdev_specs *specs)
+ struct bdev_specs *specs)
{
const char *vg, *thinpool, *fstype, *lv = n;
uint64_t sz;
len = strlen(vg) + strlen(lv) + 4 + 7;
bdev->src = malloc(len);
- if (!bdev->src) {
- ERROR("Failed to allocate memory");
+ if (!bdev->src)
return -1;
- }
ret = snprintf(bdev->src, len, "lvm:/dev/%s/%s", vg, lv);
- if (ret < 0 || ret >= len) {
- ERROR("Failed to create string");
+ if (ret < 0 || ret >= len)
return -1;
- }
- /* size is in bytes */
+ // fssize is in bytes.
sz = specs->fssize;
if (!sz)
sz = DEFAULT_FS_SIZE;
- ret = do_lvm_create(bdev->src + 4, sz, thinpool);
- if (ret < 0) {
- ERROR("Error creating new logical volume \"%s\" of size "
- "\"%" PRIu64 " bytes\"", bdev->src, sz);
+ if (do_lvm_create(bdev->src + 4, sz, thinpool) < 0) {
+ ERROR("Error creating new lvm blockdev %s size %"PRIu64" bytes", bdev->src, sz);
return -1;
}
fstype = DEFAULT_FSTYPE;
cmd_args[0] = fstype;
- cmd_args[1] = lxc_storage_get_path(bdev->src, bdev->type);
+ cmd_args[1] = bdev->src + 4;
ret = run_command(cmd_output, sizeof(cmd_output), do_mkfs_exec_wrapper,
(void *)cmd_args);
- if (ret < 0) {
- ERROR("Failed to create new logical volume \"%s\": %s",
- bdev->src, cmd_output);
+ if (ret < 0)
return -1;
- }
- bdev->dest = strdup(dest);
- if (!bdev->dest) {
- ERROR("Failed to duplicate string \"%s\"", dest);
+ if (!(bdev->dest = strdup(dest)))
return -1;
- }
- ret = mkdir_p(bdev->dest, 0755);
- if (ret < 0) {
- SYSERROR("Failed to create directory \"%s\"", bdev->dest);
+ if (mkdir_p(bdev->dest, 0755) < 0) {
+ ERROR("Error creating %s", bdev->dest);
return -1;
}
- TRACE("Created new logical volume \"%s\"", bdev->dest);
return 0;
}