diff options
Diffstat (limited to 'extras/multipath/libdevmapper')
| -rw-r--r-- | extras/multipath/libdevmapper/Makefile | 15 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/ioctl/libdevmapper.c | 1092 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/ioctl/libdm-compat.h | 111 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/ioctl/libdm-targets.h | 51 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/libdevmapper.h | 147 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/libdm-common.c | 382 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/libdm-common.h | 38 | ||||
| -rw-r--r-- | extras/multipath/libdevmapper/list.h | 99 | 
8 files changed, 1935 insertions, 0 deletions
| diff --git a/extras/multipath/libdevmapper/Makefile b/extras/multipath/libdevmapper/Makefile new file mode 100644 index 0000000000..72b39cedf5 --- /dev/null +++ b/extras/multipath/libdevmapper/Makefile @@ -0,0 +1,15 @@ +# Makefile +# +# Copyright (C) 2003 Christophe Varoqui, <christophe.varoqui@free.fr> + +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 0000000000..ac7ba0c86a --- /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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <limits.h> + +#ifdef linux +#  include <linux/limits.h> +#  include <linux/kdev_t.h> +#  include <linux/dm-ioctl.h> +#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 0000000000..af7a9f1f71 --- /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 <inttypes.h> +#include <linux/dm-ioctl.h> +#include <linux/kdev_t.h> +#include <sys/ioctl.h> + +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 0000000000..a8c0e20547 --- /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 <inttypes.h> + +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 0000000000..6549af3641 --- /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 <inttypes.h> +#include <sys/types.h> + +#ifdef linux +#  include <linux/types.h> +#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 0000000000..b0affd1eed --- /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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> +#include <errno.h> +#include <linux/dm-ioctl.h> +#include <linux/kdev_t.h> + +#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 0000000000..2b71242861 --- /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 0000000000..df3d32aea8 --- /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 <assert.h> + +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 | 
