From 2bf80b67d1a64f1219bd366cee355e897596b2cf Mon Sep 17 00:00:00 2001 From: "christophe.varoqui@free.fr" Date: Sun, 7 Dec 2003 08:44:59 -0800 Subject: [PATCH 1/1] [PATCH] extras/multipath update here is the next update which brings this multipath to the state i'm not ashamed of it being in udev :) * drop a libdevmapper copy in extras/multipath; maybe discussions w/ Sistina folks will bring a better solution in the future. * drop a putchar usage in libdevmapper to compile cleanly with klibc * drop another such usage of my own in main.c * massage the Makefile to compile libdevmapper against klibc * use "ld" to produce the binary rather than "gcc -static" * stop being stupid w/ uneeded major, minor & dev in main.c:dm_mk_node() * reverse to creating striped target for now because the multipath target is more hairy than expected initialy * push the version code to 009 to be in synch w/ udev builds & run here. binary size is 43ko, which is fairly gratifying after all the efforts I've put to compiling it with klibc :) --- extras/multipath/Makefile | 26 +- extras/multipath/libdevmapper/Makefile | 15 + .../libdevmapper/ioctl/libdevmapper.c | 1092 +++++++++++++++++ .../libdevmapper/ioctl/libdm-compat.h | 111 ++ .../libdevmapper/ioctl/libdm-targets.h | 51 + extras/multipath/libdevmapper/libdevmapper.h | 147 +++ extras/multipath/libdevmapper/libdm-common.c | 382 ++++++ extras/multipath/libdevmapper/libdm-common.h | 38 + extras/multipath/libdevmapper/list.h | 99 ++ extras/multipath/main.c | 16 +- extras/multipath/main.h | 6 +- 11 files changed, 1965 insertions(+), 18 deletions(-) create mode 100644 extras/multipath/libdevmapper/Makefile create mode 100644 extras/multipath/libdevmapper/ioctl/libdevmapper.c create mode 100644 extras/multipath/libdevmapper/ioctl/libdm-compat.h create mode 100644 extras/multipath/libdevmapper/ioctl/libdm-targets.h create mode 100644 extras/multipath/libdevmapper/libdevmapper.h create mode 100644 extras/multipath/libdevmapper/libdm-common.c create mode 100644 extras/multipath/libdevmapper/libdm-common.h create mode 100644 extras/multipath/libdevmapper/list.h diff --git a/extras/multipath/Makefile b/extras/multipath/Makefile index 9e0ce124c..bccf1264f 100644 --- a/extras/multipath/Makefile +++ b/extras/multipath/Makefile @@ -13,17 +13,35 @@ CFLAGS = -pipe -g -O2 -Wall -Wunused -Wstrict-prototypes -nostdinc -I../../klibc LDFLAGS = -lsysfs -ldevmapper -ldlist OBJS = main.o - -all: $(EXEC) - strip $(EXEC) +CRT0 = ../../klibc/klibc/crt0.o +LIB = ../../klibc/klibc/libc.a +LIBGCC = /usr/lib/gcc-lib/i586-mandrake-linux-gnu/3.3.1/libgcc.a +DMOBJS = libdevmapper/libdm-common.o libdevmapper/ioctl/libdevmapper.o +SYSFSOBJS = ../../libsysfs/dlist.o ../../libsysfs/sysfs_bus.o \ + ../../libsysfs/sysfs_class.o ../../libsysfs/sysfs_device.o \ + ../../libsysfs/sysfs_dir.o ../../libsysfs/sysfs_driver.o \ + ../../libsysfs/sysfs_utils.o + +SUBDIRS = libdevmapper + +recurse: + @for dir in $(SUBDIRS); do\ + $(MAKE) -C $$dir ; \ + done + $(MAKE) $(EXEC) + +all: recurse @echo "" @echo "Make complete" + $(EXEC): $(OBJS) - $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) + $(LD) -o $(EXEC) $(CRT0) $(OBJS) $(SYSFSOBJS) $(DMOBJS) $(LIB) $(LIBGCC) + strip $(EXEC) clean: rm -f core *.o $(EXEC) + $(MAKE) -C libdevmapper clean install: install -d $(bindir) diff --git a/extras/multipath/libdevmapper/Makefile b/extras/multipath/libdevmapper/Makefile new file mode 100644 index 000000000..72b39cedf --- /dev/null +++ b/extras/multipath/libdevmapper/Makefile @@ -0,0 +1,15 @@ +# Makefile +# +# Copyright (C) 2003 Christophe Varoqui, + +CC = gcc +CFLAGS = -pipe -g -O2 -Wall -Wunused -Wstrict-prototypes -nostdinc -I../../../klibc/klibc/include -I../../../klibc/klibc/include/bits32 -I/usr/lib/gcc-lib/i586-mandrake-linux-gnu/3.3.1/include -I../../../klibc/linux/include -I. -Iioctl + +OBJS = ioctl/libdevmapper.o libdm-common.o + +all: $(OBJS) + @echo "" + @echo "Make complete" + +clean: + rm -f core *.o ioctl/*.o ioctl/*.so diff --git a/extras/multipath/libdevmapper/ioctl/libdevmapper.c b/extras/multipath/libdevmapper/ioctl/libdevmapper.c new file mode 100644 index 000000000..ac7ba0c86 --- /dev/null +++ b/extras/multipath/libdevmapper/ioctl/libdevmapper.c @@ -0,0 +1,1092 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "libdm-targets.h" +#include "libdm-common.h" + +#ifdef DM_COMPAT +# include "libdm-compat.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef linux +# include +# include +# include +#else +# define MAJOR(x) major((x)) +# define MINOR(x) minor((x)) +# define MKDEV(x,y) makedev((x),(y)) +#endif + +/* + * Ensure build compatibility. + * The hard-coded versions here are the highest present + * in the _cmd_data arrays. + */ + +#if !((DM_VERSION_MAJOR == 1 && DM_VERSION_MINOR >= 0) || \ + (DM_VERSION_MAJOR == 4 && DM_VERSION_MINOR >= 0)) +#error The version of dm-ioctl.h included is incompatible. +#endif + +/* dm major version no for running kernel */ +static int _dm_version = DM_VERSION_MAJOR; +static int _log_suppress = 0; + +static int _control_fd = -1; +static int _version_checked = 0; +static int _version_ok = 1; + +/* + * Support both old and new major numbers to ease the transition. + * Clumsy, but only temporary. + */ +#if DM_VERSION_MAJOR == 4 && defined(DM_COMPAT) +const int _dm_compat = 1; +#else +const int _dm_compat = 0; +#endif + + +/* *INDENT-OFF* */ +static struct cmd_data _cmd_data_v4[] = { + {"create", DM_DEV_CREATE, {4, 0, 0}}, + {"reload", DM_TABLE_LOAD, {4, 0, 0}}, + {"remove", DM_DEV_REMOVE, {4, 0, 0}}, + {"remove_all", DM_REMOVE_ALL, {4, 0, 0}}, + {"suspend", DM_DEV_SUSPEND, {4, 0, 0}}, + {"resume", DM_DEV_SUSPEND, {4, 0, 0}}, + {"info", DM_DEV_STATUS, {4, 0, 0}}, + {"deps", DM_TABLE_DEPS, {4, 0, 0}}, + {"rename", DM_DEV_RENAME, {4, 0, 0}}, + {"version", DM_VERSION, {4, 0, 0}}, + {"status", DM_TABLE_STATUS, {4, 0, 0}}, + {"table", DM_TABLE_STATUS, {4, 0, 0}}, + {"waitevent", DM_DEV_WAIT, {4, 0, 0}}, + {"names", DM_LIST_DEVICES, {4, 0, 0}}, + {"clear", DM_TABLE_CLEAR, {4, 0, 0}}, + {"mknodes", DM_DEV_STATUS, {4, 0, 0}}, +}; +/* *INDENT-ON* */ + +#define ALIGNMENT_V1 sizeof(int) +#define ALIGNMENT 8 + +/* FIXME Rejig library to record & use errno instead */ +#ifndef DM_EXISTS_FLAG +# define DM_EXISTS_FLAG 0x00000004 +#endif + +static void *_align(void *ptr, unsigned int a) +{ + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +static int _open_control(void) +{ + char control[PATH_MAX]; + + if (_control_fd != -1) + return 1; + + snprintf(control, sizeof(control), "%s/control", dm_dir()); + + if ((_control_fd = open(control, O_RDWR)) < 0) { + log_error("%s: open failed: %s", control, strerror(errno)); + log_error("Is device-mapper driver missing from kernel?"); + return 0; + } + + return 1; +} + +void dm_task_destroy(struct dm_task *dmt) +{ + struct target *t, *n; + + for (t = dmt->head; t; t = n) { + n = t->next; + free(t->params); + free(t->type); + free(t); + } + + if (dmt->dev_name) + free(dmt->dev_name); + + if (dmt->newname) + free(dmt->newname); + + if (dmt->dmi.v4) + free(dmt->dmi.v4); + + if (dmt->uuid) + free(dmt->uuid); + + free(dmt); +} + +/* + * Protocol Version 1 compatibility functions. + */ + +#ifdef DM_COMPAT + +static int _dm_task_get_driver_version_v1(struct dm_task *dmt, char *version, + size_t size) +{ + unsigned int *v; + + if (!dmt->dmi.v1) { + version[0] = '\0'; + return 0; + } + + v = dmt->dmi.v1->version; + snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]); + return 1; +} + +/* Unmarshall the target info returned from a status call */ +static int _unmarshal_status_v1(struct dm_task *dmt, struct dm_ioctl_v1 *dmi) +{ + char *outbuf = (char *) dmi + dmi->data_start; + char *outptr = outbuf; + int32_t i; + struct dm_target_spec_v1 *spec; + + for (i = 0; i < dmi->target_count; i++) { + spec = (struct dm_target_spec_v1 *) outptr; + + if (!dm_task_add_target(dmt, spec->sector_start, + (uint64_t) spec->length, + spec->target_type, + outptr + sizeof(*spec))) + return 0; + + outptr = outbuf + spec->next; + } + + return 1; +} + +static int _dm_format_dev_v1(char *buf, int bufsize, uint32_t dev_major, + uint32_t dev_minor) +{ + int r; + + if (bufsize < 8) + return 0; + + r = snprintf(buf, bufsize, "%03x:%03x", dev_major, dev_minor); + if (r < 0 || r > bufsize - 1) + return 0; + + return 1; +} + +static int _dm_task_get_info_v1(struct dm_task *dmt, struct dm_info *info) +{ + if (!dmt->dmi.v1) + return 0; + + memset(info, 0, sizeof(*info)); + + info->exists = dmt->dmi.v1->flags & DM_EXISTS_FLAG ? 1 : 0; + if (!info->exists) + return 1; + + info->suspended = dmt->dmi.v1->flags & DM_SUSPEND_FLAG ? 1 : 0; + info->read_only = dmt->dmi.v1->flags & DM_READONLY_FLAG ? 1 : 0; + info->target_count = dmt->dmi.v1->target_count; + info->open_count = dmt->dmi.v1->open_count; + info->event_nr = 0; + info->major = MAJOR(dmt->dmi.v1->dev); + info->minor = MINOR(dmt->dmi.v1->dev); + info->live_table = 1; + info->inactive_table = 0; + + return 1; +} + +static const char *_dm_task_get_name_v1(struct dm_task *dmt) +{ + return (dmt->dmi.v1->name); +} + +static const char *_dm_task_get_uuid_v1(struct dm_task *dmt) +{ + return (dmt->dmi.v1->uuid); +} + +static struct dm_deps *_dm_task_get_deps_v1(struct dm_task *dmt) +{ + log_error("deps version 1 no longer supported by libdevmapper"); + return NULL; +} + +static struct dm_names *_dm_task_get_names_v1(struct dm_task *dmt) +{ + return (struct dm_names *) (((void *) dmt->dmi.v1) + + dmt->dmi.v1->data_start); +} + +static void *_add_target_v1(struct target *t, void *out, void *end) +{ + void *out_sp = out; + struct dm_target_spec_v1 sp; + size_t sp_size = sizeof(struct dm_target_spec_v1); + int len; + const char no_space[] = "Ran out of memory building ioctl parameter"; + + out += sp_size; + if (out >= end) { + log_error(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_error(no_space); + + log_error("t->params= '%s'", t->params); + return NULL; + } + strcpy((char *) out, t->params); + out += len + 1; + + /* align next block */ + out = _align(out, ALIGNMENT_V1); + + sp.next = out - out_sp; + + memcpy(out_sp, &sp, sp_size); + + return out; +} + +static struct dm_ioctl_v1 *_flatten_v1(struct dm_task *dmt) +{ + const size_t min_size = 16 * 1024; + const int (*version)[3]; + + struct dm_ioctl_v1 *dmi; + struct target *t; + size_t len = sizeof(struct dm_ioctl_v1); + void *b, *e; + int count = 0; + + for (t = dmt->head; t; t = t->next) { + len += sizeof(struct dm_target_spec_v1); + len += strlen(t->params) + 1 + ALIGNMENT_V1; + count++; + } + + if (count && dmt->newname) { + log_error("targets and newname are incompatible"); + return NULL; + } + + if (dmt->newname) + len += strlen(dmt->newname) + 1; + + /* + * Give len a minimum size so that we have space to store + * dependencies or status information. + */ + if (len < min_size) + len = min_size; + + if (!(dmi = malloc(len))) + return NULL; + + memset(dmi, 0, len); + + version = &_cmd_data_v1[dmt->type].version; + + dmi->version[0] = (*version)[0]; + dmi->version[1] = (*version)[1]; + dmi->version[2] = (*version)[2]; + + dmi->data_size = len; + dmi->data_start = sizeof(struct dm_ioctl_v1); + + if (dmt->dev_name) + strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name)); + + if (dmt->type == DM_DEVICE_SUSPEND) + dmi->flags |= DM_SUSPEND_FLAG; + if (dmt->read_only) + dmi->flags |= DM_READONLY_FLAG; + + if (dmt->minor >= 0) { + if (dmt->major <= 0) { + log_error("Missing major number for persistent device"); + return NULL; + } + dmi->flags |= DM_PERSISTENT_DEV_FLAG; + dmi->dev = MKDEV(dmt->major, dmt->minor); + } + + if (dmt->uuid) + strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid)); + + 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_v1(t, b, e))) + goto bad; + + if (dmt->newname) + strcpy(b, dmt->newname); + + return dmi; + + bad: + free(dmi); + return NULL; +} + +static int _dm_names_v1(struct dm_ioctl_v1 *dmi) +{ + const char *dev_dir = dm_dir(); + int r = 1, len; + const char *name; + struct dirent *dirent; + DIR *d; + struct dm_names *names, *old_names = NULL; + void *end = (void *) dmi + dmi->data_size; + struct stat buf; + char path[PATH_MAX]; + + if (!(d = opendir(dev_dir))) { + log_error("%s: opendir failed: %s", dev_dir, strerror(errno)); + return 0; + } + + names = (struct dm_names *) ((void *) dmi + dmi->data_start); + + names->dev = 0; /* Flags no data */ + + while ((dirent = readdir(d))) { + name = dirent->d_name; + + if (name[0] == '.' || !strcmp(name, "control")) + continue; + + if (old_names) + old_names->next = (uint32_t) ((void *) names - + (void *) old_names); + snprintf(path, sizeof(path), "%s/%s", dev_dir, name); + if (stat(path, &buf)) { + log_error("%s: stat failed: %s", path, strerror(errno)); + continue; + } + if (!S_ISBLK(buf.st_mode)) + continue; + names->dev = (uint64_t) buf.st_rdev; + names->next = 0; + len = strlen(name); + if (((void *) (names + 1) + len + 1) >= end) { + log_error("Insufficient buffer space for device list"); + r = 0; + break; + } + + strcpy(names->name, name); + + old_names = names; + names = _align((void *) ++names + len + 1, ALIGNMENT); + } + + if (closedir(d)) + log_error("%s: closedir failed: %s", dev_dir, strerror(errno)); + + return r; +} + +static int _dm_task_run_v1(struct dm_task *dmt) +{ + struct dm_ioctl_v1 *dmi; + unsigned int command; + + dmi = _flatten_v1(dmt); + if (!dmi) { + log_error("Couldn't create ioctl argument"); + return 0; + } + + if (!_open_control()) + return 0; + + if ((unsigned) dmt->type >= + (sizeof(_cmd_data_v1) / sizeof(*_cmd_data_v1))) { + log_error("Internal error: unknown device-mapper task %d", + dmt->type); + goto bad; + } + + command = _cmd_data_v1[dmt->type].cmd; + + if (dmt->type == DM_DEVICE_TABLE) + dmi->flags |= DM_STATUS_TABLE_FLAG; + + log_debug("dm %s %s %s %s", _cmd_data_v1[dmt->type].name, dmi->name, + dmi->uuid, dmt->newname ? dmt->newname : ""); + if (dmt->type == DM_DEVICE_LIST) { + if (!_dm_names_v1(dmi)) + goto bad; + } else if (ioctl(_control_fd, command, dmi) < 0) { + if (_log_suppress) + log_verbose("device-mapper ioctl cmd %d failed: %s", + _IOC_NR(command), strerror(errno)); + else + log_error("device-mapper ioctl cmd %d failed: %s", + _IOC_NR(command), strerror(errno)); + goto bad; + } + + switch (dmt->type) { + case DM_DEVICE_CREATE: + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev)); + break; + + case DM_DEVICE_REMOVE: + rm_dev_node(dmt->dev_name); + break; + + case DM_DEVICE_RENAME: + rename_dev_node(dmt->dev_name, dmt->newname); + break; + + case DM_DEVICE_MKNODES: + if (dmi->flags & DM_EXISTS_FLAG) + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), + MINOR(dmi->dev)); + else + rm_dev_node(dmt->dev_name); + break; + + case DM_DEVICE_STATUS: + case DM_DEVICE_TABLE: + if (!_unmarshal_status_v1(dmt, dmi)) + goto bad; + break; + + case DM_DEVICE_SUSPEND: + case DM_DEVICE_RESUME: + dmt->type = DM_DEVICE_INFO; + if (!dm_task_run(dmt)) + goto bad; + free(dmi); /* We'll use what info returned */ + return 1; + } + + dmt->dmi.v1 = dmi; + return 1; + + bad: + free(dmi); + return 0; +} + +#endif + +/* + * Protocol Version 4 functions. + */ + +int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size) +{ + unsigned int *v; + +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_driver_version_v1(dmt, version, size); +#endif + + if (!dmt->dmi.v4) { + version[0] = '\0'; + return 0; + } + + v = dmt->dmi.v4->version; + snprintf(version, size, "%u.%u.%u", v[0], v[1], v[2]); + return 1; +} + +static int _check_version(char *version, size_t size, int log_suppress) +{ + struct dm_task *task; + int r; + + if (!(task = dm_task_create(DM_DEVICE_VERSION))) { + log_error("Failed to get device-mapper version"); + version[0] = '\0'; + return 0; + } + + if (log_suppress) + _log_suppress = 1; + + r = dm_task_run(task); + dm_task_get_driver_version(task, version, size); + dm_task_destroy(task); + _log_suppress = 0; + + return r; +} + +/* + * Find out device-mapper's major version number the first time + * this is called and whether or not we support it. + */ +int dm_check_version(void) +{ + char libversion[64], dmversion[64]; + const char *compat = ""; + + if (_version_checked) + return _version_ok; + + _version_checked = 1; + + if (_check_version(dmversion, sizeof(dmversion), _dm_compat)) + return 1; + + if (!_dm_compat) + goto bad; + + log_verbose("device-mapper ioctl protocol version %d failed. " + "Trying protocol version 1.", _dm_version); + _dm_version = 1; + if (_check_version(dmversion, sizeof(dmversion), 0)) { + log_verbose("Using device-mapper ioctl protocol version 1"); + return 1; + } + + compat = "(compat)"; + + dm_get_library_version(libversion, sizeof(libversion)); + + log_error("Incompatible libdevmapper %s%s and kernel driver %s", + libversion, compat, dmversion); + + bad: + _version_ok = 0; + return 0; +} + +void *dm_get_next_target(struct dm_task *dmt, void *next, + uint64_t *start, uint64_t *length, + char **target_type, char **params) +{ + struct target *t = (struct target *) next; + + if (!t) + t = dmt->head; + + if (!t) + return NULL; + + *start = t->start; + *length = t->length; + *target_type = t->type; + *params = t->params; + + return t->next; +} + +/* Unmarshall the target info returned from a status call */ +static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi) +{ + char *outbuf = (char *) dmi + dmi->data_start; + char *outptr = outbuf; + uint32_t i; + struct dm_target_spec *spec; + + for (i = 0; i < dmi->target_count; i++) { + spec = (struct dm_target_spec *) outptr; + if (!dm_task_add_target(dmt, spec->sector_start, + spec->length, + spec->target_type, + outptr + sizeof(*spec))) + return 0; + + outptr = outbuf + spec->next; + } + + return 1; +} + +int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, + uint32_t dev_minor) +{ + int r; + +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_format_dev_v1(buf, bufsize, dev_major, dev_minor); +#endif + + if (bufsize < 8) + return 0; + + r = snprintf(buf, bufsize, "%03u:%03u", dev_major, dev_minor); + if (r < 0 || r > bufsize - 1) + return 0; + + return 1; +} + +int dm_task_get_info(struct dm_task *dmt, struct dm_info *info) +{ +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_info_v1(dmt, info); +#endif + + if (!dmt->dmi.v4) + return 0; + + memset(info, 0, sizeof(*info)); + + info->exists = dmt->dmi.v4->flags & DM_EXISTS_FLAG ? 1 : 0; + if (!info->exists) + return 1; + + info->suspended = dmt->dmi.v4->flags & DM_SUSPEND_FLAG ? 1 : 0; + info->read_only = dmt->dmi.v4->flags & DM_READONLY_FLAG ? 1 : 0; + info->live_table = dmt->dmi.v4->flags & DM_ACTIVE_PRESENT_FLAG ? 1 : 0; + info->inactive_table = dmt->dmi.v4->flags & DM_INACTIVE_PRESENT_FLAG ? + 1 : 0; + info->target_count = dmt->dmi.v4->target_count; + info->open_count = dmt->dmi.v4->open_count; + info->event_nr = dmt->dmi.v4->event_nr; + info->major = MAJOR(dmt->dmi.v4->dev); + info->minor = MINOR(dmt->dmi.v4->dev); + + return 1; +} + +const char *dm_task_get_name(struct dm_task *dmt) +{ +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_name_v1(dmt); +#endif + + return (dmt->dmi.v4->name); +} + +const char *dm_task_get_uuid(struct dm_task *dmt) +{ +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_uuid_v1(dmt); +#endif + + return (dmt->dmi.v4->uuid); +} + +struct dm_deps *dm_task_get_deps(struct dm_task *dmt) +{ +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_deps_v1(dmt); +#endif + + return (struct dm_deps *) (((void *) dmt->dmi.v4) + + dmt->dmi.v4->data_start); +} + +struct dm_names *dm_task_get_names(struct dm_task *dmt) +{ +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_get_names_v1(dmt); +#endif + + return (struct dm_names *) (((void *) dmt->dmi.v4) + + dmt->dmi.v4->data_start); +} + +int dm_task_set_ro(struct dm_task *dmt) +{ + dmt->read_only = 1; + return 1; +} + +int dm_task_set_newname(struct dm_task *dmt, const char *newname) +{ + if (!(dmt->newname = strdup(newname))) { + log_error("dm_task_set_newname: strdup(%s) failed", newname); + return 0; + } + + return 1; +} + +int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr) +{ + dmt->event_nr = event_nr; + + return 1; +} + +struct target *create_target(uint64_t start, uint64_t len, const char *type, + const char *params) +{ + struct target *t = malloc(sizeof(*t)); + + if (!t) { + log_error("create_target: malloc(%d) failed", sizeof(*t)); + return NULL; + } + + memset(t, 0, sizeof(*t)); + + if (!(t->params = strdup(params))) { + log_error("create_target: strdup(params) failed"); + goto bad; + } + + if (!(t->type = strdup(type))) { + log_error("create_target: strdup(type) failed"); + goto bad; + } + + t->start = start; + t->length = len; + return t; + + bad: + free(t->params); + free(t->type); + free(t); + return NULL; +} + +static void *_add_target(struct target *t, void *out, void *end) +{ + void *out_sp = out; + struct dm_target_spec sp; + size_t sp_size = sizeof(struct dm_target_spec); + int len; + const char no_space[] = "Ran out of memory building ioctl parameter"; + + out += sp_size; + if (out >= end) { + log_error(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_error(no_space); + + log_error("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, sp_size); + + return out; +} + +static struct dm_ioctl *_flatten(struct dm_task *dmt) +{ + const size_t min_size = 16 * 1024; + const int (*version)[3]; + + 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 (count && dmt->newname) { + log_error("targets and newname are incompatible"); + return NULL; + } + + if (dmt->newname) + len += strlen(dmt->newname) + 1; + + /* + * Give len a minimum size so that we have space to store + * dependencies or status information. + */ + if (len < min_size) + len = min_size; + + if (!(dmi = malloc(len))) + return NULL; + + memset(dmi, 0, len); + + version = &_cmd_data_v4[dmt->type].version; + + dmi->version[0] = (*version)[0]; + dmi->version[1] = (*version)[1]; + dmi->version[2] = (*version)[2]; + + dmi->data_size = len; + dmi->data_start = sizeof(struct dm_ioctl); + + if (dmt->dev_name) + strncpy(dmi->name, dmt->dev_name, sizeof(dmi->name)); + + if (dmt->type == DM_DEVICE_SUSPEND) + dmi->flags |= DM_SUSPEND_FLAG; + if (dmt->read_only) + dmi->flags |= DM_READONLY_FLAG; + + if (dmt->minor >= 0) { + if (dmt->major <= 0) { + log_error("Missing major number for persistent device"); + return NULL; + } + dmi->flags |= DM_PERSISTENT_DEV_FLAG; + dmi->dev = MKDEV(dmt->major, dmt->minor); + } + + if (dmt->uuid) + strncpy(dmi->uuid, dmt->uuid, sizeof(dmi->uuid)); + + dmi->target_count = count; + dmi->event_nr = dmt->event_nr; + + 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; + + if (dmt->newname) + strcpy(b, dmt->newname); + + return dmi; + + bad: + free(dmi); + return NULL; +} + +static int _create_and_load_v4(struct dm_task *dmt) +{ + struct dm_task *task; + int r; + + /* Use new task struct to create the device */ + if (!(task = dm_task_create(DM_DEVICE_CREATE))) { + log_error("Failed to create device-mapper task struct"); + return 0; + } + + /* Copy across relevant fields */ + if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) { + dm_task_destroy(task); + return 0; + } + + if (dmt->uuid && !dm_task_set_uuid(task, dmt->uuid)) { + dm_task_destroy(task); + return 0; + } + + task->major = dmt->major; + task->minor = dmt->minor; + + r = dm_task_run(task); + dm_task_destroy(task); + if (!r) + return r; + + /* Next load the table */ + if (!(task = dm_task_create(DM_DEVICE_RELOAD))) { + log_error("Failed to create device-mapper task struct"); + return 0; + } + + /* Copy across relevant fields */ + if (dmt->dev_name && !dm_task_set_name(task, dmt->dev_name)) { + dm_task_destroy(task); + return 0; + } + + task->read_only = dmt->read_only; + task->head = dmt->head; + task->tail = dmt->tail; + + r = dm_task_run(task); + + task->head = NULL; + task->tail = NULL; + dm_task_destroy(task); + if (!r) + return r; + + /* Use the original structure last so the info will be correct */ + dmt->type = DM_DEVICE_RESUME; + dmt->uuid = NULL; + free(dmt->uuid); + + r = dm_task_run(dmt); + + return r; +} + +int dm_task_run(struct dm_task *dmt) +{ + struct dm_ioctl *dmi = NULL; + unsigned int command; + +#ifdef DM_COMPAT + if (_dm_version == 1) + return _dm_task_run_v1(dmt); +#endif + + if ((unsigned) dmt->type >= + (sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) { + log_error("Internal error: unknown device-mapper task %d", + dmt->type); + goto bad; + } + + command = _cmd_data_v4[dmt->type].cmd; + + /* Old-style creation had a table supplied */ + if (dmt->type == DM_DEVICE_CREATE && dmt->head) + return _create_and_load_v4(dmt); + + if (!_open_control()) + return 0; + + dmi = _flatten(dmt); + if (!dmi) { + log_error("Couldn't create ioctl argument"); + return 0; + } + + if (dmt->type == DM_DEVICE_TABLE) + dmi->flags |= DM_STATUS_TABLE_FLAG; + + dmi->flags |= DM_EXISTS_FLAG; /* FIXME */ + log_debug("dm %s %s %s %s", _cmd_data_v4[dmt->type].name, dmi->name, + dmi->uuid, dmt->newname ? dmt->newname : ""); + if (ioctl(_control_fd, command, dmi) < 0) { + if (errno == ENXIO && ((dmt->type == DM_DEVICE_INFO) || + (dmt->type == DM_DEVICE_MKNODES))) { + dmi->flags &= ~DM_EXISTS_FLAG; /* FIXME */ + goto ignore_error; + } + if (_log_suppress) + log_verbose("device-mapper ioctl cmd %d failed: %s", + _IOC_NR(command), strerror(errno)); + else + log_error("device-mapper ioctl cmd %d failed: %s", + _IOC_NR(command), strerror(errno)); + goto bad; + } + + ignore_error: + switch (dmt->type) { + case DM_DEVICE_CREATE: + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev)); + break; + + case DM_DEVICE_REMOVE: + rm_dev_node(dmt->dev_name); + break; + + case DM_DEVICE_RENAME: + rename_dev_node(dmt->dev_name, dmt->newname); + break; + + case DM_DEVICE_MKNODES: + if (dmi->flags & DM_EXISTS_FLAG) + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), + MINOR(dmi->dev)); + else + rm_dev_node(dmt->dev_name); + break; + + case DM_DEVICE_STATUS: + case DM_DEVICE_TABLE: + case DM_DEVICE_WAITEVENT: + if (!_unmarshal_status(dmt, dmi)) + goto bad; + break; + } + + dmt->dmi.v4 = dmi; + return 1; + + bad: + free(dmi); + return 0; +} + +void dm_lib_release(void) +{ + if (_control_fd != -1) { + close(_control_fd); + _control_fd = -1; + } + update_devs(); +} + +void dm_lib_exit(void) +{ + if (_control_fd != -1) { + close(_control_fd); + _control_fd = -1; + } + _version_ok = 1; + _version_checked = 0; +} diff --git a/extras/multipath/libdevmapper/ioctl/libdm-compat.h b/extras/multipath/libdevmapper/ioctl/libdm-compat.h new file mode 100644 index 000000000..af7a9f1f7 --- /dev/null +++ b/extras/multipath/libdevmapper/ioctl/libdm-compat.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_LIBDM_COMPAT_H +#define _LINUX_LIBDM_COMPAT_H + +#include +#include +#include +#include + +struct dm_task; +struct dm_info; + +/* + * Old versions of structures for backwards compatibility. + */ + +struct dm_ioctl_v1 { + uint32_t version[3]; /* in/out */ + uint32_t data_size; /* total size of data passed in + * including this struct */ + + uint32_t data_start; /* offset to start of data + * relative to start of this struct */ + + int32_t target_count; /* in/out */ + int32_t open_count; /* out */ + uint32_t flags; /* in/out */ + + __kernel_dev_t dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ +}; + +struct dm_target_spec_v1 { + int32_t status; /* used when reading from kernel only */ + uint64_t sector_start; + uint32_t length; + uint32_t next; + + char target_type[DM_MAX_TYPE_NAME]; + +}; + +struct dm_target_deps_v1 { + uint32_t count; + + __kernel_dev_t dev[0]; /* out */ +}; + +enum { + /* Top level cmds */ + DM_VERSION_CMD_V1 = 0, + DM_REMOVE_ALL_CMD_V1, + + /* device level cmds */ + DM_DEV_CREATE_CMD_V1, + DM_DEV_REMOVE_CMD_V1, + DM_DEV_RELOAD_CMD_V1, + DM_DEV_RENAME_CMD_V1, + DM_DEV_SUSPEND_CMD_V1, + DM_DEV_DEPS_CMD_V1, + DM_DEV_STATUS_CMD_V1, + + /* target level cmds */ + DM_TARGET_STATUS_CMD_V1, + DM_TARGET_WAIT_CMD_V1, +}; + +#define DM_VERSION_V1 _IOWR(DM_IOCTL, DM_VERSION_CMD_V1, struct dm_ioctl) +#define DM_REMOVE_ALL_V1 _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD_V1, struct dm_ioctl) + +#define DM_DEV_CREATE_V1 _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD_V1, struct dm_ioctl) +#define DM_DEV_REMOVE_V1 _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD_V1, struct dm_ioctl) +#define DM_DEV_RELOAD_V1 _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD_V1, struct dm_ioctl) +#define DM_DEV_SUSPEND_V1 _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD_V1, struct dm_ioctl) +#define DM_DEV_RENAME_V1 _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD_V1, struct dm_ioctl) +#define DM_DEV_DEPS_V1 _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD_V1, struct dm_ioctl) +#define DM_DEV_STATUS_V1 _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD_V1, struct dm_ioctl) + +#define DM_TARGET_STATUS_V1 _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD_V1, struct dm_ioctl) +#define DM_TARGET_WAIT_V1 _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD_V1, struct dm_ioctl) + +/* *INDENT-OFF* */ +static struct cmd_data _cmd_data_v1[] = { + { "create", DM_DEV_CREATE_V1, {1, 0, 0} }, + { "reload", DM_DEV_RELOAD_V1, {1, 0, 0} }, + { "remove", DM_DEV_REMOVE_V1, {1, 0, 0} }, + { "remove_all", DM_REMOVE_ALL_V1, {1, 0, 0} }, + { "suspend", DM_DEV_SUSPEND_V1, {1, 0, 0} }, + { "resume", DM_DEV_SUSPEND_V1, {1, 0, 0} }, + { "info", DM_DEV_STATUS_V1, {1, 0, 0} }, + { "deps", DM_DEV_DEPS_V1, {1, 0, 0} }, + { "rename", DM_DEV_RENAME_V1, {1, 0, 0} }, + { "version", DM_VERSION_V1, {1, 0, 0} }, + { "status", DM_TARGET_STATUS_V1, {1, 0, 0} }, + { "table", DM_TARGET_STATUS_V1, {1, 0, 0} }, + { "waitevent", DM_TARGET_WAIT_V1, {1, 0, 0} }, + { "names", 0, {4, 0, 0} }, + { "clear", 0, {4, 0, 0} }, + { "mknodes", 0, {4, 0, 0} }, +}; +/* *INDENT-ON* */ + +#endif diff --git a/extras/multipath/libdevmapper/ioctl/libdm-targets.h b/extras/multipath/libdevmapper/ioctl/libdm-targets.h new file mode 100644 index 000000000..a8c0e2054 --- /dev/null +++ b/extras/multipath/libdevmapper/ioctl/libdm-targets.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef LIB_DMTARGETS_H +#define LIB_DMTARGETS_H + +#include + +struct dm_ioctl; +struct dm_ioctl_v1; + +struct target { + uint64_t start; + uint64_t length; + char *type; + char *params; + + struct target *next; +}; + +struct dm_task { + int type; + char *dev_name; + + struct target *head, *tail; + + int read_only; + uint32_t event_nr; + int major; + int minor; + union { + struct dm_ioctl *v4; + struct dm_ioctl_v1 *v1; + } dmi; + char *newname; + + char *uuid; +}; + +struct cmd_data { + const char *name; + const int cmd; + const int version[3]; +}; + +int dm_check_version(void); + +#endif diff --git a/extras/multipath/libdevmapper/libdevmapper.h b/extras/multipath/libdevmapper/libdevmapper.h new file mode 100644 index 000000000..6549af364 --- /dev/null +++ b/extras/multipath/libdevmapper/libdevmapper.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef LIB_DEVICE_MAPPER_H +#define LIB_DEVICE_MAPPER_H + +#include +#include + +#ifdef linux +# include +#endif + +/* + * 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); +void dm_log_init_verbose(int level); + +enum { + DM_DEVICE_CREATE, + DM_DEVICE_RELOAD, + DM_DEVICE_REMOVE, + DM_DEVICE_REMOVE_ALL, + + DM_DEVICE_SUSPEND, + DM_DEVICE_RESUME, + + DM_DEVICE_INFO, + DM_DEVICE_DEPS, + DM_DEVICE_RENAME, + + DM_DEVICE_VERSION, + + DM_DEVICE_STATUS, + DM_DEVICE_TABLE, + DM_DEVICE_WAITEVENT, + + DM_DEVICE_LIST, + + DM_DEVICE_CLEAR, + + DM_DEVICE_MKNODES +}; + +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); +int dm_task_set_uuid(struct dm_task *dmt, const char *uuid); + +/* + * Retrieve attributes after an info. + */ +struct dm_info { + int exists; + int suspended; + int live_table; + int inactive_table; + int32_t open_count; + uint32_t event_nr; + uint32_t major; + uint32_t minor; /* minor device number */ + int read_only; /* 0:read-write; 1:read-only */ + + int32_t target_count; +}; + +struct dm_deps { + uint32_t count; + uint32_t filler; + uint64_t device[0]; +}; + +struct dm_names { + uint64_t dev; + uint32_t next; /* Offset to next struct from start of this struct */ + char name[0]; +}; + +int dm_get_library_version(char *version, size_t size); +int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size); +int dm_task_get_info(struct dm_task *dmt, struct dm_info *dmi); +const char *dm_task_get_name(struct dm_task *dmt); +const char *dm_task_get_uuid(struct dm_task *dmt); + +struct dm_deps *dm_task_get_deps(struct dm_task *dmt); +struct dm_names *dm_task_get_names(struct dm_task *dmt); + +int dm_task_set_ro(struct dm_task *dmt); +int dm_task_set_newname(struct dm_task *dmt, const char *newname); +int dm_task_set_minor(struct dm_task *dmt, int minor); +int dm_task_set_major(struct dm_task *dmt, int major); +int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr); + +/* + * Use these to prepare for a create or reload. + */ +int dm_task_add_target(struct dm_task *dmt, + uint64_t start, + uint64_t size, const char *ttype, const char *params); + +/* + * Format major/minor numbers correctly for input to driver + */ +int dm_format_dev(char *buf, int bufsize, uint32_t dev_major, uint32_t dev_minor); + +/* Use this to retrive target information returned from a STATUS call */ +void *dm_get_next_target(struct dm_task *dmt, + void *next, uint64_t *start, uint64_t *length, + char **target_type, char **params); + +/* + * Call this to actually run the ioctl. + */ +int dm_task_run(struct dm_task *dmt); + +/* + * Configure the device-mapper directory + */ +int dm_set_dev_dir(const char *dir); +const char *dm_dir(void); + +/* Release library resources */ +void dm_lib_release(void); +void dm_lib_exit(void); + +#endif /* LIB_DEVICE_MAPPER_H */ diff --git a/extras/multipath/libdevmapper/libdm-common.c b/extras/multipath/libdevmapper/libdm-common.c new file mode 100644 index 000000000..b0affd1ee --- /dev/null +++ b/extras/multipath/libdevmapper/libdm-common.c @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#include "libdm-targets.h" +#include "libdm-common.h" +#include "list.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEV_DIR "/dev/" + +static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR; + +static int _verbose = 0; + +/* + * 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; + + if (level > _LOG_WARN && !_verbose) + return; + + va_start(ap, f); + + if (level < _LOG_WARN) + vfprintf(stderr, f, ap); + else + vprintf(f, ap); + + va_end(ap); + + if (level < _LOG_WARN) + fprintf(stderr, "\n"); + else + fprintf(stdout, "\n"); +} + +dm_log_fn _log = _default_log; + +void dm_log_init(dm_log_fn fn) +{ + _log = fn; +} + +void dm_log_init_verbose(int level) +{ + _verbose = level; +} + +static void _build_dev_path(char *buffer, size_t len, const char *dev_name) +{ + /* If there's a /, assume caller knows what they're doing */ + if (strchr(dev_name, '/')) + snprintf(buffer, len, "%s", dev_name); + else + snprintf(buffer, len, "%s/%s", _dm_dir, dev_name); +} + +int dm_get_library_version(char *version, size_t size) +{ + strncpy(version, DM_LIB_VERSION, size); + return 1; +} + +struct dm_task *dm_task_create(int type) +{ + struct dm_task *dmt = malloc(sizeof(*dmt)); + + if (!dm_check_version()) + return NULL; + + if (!dmt) { + log_error("dm_task_create: malloc(%d) failed", sizeof(*dmt)); + return NULL; + } + + memset(dmt, 0, sizeof(*dmt)); + + dmt->type = type; + dmt->minor = -1; + dmt->major = -1; + + return dmt; +} + +int dm_task_set_name(struct dm_task *dmt, const char *name) +{ + char *pos; + char path[PATH_MAX]; + struct stat st1, st2; + + if (dmt->dev_name) { + free(dmt->dev_name); + dmt->dev_name = NULL; + } + + /* If path was supplied, remove it if it points to the same device + * as its last component. + */ + if ((pos = strrchr(name, '/'))) { + snprintf(path, sizeof(path), "%s/%s", _dm_dir, pos + 1); + + if (stat(name, &st1) || stat(path, &st2) || + !(st1.st_dev == st2.st_dev)) { + log_error("dm_task_set_name: Device %s not found", + name); + return 0; + } + + name = pos + 1; + } + + if (!(dmt->dev_name = strdup(name))) { + log_error("dm_task_set_name: strdup(%s) failed", name); + return 0; + } + + return 1; +} + +int dm_task_set_uuid(struct dm_task *dmt, const char *uuid) +{ + if (dmt->uuid) { + free(dmt->uuid); + dmt->uuid = NULL; + } + + if (!(dmt->uuid = strdup(uuid))) { + log_error("dm_task_set_uuid: strdup(%s) failed", uuid); + return 0; + } + + return 1; +} + +int dm_task_set_major(struct dm_task *dmt, int major) +{ + dmt->major = major; + log_debug("Setting major: %d", dmt->major); + + return 1; +} + +int dm_task_set_minor(struct dm_task *dmt, int minor) +{ + dmt->minor = minor; + log_debug("Setting minor: %d", dmt->minor); + + return 1; +} + +int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t 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 int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor) +{ + char path[PATH_MAX]; + struct stat info; + dev_t dev = MKDEV(major, minor); + + _build_dev_path(path, sizeof(path), dev_name); + + if (stat(path, &info) >= 0) { + if (!S_ISBLK(info.st_mode)) { + log_error("A non-block device file at '%s' " + "is already present", path); + return 0; + } + + if (info.st_rdev == dev) + return 1; + + if (unlink(path) < 0) { + log_error("Unable to unlink device node for '%s'", + dev_name); + return 0; + } + } + + if (mknod(path, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, dev) < 0) { + log_error("Unable to make device node for '%s'", dev_name); + return 0; + } + + return 1; +} + +static int _rename_dev_node(const char *old_name, const char *new_name) +{ + char oldpath[PATH_MAX]; + char newpath[PATH_MAX]; + struct stat info; + + _build_dev_path(oldpath, sizeof(oldpath), old_name); + _build_dev_path(newpath, sizeof(newpath), new_name); + + if (stat(newpath, &info) == 0) { + if (!S_ISBLK(info.st_mode)) { + log_error("A non-block device file at '%s' " + "is already present", newpath); + return 0; + } + + if (unlink(newpath) < 0) { + if (errno == EPERM) { + /* devfs, entry has already been renamed */ + return 1; + } + log_error("Unable to unlink device node for '%s'", + new_name); + return 0; + } + } + + if (rename(oldpath, newpath) < 0) { + log_error("Unable to rename device node from '%s' to '%s'", + old_name, new_name); + return 0; + } + + return 1; +} + +static int _rm_dev_node(const char *dev_name) +{ + char path[PATH_MAX]; + struct stat info; + + _build_dev_path(path, sizeof(path), dev_name); + + if (stat(path, &info) < 0) + return 1; + + if (unlink(path) < 0) { + log_error("Unable to unlink device node for '%s'", dev_name); + return 0; + } + + return 1; +} + +typedef enum { + NODE_ADD, + NODE_DEL, + NODE_RENAME +} node_op_t; + +static int _do_node_op(node_op_t type, const char *dev_name, uint32_t major, + uint32_t minor, const char *old_name) +{ + switch (type) { + case NODE_ADD: + return _add_dev_node(dev_name, major, minor); + case NODE_DEL: + return _rm_dev_node(dev_name); + case NODE_RENAME: + return _rename_dev_node(old_name, dev_name); + } + + return 1; +} + +static LIST_INIT(_node_ops); + +struct node_op_parms { + struct list list; + node_op_t type; + char *dev_name; + uint32_t major; + uint32_t minor; + char *old_name; + char names[0]; +}; + +static void _store_str(char **pos, char **ptr, const char *str) +{ + strcpy(*pos, str); + *ptr = *pos; + *pos += strlen(*ptr) + 1; +} + +static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major, + uint32_t minor, const char *old_name) +{ + struct node_op_parms *nop; + size_t len = strlen(dev_name) + strlen(old_name) + 2; + char *pos; + + if (!(nop = malloc(sizeof(*nop) + len))) { + log_error("Insufficient memory to stack mknod operation"); + return 0; + } + + pos = nop->names; + nop->type = type; + nop->major = major; + nop->minor = minor; + + _store_str(&pos, &nop->dev_name, dev_name); + _store_str(&pos, &nop->old_name, old_name); + + list_add(&_node_ops, &nop->list); + + return 1; +} + +static void _pop_node_ops(void) +{ + struct list *noph, *nopht; + struct node_op_parms *nop; + + list_iterate_safe(noph, nopht, &_node_ops) { + nop = list_item(noph, struct node_op_parms); + _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor, + nop->old_name); + list_del(&nop->list); + free(nop); + } +} + +int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor) +{ + return _stack_node_op(NODE_ADD, dev_name, major, minor, ""); +} + +int rename_dev_node(const char *old_name, const char *new_name) +{ + return _stack_node_op(NODE_RENAME, new_name, 0, 0, old_name); +} + +int rm_dev_node(const char *dev_name) +{ + return _stack_node_op(NODE_DEL, dev_name, 0, 0, ""); +} + +void update_devs(void) +{ + _pop_node_ops(); +} + +int dm_set_dev_dir(const char *dir) +{ + snprintf(_dm_dir, sizeof(_dm_dir), "%s%s", dir, DM_DIR); + return 1; +} + +const char *dm_dir(void) +{ + return _dm_dir; +} diff --git a/extras/multipath/libdevmapper/libdm-common.h b/extras/multipath/libdevmapper/libdm-common.h new file mode 100644 index 000000000..2b7124286 --- /dev/null +++ b/extras/multipath/libdevmapper/libdm-common.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2001 Sistina Software (UK) Limited. + * + * This file is released under the LGPL. + */ + +#ifndef LIB_DMCOMMON_H +#define LIB_DMCOMMON_H + +#include "libdevmapper.h" + +#define _LOG_DEBUG 7 +#define _LOG_INFO 6 +#define _LOG_NOTICE 5 +#define _LOG_WARN 4 +#define _LOG_ERR 3 +#define _LOG_FATAL 2 + +extern dm_log_fn _log; + +#define log_error(msg, x...) _log(_LOG_ERR, __FILE__, __LINE__, msg, ## x) +#define log_print(msg, x...) _log(_LOG_WARN, __FILE__, __LINE__, msg, ## x) +#define log_verbose(msg, x...) _log(_LOG_NOTICE, __FILE__, __LINE__, msg, ## x) +#define log_very_verbose(msg, x...) _log(_LOG_INFO, __FILE__, __LINE__, msg, ## x) +#define log_debug(msg, x...) _log(_LOG_DEBUG, __FILE__, __LINE__, msg, ## x) + +struct target *create_target(uint64_t start, + uint64_t len, + const char *type, const char *params); + +int add_dev_node(const char *dev_name, uint32_t minor, uint32_t major); +int rm_dev_node(const char *dev_name); +int rename_dev_node(const char *old_name, const char *new_name); +void update_devs(void); + +#define DM_LIB_VERSION "1.00.07-ioctl (2003-11-21)" + +#endif diff --git a/extras/multipath/libdevmapper/list.h b/extras/multipath/libdevmapper/list.h new file mode 100644 index 000000000..df3d32aea --- /dev/null +++ b/extras/multipath/libdevmapper/list.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2001 Sistina Software + * + * This file is released under the LGPL. + */ + +#ifndef _LVM_LIST_H +#define _LVM_LIST_H + +#include + +struct list { + struct list *n, *p; +}; + +#define LIST_INIT(name) struct list name = { &(name), &(name) } + +static inline void list_init(struct list *head) +{ + head->n = head->p = head; +} + +static inline void list_add(struct list *head, struct list *elem) +{ + assert(head->n); + + elem->n = head; + elem->p = head->p; + + head->p->n = elem; + head->p = elem; +} + +static inline void list_add_h(struct list *head, struct list *elem) +{ + assert(head->n); + + elem->n = head->n; + elem->p = head; + + head->n->p = elem; + head->n = elem; +} + +static inline void list_del(struct list *elem) +{ + elem->n->p = elem->p; + elem->p->n = elem->n; +} + +static inline int list_empty(struct list *head) +{ + return head->n == head; +} + +static inline int list_end(struct list *head, struct list *elem) +{ + return elem->n == head; +} + +static inline struct list *list_next(struct list *head, struct list *elem) +{ + return (list_end(head, elem) ? NULL : elem->n); +} + +#define list_iterate(v, head) \ + for (v = (head)->n; v != head; v = v->n) + +#define list_uniterate(v, head, start) \ + for (v = (start)->p; v != head; v = v->p) + +#define list_iterate_safe(v, t, head) \ + for (v = (head)->n, t = v->n; v != head; v = t, t = v->n) + +static inline unsigned int list_size(const struct list *head) +{ + unsigned int s = 0; + const struct list *v; + + list_iterate(v, head) + s++; + + return s; +} + +#define list_item(v, t) \ + ((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->list)) + +#define list_struct_base(v, t, h) \ + ((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->h)) + +/* Given a known element in a known structure, locate another */ +#define struct_field(v, t, e, f) \ + (((t *)((uintptr_t)(v) - (uintptr_t)&((t *) 0)->e))->f) + +/* Given a known element in a known structure, locate the list head */ +#define list_head(v, t, e) struct_field(v, t, e, list) + +#endif diff --git a/extras/multipath/main.c b/extras/multipath/main.c index b5ab66443..f3f92e7eb 100644 --- a/extras/multipath/main.c +++ b/extras/multipath/main.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include "libdevmapper/libdevmapper.h" #include "main.h" static int @@ -458,9 +458,7 @@ static int make_dm_node(char * str) { int r = 0; - dev_t dev; char buff[FILE_NAME_SIZE]; - int major, minor; struct dm_names * names; unsigned next = 0; struct dm_task *dmt; @@ -486,14 +484,9 @@ make_dm_node(char * str) names = (void *) names + next; } while (next); - major = (int) MAJOR(names->dev); - minor = (int) MINOR(names->dev); - - dev = major << sizeof(dev_t); - dev = dev | minor; sprintf(buff, "/dev/mapper/%s", str); unlink(buff); - mknod(buff, 0600 | S_IFBLK, dev); + mknod(buff, 0600 | S_IFBLK, names->dev); out: dm_task_destroy(dmt); @@ -729,9 +722,10 @@ main(int argc, char *argv[]) if (conf.verbose) { print_all_path(&conf, all_paths); - printf("\n"); + fprintf(stdout, "\n"); print_all_mp(all_paths, mp, nmp); - printf("\n"); + fprintf(stdout, "\n"); + //printf("\n"); } if (conf.dry_run) diff --git a/extras/multipath/main.h b/extras/multipath/main.h index 019cb843a..692e5e459 100644 --- a/extras/multipath/main.h +++ b/extras/multipath/main.h @@ -46,7 +46,7 @@ #define TUR_CMD_LEN 6 #define MX_ALLOC_LEN 255 #define BLKGETSIZE _IO(0x12,96) -#define DM_TARGET "multipath" +#define DM_TARGET "striped" #define PINDEX(x,y) mp[(x)].pindex[(y)] @@ -105,8 +105,8 @@ struct env { /* Build version */ #define PROG "multipath" -#define VERSION_CODE 0x000006 -#define DATE_CODE 0x271103 +#define VERSION_CODE 0x000009 +#define DATE_CODE 0x0C0503 #define MULTIPATH_VERSION(version) \ (version >> 16) & 0xFF, \ -- 2.30.2