#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
+#include <sys/param.h>
#include <errno.h>
+#include <linux/kdev_t.h>
#define DEV_DIR "/dev/"
-#define DM_DIR "device-mapper"
-#define ALIGNMENT sizeof(int)
-#ifndef PATH_MAX
-#define PATH_MAX 4096
-#endif
-#define MKDEV(x,y) ((x) << 8 | (y))
+/* FIXME Obtain from kernel header */
+#define DM_DIR "device-mapper"
static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR;
+typedef enum {
+ DIR_CREATE,
+ DIR_REMOVE
+} do_newold_t;
+
/*
* Library users can provide their own logging
* function.
#define NR_ARGS 16
-static char *mkpath(int n, char *base, ...)
+/*
+ * Join n path components together with /'s.
+ */
+static char *mkpath(int n, ...)
{
va_list va;
- char *args[NR_ARGS];
- int len = strlen(base) + 1;
- int i = 0;
- char *str;
+ int len = 0, i;
+ char *str, *r;
- if (n > NR_ARGS)
- return NULL;
+ va_start(va, n);
+ for (i = 0; i < n; i++)
+ len += strlen(va_arg(va, char *)) + 1;
- va_start(va, base);
- for(i = 0; i < n; i++) {
- args[i] = (char *)va_arg(va, char *);
- len += strlen(args[i]);
- len++;
- }
va_end(va);
- str = malloc(len);
- if (str) {
- strcpy(str, base);
- for(i = 0; i < n; i++) {
- strcat(str, "/");
- strcat(str, args[i]);
- }
+ if (!(r = str = malloc(len))) {
+ log("mkpath: malloc(%d) failed", len);
+ return NULL;
}
- return str;
+
+ va_start(va, n);
+ for (i = 0; i < n; i++)
+ str += sprintf(str, "%s%s", i ? "/" : "", va_arg(va, char *));
+ va_end(va);
+
+ return r;
}
struct dm_task *dm_task_create(int type)
{
struct dm_task *dmt = malloc(sizeof(*dmt));
- if (!dmt)
+ if (!dmt) {
+ log("dm_task_create: malloc(%d) failed", sizeof(*dmt));
return NULL;
+ }
memset(dmt, 0, sizeof(*dmt));
int size = strlen(params) + strlen(type);
int ret;
- size += 64; /* Guess at max size of start and len */
+ size += 64; /* Guess at max size of start and len */
t = malloc(size + sizeof(struct target));
- if (!t)
+ if (!t) {
+ log("_create_target: malloc(%d) failed",
+ size + sizeof(struct target));
return NULL;
+ }
+
memset(t, 0, size + sizeof(struct target));
- t->str = (char *)(t + 1);
+ t->str = (char *) (t + 1);
ret = sprintf(t->str, "%Lu %Lu %s %s\n", start, len, type, params);
if (ret > size) {
/* This should be impossible, but check anyway */
- log("Internal error - ran out of buffer space");
- exit(-1);
+ log("_create_target internal error: Ran out of buffer space");
+ free(t);
+ return NULL;
}
return t;
int dm_task_add_target(struct dm_task *dmt,
unsigned long long start,
unsigned long long size,
- const char *ttype,
- const char *params)
+ const char *ttype, const char *params)
{
struct target *t = _create_target(start, size, ttype, params);
static int do_suspend(char *mnt, char *name, int on)
{
- char *path = mkpath(2, mnt, name, "suspend");
- int ret = -1;
-
- if (path) {
- FILE *fp = fopen(path, "rw");
- if (fp) {
- if (fprintf(fp, "%d\n", on) > 0)
- ret = 0;
- fclose(fp);
- }
- free(path);
- }
+ char *path;
+ FILE *fp;
+ int ret = 0;
+
+ if (!(path = mkpath(3, mnt, name, "suspend")))
+ return 0;
+
+ if ((fp = fopen(path, "rw"))) {
+ if (fprintf(fp, "%d\n", on) > 0)
+ ret = 1;
+ else
+ log("%s: fprintf failed: %s", path, strerror(errno));
+ fclose(fp);
+ } else
+ log("%s: fopen failed: %s", path, strerror(errno));
+
+ free(path);
return ret;
}
-static int do_newold(char *mnt, char *name, int create)
+static int do_newold(char *mnt, char *name, do_newold_t create)
{
- char *path = mkpath(1, mnt, name);
- int ret = -1;
+ char *path = mkpath(2, mnt, name);
+ int ret;
- if (path) {
- if (create)
- ret = mkdir(path, 0750);
- else
- ret = rmdir(path);
- free(path);
- }
+ if (!path)
+ return 0;
- return ret;
+ if (create == DIR_CREATE) {
+ if ((ret = mkdir(path, 0750)) < 0)
+ log("%s: mkdir failed: %s", path, strerror(errno));
+ } else if ((ret = rmdir(path)) < 0)
+ log("%s: rmdir failed: %s", path, strerror(errno));
+
+ free(path);
+
+ return (ret < 0) ? 0 : 1;
}
static int do_device(char *mnt, char *name, struct dm_info *info)
{
- char *path = mkpath(2, mnt, name, "device");
- int ret = -1;
- if (path) {
- struct stat st;
- if (stat(path, &st) == 0) {
- info->major = st.st_rdev >> 8;
- info->minor = st.st_rdev & 0xff;
- }
- free(path);
- }
- return ret;
+ char *path;
+ struct stat st;
+
+ if (!(path = mkpath(3, mnt, name, "device")))
+ return 0;
+
+ if (!stat(path, &st)) {
+ info->major = MAJOR(st.st_rdev);
+ info->minor = MINOR(st.st_rdev);
+ info->exists = 1;
+ } else
+ info->exists = 0;
+
+ free(path);
+ return 1;
}
static int do_suspend_state(char *mnt, char *name, struct dm_info *info)
{
- char *path = mkpath(2, mnt, name, "suspend");
- int ret = -1;
- if (path) {
- FILE *fp = fopen(path, "r");
- if (fp) {
- int state;
- if (fscanf(fp, "%d", &state) == 1) {
- info->suspended = state;
- ret = 0;
- }
- fclose(fp);
- }
- free(path);
- }
+ char *path;
+ FILE *fp;
+ int ret = 0;
+
+ if (!(path = mkpath(3, mnt, name, "suspend")))
+ return 0;
+
+ if ((fp = fopen(path, "r"))) {
+ if (fscanf(fp, "%d", &info->suspended) == 1)
+ ret = 1;
+ else
+ log("%s fscanf failed: %s", path, strerror(errno));
+ fclose(fp);
+ } else
+ log("%s: fopen failed: %s", path, strerror(errno));
+
+ free(path);
+
return ret;
}
static int do_info(char *mnt, char *name, struct dm_info *info)
{
- int ret;
-
memset(info, 0, sizeof(struct dm_info));
- ret = do_device(mnt, name, info);
- if (ret < 0)
- goto out;
- ret = do_suspend_state(mnt, name, info);
- if (ret < 0)
- goto out;
- info->exists = 1;
-out:
- return ret;
+ if (!do_device(mnt, name, info))
+ return 0;
+
+ if (info->exists && !do_suspend_state(mnt, name, info))
+ return 0;
+
+ return 1;
}
-static int write_data(int fd, struct dm_task *dmt)
+/*
+ * Writes a buffer out to a file, returns 0 on failure.
+ */
+static int write_buffer(int fd, const void *buf, size_t count)
{
- struct target *tmp = dmt->head;
- int n = 0;
- struct iovec *iov;
- int ret = -1;
-
- while(tmp) {
- n++;
- tmp = tmp->next;
- }
+ size_t n = 0;
+ int tot = 0;
- iov = malloc(n * sizeof(struct iovec));
- if (iov) {
- tmp = dmt->head;
- n = 0;
- while(tmp) {
- iov[n].iov_base = tmp->str;
- iov[n].iov_len = strlen(tmp->str);
- n++;
- tmp = tmp->next;
- }
+ while (tot < count) {
+ do
+ n = write(fd, buf, count - tot);
+ while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
+
+ if (n <= 0)
+ return 0;
- ret = writev(fd, iov, n);
- free(iov);
+ tot += n;
+ buf += n;
}
- return ret;
+ return 1;
+}
+
+static int write_data(int fd, struct dm_task *dmt)
+{
+ struct target *t;
+
+ for (t = dmt->head; t; t = t->next)
+ if (!write_buffer(fd, t->str, strlen(t->str)))
+ return 0;
+
+ return 1;
}
static int do_load(char *mnt, char *name, struct dm_task *dmt)
{
- char *path = mkpath(2, mnt, name, "table");
- int ret = -1;
- if (path) {
- int fd = open(path, O_RDWR);
- if (fd >= 0) {
- ret = write_data(fd, dmt);
- close(fd);
- }
+ char *path;
+ int fd, ret = 0;
+
+ if (!(path = mkpath(3, mnt, name, "table")))
+ return 0;
+
+ if ((fd = open(path, O_RDWR)) != -1) {
+ if (!(ret = write_data(fd, dmt)))
+ log("%s: write failed: %s", path, strerror(errno));
+ close(fd);
}
+
+ free(path);
+
return ret;
}
static void strip_nl(char *str)
{
- while(*str && *str != '\n' && *str != '\r')
+ while (*str && *str != '\n' && *str != '\r')
str++;
*str = 0;
}
static int do_error_check(char *mnt, char *name)
{
- char *path = mkpath(2, mnt, name, "error");
- int ret = -1;
- if (path) {
- FILE *fp = fopen(path, "r");
- if (fp) {
- char buf[1024];
- ret = 0;
- while(fgets(buf, 1023, fp)) {
- buf[1023] = 0;
- strip_nl(buf);
- log(buf);
- ret = -1;
- }
- fclose(fp);
- }
+ char *path;
+ FILE *fp;
+ int ret = 1;
+ char buf[1024];
+
+ if (!(path = mkpath(3, mnt, name, "error")))
+ return 0;
+
+ if (!(fp = fopen(path, "r"))) {
+ log("%s: fopen failed: %s", path, strerror(errno));
free(path);
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ strip_nl(buf);
+ log(buf);
+ ret = 0;
}
+
+ fclose(fp);
+ free(path);
return ret;
}
static char *find_mount_point(void)
{
- FILE *fp = fopen("/proc/mounts", "r");
-static char mpoint[4096];
- if (fp) {
- int fsdump, fspass;
- char device[1024];
- char fstype[30];
- char fsoptions[4096];
- while(fscanf(fp, "%1024s %4096s %30s %4096s %d %d", device, mpoint, fstype, fsoptions, &fsdump, &fspass) == 6) {
- if (strlen(fstype) != 4)
- continue;
- if (strcmp(fstype, "dmfs"))
- continue;
+ FILE *fp;
+ static char mpoint[4096];
+ char fstype[30];
+
+ if ((fp = fopen("/proc/mounts", "r")) < 0) {
+ log("/proc/mounts: fopen failed: %s", strerror(errno));
+ return NULL;
+ }
+
+ while (fscanf(fp, "%*s%4096s%30s%*s%*d%*d", mpoint, fstype) == 2) {
+ if (!strcmp(fstype, "dmfs")) {
fclose(fp);
return mpoint;
}
- fclose(fp);
}
+ fclose(fp);
return NULL;
}
char *mnt = find_mount_point();
if (mnt == NULL) {
+ /* FIXME Mount it temporarily if not mounted */
log("Cannot find mount point for dmfs or dmfs not mounted");
- goto bad;
+ return 0;
+ }
+
+ if (!dmt->dev_name || !*dmt->dev_name) {
+ log("dm_task_run: Device name not supplied");
+ return 0;
}
switch (dmt->type) {
case DM_DEVICE_CREATE:
- do_newold(mnt, dmt->dev_name, 1);
- do_info(mnt, dmt->dev_name, &dmt->info);
+ if (!do_newold(mnt, dmt->dev_name, DIR_CREATE) ||
+ !do_load(mnt, dmt->dev_name, dmt) ||
+ !do_error_check(mnt, dmt->dev_name) ||
+ !do_info(mnt, dmt->dev_name, &dmt->info))
+ return 0;
+ _add_dev_node(dmt->dev_name,
+ MKDEV(dmt->info.major, dmt->info.minor));
break;
case DM_DEVICE_RELOAD:
- do_load(mnt, dmt->dev_name, dmt);
- do_error_check(mnt, dmt->dev_name);
+ if (!do_load(mnt, dmt->dev_name, dmt) ||
+ !do_error_check(mnt, dmt->dev_name)) return 0;
break;
case DM_DEVICE_REMOVE:
- do_newold(mnt, dmt->dev_name, 1);
- dmt->info.exists = 0;
+ if (!do_newold(mnt, dmt->dev_name, DIR_REMOVE) ||
+ !do_info(mnt, dmt->dev_name, &dmt->info))
+ return 0;
+ _rm_dev_node(dmt->dev_name);
break;
case DM_DEVICE_SUSPEND:
- do_suspend(mnt, dmt->dev_name, 1);
+ if (!do_suspend(mnt, dmt->dev_name, 1))
+ return 0;
break;
case DM_DEVICE_RESUME:
- do_suspend(mnt, dmt->dev_name, 0);
+ if (!do_suspend(mnt, dmt->dev_name, 0))
+ return 0;
break;
case DM_DEVICE_INFO:
- do_info(mnt, dmt->dev_name, &dmt->info);
+ if (!do_info(mnt, dmt->dev_name, &dmt->info))
+ return 0;
break;
default:
- log("Internal error: unknown device-mapper task %d",
- dmt->type);
- goto bad;
- }
-
- switch (dmt->type) {
- case DM_DEVICE_CREATE:
- _add_dev_node(dmt->dev_name,
- MKDEV(dmt->info.major, dmt->info.minor));
- break;
-
- case DM_DEVICE_REMOVE:
- _rm_dev_node(dmt->dev_name);
- break;
+ log("Internal error: unknown device-mapper task %d", dmt->type);
+ return 0;
}
return 1;
-
- bad:
- return 0;
}
int dm_set_dev_dir(const char *dir)