diff --git a/libdm/Makefile b/libdm/Makefile new file mode 100644 index 000000000..f9971824d --- /dev/null +++ b/libdm/Makefile @@ -0,0 +1,54 @@ +# +# Copyright (C) 2001 Sistina Software (UK) Limited. +# +# This file is released under the LGPL. +# + +srcdir = . +top_srcdir = .. +VPATH = . +SHELL = /bin/sh + +CC = gcc +RANLIB = ranlib +SHELL = /bin/sh +INSTALL = /usr/bin/install -c +LN_S = ln -s + +prefix = /usr +libdir = ${prefix}/lib +incdir = ${prefix}/include +kernelsrcdir = /scratch/alphalinux/linux + +OWNER=root +GROUP=root + +INCLUDES=-I. -I${kernelsrcdir}/include +CFLAGS+=-Wall +#CFLAGS+=-O2 +CFLAGS+=-g -fno-omit-frame-pointer +#CFLAGS+=-pg +#LD_FLAGS=-pg + +libdevmapper.so: libdm.o + $(CC) -shared -o libdevmapper.so libdm.o + +SUFFIXES= +SUFFIXES=.c .o .so + +%.o: %.c + $(CC) -c $(INCLUDES) $(CFLAGS) $< -o $@ + +install: libdevmapper.so + $(INSTALL) -c -o $(OWNER) -g $(GROUP) -m 555 -s libdevmapper.so \ + $(libdir) + $(INSTALL) -D -c -o $(OWNER) -g $(GROUP) -m 444 libdevmapper.h \ + $(incdir)/devmapper/libdevmapper.h + +clean: + $(RM) libdevmapper.so libdm.o + +distclean: clean + +.PHONY: install test clean distclean + diff --git a/libdm/libdevmapper.h b/libdm/libdevmapper.h new file mode 100644 index 000000000..dc35129ee --- /dev/null +++ b/libdm/libdevmapper.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef LIB_DEVICE_MAPPER_H +#define LIB_DEVICE_MAPPER_H + +/* + * Since it is quite laborious to build the ioctl + * arguments for the device-mapper people are + * encouraged to use this library. + * + * You will need to build a struct dm_task for + * each ioctl command you want to execute. + */ + + +typedef void (*dm_log_fn)(int level, const char *file, int line, + const char *f, ...); + +/* + * The library user may wish to register their own + * logging function, by default errors go to + * stderr. + */ +void dm_log_init(dm_log_fn fn); + +enum { + DM_DEVICE_CREATE, + DM_DEVICE_RELOAD, + DM_DEVICE_REMOVE, + + DM_DEVICE_SUSPEND, + DM_DEVICE_RESUME, + + DM_DEVICE_INFO, +}; + + +struct dm_task; + +struct dm_task *dm_task_create(int type); +void dm_task_destroy(struct dm_task *dmt); + +int dm_task_set_name(struct dm_task *dmt, const char *name); + +/* + * Retrieve attributes after an info. + */ +struct dm_info { + int exists; + int suspended; + unsigned int open_count; + int minor; /* minor device number */ + unsigned int target_count; +}; + +int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi); + +/* + * Use these to prepare for a create or reload. + */ +int dm_task_add_target(struct dm_task *dmt, + unsigned long long start, + unsigned long long size, + const char *ttype, + const char *params); + +/* + * Call this to actually run the ioctl. + */ +int dm_task_run(struct dm_task *dmt); + +/* + * Return the device-mapper directory + */ +const char *dm_dir(void); + +#endif /* LIB_DEVICE_MAPPER_H */ diff --git a/libdm/libdm.c b/libdm/libdm.c new file mode 100644 index 000000000..16bb5aef7 --- /dev/null +++ b/libdm/libdm.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "libdevmapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEVICE_MAPPER_CONTROL "/dev/device-mapper/control" +#define ALIGNMENT sizeof(int) + +/* + * Library users can provide their own logging + * function. + */ +static void _default_log(int level, const char *file, int line, + const char *f, ...) +{ + va_list ap; + + //fprintf(stderr, "%s:%d ", file, line); + + va_start(ap, f); + vfprintf(stderr, f, ap); + va_end(ap); + + fprintf(stderr, "\n"); +} + +static dm_log_fn _log = _default_log; + +void dm_log_init(dm_log_fn fn) +{ + _log = fn; +} + +#define log(msg, x...) _log(1, __FILE__, __LINE__, msg, ## x) + +struct target { + + unsigned long long start; + unsigned long long length; + char *type; + char *params; + + struct target *next; +}; + +struct dm_task { + int type; + char *dev_name; + + struct target *head, *tail; + + struct dm_ioctl *dmi; +}; + +struct dm_task *dm_task_create(int type) +{ + struct dm_task *dmt = malloc(sizeof(*dmt)); + + if (!dmt) + return NULL; + + memset(dmt, 0, sizeof(*dmt)); + + dmt->type = type; + return dmt; +} + +void dm_task_destroy(struct dm_task *dmt) +{ + struct target *t, *n; + + for (t = dmt->head; t; t = n) { + n = t->next; + free(t); + } + + if (dmt->dmi) + free(dmt->dmi); + + free(dmt); +} + +int dm_task_set_name(struct dm_task *dmt, const char *name) +{ + if (dmt->dev_name) + free(dmt->dev_name); + + return (dmt->dev_name = strdup(name)) ? 1 : 0; +} + +int dm_task_get_info(struct dm_task *dmt, struct dm_info *info) +{ + if (!dmt->dmi) + return 0; + + info->exists = dmt->dmi->exists; + info->suspended = dmt->dmi->suspend; + info->open_count = dmt->dmi->open_count; + info->minor = dmt->dmi->minor; + info->target_count = dmt->dmi->target_count; + return 1; +} + +static struct target *_create_target(unsigned long long start, + unsigned long long len, + const char *type, const char *params) +{ + struct target *t = malloc(sizeof(*t)); + + if (!t) + return NULL; + memset(t, 0, sizeof(*t)); + + if (!(t->params = strdup(params))) { + log("Out of memory"); + goto bad; + } + + if (!(t->type = strdup(type))) { + log("Out of memory"); + goto bad; + } + + t->start = start; + t->length = len; + return t; + + bad: + free(t->params); + free(t->type); + free(t); + return NULL; +} + +int dm_task_add_target(struct dm_task *dmt, + unsigned long long start, + unsigned long long size, + const char *ttype, + const char *params) +{ + struct target *t = _create_target(start, size, ttype, params); + + if (!t) + return 0; + + if (!dmt->head) + dmt->head = dmt->tail = t; + else { + dmt->tail->next = t; + dmt->tail = t; + } + + return 1; +} + +static void *_align(void *ptr, unsigned int align) +{ + align--; + return (void *) (((long) ptr + align) & ~align); +} + +static void *_add_target(struct target *t, void *out, void *end) +{ + void *out_sp = out; + struct dm_target_spec sp; + int len; + const char no_space[] = "Ran out of memory building ioctl parameter"; + + out += sizeof(struct dm_target_spec); + if (out >= end) { + log(no_space); + return NULL; + } + + sp.status = 0; + sp.sector_start = t->start; + sp.length = t->length; + strncpy(sp.target_type, t->type, sizeof(sp.target_type)); + + len = strlen(t->params); + + if ((out + len + 1) >= end) { + log(no_space); + + log("t->params= '%s'", t->params); + return NULL; + } + strcpy((char *) out, t->params); + out += len + 1; + + /* align next block */ + out = _align(out, ALIGNMENT); + + sp.next = out - out_sp; + memcpy(out_sp, &sp, sizeof(sp)); + + return out; +} + +static struct dm_ioctl *_flatten(struct dm_task *dmt) +{ + struct dm_ioctl *dmi; + struct target *t; + size_t len = sizeof(struct dm_ioctl); + void *b, *e; + int count = 0; + + for (t = dmt->head; t; t = t->next) { + len += sizeof(struct dm_target_spec); + len += strlen(t->params) + 1 + ALIGNMENT; + count++; + } + + if (!(dmi = malloc(len))) + return NULL; + + dmi->data_size = len; + strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name)); + dmi->suspend = (dmt->type == DM_DEVICE_SUSPEND) ? 1 : 0; + dmi->open_count = 0; + dmi->minor = -1; + + dmi->target_count = count; + + b = (void *) (dmi + 1); + e = (void *) ((char *) dmi + len); + + for (t = dmt->head; t; t = t->next) + if (!(b = _add_target(t, b, e))) + goto bad; + + return dmi; + + bad: + free(dmi); + return NULL; +} + +/* + * FIXME: This function is copied straight from + * LVM1 without an audit. + */ +static int __check_devfs(void) +{ + int r = 0, len; + char dir[PATH_MAX], line[512]; + char type[32]; + FILE *mounts = NULL; + const char *dev_dir = DM_DIR; + + /* trim the trailing slash off dev_dir, yuck */ + len = strlen(dev_dir) - 1; + while(len && dev_dir[len] == '/') + len--; + + if (!(mounts = fopen("/proc/mounts", "r"))) { + log("Unable to open /proc/mounts to determine " + "if devfs is mounted"); + return 0; + } + + while (!feof(mounts)) { + fgets(line, sizeof(line) - 1, mounts); + if (sscanf(line, "%*s %s %s %*s", dir, type) != 2) + continue; + + if (!strcmp(type, "devfs") && !strncmp(dir, dev_dir, len)) { + r = 1; + break; + } + } + + fclose(mounts); + return r; +} + +/* + * Memo the result of __check_devfs. + */ +static int _check_devfs(void) +{ + static int prev_result = -1; + + if (prev_result >= 0) + return prev_result; + + return (prev_result = __check_devfs()); +} + +static void _build_dev_path(char *buffer, size_t len, const char *dev_name) +{ + snprintf(buffer, len, "/dev/%s/%s", DM_DIR, dev_name); +} + +static int _add_dev_node(const char *dev_name, dev_t dev) +{ + char path[PATH_MAX]; + + if (_check_devfs()) + return 1; + + _build_dev_path(path, sizeof(path), dev_name); + + if (mknod(path, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, dev) < 0) { + log("Unable to make device node for '%s'", dev_name); + return 0; + } + + return 1; +} + +static int _rm_dev_node(const char *dev_name) +{ + char path[PATH_MAX]; + + if (_check_devfs()) + return 1; + + _build_dev_path(path, sizeof(path), dev_name); + + if (unlink(path) < 0) { + log("Unable to unlink device node for '%s'", dev_name); + return 0; + } + + return 1; +} + +int dm_task_run(struct dm_task *dmt) +{ + int fd = -1; + struct dm_ioctl *dmi = _flatten(dmt); + unsigned int command; + + if (!dmi) { + log("Couldn't create ioctl argument"); + return 0; + } + + if ((fd = open(DEVICE_MAPPER_CONTROL, O_RDWR)) < 0) { + log("Couldn't open device-mapper control device"); + goto bad; + } + + switch (dmt->type) { + case DM_DEVICE_CREATE: + command = DM_CREATE; + break; + + case DM_DEVICE_RELOAD: + command = DM_RELOAD; + break; + + case DM_DEVICE_REMOVE: + command = DM_REMOVE; + break; + + case DM_DEVICE_SUSPEND: + command = DM_SUSPEND; + break; + + case DM_DEVICE_RESUME: + command = DM_SUSPEND; + break; + + case DM_DEVICE_INFO: + command = DM_INFO; + break; + + default: + log("Internal error: unknown device-mapper task %d", + dmt->type); + goto bad; + } + + if (ioctl(fd, command, dmi) < 0) { + log("device-mapper ioctl cmd %d failed: %s", dmt->type, + strerror(errno)); + goto bad; + } + + switch (dmt->type) { + case DM_DEVICE_CREATE: + _add_dev_node(dmt->dev_name, dmt->dmi->minor); + break; + + case DM_DEVICE_REMOVE: + _rm_dev_node(dmt->dev_name); + break; + } + + dmt->dmi = dmi; + return 1; + + bad: + free(dmi); + if (fd >= 0) + close(fd); + return 0; +} + +const char *dm_dir(void) +{ + return DM_DIR; +} diff --git a/tools/dmsetup.c b/tools/dmsetup.c new file mode 100644 index 000000000..96ba16536 --- /dev/null +++ b/tools/dmsetup.c @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the GPL. + */ + +#include "libdm.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LINE_SIZE 1024 + +#define err(msg, x...) fprintf(stderr, msg "\n", ##x) + +static int _parse_file(struct dm_task *dmt, const char *file) +{ + char buffer[LINE_SIZE], *ttype, *ptr, *comment; + FILE *fp = fopen(file, "r"); + unsigned long long start, size; + int r = 0, n, line = 0; + + if (!fp) { + err("Couldn't open '%s' for reading", file); + return 0; + } + + while (fgets(buffer, sizeof(buffer), fp)) { + line++; + + /* trim trailing space */ + for (ptr = buffer + strlen(buffer) - 1; ptr >= buffer; ptr--) + if (!isspace((int) *ptr)) + break; + ptr++; + *ptr = '\0'; + + /* trim leading space */ + for (ptr = buffer; *ptr && isspace((int) *ptr); ptr++) + ; + + if (!*ptr || *ptr == '#') + continue; + + if (sscanf(ptr, "%llu %llu %as %n", + &start, &size, &ttype, &n) < 3) { + err("%s:%d Invalid format", file, line); + goto out; + } + + ptr += n; + if ((comment = strchr(ptr, (int) '#'))) + *comment = '\0'; + + if (!dm_task_add_target(dmt, start, size, ttype, ptr)) + goto out; + + free(ttype); + } + r = 1; + + out: + fclose(fp); + return r; +} + +static int _load(int task, const char *name, const char *file) +{ + int r = 0; + struct dm_task *dmt; + + if (!(dmt = dm_task_create(task))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + if (!_parse_file(dmt, file)) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + r = 1; + +out: + dm_task_destroy(dmt); + + return r; +} + +static int _create(int argc, char **argv) +{ + return _load(DM_DEVICE_CREATE, argv[1], argv[2]); +} + +static int _reload(int argc, char **argv) +{ + return _load(DM_DEVICE_RELOAD, argv[1], argv[2]); +} + + +static int _simple(int task, const char *name) +{ + int r = 0; + + /* remove */ + struct dm_task *dmt; + + if (!(dmt = dm_task_create(task))) + return 0; + + if (!dm_task_set_name(dmt, name)) + goto out; + + r = dm_task_run(dmt); + + out: + dm_task_destroy(dmt); + return r; +} + +static int _remove(int argc, char **argv) +{ + return _simple(DM_DEVICE_REMOVE, argv[1]); +} + +static int _suspend(int argc, char **argv) +{ + return _simple(DM_DEVICE_SUSPEND, argv[1]); +} + +static int _resume(int argc, char **argv) +{ + return _simple(DM_DEVICE_RESUME, argv[1]); +} + +static int _info(int argc, char **argv) +{ + int r = 0; + + /* remove */ + struct dm_task *dmt; + struct dm_info info; + + if (!(dmt = dm_task_create(DM_DEVICE_INFO))) + return 0; + + if (!dm_task_set_name(dmt, argv[1])) + goto out; + + if (!dm_task_run(dmt)) + goto out; + + if (!dm_task_get_info(dmt, &info)) + goto out; + + if (!info.exists) { + printf("No such device.\n"); + r = 1; + goto out; + } + + printf("%s\t", info.suspended ? "SUSPENDED" : "ACTIVE"); + printf("%d\t", info.open_count); + printf("%d\t", info.minor); + printf("%d\n", info.target_count); + r = 1; + + out: + dm_task_destroy(dmt); + return r; +} + + +/* + * dispatch table + */ +typedef int (*command_fn)(int argc, char **argv); + +struct command { + char *name; + char *help; + int num_args; + command_fn fn; +}; + +static struct command _commands[] = { + {"create", " ", 2, _create}, + {"remove", "", 1, _remove}, + {"suspend", "", 1, _suspend}, + {"resume", "", 1, _resume}, + {"reload", " ", 2, _reload}, + {"info", "", 1, _info}, + {NULL, NULL, 0, NULL} +}; + +static void _usage(FILE *out) +{ + int i; + + fprintf(out, "usage:\n"); + for (i = 0; _commands[i].name; i++) + fprintf(out, "\t%s %s\n", + _commands[i].name, _commands[i].help); + return; +} + +struct command *_find_command(const char *name) +{ + int i; + + for (i = 0; _commands[i].name; i++) + if (!strcmp(_commands[i].name, name)) + return _commands + i; + + return NULL; +} + + +int main(int argc, char **argv) +{ + struct command *c; + + if (argc < 2) { + _usage(stderr); + exit(1); + } + + if (!(c = _find_command(argv[1]))) { + fprintf(stderr, "Unknown command\n"); + _usage(stderr); + exit(1); + } + + if (argc != c->num_args + 2) { + fprintf(stderr, "Incorrect number of arguments\n"); + _usage(stderr); + exit(1); + } + + if (!c->fn(argc - 1, argv + 1)) { + //fprintf(stderr, "Command failed\n"); + exit(1); + } + + return 0; +} +