From 80f0b1f15e7a3bc63636a256ed1b86450d17a965 Mon Sep 17 00:00:00 2001 From: Alasdair Kergon Date: Tue, 21 Jan 2003 21:27:36 +0000 Subject: [PATCH] Prepare for ioctl version number change. --- lib/ioctl/libdevmapper.c | 510 ++++++++++++++++++++++++++++++++------ lib/ioctl/libdm-compat.h | 75 ++++++ lib/ioctl/libdm-targets.h | 19 +- 3 files changed, 532 insertions(+), 72 deletions(-) create mode 100644 lib/ioctl/libdm-compat.h diff --git a/lib/ioctl/libdevmapper.c b/lib/ioctl/libdevmapper.c index 028e034..d5f1509 100644 --- a/lib/ioctl/libdevmapper.c +++ b/lib/ioctl/libdevmapper.c @@ -4,60 +4,339 @@ * This file is released under the LGPL. */ -#include "libdevmapper.h" +#include "libdm-targets.h" +#include "libdm-common.h" +#include "libdm-compat.h" #include #include -#include #include #include #include -#include #include -#include - #include -#include -#include -#include "libdm-targets.h" -#include "libdm-common.h" -#define ALIGNMENT sizeof(int) +/* + * 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 == 2 && 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; /* - * Ensure build compatibility. The hard-coded version here (major, minor) - * is the highest present in the _cmd_data array below. + * Support both old and new major numbers to ease the transition. + * Clumsy, but only temporary. */ -#if DM_VERSION_MAJOR != 1 || DM_VERSION_MINOR < 0 - #error The version of dm-ioctl.h included is incompatible. +#if DM_VERSION_MAJOR == 2 && defined(DM_COMPAT) +const int _dm_compat = 1; +#else +const int _dm_compat = 0; #endif -static struct { - char *name; - int cmd; - int version[3]; -} _cmd_data[] = { - { "create", DM_DEV_CREATE, {1, 0, 0} }, - { "reload", DM_DEV_RELOAD, {1, 0, 0} }, - { "remove", DM_DEV_REMOVE, {1, 0, 0} }, - { "remove_all", DM_REMOVE_ALL, {1, 0, 0} }, - { "suspend", DM_DEV_SUSPEND, {1, 0, 0} }, - { "resume", DM_DEV_SUSPEND, {1, 0, 0} }, - { "info", DM_DEV_STATUS, {1, 0, 0} }, - { "deps", DM_DEV_DEPS, {1, 0, 0} }, - { "rename", DM_DEV_RENAME, {1, 0, 0} }, - { "version", DM_VERSION, {1, 0, 0} }, - { "status", DM_TARGET_STATUS, {1, 0, 0} }, - { "table", DM_TARGET_STATUS, {1, 0, 0} }, - { "waitevent", DM_TARGET_WAIT, {1, 0, 0} }, + +/* *INDENT-OFF* */ +static struct cmd_data _cmd_data_v2[] = { + {"create", DM_DEV_CREATE, {2, 0, 0}}, + {"reload", DM_DEV_RELOAD, {2, 0, 0}}, + {"remove", DM_DEV_REMOVE, {2, 0, 0}}, + {"remove_all", DM_REMOVE_ALL, {2, 0, 0}}, + {"suspend", DM_DEV_SUSPEND, {2, 0, 0}}, + {"resume", DM_DEV_SUSPEND, {2, 0, 0}}, + {"info", DM_DEV_STATUS, {2, 0, 0}}, + {"deps", DM_DEV_DEPS, {2, 0, 0}}, + {"rename", DM_DEV_RENAME, {2, 0, 0}}, + {"version", DM_VERSION, {2, 0, 0}}, + {"status", DM_TARGET_STATUS, {2, 0, 0}}, + {"table", DM_TARGET_STATUS, {2, 0, 0}}, + {"waitevent", DM_TARGET_WAIT, {2, 0, 0}}, }; +/* *INDENT-ON* */ + +#define ALIGNMENT sizeof(int) static void *_align(void *ptr, unsigned int a) { - register unsigned long align = --a; + register unsigned long agn = --a; + + return (void *) (((unsigned long) ptr + agn) & ~agn); +} + +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 + sizeof(struct dm_ioctl_v1); + char *outptr = outbuf; + uint32_t i; + struct dm_target_spec_v1 *spec = (struct dm_target_spec_v1 *) outptr; + size_t spec_size = sizeof(struct dm_target_spec_v1); + + for (i = 0; i < dmi->target_count; i++) { + + if (!dm_task_add_target(dmt, spec->sector_start, + (uint64_t) spec->length, + spec->target_type, outptr + spec_size)) + return 0; + + outptr += spec_size; + outptr += strlen(outptr) + 1; + + _align(outptr, ALIGNMENT); + } + + 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->major = MAJOR(dmt->dmi.v1->dev); + info->minor = MINOR(dmt->dmi.v1->dev); + + return 1; +} + +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) +{ + return (struct dm_deps *) (((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); + + 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; + 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) { + dmi->flags |= DM_PERSISTENT_DEV_FLAG; + dmi->dev = MKDEV(0, 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_task_run_v1(struct dm_task *dmt) +{ + int fd = -1; + struct dm_ioctl_v1 *dmi; + unsigned int command; + char control[PATH_MAX]; + + dmi = _flatten_v1(dmt); + if (!dmi) { + log_error("Couldn't create ioctl argument"); + return 0; + } + + snprintf(control, sizeof(control), "%s/control", dm_dir()); + + if ((fd = open(control, O_RDWR)) < 0) { + log_error("%s: open failed: %s", control, strerror(errno)); + log_error("Is device-mapper driver missing from kernel?"); + goto bad; + } + + 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 (ioctl(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_STATUS: + case DM_DEVICE_TABLE: + if (!_unmarshal_status_v1(dmt, dmi)) + goto bad; + break; + } - return (void *) (((unsigned long) ptr + align) & ~align); + dmt->dmi.v1 = dmi; + close(fd); + return 1; + + bad: + free(dmi); + if (fd >= 0) + close(fd); + return 0; } void dm_task_destroy(struct dm_task *dmt) @@ -77,8 +356,8 @@ void dm_task_destroy(struct dm_task *dmt) if (dmt->newname) free(dmt->newname); - if (dmt->dmi) - free(dmt->dmi); + if (dmt->dmi.v2) + free(dmt->dmi.v2); if (dmt->uuid) free(dmt->uuid); @@ -90,14 +369,83 @@ int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size) { unsigned int *v; - if (!dmt->dmi) + if (_dm_version == 1) + return _dm_task_get_driver_version_v1(dmt, version, size); + + if (!dmt->dmi.v2) { + version[0] = '\0'; return 0; + } - v = dmt->dmi->version; + v = dmt->dmi.v2->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) +{ + static int _checked = 0; + static int _ok = 1; + char libversion[64], dmversion[64]; + const char *compat = ""; + + if (_checked) + return _ok; + + _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: + _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) @@ -123,18 +471,19 @@ static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi) { char *outbuf = (char *) dmi + sizeof(struct dm_ioctl); char *outptr = outbuf; - int i; + uint32_t i; + struct dm_target_spec *spec = (struct dm_target_spec *) outptr; + size_t spec_size = sizeof(struct dm_target_spec); for (i = 0; i < dmi->target_count; i++) { - struct dm_target_spec *spec = (struct dm_target_spec *) outptr; - - if (!dm_task_add_target(dmt, spec->sector_start, spec->length, - spec->target_type, - outptr + sizeof(*spec))) + if (!dm_task_add_target(dmt, spec->sector_start, + spec->length, + spec->target_type, outptr + spec_size)) return 0; - outptr += sizeof(struct dm_target_spec); + outptr += spec_size; outptr += strlen(outptr) + 1; + _align(outptr, ALIGNMENT); } @@ -143,33 +492,43 @@ static int _unmarshal_status(struct dm_task *dmt, struct dm_ioctl *dmi) int dm_task_get_info(struct dm_task *dmt, struct dm_info *info) { - if (!dmt->dmi) + if (_dm_version == 1) + return _dm_task_get_info_v1(dmt, info); + + if (!dmt->dmi.v2) return 0; memset(info, 0, sizeof(*info)); - info->exists = dmt->dmi->flags & DM_EXISTS_FLAG ? 1 : 0; + info->exists = dmt->dmi.v2->flags & DM_EXISTS_FLAG ? 1 : 0; if (!info->exists) return 1; - info->suspended = dmt->dmi->flags & DM_SUSPEND_FLAG ? 1 : 0; - info->read_only = dmt->dmi->flags & DM_READONLY_FLAG ? 1 : 0; - info->target_count = dmt->dmi->target_count; - info->open_count = dmt->dmi->open_count; - info->major = MAJOR(dmt->dmi->dev); - info->minor = MINOR(dmt->dmi->dev); + info->suspended = dmt->dmi.v2->flags & DM_SUSPEND_FLAG ? 1 : 0; + info->read_only = dmt->dmi.v2->flags & DM_READONLY_FLAG ? 1 : 0; + info->target_count = dmt->dmi.v2->target_count; + info->open_count = dmt->dmi.v2->open_count; + info->major = MAJOR(dmt->dmi.v2->dev); + info->minor = MINOR(dmt->dmi.v2->dev); return 1; } const char *dm_task_get_uuid(struct dm_task *dmt) { - return (dmt->dmi->uuid); + if (_dm_version == 1) + return _dm_task_get_uuid_v1(dmt); + + return (dmt->dmi.v2->uuid); } struct dm_deps *dm_task_get_deps(struct dm_task *dmt) { - return (struct dm_deps *) (((void *) dmt->dmi) + dmt->dmi->data_start); + if (_dm_version == 1) + return _dm_task_get_deps_v1(dmt); + + return (struct dm_deps *) (((void *) dmt->dmi.v2) + + dmt->dmi.v2->data_start); } int dm_task_set_ro(struct dm_task *dmt) @@ -225,10 +584,11 @@ 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 += sizeof(struct dm_target_spec); + out += sp_size; if (out >= end) { log_error(no_space); return NULL; @@ -254,7 +614,7 @@ static void *_add_target(struct target *t, void *out, void *end) out = _align(out, ALIGNMENT); sp.next = out - out_sp; - memcpy(out_sp, &sp, sizeof(sp)); + memcpy(out_sp, &sp, sp_size); return out; } @@ -262,6 +622,7 @@ static void *_add_target(struct target *t, void *out, void *end) 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; @@ -295,9 +656,12 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt) memset(dmi, 0, len); - dmi->version[0] = _cmd_data[dmt->type].version[0]; - dmi->version[1] = _cmd_data[dmt->type].version[1]; - dmi->version[2] = _cmd_data[dmt->type].version[2]; + version = &_cmd_data_v2[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); @@ -339,10 +703,14 @@ static struct dm_ioctl *_flatten(struct dm_task *dmt) int dm_task_run(struct dm_task *dmt) { int fd = -1; - struct dm_ioctl *dmi = _flatten(dmt); + struct dm_ioctl *dmi; unsigned int command; char control[PATH_MAX]; + if (_dm_version == 1) + return _dm_task_run_v1(dmt); + + dmi = _flatten(dmt); if (!dmi) { log_error("Couldn't create ioctl argument"); return 0; @@ -356,29 +724,33 @@ int dm_task_run(struct dm_task *dmt) goto bad; } - if (dmt->type >= (sizeof(_cmd_data) / sizeof(*_cmd_data))) { + if ((unsigned) dmt->type >= + (sizeof(_cmd_data_v2) / sizeof(*_cmd_data_v2))) { log_error("Internal error: unknown device-mapper task %d", dmt->type); goto bad; } - command = _cmd_data[dmt->type].cmd; + command = _cmd_data_v2[dmt->type].cmd; if (dmt->type == DM_DEVICE_TABLE) dmi->flags |= DM_STATUS_TABLE_FLAG; - - log_debug("dm %s %s %s %s", _cmd_data[dmt->type].name, dmi->name, + log_debug("dm %s %s %s %s", _cmd_data_v2[dmt->type].name, dmi->name, dmi->uuid, dmt->newname ? dmt->newname : ""); if (ioctl(fd, command, dmi) < 0) { - log_error("device-mapper ioctl cmd %d failed: %s", - _IOC_NR(command), strerror(errno)); + 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, dmi->dev); + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev)); break; case DM_DEVICE_REMOVE: @@ -396,7 +768,7 @@ int dm_task_run(struct dm_task *dmt) break; } - dmt->dmi = dmi; + dmt->dmi.v2 = dmi; close(fd); return 1; diff --git a/lib/ioctl/libdm-compat.h b/lib/ioctl/libdm-compat.h new file mode 100644 index 0000000..4d96a9b --- /dev/null +++ b/lib/ioctl/libdm-compat.h @@ -0,0 +1,75 @@ +/* + * 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 */ + + uint32_t target_count; /* in/out */ + uint32_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 */ +}; + +/* *INDENT-OFF* */ +static struct cmd_data _cmd_data_v1[] = { + { "create", DM_DEV_CREATE, {1, 0, 0} }, + { "reload", DM_DEV_RELOAD, {1, 0, 0} }, + { "remove", DM_DEV_REMOVE, {1, 0, 0} }, + { "remove_all", DM_REMOVE_ALL, {1, 0, 0} }, + { "suspend", DM_DEV_SUSPEND, {1, 0, 0} }, + { "resume", DM_DEV_SUSPEND, {1, 0, 0} }, + { "info", DM_DEV_STATUS, {1, 0, 0} }, + { "deps", DM_DEV_DEPS, {1, 0, 0} }, + { "rename", DM_DEV_RENAME, {1, 0, 0} }, + { "version", DM_VERSION, {1, 0, 0} }, + { "status", DM_TARGET_STATUS, {1, 0, 0} }, + { "table", DM_TARGET_STATUS, {1, 0, 0} }, + { "waitevent", DM_TARGET_WAIT, {1, 0, 0} }, +}; +/* *INDENT-ON* */ + +#endif diff --git a/lib/ioctl/libdm-targets.h b/lib/ioctl/libdm-targets.h index 6e5838d..51ce526 100644 --- a/lib/ioctl/libdm-targets.h +++ b/lib/ioctl/libdm-targets.h @@ -7,7 +7,10 @@ #ifndef LIB_DMTARGETS_H #define LIB_DMTARGETS_H -#include "libdevmapper.h" +#include + +struct dm_ioctl; +struct dm_ioctl_v1; struct target { uint64_t start; @@ -26,11 +29,21 @@ struct dm_task { int read_only; int minor; - struct dm_ioctl *dmi; + union { + struct dm_ioctl *v2; + struct dm_ioctl_v1 *v1; + } dmi; char *newname; char *uuid; }; -#endif +struct cmd_data { + const char *name; + const int cmd; + const int version[3]; +}; +int dm_check_version(void); + +#endif -- 2.43.5