# # old_revision [2a1aa0228658a33cec42dc65322034564ef2447d] # # add_dir "include/netbsd" # # add_file "include/netbsd/netbsd-dm.h" # content [67689abc00cc39c29634a64e7c48ccc967b5f240] # # add_file "lib/ioctl/libdm-nbsd-iface.c" # content [ea49d27800b9cbd549b444a85592d7de2096300f] # # add_file "lib/ioctl/libdm_netbsd.c" # content [577de2513c661be5b82574c06e924d762a6f3380] # ============================================================ --- include/netbsd/netbsd-dm.h 67689abc00cc39c29634a64e7c48ccc967b5f240 +++ include/netbsd/netbsd-dm.h 67689abc00cc39c29634a64e7c48ccc967b5f240 @@ -0,0 +1,173 @@ +#ifndef __NETBSD_DM_H__ +#define __NETBSD_DM_H__ + +#include + +#define DM_CMD_LEN 16 + +#define DM_IOCTL 0xfd + +#define DM_IOCTL_CMD 0 + +#define NETBSD_DM_IOCTL _IOWR(DM_IOCTL, DM_IOCTL_CMD, struct plistref) + + +/* + * DM-ioctl dictionary. + * + * This contains general information about dm device. + * + * + * command + * ... + * + * event_nr + * ... + * + * name + * ... + * + * uuid + * ... + * + * dev + * + * + * flags + * + * + * version + * + * ... + * ... + * ... + * + * + * cmd_data + * + * + * + * + * + * Available commands from _cmd_data_v4. + * + * create, reload, remove, remove_all, suspend, + * resume, info, deps, rename, version, status, + * table, waitevent, names, clear, mknodes, + * targets, message, setgeometry + * + */ + +/* + * DM_LIST_VERSIONS command dictionary entry. + * Lists all available targets with their version. + * + * + * + * name + * ... + * + * version + * + * ... + * ... + * ... + * + * + * + * + */ + +#define DM_IOCTL_COMMAND "command" +#define DM_IOCTL_VERSION "version" +#define DM_IOCTL_OPEN "open_count" +#define DM_IOCTL_DEV "dev" +#define DM_IOCTL_NAME "name" +#define DM_IOCTL_UUID "uuid" +#define DM_IOCTL_EVENT "event_nr" +#define DM_IOCTL_FLAGS "flags" +#define DM_IOCTL_CMD_DATA "cmd_data" + +#define DM_TARGETS_NAME "name" +#define DM_TARGETS_VERSION "ver" + +#define DM_DEV_NEWNAME "newname" +#define DM_DEV_NAME "name" +#define DM_DEV_DEV "dev" + +#define DM_TABLE_TYPE "type" +#define DM_TABLE_START "start" +#define DM_TABLE_STAT "status" +#define DM_TABLE_LENGTH "length" +#define DM_TABLE_PARAMS "params" + + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_EXISTS_FLAG (1 << 2) /* In/Out */ /* XXX. This flag is undocumented. */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +/* + * This flag is now ignored. + */ +#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ + +/* + * Set this to avoid attempting to freeze any filesystem when suspending. + */ +#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */ + +/* + * Set this to suspend without flushing queued ios. + */ +#define DM_NOFLUSH_FLAG (1 << 11) /* In */ + + +#ifdef __LIB_DEVMAPPER__ + +# define MAJOR(x) major((x)) +# define MINOR(x) minor((x)) +# define MKDEV(x,y) makedev((x),(y)) + +/* Name of device-mapper driver in kernel */ +#define DM_NAME "dm" + +/* Types for nbsd_get_dm_major */ +#define DM_CHAR_MAJOR 1 +#define DM_BLOCK_MAJOR 2 + +/* libdm_netbsd.c */ + +int nbsd_get_dm_major(uint32_t *, uint32_t *, int); /* Get dm device major/minor numbers */ + +int nbsd_dmi_add_cmd(const char *, prop_dictionary_t); +int nbsd_dmi_add_version(const int [3], prop_dictionary_t); +int nbsd_dm_add_uint(const char *, uint64_t, prop_dictionary_t); +int nbsd_dm_add_str(const char *, char *, prop_dictionary_t ); + +struct dm_ioctl* nbsd_dm_dict_to_dmi(prop_dictionary_t, const int); + +#endif /* __LIB_DEVMAPPER__ */ + +#endif /* __NETBSD_DM_H__ */ ============================================================ --- lib/ioctl/libdm-nbsd-iface.c ea49d27800b9cbd549b444a85592d7de2096300f +++ lib/ioctl/libdm-nbsd-iface.c ea49d27800b9cbd549b444a85592d7de2096300f @@ -0,0 +1,1060 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2008 Adam Hamsik. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU Lesser General Public License v.2.1. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" +#include "libdm-targets.h" +#include "libdm-common.h" + +#include +#include +#include +#include + +#include + +#include + +/* + * 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 unsigned _dm_version_minor = 0; +static unsigned _dm_version_patchlevel = 0; + +static int _control_fd = -1; +static int _version_checked = 0; +static int _version_ok = 1; +static unsigned _ioctl_buffer_double_factor = 0; + +/* + * If ioctl call is added to this list + */ + +/* *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}}, +#ifdef DM_LIST_VERSIONS + {"targets", DM_LIST_VERSIONS, {4, 1, 0}}, +#endif +#ifdef DM_TARGET_MSG + {"message", DM_TARGET_MSG, {4, 2, 0}}, +#endif +#ifdef DM_DEV_SET_GEOMETRY + {"setgeometry", DM_DEV_SET_GEOMETRY, {4, 6, 0}}, +#endif +}; +/* *INDENT-ON* */ + +/* + * In NetBSD we use sysctl to get kernel drivers info. control device + * has predefined minor number 0 and major number = char major number + * of dm driver. First slot is therefore ocupied with control device + * and minor device starts from 1; + */ + +static int _control_device_number(uint32_t *major, uint32_t *minor) +{ + + nbsd_get_dm_major(major,minor,DM_CHAR_MAJOR); + +#ifdef __NETBSD_PUD__ + +#define DM_MAJOR 377; + + *major = DM_MAJOR; + *minor = 0; +#endif + + return 1; +} + +/* + * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong + */ +static int _control_exists(const char *control, uint32_t major, uint32_t minor) +{ + struct stat buf; + + if (stat(control, &buf) < 0) { + if (errno != ENOENT) + log_sys_error("stat", control); + return 0; + } + + if (!S_ISCHR(buf.st_mode)) { + log_verbose("%s: Wrong inode type", control); + if (!unlink(control)) + return 0; + log_sys_error("unlink", control); + return -1; + } + + if (major && buf.st_rdev != MKDEV(major, minor)) { + log_verbose("%s: Wrong device number: (%u, %u) instead of " + "(%u, %u)", control, + MAJOR(buf.st_mode), MINOR(buf.st_mode), + major, minor); + if (!unlink(control)) + return 0; + log_sys_error("unlink", control); + return -1; + } + + return 1; +} + +static int _create_control(const char *control, uint32_t major, uint32_t minor) +{ + int ret; + mode_t old_umask; + + if (!major) + return 0; + + old_umask = umask(0022); + ret = dm_create_dir(dm_dir()); + umask(old_umask); + + if (!ret) + return 0; + + log_verbose("Creating device %s (%u, %u)", control, major, minor); + + if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR, + MKDEV(major, minor)) < 0) { + log_sys_error("mknod", control); + return 0; + } + + + return 1; +} + +/* Keep only for compatibility with linux version in NetBSD. */ +int dm_is_dm_major(uint32_t major) +{ + return 1; +} + +/* Open control device if doesn't exist create it. */ +static int _open_control(void) +{ + char control[PATH_MAX]; + uint32_t major = 0, minor = 0; + + if (_control_fd != -1) + return 1; + + snprintf(control, sizeof(control), "%s/control", dm_dir()); + + if (!_control_device_number(&major, &minor)) + log_error("Is device-mapper driver missing from kernel?"); + + if (!_control_exists(control, major, minor) && + !_create_control(control, major, minor)) + goto error; + + if ((_control_fd = open(control, O_RDWR)) < 0) { + log_sys_error("open", control); + goto error; + } + + return 1; + +error: + log_error("Failure to communicate with kernel device-mapper driver."); + return 0; +} + +/* + * Destroy dm task structure there are some dynamically alocated values there. + * name, uuid, head, tail list. + */ +void dm_task_destroy(struct dm_task *dmt) +{ + struct target *t, *n; + + for (t = dmt->head; t; t = n) { + n = t->next; + dm_free(t->params); + dm_free(t->type); + dm_free(t); + } + + if (dmt->dev_name) + dm_free(dmt->dev_name); + + if (dmt->newname) + dm_free(dmt->newname); + + if (dmt->message) + dm_free(dmt->message); + + if (dmt->dmi.v4) + dm_free(dmt->dmi.v4); + + if (dmt->uuid) + dm_free(dmt->uuid); + + dm_free(dmt); + +} + +/* Get kernel driver version from dm_ioctl structure. */ +int dm_task_get_driver_version(struct dm_task *dmt, char *version, size_t size) +{ + unsigned *v; + + 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]); + _dm_version_minor = v[1]; + _dm_version_patchlevel = v[2]; + + return 1; +} + +/* Get kernel driver protocol version and comapre it with library version. */ +static int _check_version(char *version, size_t size) +{ + 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; + } + + r = dm_task_run(task); + dm_task_get_driver_version(task, version, size); + dm_task_destroy(task); + + 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 dmversion[64]; + + if (_version_checked) + return _version_ok; + + _version_checked = 1; + + if (_check_version(dmversion, sizeof(dmversion))) + return 1; + + + return 0; +} + +/* Get next target(table description) from list pointed by dmt->head. */ +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; + + if (bufsize < 8) + return 0; + + r = snprintf(buf, (size_t) bufsize, "%u:%u", dev_major, dev_minor); + if (r < 0 || r > bufsize - 1) + return 0; + + return 1; +} + +/* Fill info from dm_ioctl structure. Look at DM_EXISTS_FLAG*/ +int dm_task_get_info(struct dm_task *dmt, struct dm_info *info) +{ + 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; +} + +/* Unsupported on NetBSD */ +uint32_t dm_task_get_read_ahead(const struct dm_task *dmt, uint32_t *read_ahead) +{ + return 0; +} + +const char *dm_task_get_name(const struct dm_task *dmt) +{ + + return (dmt->dmi.v4->name); +} + +const char *dm_task_get_uuid(const struct dm_task *dmt) +{ + + return (dmt->dmi.v4->uuid); +} + +struct dm_deps *dm_task_get_deps(struct dm_task *dmt) +{ + return (struct dm_deps *) (((void *) dmt->dmi.v4) + + dmt->dmi.v4->data_start); +} + +struct dm_names *dm_task_get_names(struct dm_task *dmt) +{ + return (struct dm_names *) (((void *) dmt->dmi.v4) + + dmt->dmi.v4->data_start); +} + +struct dm_versions *dm_task_get_versions(struct dm_task *dmt) +{ + return (struct dm_versions *) (((void *) dmt->dmi.v4) + + dmt->dmi.v4->data_start); +} + +int dm_task_set_ro(struct dm_task *dmt) +{ + dmt->read_only = 1; + return 1; +} + +/* Unsupported on NetBSD */ +int dm_task_set_read_ahead(struct dm_task *dmt, uint32_t read_ahead, + uint32_t read_ahead_flags) +{ + return 0; +} + +int dm_task_suppress_identical_reload(struct dm_task *dmt) +{ + dmt->suppress_identical_reload = 1; + return 1; +} + +int dm_task_set_newname(struct dm_task *dmt, const char *newname) +{ + if (!(dmt->newname = dm_strdup(newname))) { + log_error("dm_task_set_newname: strdup(%s) failed", newname); + return 0; + } + + return 1; +} + +int dm_task_set_message(struct dm_task *dmt, const char *message) +{ + if (!(dmt->message = dm_strdup(message))) { + log_error("dm_task_set_message: strdup(%s) failed", message); + return 0; + } + + return 1; +} + +int dm_task_set_sector(struct dm_task *dmt, uint64_t sector) +{ + dmt->sector = sector; + + return 1; +} + +/* Unsupported in NetBSD */ +int dm_task_set_geometry(struct dm_task *dmt, const char *cylinders, + const char *heads, const char *sectors, const char *start) +{ + return 0; +} + +int dm_task_no_flush(struct dm_task *dmt) +{ + dmt->no_flush = 1; + + return 1; +} + +int dm_task_no_open_count(struct dm_task *dmt) +{ + dmt->no_open_count = 1; + + return 1; +} + +int dm_task_skip_lockfs(struct dm_task *dmt) +{ + dmt->skip_lockfs = 1; + + return 1; +} + +int dm_task_set_event_nr(struct dm_task *dmt, uint32_t event_nr) +{ + dmt->event_nr = event_nr; + + return 1; +} + +/* Allocate one target(table description) entry. */ +struct target *create_target(uint64_t start, uint64_t len, const char *type, + const char *params) +{ + struct target *t = dm_malloc(sizeof(*t)); + + if (!t) { + log_error("create_target: malloc(%" PRIsize_t ") failed", + sizeof(*t)); + return NULL; + } + + memset(t, 0, sizeof(*t)); + + if (!(t->params = dm_strdup(params))) { + log_error("create_target: strdup(params) failed"); + goto bad; + } + + if (!(t->type = dm_strdup(type))) { + log_error("create_target: strdup(type) failed"); + goto bad; + } + + t->start = start; + t->length = len; + return t; + + bad: + dm_free(t->params); + dm_free(t->type); + dm_free(t); + return NULL; +} + +/* Parse given dm task structure to proplib dictionary. */ +static int _flatten(struct dm_task *dmt, unsigned repeat_count, + prop_dictionary_t dm_dict) +{ + prop_array_t cmd_array; + prop_dictionary_t target_spec; + + struct target *t; + + size_t len; + char type[DM_MAX_TYPE_NAME]; + + uint32_t flags; + int count = 0; + const int (*version)[3]; + + flags = 0; + version = &_cmd_data_v4[dmt->type].version; + + cmd_array = prop_array_create(); + + for (t = dmt->head; t; t = t->next) { + target_spec = prop_dictionary_create(); + + prop_dictionary_set_uint64(target_spec,DM_TABLE_START,t->start); + prop_dictionary_set_uint64(target_spec,DM_TABLE_LENGTH,t->length); + + strlcpy(type,t->type,DM_MAX_TYPE_NAME); + + prop_dictionary_set_cstring(target_spec,DM_TABLE_TYPE,type); + prop_dictionary_set_cstring(target_spec,DM_TABLE_PARAMS,t->params); + + prop_array_set(cmd_array,count,target_spec); + + prop_object_release(target_spec); + + count++; + } + + + if (count && (dmt->sector || dmt->message)) { + log_error("targets and message are incompatible"); + return -1; + } + + if (count && dmt->newname) { + log_error("targets and newname are incompatible"); + return -1; + } + + if (count && dmt->geometry) { + log_error("targets and geometry are incompatible"); + return -1; + } + + if (dmt->newname && (dmt->sector || dmt->message)) { + log_error("message and newname are incompatible"); + return -1; + } + + if (dmt->newname && dmt->geometry) { + log_error("geometry and newname are incompatible"); + return -1; + } + + if (dmt->geometry && (dmt->sector || dmt->message)) { + log_error("geometry and message are incompatible"); + return -1; + } + + if (dmt->sector && !dmt->message) { + log_error("message is required with sector"); + return -1; + } + + if (dmt->newname) + len += strlen(dmt->newname) + 1; + + if (dmt->message) + len += sizeof(struct dm_target_msg) + strlen(dmt->message) + 1; + + if (dmt->geometry) + len += strlen(dmt->geometry) + 1; + + nbsd_dmi_add_version((*version), dm_dict); + + if (dmt->minor >= 0) { + if (dmt->major <= 0) { + log_error("Missing major number for persistent device."); + goto bad; + } + flags |= DM_PERSISTENT_DEV_FLAG; + prop_dictionary_set_uint64(dm_dict,DM_IOCTL_DEV, + MKDEV(dmt->major, dmt->minor)); + } + + /* Set values to dictionary. */ + if (dmt->dev_name) + prop_dictionary_set_cstring(dm_dict,DM_IOCTL_NAME,dmt->dev_name); + + if (dmt->uuid) + prop_dictionary_set_cstring(dm_dict,DM_IOCTL_UUID,dmt->uuid); + + if (dmt->type == DM_DEVICE_SUSPEND) + flags |= DM_SUSPEND_FLAG; + if (dmt->no_flush) + flags |= DM_NOFLUSH_FLAG; + if (dmt->read_only) + flags |= DM_READONLY_FLAG; + if (dmt->skip_lockfs) + flags |= DM_SKIP_LOCKFS_FLAG; + + prop_dictionary_set_uint32(dm_dict,DM_IOCTL_FLAGS,flags); + + prop_dictionary_set_uint32(dm_dict,DM_IOCTL_EVENT,dmt->event_nr); + + /* Add array for all COMMAND specific data. */ + prop_dictionary_set(dm_dict,DM_IOCTL_CMD_DATA,cmd_array); + + if (dmt->newname) + prop_array_set_cstring(cmd_array,0,dmt->newname); + + return 0; +bad: + return -1; +} + +static int _process_mapper_dir(struct dm_task *dmt) +{ + struct dirent *dirent; + DIR *d; + const char *dir; + int r = 1; + + dir = dm_dir(); + if (!(d = opendir(dir))) { + log_sys_error("opendir", dir); + return 0; + } + + while ((dirent = readdir(d))) { + if (!strcmp(dirent->d_name, ".") || + !strcmp(dirent->d_name, "..") || + !strcmp(dirent->d_name, "control")) + continue; + dm_task_set_name(dmt, dirent->d_name); + dm_task_run(dmt); + } + + if (closedir(d)) + log_sys_error("closedir", dir); + + return r; +} + +/* Get list of all devices. */ +static int _process_all_v4(struct dm_task *dmt) +{ + struct dm_task *task; + struct dm_names *names; + unsigned next = 0; + int r = 1; + + if (!(task = dm_task_create(DM_DEVICE_LIST))) + return 0; + + if (!dm_task_run(task)) { + r = 0; + goto out; + } + + if (!(names = dm_task_get_names(task))) { + r = 0; + goto out; + } + + if (!names->dev) + goto out; + + do { + names = (void *) names + next; + if (!dm_task_set_name(dmt, names->name)) { + r = 0; + goto out; + } + if (!dm_task_run(dmt)) + r = 0; + next = names->next; + } while (next); + + out: + dm_task_destroy(task); + return r; +} + +static int _mknodes_v4(struct dm_task *dmt) +{ + (void) _process_mapper_dir(dmt); + + return _process_all_v4(dmt); +} + +/* Create new device and load table to it. */ +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; + task->uid = dmt->uid; + task->gid = dmt->gid; + task->mode = dmt->mode; + + 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) + goto revert; + + /* Use the original structure last so the info will be correct */ + dmt->type = DM_DEVICE_RESUME; + dm_free(dmt->uuid); + dmt->uuid = NULL; + + r = dm_task_run(dmt); + + if (r) + return r; + + revert: + dmt->type = DM_DEVICE_REMOVE; + dm_free(dmt->uuid); + dmt->uuid = NULL; + + if (!dm_task_run(dmt)) + log_error("Failed to revert device creation."); + + return r; +} + +static int _reload_with_suppression_v4(struct dm_task *dmt) +{ + struct dm_task *task; + struct target *t1, *t2; + int r; + + /* New task to get existing table information */ + if (!(task = dm_task_create(DM_DEVICE_TABLE))) { + 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); + + if (!r) { + dm_task_destroy(task); + return r; + } + + if ((task->dmi.v4->flags & DM_READONLY_FLAG) ? 1 : 0 != dmt->read_only) + goto no_match; + + t1 = dmt->head; + t2 = task->head; + + while (t1 && t2) { + while (t2->params[strlen(t2->params) - 1] == ' ') + t2->params[strlen(t2->params) - 1] = '\0'; + if ((t1->start != t2->start) || + (t1->length != t2->length) || + (strcmp(t1->type, t2->type)) || + (strcmp(t1->params, t2->params))) + goto no_match; + t1 = t1->next; + t2 = t2->next; + } + + if (!t1 && !t2) { + dmt->dmi.v4 = task->dmi.v4; + task->dmi.v4 = NULL; + dm_task_destroy(task); + return 1; + } + +no_match: + dm_task_destroy(task); + + /* Now do the original reload */ + dmt->suppress_identical_reload = 0; + r = dm_task_run(dmt); + + return r; +} + +/* + * This function is heart of NetBSD libdevmapper-> device-mapper kernel protocol + * It creates proplib_dictionary from dm task structure and sends it to NetBSD + * kernel driver. After succesfull ioctl it create dmi structure from returned + * proplib dictionary. This way I keep number of changes in NetBSD version of + * libdevmapper as small as posible. + */ +static struct dm_ioctl *_do_dm_ioctl(struct dm_task *dmt, unsigned command, + unsigned repeat_count) +{ + struct dm_ioctl *dmi; + prop_dictionary_t dm_dict_in, dm_dict_out; + + uint32_t flags; + + dm_dict_in = NULL; + + prop_dictionary_get_uint32(dm_dict_in,DM_IOCTL_FLAGS,&flags); + + dm_dict_in = prop_dictionary_create(); /* Dictionary send to kernel */ + dm_dict_out = prop_dictionary_create(); /* Dictionary received from kernel */ + + /* Set command name to dictionary */ + prop_dictionary_set_cstring(dm_dict_in,DM_IOCTL_COMMAND, + _cmd_data_v4[dmt->type].name); + + /* Parse dmi from libdevmapper to dictionary */ + _flatten(dmt, repeat_count, dm_dict_in); + + if (dmt->type == DM_DEVICE_TABLE) + flags |= DM_STATUS_TABLE_FLAG; + + if (dmt->no_open_count) + flags |= DM_SKIP_BDGET_FLAG; + + flags |= DM_EXISTS_FLAG; + + /* Set flags to dictionary. */ + prop_dictionary_set_uint32(dm_dict_in,DM_IOCTL_FLAGS,flags); + + prop_dictionary_externalize_to_file(dm_dict_in,"/tmp/test_in"); + + /* Send dictionary to kernel and wait for reply. */ + if (prop_dictionary_sendrecv_ioctl(dm_dict_in,_control_fd, + NETBSD_DM_IOCTL,&dm_dict_out) != 0) + return NULL; + + prop_dictionary_externalize_to_file(dm_dict_out,"/tmp/test_out"); + + /* Parse kernel dictionary to dmi structure and return it to libdevmapper. */ + dmi = nbsd_dm_dict_to_dmi(dm_dict_out,_cmd_data_v4[dmt->type].cmd); + + return dmi; +} + +/* Create new edvice nodes in mapper/ dir. */ +void dm_task_update_nodes(void) +{ + update_devs(); +} + +/* Run dm command which is descirbed in dm_task structure. */ +int dm_task_run(struct dm_task *dmt) +{ + struct dm_ioctl *dmi; + unsigned command; + + if ((unsigned) dmt->type >= + (sizeof(_cmd_data_v4) / sizeof(*_cmd_data_v4))) { + log_error("Internal error: unknown device-mapper task %d", + dmt->type); + return 0; + } + + 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 (dmt->type == DM_DEVICE_MKNODES && !dmt->dev_name && + !dmt->uuid && dmt->major <= 0) + return _mknodes_v4(dmt); + + if ((dmt->type == DM_DEVICE_RELOAD) && dmt->suppress_identical_reload) + return _reload_with_suppression_v4(dmt); + + if (!_open_control()) + return 0; + +repeat_ioctl: + if (!(dmi = _do_dm_ioctl(dmt, command, _ioctl_buffer_double_factor))) + return 0; + + if (dmi->flags & DM_BUFFER_FULL_FLAG) { + switch (dmt->type) { + case DM_DEVICE_LIST_VERSIONS: + case DM_DEVICE_LIST: + case DM_DEVICE_DEPS: + case DM_DEVICE_STATUS: + case DM_DEVICE_TABLE: + case DM_DEVICE_WAITEVENT: + _ioctl_buffer_double_factor++; + dm_free(dmi); + goto repeat_ioctl; + default: + log_error("WARNING: libdevmapper buffer too small for data"); + } + } + + switch (dmt->type) { + case DM_DEVICE_CREATE: + add_dev_node(dmt->dev_name, MAJOR(dmi->dev), MINOR(dmi->dev), + dmt->uid, dmt->gid, dmt->mode); + break; + + case DM_DEVICE_REMOVE: + /* FIXME Kernel needs to fill in dmi->name */ + if (dmt->dev_name) + rm_dev_node(dmt->dev_name); + break; + + case DM_DEVICE_RENAME: + /* FIXME Kernel needs to fill in dmi->name */ + if (dmt->dev_name) + rename_dev_node(dmt->dev_name, dmt->newname); + break; + + case DM_DEVICE_RESUME: + /* FIXME Kernel needs to fill in dmi->name */ + set_dev_node_read_ahead(dmt->dev_name, dmt->read_ahead, + dmt->read_ahead_flags); + break; + + case DM_DEVICE_MKNODES: + if (dmi->flags & DM_EXISTS_FLAG) + add_dev_node(dmi->name, MAJOR(dmi->dev), + MINOR(dmi->dev), + dmt->uid, dmt->gid, dmt->mode); + else if (dmt->dev_name) + 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; + } + + /* Was structure reused? */ + if (dmt->dmi.v4) + dm_free(dmt->dmi.v4); + dmt->dmi.v4 = dmi; + return 1; + + bad: + dm_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) +{ + dm_lib_release(); + dm_dump_memory(); + _version_ok = 1; + _version_checked = 0; +} ============================================================ --- lib/ioctl/libdm_netbsd.c 577de2513c661be5b82574c06e924d762a6f3380 +++ lib/ioctl/libdm_netbsd.c 577de2513c661be5b82574c06e924d762a6f3380 @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1996, 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Adam Hamsik. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include "lib.h" + +#define DMI_SIZE 16 * 1024 + +static int dm_list_versions(prop_dictionary_t, struct dm_ioctl *); +static int dm_list_devices(prop_dictionary_t, struct dm_ioctl *); +static int dm_dev_deps(prop_dictionary_t, struct dm_ioctl *); +static int dm_table_status(prop_dictionary_t, struct dm_ioctl *); + +int +nbsd_get_dm_major(uint32_t *major, uint32_t *minor, int type) +{ + size_t val_len,i; + struct kinfo_drivers *kd; + + if (sysctlbyname("kern.drivers",NULL,&val_len,NULL,0) < 0) { + printf("sysctlbyname failed"); + return 0; + } + + if ((kd = malloc (val_len)) == NULL){ + printf("malloc kd info error\n"); + return 0; + } + + if (sysctlbyname("kern.drivers", kd, &val_len, NULL, 0) < 0) { + printf("sysctlbyname failed kd"); + return 0; + } + + for (i = 0, val_len /= sizeof(*kd); i < val_len; i++) { + + if (strncmp(kd[i].d_name,DM_NAME,strlen(kd[i].d_name)) == 0){ + + if (type == DM_CHAR_MAJOR) + /* Set major to dm-driver char major number. */ + *major = kd[i].d_cmajor; + else + if (type == DM_BLOCK_MAJOR) + *major = kd[i].d_bmajor; + + /* Minor number is predefined to 0. */ + *minor = 0; + + free(kd); + + return 1; + } + } + + free(kd); + + return 0; +} + +int +nbsd_dmi_add_version(const int *version, prop_dictionary_t dm_dict) +{ + prop_array_t ver; + size_t i; + + if ((ver = prop_array_create()) == NULL) + return -1; + + for (i=0;i<3;i++) + prop_array_set_uint32(ver,i,version[i]); + + if ((prop_dictionary_set(dm_dict,"version",ver)) == false) + return -1; + + prop_object_release(ver); + + return 0; +} + +struct dm_ioctl* +nbsd_dm_dict_to_dmi(prop_dictionary_t dm_dict,const int cmd) +{ + struct dm_ioctl *dmi; + prop_array_t ver; + + size_t i; + int r; + char *name, *uuid; + uint32_t major,minor; + + name = NULL; + uuid = NULL; + + nbsd_get_dm_major(&major,&minor,DM_BLOCK_MAJOR); + + if (!(dmi = dm_malloc(DMI_SIZE))) + return NULL; + + memset(dmi,0,DMI_SIZE); + + prop_dictionary_get_int32(dm_dict,DM_IOCTL_OPEN,&dmi->open_count); + prop_dictionary_get_uint32(dm_dict,DM_IOCTL_EVENT,&dmi->event_nr); + prop_dictionary_get_uint32(dm_dict,DM_IOCTL_FLAGS,&dmi->flags); + + if (prop_dictionary_get_uint32(dm_dict,DM_IOCTL_DEV,&minor)) + dmi->dev = MKDEV(major,minor); + else + dmi->dev = 0; + + /* Copy name and uuid to dm_ioctl. */ + if (prop_dictionary_get_cstring_nocopy(dm_dict,DM_IOCTL_NAME, + (const char **)&name)){ + strlcpy(dmi->name,name,DM_NAME_LEN); + } else + dmi->name[0] = '\0'; + + if (prop_dictionary_get_cstring_nocopy(dm_dict,DM_IOCTL_UUID, + (const char **)&uuid)){ + strlcpy(dmi->uuid,uuid,DM_UUID_LEN); + } else + dmi->uuid[0] = '\0'; + + /* dmi parsing values, size of dmi block and offset to data. */ + dmi->data_size = DMI_SIZE; + dmi->data_start = sizeof(struct dm_ioctl); + dmi->target_count = 0; + + /* Get kernel version from dm_dict. */ + ver = prop_dictionary_get(dm_dict,DM_IOCTL_VERSION); + + for(i=0; i<3; i++) + prop_array_get_uint32(ver,i,&dmi->version[i]); + + switch (cmd){ + + case DM_LIST_VERSIONS: + r = dm_list_versions(dm_dict,dmi); + if (r >= 0) + dmi->target_count = r; + break; + + case DM_LIST_DEVICES: + r = dm_list_devices(dm_dict,dmi); + if (r >= 0) + dmi->target_count = r; + break; + + case DM_TABLE_STATUS: + r = dm_table_status(dm_dict,dmi); + if (r >= 0) + dmi->target_count = r; + break; + + case DM_TABLE_DEPS: + if (name == NULL && uuid == NULL) + prop_dictionary_get_uint64(dm_dict,DM_IOCTL_DEV,&dmi->dev); + + r = dm_dev_deps(dm_dict,dmi); + if (r >= 0) + dmi->target_count = r; + break; + } + + prop_object_release(ver); + + return dmi; +} + +/* + * Parse dm_dict when targets command was called and fill dm_ioctl buffer with it. + * + * Return number of targets or if failed <0 error. + */ + +static int +dm_list_versions(prop_dictionary_t dm_dict, struct dm_ioctl *dmi) +{ + struct dm_target_versions *dmtv,*odmtv; + + prop_array_t targets,ver; + prop_dictionary_t target_dict; + prop_object_iterator_t iter; + + char *name; + size_t j,i,slen,rec_size; + + odmtv = NULL; + name = NULL; + j = 0; + + dmtv = (struct dm_target_versions *)((uint8_t *)dmi + dmi->data_start); + +/* printf("dmi: vers: %d.%d.%d data_size: %d data_start: %d name: %s t_count: %d\n", + dmi->version[0],dmi->version[1],dmi->version[2],dmi->data_size,dmi->data_start, + dmi->name,dmi->target_count); + + printf("dmi: size: %d -- %p --- %p \n",sizeof(struct dm_ioctl),dmi,dmi+dmi->data_start); + printf("dmtv: size: %p --- %p\n",dmtv,(struct dm_target_versions *)(dmi+312));*/ + + /* get prop_array of target_version dictionaries */ + if ((targets = prop_dictionary_get(dm_dict,DM_IOCTL_CMD_DATA))){ + + iter = prop_array_iterator(targets); + if (!iter) + err(EXIT_FAILURE,"dm_list_versions %s",__func__); + + while((target_dict = prop_object_iterator_next(iter)) != NULL){ + + j++; + + prop_dictionary_get_cstring_nocopy(target_dict, + DM_TARGETS_NAME,(const char **)&name); + + slen = strlen(name) + 1; + rec_size = sizeof(struct dm_target_versions) + slen + 1; + + if (rec_size > dmi->data_size) + return -ENOMEM; + + ver = prop_dictionary_get(target_dict,DM_TARGETS_VERSION); + + for (i=0; i<3; i++) + prop_array_get_uint32(ver,i,&dmtv->version[i]); + + dmtv->next = rec_size; + + strlcpy(dmtv->name,name,slen); + + odmtv = dmtv; + + dmtv =(struct dm_target_versions *)((uint8_t *)dmtv + rec_size); + } + + if (odmtv != NULL) + odmtv->next = 0; + } + + return j; +} + +static int +dm_list_devices(prop_dictionary_t dm_dict, struct dm_ioctl *dmi) +{ + struct dm_name_list *dml,*odml; + + prop_array_t targets; + prop_dictionary_t target_dict; + prop_object_iterator_t iter; + + uint32_t minor; + uint32_t major; + + char *name; + size_t j,slen,rec_size; + + nbsd_get_dm_major(&major,&minor,DM_BLOCK_MAJOR); + + odml = NULL; + name = NULL; + j = 0; + + dml = (struct dm_name_list *)((uint8_t *)dmi + dmi->data_start); + + if ((targets = prop_dictionary_get(dm_dict,DM_IOCTL_CMD_DATA))){ + + iter = prop_array_iterator(targets); + if (!iter) + err(EXIT_FAILURE,"dm_list_devices %s",__func__); + + while((target_dict = prop_object_iterator_next(iter)) != NULL){ + + prop_dictionary_get_cstring_nocopy(target_dict, + DM_DEV_NAME,(const char **)&name); + + prop_dictionary_get_uint32(target_dict,DM_DEV_DEV,&minor); + + dml->dev = MKDEV(major,minor); + + slen = strlen(name) + 1; + rec_size = sizeof(struct dm_name_list) + slen + 1; + + if (rec_size > dmi->data_size) + return -ENOMEM; + + dml->next = rec_size; + + strlcpy(dml->name,name,slen); + + odml = dml; + + dml =(struct dm_name_list *)((uint8_t *)dml + rec_size); + + j++; + } + + if (odml != NULL) + odml->next = 0; + } + return j; +} + +static int +dm_table_status(prop_dictionary_t dm_dict,struct dm_ioctl *dmi) +{ + struct dm_target_spec *dmts, *odmts; + + prop_array_t targets; + prop_dictionary_t target_dict; + prop_object_iterator_t iter; + + char *type,*params,*params_start; + + size_t j,plen,rec_size,next; + + j = 0; + next = 0; + odmts = NULL; + rec_size = 0; + + dmts = (struct dm_target_spec *)((uint8_t *)dmi + dmi->data_start); + + if ((targets = prop_dictionary_get(dm_dict,DM_IOCTL_CMD_DATA))){ + + iter = prop_array_iterator(targets); + if (!iter) + err(EXIT_FAILURE,"dm_table_status %s",__func__); + + while((target_dict = prop_object_iterator_next(iter)) != NULL){ + + prop_dictionary_get_cstring_nocopy(target_dict, + DM_TABLE_TYPE,(const char **)&type); + + prop_dictionary_get_cstring_nocopy(target_dict, + DM_TABLE_PARAMS,(const char **)¶ms); + + prop_dictionary_get_uint64(target_dict,DM_TABLE_START,&dmts->sector_start); + prop_dictionary_get_uint64(target_dict,DM_TABLE_LENGTH,&dmts->length); + prop_dictionary_get_int32(target_dict,DM_TABLE_STAT,&dmts->status); + + plen = strlen(params) + 1; + plen = 0; + + rec_size = sizeof(struct dm_target_spec) + plen; + + /* + * In linux when copying table status from kernel next is + * number of bytes from the start of the first dm_target_spec + * structure. I don't know why but, it has to be done this way. + */ + next += rec_size; + + if (rec_size > dmi->data_size) + return -ENOMEM; + + dmts->next = next; + + strlcpy(dmts->target_type,type,DM_MAX_TYPE_NAME); + + params_start =(char *)dmts + sizeof(struct dm_target_spec); + +/* if (params) + strcpy(params_start,params); + else*/ + params_start = '\0'; + + odmts = dmts; + + dmts = (struct dm_target_spec *)((uint8_t *)dmts + rec_size); + + j++; + + } + + if (odmts != NULL) + odmts->next = 0; + } + + return j; +} + +static int +dm_dev_deps(prop_dictionary_t dm_dict, struct dm_ioctl *dmi) +{ + struct dm_target_deps *dmtd; + + prop_array_t targets; + prop_object_iterator_t iter; + + size_t j; + + j = 0; + + dmtd = (struct dm_target_deps *)((uint8_t *)dmi + dmi->data_start); + + if ((targets = prop_dictionary_get(dm_dict,DM_IOCTL_CMD_DATA))){ + + iter = prop_array_iterator(targets); + if (!iter) + err(EXIT_FAILURE,"dm_target_deps %s",__func__); + + while((prop_object_iterator_next(iter)) != NULL){ + + prop_array_get_uint64(targets,j,&dmtd->dev[j]); + + j++; + } + + if (j == 0) + dmtd->count = 1; + else + dmtd->count = j; + } + + return j; +}