lkml.org 
[lkml]   [2003]   [Jul]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[RFC 3/3] dm: v4 ioctl interface
    v4 of the ioctl interface.  Note there never was a v2 or a v3 except
    in an unofficial EVMS way.

    There is now a config option to allow the user to select v1 or v4, it
    defaults to v1. If you want to use v4 you will have to update your
    tools (libdevmapper/lvm), these new tools have not been released at
    the time of writing.

    dm-ioctl.[hc] just #includes dm-ioctl-v[14].[hc] depending on the
    config option.
    --- diff/drivers/md/Kconfig 2003-05-21 11:50:15.000000000 +0100
    +++ source/drivers/md/Kconfig 2003-07-01 15:36:42.000000000 +0100
    @@ -145,5 +145,12 @@

    If unsure, say N.

    +config DM_IOCTL_V4
    + bool "ioctl interface version 4"
    + depends on BLK_DEV_DM
    + ---help---
    + Recent tools use a new version of the ioctl interface, only
    + select this option if you intend using such tools.
    +
    endmenu

    --- diff/drivers/md/dm-ioctl.c 2003-06-30 17:12:49.000000000 +0100
    +++ source/drivers/md/dm-ioctl.c 2003-07-01 15:36:42.000000000 +0100
    @@ -1,1133 +1,13 @@
    /*
    - * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
    + * Copyright (C) 2003 Sistina Software (UK) Limited.
    *
    * This file is released under the GPL.
    */

    -#include "dm.h"
    -
    -#include <linux/module.h>
    -#include <linux/vmalloc.h>
    -#include <linux/miscdevice.h>
    #include <linux/dm-ioctl.h>
    -#include <linux/init.h>
    -#include <linux/wait.h>
    -#include <linux/blk.h>
    -#include <linux/slab.h>
    -#include <linux/devfs_fs_kernel.h>
    -
    -#include <asm/uaccess.h>
    -
    -#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
    -
    -/*-----------------------------------------------------------------
    - * The ioctl interface needs to be able to look up devices by
    - * name or uuid.
    - *---------------------------------------------------------------*/
    -struct hash_cell {
    - struct list_head name_list;
    - struct list_head uuid_list;
    -
    - char *name;
    - char *uuid;
    - struct mapped_device *md;
    -};
    -
    -#define NUM_BUCKETS 64
    -#define MASK_BUCKETS (NUM_BUCKETS - 1)
    -static struct list_head _name_buckets[NUM_BUCKETS];
    -static struct list_head _uuid_buckets[NUM_BUCKETS];
    -
    -void dm_hash_remove_all(void);
    -
    -/*
    - * Guards access to all three tables.
    - */
    -static DECLARE_RWSEM(_hash_lock);
    -
    -static void init_buckets(struct list_head *buckets)
    -{
    - unsigned int i;
    -
    - for (i = 0; i < NUM_BUCKETS; i++)
    - INIT_LIST_HEAD(buckets + i);
    -}
    -
    -int dm_hash_init(void)
    -{
    - init_buckets(_name_buckets);
    - init_buckets(_uuid_buckets);
    - devfs_mk_dir(DM_DIR);
    - return 0;
    -}
    -
    -void dm_hash_exit(void)
    -{
    - dm_hash_remove_all();
    - devfs_remove(DM_DIR);
    -}
    -
    -/*-----------------------------------------------------------------
    - * Hash function:
    - * We're not really concerned with the str hash function being
    - * fast since it's only used by the ioctl interface.
    - *---------------------------------------------------------------*/
    -static unsigned int hash_str(const char *str)
    -{
    - const unsigned int hash_mult = 2654435387U;
    - unsigned int h = 0;
    -
    - while (*str)
    - h = (h + (unsigned int) *str++) * hash_mult;
    -
    - return h & MASK_BUCKETS;
    -}
    -
    -/*-----------------------------------------------------------------
    - * Code for looking up a device by name
    - *---------------------------------------------------------------*/
    -static struct hash_cell *__get_name_cell(const char *str)
    -{
    - struct list_head *tmp;
    - struct hash_cell *hc;
    - unsigned int h = hash_str(str);
    -
    - list_for_each (tmp, _name_buckets + h) {
    - hc = list_entry(tmp, struct hash_cell, name_list);
    - if (!strcmp(hc->name, str))
    - return hc;
    - }
    -
    - return NULL;
    -}
    -
    -static struct hash_cell *__get_uuid_cell(const char *str)
    -{
    - struct list_head *tmp;
    - struct hash_cell *hc;
    - unsigned int h = hash_str(str);
    -
    - list_for_each (tmp, _uuid_buckets + h) {
    - hc = list_entry(tmp, struct hash_cell, uuid_list);
    - if (!strcmp(hc->uuid, str))
    - return hc;
    - }
    -
    - return NULL;
    -}
    -
    -/*-----------------------------------------------------------------
    - * Inserting, removing and renaming a device.
    - *---------------------------------------------------------------*/
    -static inline char *kstrdup(const char *str)
    -{
    - char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
    - if (r)
    - strcpy(r, str);
    - return r;
    -}
    -
    -static struct hash_cell *alloc_cell(const char *name, const char *uuid,
    - struct mapped_device *md)
    -{
    - struct hash_cell *hc;
    -
    - hc = kmalloc(sizeof(*hc), GFP_KERNEL);
    - if (!hc)
    - return NULL;
    -
    - hc->name = kstrdup(name);
    - if (!hc->name) {
    - kfree(hc);
    - return NULL;
    - }
    -
    - if (!uuid)
    - hc->uuid = NULL;
    -
    - else {
    - hc->uuid = kstrdup(uuid);
    - if (!hc->uuid) {
    - kfree(hc->name);
    - kfree(hc);
    - return NULL;
    - }
    - }
    -
    - INIT_LIST_HEAD(&hc->name_list);
    - INIT_LIST_HEAD(&hc->uuid_list);
    - hc->md = md;
    - return hc;
    -}
    -
    -static void free_cell(struct hash_cell *hc)
    -{
    - if (hc) {
    - kfree(hc->name);
    - kfree(hc->uuid);
    - kfree(hc);
    - }
    -}
    -
    -/*
    - * devfs stuff.
    - */
    -static int register_with_devfs(struct hash_cell *hc)
    -{
    - struct gendisk *disk = dm_disk(hc->md);
    -
    - devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
    - S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
    - DM_DIR "/%s", hc->name);
    - return 0;
    -}
    -
    -static int unregister_with_devfs(struct hash_cell *hc)
    -{
    - devfs_remove(DM_DIR"/%s", hc->name);
    - return 0;
    -}
    -
    -/*
    - * The kdev_t and uuid of a device can never change once it is
    - * initially inserted.
    - */
    -int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
    -{
    - struct hash_cell *cell;
    -
    - /*
    - * Allocate the new cells.
    - */
    - cell = alloc_cell(name, uuid, md);
    - if (!cell)
    - return -ENOMEM;
    -
    - /*
    - * Insert the cell into all three hash tables.
    - */
    - down_write(&_hash_lock);
    - if (__get_name_cell(name))
    - goto bad;
    -
    - list_add(&cell->name_list, _name_buckets + hash_str(name));
    -
    - if (uuid) {
    - if (__get_uuid_cell(uuid)) {
    - list_del(&cell->name_list);
    - goto bad;
    - }
    - list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
    - }
    - register_with_devfs(cell);
    - dm_get(md);
    - up_write(&_hash_lock);
    -
    - return 0;
    -
    - bad:
    - up_write(&_hash_lock);
    - free_cell(cell);
    - return -EBUSY;
    -}
    -
    -void __hash_remove(struct hash_cell *hc)
    -{
    - /* remove from the dev hash */
    - list_del(&hc->uuid_list);
    - list_del(&hc->name_list);
    - unregister_with_devfs(hc);
    - dm_put(hc->md);
    - free_cell(hc);
    -}
    -
    -void dm_hash_remove_all(void)
    -{
    - int i;
    - struct hash_cell *hc;
    - struct list_head *tmp, *n;
    -
    - down_write(&_hash_lock);
    - for (i = 0; i < NUM_BUCKETS; i++) {
    - list_for_each_safe (tmp, n, _name_buckets + i) {
    - hc = list_entry(tmp, struct hash_cell, name_list);
    - __hash_remove(hc);
    - }
    - }
    - up_write(&_hash_lock);
    -}
    -
    -int dm_hash_rename(const char *old, const char *new)
    -{
    - char *new_name, *old_name;
    - struct hash_cell *hc;
    -
    - /*
    - * duplicate new.
    - */
    - new_name = kstrdup(new);
    - if (!new_name)
    - return -ENOMEM;
    -
    - down_write(&_hash_lock);
    -
    - /*
    - * Is new free ?
    - */
    - hc = __get_name_cell(new);
    - if (hc) {
    - DMWARN("asked to rename to an already existing name %s -> %s",
    - old, new);
    - up_write(&_hash_lock);
    - kfree(new_name);
    - return -EBUSY;
    - }
    -
    - /*
    - * Is there such a device as 'old' ?
    - */
    - hc = __get_name_cell(old);
    - if (!hc) {
    - DMWARN("asked to rename a non existent device %s -> %s",
    - old, new);
    - up_write(&_hash_lock);
    - kfree(new_name);
    - return -ENXIO;
    - }
    -
    - /*
    - * rename and move the name cell.
    - */
    - unregister_with_devfs(hc);
    -
    - list_del(&hc->name_list);
    - old_name = hc->name;
    - hc->name = new_name;
    - list_add(&hc->name_list, _name_buckets + hash_str(new_name));
    -
    - /* rename the device node in devfs */
    - register_with_devfs(hc);
    -
    - up_write(&_hash_lock);
    - kfree(old_name);
    - return 0;
    -}
    -
    -
    -/*-----------------------------------------------------------------
    - * Implementation of the ioctl commands
    - *---------------------------------------------------------------*/
    -
    -/*
    - * All the ioctl commands get dispatched to functions with this
    - * prototype.
    - */
    -typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user);
    -
    -/*
    - * Check a string doesn't overrun the chunk of
    - * memory we copied from userland.
    - */
    -static int valid_str(char *str, void *begin, void *end)
    -{
    - while (((void *) str >= begin) && ((void *) str < end))
    - if (!*str++)
    - return 0;
    -
    - return -EINVAL;
    -}
    -
    -static int next_target(struct dm_target_spec *last, uint32_t next,
    - void *begin, void *end,
    - struct dm_target_spec **spec, char **params)
    -{
    - *spec = (struct dm_target_spec *)
    - ((unsigned char *) last + next);
    - *params = (char *) (*spec + 1);
    -
    - if (*spec < (last + 1) || ((void *) *spec > end))
    - return -EINVAL;
    -
    - return valid_str(*params, begin, end);
    -}
    -
    -static int populate_table(struct dm_table *table, struct dm_ioctl *args)
    -{
    - int r, first = 1;
    - unsigned int i = 0;
    - struct dm_target_spec *spec;
    - char *params;
    - void *begin, *end;
    -
    - if (!args->target_count) {
    - DMWARN("populate_table: no targets specified");
    - return -EINVAL;
    - }
    -
    - begin = (void *) args;
    - end = begin + args->data_size;
    -
    - for (i = 0; i < args->target_count; i++) {
    -
    - if (first)
    - r = next_target((struct dm_target_spec *) args,
    - args->data_start,
    - begin, end, &spec, &params);
    - else
    - r = next_target(spec, spec->next, begin, end,
    - &spec, &params);
    -
    - if (r) {
    - DMWARN("unable to find target");
    - return -EINVAL;
    - }
    -
    - r = dm_table_add_target(table, spec->target_type,
    - (sector_t) spec->sector_start,
    - (sector_t) spec->length,
    - params);
    - if (r) {
    - DMWARN("internal error adding target to table");
    - return -EINVAL;
    - }
    -
    - first = 0;
    - }
    -
    - return dm_table_complete(table);
    -}
    -
    -/*
    - * Round up the ptr to the next 'align' boundary. Obviously
    - * 'align' must be a power of 2.
    - */
    -static inline void *align_ptr(void *ptr, unsigned int align)
    -{
    - align--;
    - return (void *) (((unsigned long) (ptr + align)) & ~align);
    -}
    -
    -/*
    - * Copies a dm_ioctl and an optional additional payload to
    - * userland.
    - */
    -static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param,
    - void *data, uint32_t len)
    -{
    - int r;
    - void *ptr = NULL;
    -
    - if (data) {
    - ptr = align_ptr(user + 1, sizeof(unsigned long));
    - param->data_start = ptr - (void *) user;
    - }
    -
    - /*
    - * The version number has already been filled in, so we
    - * just copy later fields.
    - */
    - r = copy_to_user(&user->data_size, &param->data_size,
    - sizeof(*param) - sizeof(param->version));
    - if (r)
    - return -EFAULT;
    -
    - if (data) {
    - if (param->data_start + len > param->data_size)
    - return -ENOSPC;
    -
    - if (copy_to_user(ptr, data, len))
    - r = -EFAULT;
    - }
    -
    - return r;
    -}
    -
    -/*
    - * Fills in a dm_ioctl structure, ready for sending back to
    - * userland.
    - */
    -static int __info(struct mapped_device *md, struct dm_ioctl *param)
    -{
    - struct dm_table *table;
    - struct block_device *bdev;
    - struct gendisk *disk = dm_disk(md);
    -
    - param->flags = DM_EXISTS_FLAG;
    - if (dm_suspended(md))
    - param->flags |= DM_SUSPEND_FLAG;
    -
    - bdev = bdget_disk(disk, 0);
    - if (!bdev)
    - return -ENXIO;
    -
    - param->dev = bdev->bd_dev;
    - param->open_count = bdev->bd_openers;
    - bdput(bdev);
    -
    - if (disk->policy)
    - param->flags |= DM_READONLY_FLAG;
    -
    - table = dm_get_table(md);
    - param->target_count = dm_table_get_num_targets(table);
    - dm_table_put(table);
    -
    - return 0;
    -}
    -
    -/*
    - * Always use UUID for lookups if it's present, otherwise use name.
    - */
    -static inline struct mapped_device *find_device(struct dm_ioctl *param)
    -{
    - struct hash_cell *hc;
    - struct mapped_device *md = NULL;
    -
    - down_read(&_hash_lock);
    - hc = *param->uuid ? __get_uuid_cell(param->uuid) :
    - __get_name_cell(param->name);
    - if (hc) {
    - md = hc->md;
    -
    - /*
    - * Sneakily write in both the name and the uuid
    - * while we have the cell.
    - */
    - strlcpy(param->name, hc->name, sizeof(param->name));
    - if (hc->uuid)
    - strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
    - else
    - param->uuid[0] = '\0';
    -
    - dm_get(md);
    - }
    - up_read(&_hash_lock);
    -
    - return md;
    -}
    -
    -#define ALIGNMENT sizeof(int)
    -static void *_align(void *ptr, unsigned int a)
    -{
    - register unsigned long align = --a;
    -
    - return (void *) (((unsigned long) ptr + align) & ~align);
    -}
    -
    -/*
    - * Copies device info back to user space, used by
    - * the create and info ioctls.
    - */
    -static int info(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - struct mapped_device *md;
    -
    - param->flags = 0;
    -
    - md = find_device(param);
    - if (!md)
    - /*
    - * Device not found - returns cleared exists flag.
    - */
    - goto out;
    -
    - __info(md, param);
    - dm_put(md);
    -
    - out:
    - return results_to_user(user, param, NULL, 0);
    -}
    -
    -static inline int get_mode(struct dm_ioctl *param)
    -{
    - int mode = FMODE_READ | FMODE_WRITE;
    -
    - if (param->flags & DM_READONLY_FLAG)
    - mode = FMODE_READ;
    -
    - return mode;
    -}
    -
    -static int check_name(const char *name)
    -{
    - if (name[0] == '/') {
    - DMWARN("invalid device name");
    - return -EINVAL;
    - }
    -
    - return 0;
    -}
    -
    -static int create(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - int r;
    - struct dm_table *t;
    - struct mapped_device *md;
    -
    - r = check_name(param->name);
    - if (r)
    - return r;
    -
    - r = dm_table_create(&t, get_mode(param));
    - if (r)
    - return r;
    -
    - r = populate_table(t, param);
    - if (r) {
    - dm_table_put(t);
    - return r;
    - }
    -
    - if (param->flags & DM_PERSISTENT_DEV_FLAG)
    - r = dm_create_with_minor(minor(to_kdev_t(param->dev)), t, &md);
    - else
    - r = dm_create(t, &md);
    -
    - if (r) {
    - dm_table_put(t);
    - return r;
    - }
    - dm_table_put(t); /* md will have grabbed its own reference */
    -
    - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0);
    - r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
    - dm_put(md);
    -
    - return r ? r : info(param, user);
    -}
    -
    -/*
    - * Build up the status struct for each target
    - */
    -static int __status(struct mapped_device *md, struct dm_ioctl *param,
    - char *outbuf, size_t *len)
    -{
    - unsigned int i, num_targets;
    - struct dm_target_spec *spec;
    - char *outptr;
    - status_type_t type;
    - struct dm_table *table = dm_get_table(md);
    -
    - if (param->flags & DM_STATUS_TABLE_FLAG)
    - type = STATUSTYPE_TABLE;
    - else
    - type = STATUSTYPE_INFO;
    -
    - outptr = outbuf;
    -
    - /* Get all the target info */
    - num_targets = dm_table_get_num_targets(table);
    - for (i = 0; i < num_targets; i++) {
    - struct dm_target *ti = dm_table_get_target(table, i);
    -
    - if (outptr - outbuf +
    - sizeof(struct dm_target_spec) > param->data_size) {
    - dm_table_put(table);
    - return -ENOMEM;
    - }
    -
    - spec = (struct dm_target_spec *) outptr;
    -
    - spec->status = 0;
    - spec->sector_start = ti->begin;
    - spec->length = ti->len;
    - strlcpy(spec->target_type, ti->type->name,
    - sizeof(spec->target_type));
    -
    - outptr += sizeof(struct dm_target_spec);
    -
    - /* Get the status/table string from the target driver */
    - if (ti->type->status)
    - ti->type->status(ti, type, outptr,
    - outbuf + param->data_size - outptr);
    - else
    - outptr[0] = '\0';
    -
    - outptr += strlen(outptr) + 1;
    - _align(outptr, ALIGNMENT);
    - spec->next = outptr - outbuf;
    - }
    -
    - param->target_count = num_targets;
    - *len = outptr - outbuf;
    - dm_table_put(table);
    -
    - return 0;
    -}
    -
    -/*
    - * Return the status of a device as a text string for each
    - * target.
    - */
    -static int get_status(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - struct mapped_device *md;
    - size_t len = 0;
    - int ret;
    - char *outbuf = NULL;
    -
    - md = find_device(param);
    - if (!md)
    - /*
    - * Device not found - returns cleared exists flag.
    - */
    - goto out;
    -
    - /* We haven't a clue how long the resultant data will be so
    - just allocate as much as userland has allowed us and make sure
    - we don't overun it */
    - outbuf = kmalloc(param->data_size, GFP_KERNEL);
    - if (!outbuf)
    - goto out;
    - /*
    - * Get the status of all targets
    - */
    - __status(md, param, outbuf, &len);
    -
    - /*
    - * Setup the basic dm_ioctl structure.
    - */
    - __info(md, param);
    -
    - out:
    - if (md)
    - dm_put(md);
    -
    - ret = results_to_user(user, param, outbuf, len);
    -
    - if (outbuf)
    - kfree(outbuf);
    -
    - return ret;
    -}
    -
    -/*
    - * Wait for a device to report an event
    - */
    -static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - struct mapped_device *md;
    - DECLARE_WAITQUEUE(wq, current);
    -
    - md = find_device(param);
    - if (!md)
    - /*
    - * Device not found - returns cleared exists flag.
    - */
    - goto out;
    -
    - /*
    - * Setup the basic dm_ioctl structure.
    - */
    - __info(md, param);
    -
    - /*
    - * Wait for a notification event
    - */
    - set_current_state(TASK_INTERRUPTIBLE);
    - if (!dm_add_wait_queue(md, &wq, dm_get_event_nr(md))) {
    - yield();
    - dm_remove_wait_queue(md, &wq);
    - }
    - set_current_state(TASK_RUNNING);
    - dm_put(md);
    -
    - out:
    - return results_to_user(user, param, NULL, 0);
    -}
    -
    -/*
    - * Retrieves a list of devices used by a particular dm device.
    - */
    -static int dep(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - int r;
    - unsigned int count;
    - struct mapped_device *md;
    - struct list_head *tmp;
    - size_t len = 0;
    - struct dm_target_deps *deps = NULL;
    - struct dm_table *table;
    -
    - md = find_device(param);
    - if (!md)
    - goto out;
    - table = dm_get_table(md);
    -
    - /*
    - * Setup the basic dm_ioctl structure.
    - */
    - __info(md, param);
    -
    - /*
    - * Count the devices.
    - */
    - count = 0;
    - list_for_each(tmp, dm_table_get_devices(table))
    - count++;
    -
    - /*
    - * Allocate a kernel space version of the dm_target_status
    - * struct.
    - */
    - if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) {
    - dm_table_put(table);
    - dm_put(md);
    - return -ENOMEM;
    - }
    -
    - len = sizeof(*deps) + (sizeof(*deps->dev) * count);
    - deps = kmalloc(len, GFP_KERNEL);
    - if (!deps) {
    - dm_table_put(table);
    - dm_put(md);
    - return -ENOMEM;
    - }
    -
    - /*
    - * Fill in the devices.
    - */
    - deps->count = count;
    - count = 0;
    - list_for_each(tmp, dm_table_get_devices(table)) {
    - struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
    - deps->dev[count++] = dd->bdev->bd_dev;
    - }
    - dm_table_put(table);
    - dm_put(md);
    -
    - out:
    - r = results_to_user(user, param, deps, len);
    -
    - kfree(deps);
    - return r;
    -}
    -
    -static int remove(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - struct hash_cell *hc;
    -
    - down_write(&_hash_lock);
    - hc = *param->uuid ? __get_uuid_cell(param->uuid) :
    - __get_name_cell(param->name);
    - if (!hc) {
    - DMWARN("device doesn't appear to be in the dev hash table.");
    - up_write(&_hash_lock);
    - return -EINVAL;
    - }
    -
    - /*
    - * You may ask the interface to drop its reference to an
    - * in use device. This is no different to unlinking a
    - * file that someone still has open. The device will not
    - * actually be destroyed until the last opener closes it.
    - * The name and uuid of the device (both are interface
    - * properties) will be available for reuse immediately.
    - *
    - * You don't want to drop a _suspended_ device from the
    - * interface, since that will leave you with no way of
    - * resuming it.
    - */
    - if (dm_suspended(hc->md)) {
    - DMWARN("refusing to remove a suspended device.");
    - up_write(&_hash_lock);
    - return -EPERM;
    - }
    -
    - __hash_remove(hc);
    - up_write(&_hash_lock);
    - return 0;
    -}
    -
    -static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - dm_hash_remove_all();
    - return 0;
    -}
    -
    -static int suspend(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - int r;
    - struct mapped_device *md;
    -
    - md = find_device(param);
    - if (!md)
    - return -ENXIO;
    -
    - if (param->flags & DM_SUSPEND_FLAG)
    - r = dm_suspend(md);
    - else
    - r = dm_resume(md);
    -
    - dm_put(md);
    - return r;
    -}
    -
    -static int reload(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - int r;
    - struct mapped_device *md;
    - struct dm_table *t;
    -
    - r = dm_table_create(&t, get_mode(param));
    - if (r)
    - return r;
    -
    - r = populate_table(t, param);
    - if (r) {
    - dm_table_put(t);
    - return r;
    - }
    -
    - md = find_device(param);
    - if (!md) {
    - dm_table_put(t);
    - return -ENXIO;
    - }
    -
    - r = dm_swap_table(md, t);
    - if (r) {
    - dm_put(md);
    - dm_table_put(t);
    - return r;
    - }
    - dm_table_put(t); /* md will have taken its own reference */
    -
    - set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0);
    - dm_put(md);
    -
    - r = info(param, user);
    - return r;
    -}
    -
    -static int rename(struct dm_ioctl *param, struct dm_ioctl *user)
    -{
    - int r;
    - char *new_name = (char *) param + param->data_start;
    -
    - if (valid_str(new_name, (void *) param,
    - (void *) param + param->data_size)) {
    - DMWARN("Invalid new logical volume name supplied.");
    - return -EINVAL;
    - }
    -
    - r = check_name(new_name);
    - if (r)
    - return r;
    -
    - return dm_hash_rename(param->name, new_name);
    -}
    -
    -
    -/*-----------------------------------------------------------------
    - * Implementation of open/close/ioctl on the special char
    - * device.
    - *---------------------------------------------------------------*/
    -static ioctl_fn lookup_ioctl(unsigned int cmd)
    -{
    - static struct {
    - int cmd;
    - ioctl_fn fn;
    - } _ioctls[] = {
    - {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */
    - {DM_REMOVE_ALL_CMD, remove_all},
    - {DM_DEV_CREATE_CMD, create},
    - {DM_DEV_REMOVE_CMD, remove},
    - {DM_DEV_RELOAD_CMD, reload},
    - {DM_DEV_RENAME_CMD, rename},
    - {DM_DEV_SUSPEND_CMD, suspend},
    - {DM_DEV_DEPS_CMD, dep},
    - {DM_DEV_STATUS_CMD, info},
    - {DM_TARGET_STATUS_CMD, get_status},
    - {DM_TARGET_WAIT_CMD, wait_device_event},
    - };
    -
    - return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
    -}
    -
    -/*
    - * As well as checking the version compatibility this always
    - * copies the kernel interface version out.
    - */
    -static int check_version(unsigned int cmd, struct dm_ioctl *user)
    -{
    - uint32_t version[3];
    - int r = 0;
    -
    - if (copy_from_user(version, user->version, sizeof(version)))
    - return -EFAULT;
    -
    - if ((DM_VERSION_MAJOR != version[0]) ||
    - (DM_VERSION_MINOR < version[1])) {
    - DMWARN("ioctl interface mismatch: "
    - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
    - DM_VERSION_MAJOR, DM_VERSION_MINOR,
    - DM_VERSION_PATCHLEVEL,
    - version[0], version[1], version[2], cmd);
    - r = -EINVAL;
    - }
    -
    - /*
    - * Fill in the kernel version.
    - */
    - version[0] = DM_VERSION_MAJOR;
    - version[1] = DM_VERSION_MINOR;
    - version[2] = DM_VERSION_PATCHLEVEL;
    - if (copy_to_user(user->version, version, sizeof(version)))
    - return -EFAULT;
    -
    - return r;
    -}
    -
    -static void free_params(struct dm_ioctl *param)
    -{
    - vfree(param);
    -}
    -
    -static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param)
    -{
    - struct dm_ioctl tmp, *dmi;
    -
    - if (copy_from_user(&tmp, user, sizeof(tmp)))
    - return -EFAULT;
    -
    - if (tmp.data_size < sizeof(tmp))
    - return -EINVAL;
    -
    - dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
    - if (!dmi)
    - return -ENOMEM;
    -
    - if (copy_from_user(dmi, user, tmp.data_size)) {
    - vfree(dmi);
    - return -EFAULT;
    - }
    -
    - *param = dmi;
    - return 0;
    -}
    -
    -static int validate_params(uint cmd, struct dm_ioctl *param)
    -{
    - /* Ignores parameters */
    - if (cmd == DM_REMOVE_ALL_CMD)
    - return 0;
    -
    - /* Unless creating, either name of uuid but not both */
    - if (cmd != DM_DEV_CREATE_CMD) {
    - if ((!*param->uuid && !*param->name) ||
    - (*param->uuid && *param->name)) {
    - DMWARN("one of name or uuid must be supplied");
    - return -EINVAL;
    - }
    - }
    -
    - /* Ensure strings are terminated */
    - param->name[DM_NAME_LEN - 1] = '\0';
    - param->uuid[DM_UUID_LEN - 1] = '\0';
    -
    - return 0;
    -}
    -
    -static int ctl_ioctl(struct inode *inode, struct file *file,
    - uint command, ulong u)
    -{
    - int r = 0;
    - unsigned int cmd;
    - struct dm_ioctl *param;
    - struct dm_ioctl *user = (struct dm_ioctl *) u;
    - ioctl_fn fn = NULL;
    -
    - /* only root can play with this */
    - if (!capable(CAP_SYS_ADMIN))
    - return -EACCES;
    -
    - if (_IOC_TYPE(command) != DM_IOCTL)
    - return -ENOTTY;
    -
    - cmd = _IOC_NR(command);
    -
    - /*
    - * Check the interface version passed in. This also
    - * writes out the kernels interface version.
    - */
    - r = check_version(cmd, user);
    - if (r)
    - return r;
    -
    - /*
    - * Nothing more to do for the version command.
    - */
    - if (cmd == DM_VERSION_CMD)
    - return 0;
    -
    - fn = lookup_ioctl(cmd);
    - if (!fn) {
    - DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
    - return -ENOTTY;
    - }
    -
    - /*
    - * Copy the parameters into kernel space.
    - */
    - r = copy_params(user, &param);
    - if (r)
    - return r;
    -
    - r = validate_params(cmd, param);
    - if (r) {
    - free_params(param);
    - return r;
    - }
    -
    - r = fn(param, user);
    - free_params(param);
    - return r;
    -}
    -
    -static struct file_operations _ctl_fops = {
    - .ioctl = ctl_ioctl,
    - .owner = THIS_MODULE,
    -};
    -
    -static struct miscdevice _dm_misc = {
    - .minor = MISC_DYNAMIC_MINOR,
    - .name = DM_NAME,
    - .devfs_name = "mapper/control",
    - .fops = &_ctl_fops
    -};
    -
    -/*
    - * Create misc character device and link to DM_DIR/control.
    - */
    -int __init dm_interface_init(void)
    -{
    - int r;
    -
    - r = dm_hash_init();
    - if (r)
    - return r;
    -
    - r = misc_register(&_dm_misc);
    - if (r) {
    - DMERR("misc_register failed for control device");
    - dm_hash_exit();
    - return r;
    - }
    -
    - DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
    - DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
    - DM_DRIVER_EMAIL);
    - return 0;
    -
    - if (misc_deregister(&_dm_misc) < 0)
    - DMERR("misc_deregister failed for control device");
    - dm_hash_exit();
    - return r;
    -}

    -void dm_interface_exit(void)
    -{
    - if (misc_deregister(&_dm_misc) < 0)
    - DMERR("misc_deregister failed for control device");
    - dm_hash_exit();
    -}
    +#ifdef CONFIG_DM_IOCTL_V4
    +#include "dm-ioctl-v4.c"
    +#else
    +#include "dm-ioctl-v1.c"
    +#endif
    --- diff/drivers/md/dm.c 2003-06-30 17:12:16.000000000 +0100
    +++ source/drivers/md/dm.c 2003-07-01 15:36:42.000000000 +0100
    @@ -516,6 +516,11 @@
    down_read(&md->lock);
    }

    + if (!md->map) {
    + bio_io_error(bio, bio->bi_size);
    + return 0;
    + }
    +
    __split_bio(md, bio);
    up_read(&md->lock);
    return 0;
    @@ -672,6 +677,9 @@

    static void __unbind(struct mapped_device *md)
    {
    + if (!md->map)
    + return;
    +
    dm_table_event_callback(md->map, NULL, NULL);
    dm_table_put(md->map);
    md->map = NULL;
    @@ -682,35 +690,26 @@
    * Constructor for a new device.
    */
    static int create_aux(unsigned int minor, int persistent,
    - struct dm_table *table, struct mapped_device **result)
    + struct mapped_device **result)
    {
    - int r;
    struct mapped_device *md;

    md = alloc_dev(minor, persistent);
    if (!md)
    return -ENXIO;

    - r = __bind(md, table);
    - if (r) {
    - free_dev(md);
    - return r;
    - }
    - dm_table_resume_targets(md->map);
    -
    *result = md;
    return 0;
    }

    -int dm_create(struct dm_table *table, struct mapped_device **result)
    +int dm_create(struct mapped_device **result)
    {
    - return create_aux(0, 0, table, result);
    + return create_aux(0, 0, result);
    }

    -int dm_create_with_minor(unsigned int minor,
    - struct dm_table *table, struct mapped_device **result)
    +int dm_create_with_minor(unsigned int minor, struct mapped_device **result)
    {
    - return create_aux(minor, 1, table, result);
    + return create_aux(minor, 1, result);
    }

    void dm_get(struct mapped_device *md)
    @@ -721,7 +720,7 @@
    void dm_put(struct mapped_device *md)
    {
    if (atomic_dec_and_test(&md->holders)) {
    - if (!test_bit(DMF_SUSPENDED, &md->flags))
    + if (!test_bit(DMF_SUSPENDED, &md->flags) && md->map)
    dm_table_suspend_targets(md->map);
    __unbind(md);
    free_dev(md);
    @@ -812,7 +811,8 @@
    down_write(&md->lock);
    remove_wait_queue(&md->wait, &wait);
    set_bit(DMF_SUSPENDED, &md->flags);
    - dm_table_suspend_targets(md->map);
    + if (md->map)
    + dm_table_suspend_targets(md->map);
    up_write(&md->lock);

    return 0;
    @@ -823,7 +823,8 @@
    struct deferred_io *def;

    down_write(&md->lock);
    - if (!test_bit(DMF_SUSPENDED, &md->flags) ||
    + if (!md->map ||
    + !test_bit(DMF_SUSPENDED, &md->flags) ||
    !dm_table_get_size(md->map)) {
    up_write(&md->lock);
    return -EINVAL;
    @@ -893,7 +894,8 @@

    down_read(&md->lock);
    t = md->map;
    - dm_table_get(t);
    + if (t)
    + dm_table_get(t);
    up_read(&md->lock);

    return t;
    --- diff/drivers/md/dm.h 2003-06-30 17:11:07.000000000 +0100
    +++ source/drivers/md/dm.h 2003-07-01 15:36:42.000000000 +0100
    @@ -51,9 +51,8 @@
    * Functions for manipulating a struct mapped_device.
    * Drop the reference with dm_put when you finish with the object.
    *---------------------------------------------------------------*/
    -int dm_create(struct dm_table *table, struct mapped_device **md);
    -int dm_create_with_minor(unsigned int minor, struct dm_table *table,
    - struct mapped_device **md);
    +int dm_create(struct mapped_device **md);
    +int dm_create_with_minor(unsigned int minor, struct mapped_device **md);

    /*
    * Reference counting for md.
    --- diff/include/linux/dm-ioctl.h 2002-12-30 10:17:13.000000000 +0000
    +++ source/include/linux/dm-ioctl.h 2003-07-01 15:36:42.000000000 +0100
    @@ -1,5 +1,5 @@
    /*
    - * Copyright (C) 2001 Sistina Software (UK) Limited.
    + * Copyright (C) 2003 Sistina Software (UK) Limited.
    *
    * This file is released under the LGPL.
    */
    @@ -7,143 +7,12 @@
    #ifndef _LINUX_DM_IOCTL_H
    #define _LINUX_DM_IOCTL_H

    -#include <linux/types.h>
    +#include <linux/config.h>

    -#define DM_DIR "mapper" /* Slashes not supported */
    -#define DM_MAX_TYPE_NAME 16
    -#define DM_NAME_LEN 128
    -#define DM_UUID_LEN 129
    +#ifdef CONFIG_DM_IOCTL_V4
    +#include "dm-ioctl-v4.h"
    +#else
    +#include "dm-ioctl-v1.h"
    +#endif

    -/*
    - * Implements a traditional ioctl interface to the device mapper.
    - */
    -
    -/*
    - * All ioctl arguments consist of a single chunk of memory, with
    - * this structure at the start. If a uuid is specified any
    - * lookup (eg. for a DM_INFO) will be done on that, *not* the
    - * name.
    - */
    -struct dm_ioctl {
    - /*
    - * The version number is made up of three parts:
    - * major - no backward or forward compatibility,
    - * minor - only backwards compatible,
    - * patch - both backwards and forwards compatible.
    - *
    - * All clients of the ioctl interface should fill in the
    - * version number of the interface that they were
    - * compiled with.
    - *
    - * All recognised ioctl commands (ie. those that don't
    - * return -ENOTTY) fill out this field, even if the
    - * command failed.
    - */
    - 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 */
    -};
    -
    -/*
    - * Used to specify tables. These structures appear after the
    - * dm_ioctl.
    - */
    -struct dm_target_spec {
    - int32_t status; /* used when reading from kernel only */
    - uint64_t sector_start;
    - uint32_t length;
    -
    - /*
    - * Offset in bytes (from the start of this struct) to
    - * next target_spec.
    - */
    - uint32_t next;
    -
    - char target_type[DM_MAX_TYPE_NAME];
    -
    - /*
    - * Parameter string starts immediately after this object.
    - * Be careful to add padding after string to ensure correct
    - * alignment of subsequent dm_target_spec.
    - */
    -};
    -
    -/*
    - * Used to retrieve the target dependencies.
    - */
    -struct dm_target_deps {
    - uint32_t count;
    -
    - __kernel_dev_t dev[0]; /* out */
    -};
    -
    -/*
    - * If you change this make sure you make the corresponding change
    - * to dm-ioctl.c:lookup_ioctl()
    - */
    -enum {
    - /* Top level cmds */
    - DM_VERSION_CMD = 0,
    - DM_REMOVE_ALL_CMD,
    -
    - /* device level cmds */
    - DM_DEV_CREATE_CMD,
    - DM_DEV_REMOVE_CMD,
    - DM_DEV_RELOAD_CMD,
    - DM_DEV_RENAME_CMD,
    - DM_DEV_SUSPEND_CMD,
    - DM_DEV_DEPS_CMD,
    - DM_DEV_STATUS_CMD,
    -
    - /* target level cmds */
    - DM_TARGET_STATUS_CMD,
    - DM_TARGET_WAIT_CMD
    -};
    -
    -#define DM_IOCTL 0xfd
    -
    -#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
    -#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
    -
    -#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
    -#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
    -#define DM_DEV_RELOAD _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD, struct dm_ioctl)
    -#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
    -#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
    -#define DM_DEV_DEPS _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD, struct dm_ioctl)
    -#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
    -
    -#define DM_TARGET_STATUS _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD, struct dm_ioctl)
    -#define DM_TARGET_WAIT _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD, struct dm_ioctl)
    -
    -#define DM_VERSION_MAJOR 1
    -#define DM_VERSION_MINOR 0
    -#define DM_VERSION_PATCHLEVEL 6
    -#define DM_VERSION_EXTRA "-ioctl (2002-10-15)"
    -
    -/* Status bits */
    -#define DM_READONLY_FLAG 0x00000001
    -#define DM_SUSPEND_FLAG 0x00000002
    -#define DM_EXISTS_FLAG 0x00000004
    -#define DM_PERSISTENT_DEV_FLAG 0x00000008
    -
    -/*
    - * Flag passed into ioctl STATUS command to get table information
    - * rather than current status.
    - */
    -#define DM_STATUS_TABLE_FLAG 0x00000010
    -
    -#endif /* _LINUX_DM_IOCTL_H */
    +#endif
    --- diff/drivers/md/dm-ioctl-v1.c 1970-01-01 01:00:00.000000000 +0100
    +++ source/drivers/md/dm-ioctl-v1.c 2003-07-01 15:36:42.000000000 +0100
    @@ -0,0 +1,1160 @@
    +/*
    + * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
    + *
    + * This file is released under the GPL.
    + */
    +
    +#include "dm.h"
    +
    +#include <linux/module.h>
    +#include <linux/vmalloc.h>
    +#include <linux/miscdevice.h>
    +#include <linux/dm-ioctl.h>
    +#include <linux/init.h>
    +#include <linux/wait.h>
    +#include <linux/blk.h>
    +#include <linux/slab.h>
    +#include <linux/devfs_fs_kernel.h>
    +
    +#include <asm/uaccess.h>
    +
    +#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
    +
    +/*-----------------------------------------------------------------
    + * The ioctl interface needs to be able to look up devices by
    + * name or uuid.
    + *---------------------------------------------------------------*/
    +struct hash_cell {
    + struct list_head name_list;
    + struct list_head uuid_list;
    +
    + char *name;
    + char *uuid;
    + struct mapped_device *md;
    +};
    +
    +#define NUM_BUCKETS 64
    +#define MASK_BUCKETS (NUM_BUCKETS - 1)
    +static struct list_head _name_buckets[NUM_BUCKETS];
    +static struct list_head _uuid_buckets[NUM_BUCKETS];
    +
    +void dm_hash_remove_all(void);
    +
    +/*
    + * Guards access to all three tables.
    + */
    +static DECLARE_RWSEM(_hash_lock);
    +
    +static void init_buckets(struct list_head *buckets)
    +{
    + unsigned int i;
    +
    + for (i = 0; i < NUM_BUCKETS; i++)
    + INIT_LIST_HEAD(buckets + i);
    +}
    +
    +int dm_hash_init(void)
    +{
    + init_buckets(_name_buckets);
    + init_buckets(_uuid_buckets);
    + devfs_mk_dir(DM_DIR);
    + return 0;
    +}
    +
    +void dm_hash_exit(void)
    +{
    + dm_hash_remove_all();
    + devfs_remove(DM_DIR);
    +}
    +
    +/*-----------------------------------------------------------------
    + * Hash function:
    + * We're not really concerned with the str hash function being
    + * fast since it's only used by the ioctl interface.
    + *---------------------------------------------------------------*/
    +static unsigned int hash_str(const char *str)
    +{
    + const unsigned int hash_mult = 2654435387U;
    + unsigned int h = 0;
    +
    + while (*str)
    + h = (h + (unsigned int) *str++) * hash_mult;
    +
    + return h & MASK_BUCKETS;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Code for looking up a device by name
    + *---------------------------------------------------------------*/
    +static struct hash_cell *__get_name_cell(const char *str)
    +{
    + struct list_head *tmp;
    + struct hash_cell *hc;
    + unsigned int h = hash_str(str);
    +
    + list_for_each (tmp, _name_buckets + h) {
    + hc = list_entry(tmp, struct hash_cell, name_list);
    + if (!strcmp(hc->name, str))
    + return hc;
    + }
    +
    + return NULL;
    +}
    +
    +static struct hash_cell *__get_uuid_cell(const char *str)
    +{
    + struct list_head *tmp;
    + struct hash_cell *hc;
    + unsigned int h = hash_str(str);
    +
    + list_for_each (tmp, _uuid_buckets + h) {
    + hc = list_entry(tmp, struct hash_cell, uuid_list);
    + if (!strcmp(hc->uuid, str))
    + return hc;
    + }
    +
    + return NULL;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Inserting, removing and renaming a device.
    + *---------------------------------------------------------------*/
    +static inline char *kstrdup(const char *str)
    +{
    + char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
    + if (r)
    + strcpy(r, str);
    + return r;
    +}
    +
    +static struct hash_cell *alloc_cell(const char *name, const char *uuid,
    + struct mapped_device *md)
    +{
    + struct hash_cell *hc;
    +
    + hc = kmalloc(sizeof(*hc), GFP_KERNEL);
    + if (!hc)
    + return NULL;
    +
    + hc->name = kstrdup(name);
    + if (!hc->name) {
    + kfree(hc);
    + return NULL;
    + }
    +
    + if (!uuid)
    + hc->uuid = NULL;
    +
    + else {
    + hc->uuid = kstrdup(uuid);
    + if (!hc->uuid) {
    + kfree(hc->name);
    + kfree(hc);
    + return NULL;
    + }
    + }
    +
    + INIT_LIST_HEAD(&hc->name_list);
    + INIT_LIST_HEAD(&hc->uuid_list);
    + hc->md = md;
    + return hc;
    +}
    +
    +static void free_cell(struct hash_cell *hc)
    +{
    + if (hc) {
    + kfree(hc->name);
    + kfree(hc->uuid);
    + kfree(hc);
    + }
    +}
    +
    +/*
    + * devfs stuff.
    + */
    +static int register_with_devfs(struct hash_cell *hc)
    +{
    + struct gendisk *disk = dm_disk(hc->md);
    +
    + devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
    + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
    + DM_DIR "/%s", hc->name);
    + return 0;
    +}
    +
    +static int unregister_with_devfs(struct hash_cell *hc)
    +{
    + devfs_remove(DM_DIR"/%s", hc->name);
    + return 0;
    +}
    +
    +/*
    + * The kdev_t and uuid of a device can never change once it is
    + * initially inserted.
    + */
    +int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
    +{
    + struct hash_cell *cell;
    +
    + /*
    + * Allocate the new cells.
    + */
    + cell = alloc_cell(name, uuid, md);
    + if (!cell)
    + return -ENOMEM;
    +
    + /*
    + * Insert the cell into all three hash tables.
    + */
    + down_write(&_hash_lock);
    + if (__get_name_cell(name))
    + goto bad;
    +
    + list_add(&cell->name_list, _name_buckets + hash_str(name));
    +
    + if (uuid) {
    + if (__get_uuid_cell(uuid)) {
    + list_del(&cell->name_list);
    + goto bad;
    + }
    + list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
    + }
    + register_with_devfs(cell);
    + dm_get(md);
    + up_write(&_hash_lock);
    +
    + return 0;
    +
    + bad:
    + up_write(&_hash_lock);
    + free_cell(cell);
    + return -EBUSY;
    +}
    +
    +void __hash_remove(struct hash_cell *hc)
    +{
    + /* remove from the dev hash */
    + list_del(&hc->uuid_list);
    + list_del(&hc->name_list);
    + unregister_with_devfs(hc);
    + dm_put(hc->md);
    + free_cell(hc);
    +}
    +
    +void dm_hash_remove_all(void)
    +{
    + int i;
    + struct hash_cell *hc;
    + struct list_head *tmp, *n;
    +
    + down_write(&_hash_lock);
    + for (i = 0; i < NUM_BUCKETS; i++) {
    + list_for_each_safe (tmp, n, _name_buckets + i) {
    + hc = list_entry(tmp, struct hash_cell, name_list);
    + __hash_remove(hc);
    + }
    + }
    + up_write(&_hash_lock);
    +}
    +
    +int dm_hash_rename(const char *old, const char *new)
    +{
    + char *new_name, *old_name;
    + struct hash_cell *hc;
    +
    + /*
    + * duplicate new.
    + */
    + new_name = kstrdup(new);
    + if (!new_name)
    + return -ENOMEM;
    +
    + down_write(&_hash_lock);
    +
    + /*
    + * Is new free ?
    + */
    + hc = __get_name_cell(new);
    + if (hc) {
    + DMWARN("asked to rename to an already existing name %s -> %s",
    + old, new);
    + up_write(&_hash_lock);
    + kfree(new_name);
    + return -EBUSY;
    + }
    +
    + /*
    + * Is there such a device as 'old' ?
    + */
    + hc = __get_name_cell(old);
    + if (!hc) {
    + DMWARN("asked to rename a non existent device %s -> %s",
    + old, new);
    + up_write(&_hash_lock);
    + kfree(new_name);
    + return -ENXIO;
    + }
    +
    + /*
    + * rename and move the name cell.
    + */
    + unregister_with_devfs(hc);
    +
    + list_del(&hc->name_list);
    + old_name = hc->name;
    + hc->name = new_name;
    + list_add(&hc->name_list, _name_buckets + hash_str(new_name));
    +
    + /* rename the device node in devfs */
    + register_with_devfs(hc);
    +
    + up_write(&_hash_lock);
    + kfree(old_name);
    + return 0;
    +}
    +
    +
    +/*-----------------------------------------------------------------
    + * Implementation of the ioctl commands
    + *---------------------------------------------------------------*/
    +
    +/*
    + * All the ioctl commands get dispatched to functions with this
    + * prototype.
    + */
    +typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user);
    +
    +/*
    + * Check a string doesn't overrun the chunk of
    + * memory we copied from userland.
    + */
    +static int valid_str(char *str, void *begin, void *end)
    +{
    + while (((void *) str >= begin) && ((void *) str < end))
    + if (!*str++)
    + return 0;
    +
    + return -EINVAL;
    +}
    +
    +static int next_target(struct dm_target_spec *last, uint32_t next,
    + void *begin, void *end,
    + struct dm_target_spec **spec, char **params)
    +{
    + *spec = (struct dm_target_spec *)
    + ((unsigned char *) last + next);
    + *params = (char *) (*spec + 1);
    +
    + if (*spec < (last + 1) || ((void *) *spec > end))
    + return -EINVAL;
    +
    + return valid_str(*params, begin, end);
    +}
    +
    +static int populate_table(struct dm_table *table, struct dm_ioctl *args)
    +{
    + int r, first = 1;
    + unsigned int i = 0;
    + struct dm_target_spec *spec;
    + char *params;
    + void *begin, *end;
    +
    + if (!args->target_count) {
    + DMWARN("populate_table: no targets specified");
    + return -EINVAL;
    + }
    +
    + begin = (void *) args;
    + end = begin + args->data_size;
    +
    + for (i = 0; i < args->target_count; i++) {
    +
    + if (first)
    + r = next_target((struct dm_target_spec *) args,
    + args->data_start,
    + begin, end, &spec, &params);
    + else
    + r = next_target(spec, spec->next, begin, end,
    + &spec, &params);
    +
    + if (r) {
    + DMWARN("unable to find target");
    + return -EINVAL;
    + }
    +
    + r = dm_table_add_target(table, spec->target_type,
    + (sector_t) spec->sector_start,
    + (sector_t) spec->length,
    + params);
    + if (r) {
    + DMWARN("internal error adding target to table");
    + return -EINVAL;
    + }
    +
    + first = 0;
    + }
    +
    + return dm_table_complete(table);
    +}
    +
    +/*
    + * Round up the ptr to the next 'align' boundary. Obviously
    + * 'align' must be a power of 2.
    + */
    +static inline void *align_ptr(void *ptr, unsigned int align)
    +{
    + align--;
    + return (void *) (((unsigned long) (ptr + align)) & ~align);
    +}
    +
    +/*
    + * Copies a dm_ioctl and an optional additional payload to
    + * userland.
    + */
    +static int results_to_user(struct dm_ioctl *user, struct dm_ioctl *param,
    + void *data, uint32_t len)
    +{
    + int r;
    + void *ptr = NULL;
    +
    + if (data) {
    + ptr = align_ptr(user + 1, sizeof(unsigned long));
    + param->data_start = ptr - (void *) user;
    + }
    +
    + /*
    + * The version number has already been filled in, so we
    + * just copy later fields.
    + */
    + r = copy_to_user(&user->data_size, &param->data_size,
    + sizeof(*param) - sizeof(param->version));
    + if (r)
    + return -EFAULT;
    +
    + if (data) {
    + if (param->data_start + len > param->data_size)
    + return -ENOSPC;
    +
    + if (copy_to_user(ptr, data, len))
    + r = -EFAULT;
    + }
    +
    + return r;
    +}
    +
    +/*
    + * Fills in a dm_ioctl structure, ready for sending back to
    + * userland.
    + */
    +static int __info(struct mapped_device *md, struct dm_ioctl *param)
    +{
    + struct dm_table *table;
    + struct block_device *bdev;
    + struct gendisk *disk = dm_disk(md);
    +
    + param->flags = DM_EXISTS_FLAG;
    + if (dm_suspended(md))
    + param->flags |= DM_SUSPEND_FLAG;
    +
    + bdev = bdget_disk(disk, 0);
    + if (!bdev)
    + return -ENXIO;
    +
    + param->dev = bdev->bd_dev;
    + param->open_count = bdev->bd_openers;
    + bdput(bdev);
    +
    + if (disk->policy)
    + param->flags |= DM_READONLY_FLAG;
    +
    + table = dm_get_table(md);
    + param->target_count = dm_table_get_num_targets(table);
    + dm_table_put(table);
    +
    + return 0;
    +}
    +
    +/*
    + * Always use UUID for lookups if it's present, otherwise use name.
    + */
    +static inline struct mapped_device *find_device(struct dm_ioctl *param)
    +{
    + struct hash_cell *hc;
    + struct mapped_device *md = NULL;
    +
    + down_read(&_hash_lock);
    + hc = *param->uuid ? __get_uuid_cell(param->uuid) :
    + __get_name_cell(param->name);
    + if (hc) {
    + md = hc->md;
    +
    + /*
    + * Sneakily write in both the name and the uuid
    + * while we have the cell.
    + */
    + strlcpy(param->name, hc->name, sizeof(param->name));
    + if (hc->uuid)
    + strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
    + else
    + param->uuid[0] = '\0';
    +
    + dm_get(md);
    + }
    + up_read(&_hash_lock);
    +
    + return md;
    +}
    +
    +#define ALIGNMENT sizeof(int)
    +static void *_align(void *ptr, unsigned int a)
    +{
    + register unsigned long align = --a;
    +
    + return (void *) (((unsigned long) ptr + align) & ~align);
    +}
    +
    +/*
    + * Copies device info back to user space, used by
    + * the create and info ioctls.
    + */
    +static int info(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + struct mapped_device *md;
    +
    + param->flags = 0;
    +
    + md = find_device(param);
    + if (!md)
    + /*
    + * Device not found - returns cleared exists flag.
    + */
    + goto out;
    +
    + __info(md, param);
    + dm_put(md);
    +
    + out:
    + return results_to_user(user, param, NULL, 0);
    +}
    +
    +static inline int get_mode(struct dm_ioctl *param)
    +{
    + int mode = FMODE_READ | FMODE_WRITE;
    +
    + if (param->flags & DM_READONLY_FLAG)
    + mode = FMODE_READ;
    +
    + return mode;
    +}
    +
    +static int check_name(const char *name)
    +{
    + if (name[0] == '/') {
    + DMWARN("invalid device name");
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +static int create(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + int r;
    + struct dm_table *t;
    + struct mapped_device *md;
    +
    + r = check_name(param->name);
    + if (r)
    + return r;
    +
    + r = dm_table_create(&t, get_mode(param));
    + if (r)
    + return r;
    +
    + r = populate_table(t, param);
    + if (r) {
    + dm_table_put(t);
    + return r;
    + }
    +
    + if (param->flags & DM_PERSISTENT_DEV_FLAG)
    + r = dm_create_with_minor(minor(to_kdev_t(param->dev)), &md);
    + else
    + r = dm_create(&md);
    +
    + if (r) {
    + dm_table_put(t);
    + return r;
    + }
    +
    + /* suspend the device */
    + r = dm_suspend(md);
    + if (r) {
    + DMWARN("suspend failed");
    + dm_table_put(t);
    + dm_put(md);
    + return r;
    + }
    + /* swap in the table */
    + r = dm_swap_table(md, t);
    + if (r) {
    + DMWARN("table swap failed");
    + dm_table_put(t);
    + dm_put(md);
    + return r;
    + }
    +
    + /* resume the device */
    + r = dm_resume(md);
    + if (r) {
    + DMWARN("resume failed");
    + dm_table_put(t);
    + dm_put(md);
    + return r;
    + }
    +
    + dm_table_put(t); /* md will have grabbed its own reference */
    +
    + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0);
    + r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
    + dm_put(md);
    +
    + return r ? r : info(param, user);
    +}
    +
    +/*
    + * Build up the status struct for each target
    + */
    +static int __status(struct mapped_device *md, struct dm_ioctl *param,
    + char *outbuf, size_t *len)
    +{
    + unsigned int i, num_targets;
    + struct dm_target_spec *spec;
    + char *outptr;
    + status_type_t type;
    + struct dm_table *table = dm_get_table(md);
    +
    + if (param->flags & DM_STATUS_TABLE_FLAG)
    + type = STATUSTYPE_TABLE;
    + else
    + type = STATUSTYPE_INFO;
    +
    + outptr = outbuf;
    +
    + /* Get all the target info */
    + num_targets = dm_table_get_num_targets(table);
    + for (i = 0; i < num_targets; i++) {
    + struct dm_target *ti = dm_table_get_target(table, i);
    +
    + if (outptr - outbuf +
    + sizeof(struct dm_target_spec) > param->data_size) {
    + dm_table_put(table);
    + return -ENOMEM;
    + }
    +
    + spec = (struct dm_target_spec *) outptr;
    +
    + spec->status = 0;
    + spec->sector_start = ti->begin;
    + spec->length = ti->len;
    + strlcpy(spec->target_type, ti->type->name,
    + sizeof(spec->target_type));
    +
    + outptr += sizeof(struct dm_target_spec);
    +
    + /* Get the status/table string from the target driver */
    + if (ti->type->status)
    + ti->type->status(ti, type, outptr,
    + outbuf + param->data_size - outptr);
    + else
    + outptr[0] = '\0';
    +
    + outptr += strlen(outptr) + 1;
    + _align(outptr, ALIGNMENT);
    + spec->next = outptr - outbuf;
    + }
    +
    + param->target_count = num_targets;
    + *len = outptr - outbuf;
    + dm_table_put(table);
    +
    + return 0;
    +}
    +
    +/*
    + * Return the status of a device as a text string for each
    + * target.
    + */
    +static int get_status(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + struct mapped_device *md;
    + size_t len = 0;
    + int ret;
    + char *outbuf = NULL;
    +
    + md = find_device(param);
    + if (!md)
    + /*
    + * Device not found - returns cleared exists flag.
    + */
    + goto out;
    +
    + /* We haven't a clue how long the resultant data will be so
    + just allocate as much as userland has allowed us and make sure
    + we don't overun it */
    + outbuf = kmalloc(param->data_size, GFP_KERNEL);
    + if (!outbuf)
    + goto out;
    + /*
    + * Get the status of all targets
    + */
    + __status(md, param, outbuf, &len);
    +
    + /*
    + * Setup the basic dm_ioctl structure.
    + */
    + __info(md, param);
    +
    + out:
    + if (md)
    + dm_put(md);
    +
    + ret = results_to_user(user, param, outbuf, len);
    +
    + if (outbuf)
    + kfree(outbuf);
    +
    + return ret;
    +}
    +
    +/*
    + * Wait for a device to report an event
    + */
    +static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + struct mapped_device *md;
    + DECLARE_WAITQUEUE(wq, current);
    +
    + md = find_device(param);
    + if (!md)
    + /*
    + * Device not found - returns cleared exists flag.
    + */
    + goto out;
    +
    + /*
    + * Setup the basic dm_ioctl structure.
    + */
    + __info(md, param);
    +
    + /*
    + * Wait for a notification event
    + */
    + set_current_state(TASK_INTERRUPTIBLE);
    + if (!dm_add_wait_queue(md, &wq, dm_get_event_nr(md))) {
    + yield();
    + dm_remove_wait_queue(md, &wq);
    + }
    + set_current_state(TASK_RUNNING);
    + dm_put(md);
    +
    + out:
    + return results_to_user(user, param, NULL, 0);
    +}
    +
    +/*
    + * Retrieves a list of devices used by a particular dm device.
    + */
    +static int dep(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + int r;
    + unsigned int count;
    + struct mapped_device *md;
    + struct list_head *tmp;
    + size_t len = 0;
    + struct dm_target_deps *deps = NULL;
    + struct dm_table *table;
    +
    + md = find_device(param);
    + if (!md)
    + goto out;
    + table = dm_get_table(md);
    +
    + /*
    + * Setup the basic dm_ioctl structure.
    + */
    + __info(md, param);
    +
    + /*
    + * Count the devices.
    + */
    + count = 0;
    + list_for_each(tmp, dm_table_get_devices(table))
    + count++;
    +
    + /*
    + * Allocate a kernel space version of the dm_target_status
    + * struct.
    + */
    + if (array_too_big(sizeof(*deps), sizeof(*deps->dev), count)) {
    + dm_table_put(table);
    + dm_put(md);
    + return -ENOMEM;
    + }
    +
    + len = sizeof(*deps) + (sizeof(*deps->dev) * count);
    + deps = kmalloc(len, GFP_KERNEL);
    + if (!deps) {
    + dm_table_put(table);
    + dm_put(md);
    + return -ENOMEM;
    + }
    +
    + /*
    + * Fill in the devices.
    + */
    + deps->count = count;
    + count = 0;
    + list_for_each(tmp, dm_table_get_devices(table)) {
    + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
    + deps->dev[count++] = dd->bdev->bd_dev;
    + }
    + dm_table_put(table);
    + dm_put(md);
    +
    + out:
    + r = results_to_user(user, param, deps, len);
    +
    + kfree(deps);
    + return r;
    +}
    +
    +static int remove(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + struct hash_cell *hc;
    +
    + down_write(&_hash_lock);
    + hc = *param->uuid ? __get_uuid_cell(param->uuid) :
    + __get_name_cell(param->name);
    + if (!hc) {
    + DMWARN("device doesn't appear to be in the dev hash table.");
    + up_write(&_hash_lock);
    + return -EINVAL;
    + }
    +
    + /*
    + * You may ask the interface to drop its reference to an
    + * in use device. This is no different to unlinking a
    + * file that someone still has open. The device will not
    + * actually be destroyed until the last opener closes it.
    + * The name and uuid of the device (both are interface
    + * properties) will be available for reuse immediately.
    + *
    + * You don't want to drop a _suspended_ device from the
    + * interface, since that will leave you with no way of
    + * resuming it.
    + */
    + if (dm_suspended(hc->md)) {
    + DMWARN("refusing to remove a suspended device.");
    + up_write(&_hash_lock);
    + return -EPERM;
    + }
    +
    + __hash_remove(hc);
    + up_write(&_hash_lock);
    + return 0;
    +}
    +
    +static int remove_all(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + dm_hash_remove_all();
    + return 0;
    +}
    +
    +static int suspend(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + int r;
    + struct mapped_device *md;
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + if (param->flags & DM_SUSPEND_FLAG)
    + r = dm_suspend(md);
    + else
    + r = dm_resume(md);
    +
    + dm_put(md);
    + return r;
    +}
    +
    +static int reload(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + int r;
    + struct mapped_device *md;
    + struct dm_table *t;
    +
    + r = dm_table_create(&t, get_mode(param));
    + if (r)
    + return r;
    +
    + r = populate_table(t, param);
    + if (r) {
    + dm_table_put(t);
    + return r;
    + }
    +
    + md = find_device(param);
    + if (!md) {
    + dm_table_put(t);
    + return -ENXIO;
    + }
    +
    + r = dm_swap_table(md, t);
    + if (r) {
    + dm_put(md);
    + dm_table_put(t);
    + return r;
    + }
    + dm_table_put(t); /* md will have taken its own reference */
    +
    + set_disk_ro(dm_disk(md), (param->flags & DM_READONLY_FLAG) ? 1 : 0);
    + dm_put(md);
    +
    + r = info(param, user);
    + return r;
    +}
    +
    +static int rename(struct dm_ioctl *param, struct dm_ioctl *user)
    +{
    + int r;
    + char *new_name = (char *) param + param->data_start;
    +
    + if (valid_str(new_name, (void *) param,
    + (void *) param + param->data_size)) {
    + DMWARN("Invalid new logical volume name supplied.");
    + return -EINVAL;
    + }
    +
    + r = check_name(new_name);
    + if (r)
    + return r;
    +
    + return dm_hash_rename(param->name, new_name);
    +}
    +
    +
    +/*-----------------------------------------------------------------
    + * Implementation of open/close/ioctl on the special char
    + * device.
    + *---------------------------------------------------------------*/
    +static ioctl_fn lookup_ioctl(unsigned int cmd)
    +{
    + static struct {
    + int cmd;
    + ioctl_fn fn;
    + } _ioctls[] = {
    + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */
    + {DM_REMOVE_ALL_CMD, remove_all},
    + {DM_DEV_CREATE_CMD, create},
    + {DM_DEV_REMOVE_CMD, remove},
    + {DM_DEV_RELOAD_CMD, reload},
    + {DM_DEV_RENAME_CMD, rename},
    + {DM_DEV_SUSPEND_CMD, suspend},
    + {DM_DEV_DEPS_CMD, dep},
    + {DM_DEV_STATUS_CMD, info},
    + {DM_TARGET_STATUS_CMD, get_status},
    + {DM_TARGET_WAIT_CMD, wait_device_event},
    + };
    +
    + return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
    +}
    +
    +/*
    + * As well as checking the version compatibility this always
    + * copies the kernel interface version out.
    + */
    +static int check_version(unsigned int cmd, struct dm_ioctl *user)
    +{
    + uint32_t version[3];
    + int r = 0;
    +
    + if (copy_from_user(version, user->version, sizeof(version)))
    + return -EFAULT;
    +
    + if ((DM_VERSION_MAJOR != version[0]) ||
    + (DM_VERSION_MINOR < version[1])) {
    + DMWARN("ioctl interface mismatch: "
    + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
    + DM_VERSION_MAJOR, DM_VERSION_MINOR,
    + DM_VERSION_PATCHLEVEL,
    + version[0], version[1], version[2], cmd);
    + r = -EINVAL;
    + }
    +
    + /*
    + * Fill in the kernel version.
    + */
    + version[0] = DM_VERSION_MAJOR;
    + version[1] = DM_VERSION_MINOR;
    + version[2] = DM_VERSION_PATCHLEVEL;
    + if (copy_to_user(user->version, version, sizeof(version)))
    + return -EFAULT;
    +
    + return r;
    +}
    +
    +static void free_params(struct dm_ioctl *param)
    +{
    + vfree(param);
    +}
    +
    +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param)
    +{
    + struct dm_ioctl tmp, *dmi;
    +
    + if (copy_from_user(&tmp, user, sizeof(tmp)))
    + return -EFAULT;
    +
    + if (tmp.data_size < sizeof(tmp))
    + return -EINVAL;
    +
    + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
    + if (!dmi)
    + return -ENOMEM;
    +
    + if (copy_from_user(dmi, user, tmp.data_size)) {
    + vfree(dmi);
    + return -EFAULT;
    + }
    +
    + *param = dmi;
    + return 0;
    +}
    +
    +static int validate_params(uint cmd, struct dm_ioctl *param)
    +{
    + /* Ignores parameters */
    + if (cmd == DM_REMOVE_ALL_CMD)
    + return 0;
    +
    + /* Unless creating, either name of uuid but not both */
    + if (cmd != DM_DEV_CREATE_CMD) {
    + if ((!*param->uuid && !*param->name) ||
    + (*param->uuid && *param->name)) {
    + DMWARN("one of name or uuid must be supplied");
    + return -EINVAL;
    + }
    + }
    +
    + /* Ensure strings are terminated */
    + param->name[DM_NAME_LEN - 1] = '\0';
    + param->uuid[DM_UUID_LEN - 1] = '\0';
    +
    + return 0;
    +}
    +
    +static int ctl_ioctl(struct inode *inode, struct file *file,
    + uint command, ulong u)
    +{
    + int r = 0;
    + unsigned int cmd;
    + struct dm_ioctl *param;
    + struct dm_ioctl *user = (struct dm_ioctl *) u;
    + ioctl_fn fn = NULL;
    +
    + /* only root can play with this */
    + if (!capable(CAP_SYS_ADMIN))
    + return -EACCES;
    +
    + if (_IOC_TYPE(command) != DM_IOCTL)
    + return -ENOTTY;
    +
    + cmd = _IOC_NR(command);
    +
    + /*
    + * Check the interface version passed in. This also
    + * writes out the kernels interface version.
    + */
    + r = check_version(cmd, user);
    + if (r)
    + return r;
    +
    + /*
    + * Nothing more to do for the version command.
    + */
    + if (cmd == DM_VERSION_CMD)
    + return 0;
    +
    + fn = lookup_ioctl(cmd);
    + if (!fn) {
    + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
    + return -ENOTTY;
    + }
    +
    + /*
    + * Copy the parameters into kernel space.
    + */
    + r = copy_params(user, &param);
    + if (r)
    + return r;
    +
    + r = validate_params(cmd, param);
    + if (r) {
    + free_params(param);
    + return r;
    + }
    +
    + r = fn(param, user);
    + free_params(param);
    + return r;
    +}
    +
    +static struct file_operations _ctl_fops = {
    + .ioctl = ctl_ioctl,
    + .owner = THIS_MODULE,
    +};
    +
    +static struct miscdevice _dm_misc = {
    + .minor = MISC_DYNAMIC_MINOR,
    + .name = DM_NAME,
    + .devfs_name = "mapper/control",
    + .fops = &_ctl_fops
    +};
    +
    +/*
    + * Create misc character device and link to DM_DIR/control.
    + */
    +int __init dm_interface_init(void)
    +{
    + int r;
    +
    + r = dm_hash_init();
    + if (r)
    + return r;
    +
    + r = misc_register(&_dm_misc);
    + if (r) {
    + DMERR("misc_register failed for control device");
    + dm_hash_exit();
    + return r;
    + }
    +
    + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
    + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
    + DM_DRIVER_EMAIL);
    + return 0;
    +
    + if (misc_deregister(&_dm_misc) < 0)
    + DMERR("misc_deregister failed for control device");
    + dm_hash_exit();
    + return r;
    +}
    +
    +void dm_interface_exit(void)
    +{
    + if (misc_deregister(&_dm_misc) < 0)
    + DMERR("misc_deregister failed for control device");
    + dm_hash_exit();
    +}
    --- diff/drivers/md/dm-ioctl-v4.c 1970-01-01 01:00:00.000000000 +0100
    +++ source/drivers/md/dm-ioctl-v4.c 2003-07-01 15:36:42.000000000 +0100
    @@ -0,0 +1,1248 @@
    +/*
    + * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
    + *
    + * This file is released under the GPL.
    + */
    +
    +#include "dm.h"
    +
    +#include <linux/module.h>
    +#include <linux/vmalloc.h>
    +#include <linux/miscdevice.h>
    +#include <linux/init.h>
    +#include <linux/wait.h>
    +#include <linux/blk.h>
    +#include <linux/slab.h>
    +#include <linux/devfs_fs_kernel.h>
    +
    +#include <asm/uaccess.h>
    +
    +#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
    +
    +/*-----------------------------------------------------------------
    + * The ioctl interface needs to be able to look up devices by
    + * name or uuid.
    + *---------------------------------------------------------------*/
    +struct hash_cell {
    + struct list_head name_list;
    + struct list_head uuid_list;
    +
    + char *name;
    + char *uuid;
    + struct mapped_device *md;
    + struct dm_table *new_map;
    +};
    +
    +#define NUM_BUCKETS 64
    +#define MASK_BUCKETS (NUM_BUCKETS - 1)
    +static struct list_head _name_buckets[NUM_BUCKETS];
    +static struct list_head _uuid_buckets[NUM_BUCKETS];
    +
    +void dm_hash_remove_all(void);
    +
    +/*
    + * Guards access to all three tables.
    + */
    +static DECLARE_RWSEM(_hash_lock);
    +
    +static void init_buckets(struct list_head *buckets)
    +{
    + unsigned int i;
    +
    + for (i = 0; i < NUM_BUCKETS; i++)
    + INIT_LIST_HEAD(buckets + i);
    +}
    +
    +int dm_hash_init(void)
    +{
    + init_buckets(_name_buckets);
    + init_buckets(_uuid_buckets);
    + devfs_mk_dir(DM_DIR);
    + return 0;
    +}
    +
    +void dm_hash_exit(void)
    +{
    + dm_hash_remove_all();
    + devfs_remove(DM_DIR);
    +}
    +
    +/*-----------------------------------------------------------------
    + * Hash function:
    + * We're not really concerned with the str hash function being
    + * fast since it's only used by the ioctl interface.
    + *---------------------------------------------------------------*/
    +static unsigned int hash_str(const char *str)
    +{
    + const unsigned int hash_mult = 2654435387U;
    + unsigned int h = 0;
    +
    + while (*str)
    + h = (h + (unsigned int) *str++) * hash_mult;
    +
    + return h & MASK_BUCKETS;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Code for looking up a device by name
    + *---------------------------------------------------------------*/
    +static struct hash_cell *__get_name_cell(const char *str)
    +{
    + struct list_head *tmp;
    + struct hash_cell *hc;
    + unsigned int h = hash_str(str);
    +
    + list_for_each (tmp, _name_buckets + h) {
    + hc = list_entry(tmp, struct hash_cell, name_list);
    + if (!strcmp(hc->name, str))
    + return hc;
    + }
    +
    + return NULL;
    +}
    +
    +static struct hash_cell *__get_uuid_cell(const char *str)
    +{
    + struct list_head *tmp;
    + struct hash_cell *hc;
    + unsigned int h = hash_str(str);
    +
    + list_for_each (tmp, _uuid_buckets + h) {
    + hc = list_entry(tmp, struct hash_cell, uuid_list);
    + if (!strcmp(hc->uuid, str))
    + return hc;
    + }
    +
    + return NULL;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Inserting, removing and renaming a device.
    + *---------------------------------------------------------------*/
    +static inline char *kstrdup(const char *str)
    +{
    + char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
    + if (r)
    + strcpy(r, str);
    + return r;
    +}
    +
    +static struct hash_cell *alloc_cell(const char *name, const char *uuid,
    + struct mapped_device *md)
    +{
    + struct hash_cell *hc;
    +
    + hc = kmalloc(sizeof(*hc), GFP_KERNEL);
    + if (!hc)
    + return NULL;
    +
    + hc->name = kstrdup(name);
    + if (!hc->name) {
    + kfree(hc);
    + return NULL;
    + }
    +
    + if (!uuid)
    + hc->uuid = NULL;
    +
    + else {
    + hc->uuid = kstrdup(uuid);
    + if (!hc->uuid) {
    + kfree(hc->name);
    + kfree(hc);
    + return NULL;
    + }
    + }
    +
    + INIT_LIST_HEAD(&hc->name_list);
    + INIT_LIST_HEAD(&hc->uuid_list);
    + hc->md = md;
    + hc->new_map = NULL;
    + return hc;
    +}
    +
    +static void free_cell(struct hash_cell *hc)
    +{
    + if (hc) {
    + kfree(hc->name);
    + kfree(hc->uuid);
    + kfree(hc);
    + }
    +}
    +
    +/*
    + * devfs stuff.
    + */
    +static int register_with_devfs(struct hash_cell *hc)
    +{
    + struct gendisk *disk = dm_disk(hc->md);
    +
    + devfs_mk_bdev(MKDEV(disk->major, disk->first_minor),
    + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
    + DM_DIR "/%s", hc->name);
    + return 0;
    +}
    +
    +static int unregister_with_devfs(struct hash_cell *hc)
    +{
    + devfs_remove(DM_DIR"/%s", hc->name);
    + return 0;
    +}
    +
    +/*
    + * The kdev_t and uuid of a device can never change once it is
    + * initially inserted.
    + */
    +int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
    +{
    + struct hash_cell *cell;
    +
    + /*
    + * Allocate the new cells.
    + */
    + cell = alloc_cell(name, uuid, md);
    + if (!cell)
    + return -ENOMEM;
    +
    + /*
    + * Insert the cell into all three hash tables.
    + */
    + down_write(&_hash_lock);
    + if (__get_name_cell(name))
    + goto bad;
    +
    + list_add(&cell->name_list, _name_buckets + hash_str(name));
    +
    + if (uuid) {
    + if (__get_uuid_cell(uuid)) {
    + list_del(&cell->name_list);
    + goto bad;
    + }
    + list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
    + }
    + register_with_devfs(cell);
    + dm_get(md);
    + up_write(&_hash_lock);
    +
    + return 0;
    +
    + bad:
    + up_write(&_hash_lock);
    + free_cell(cell);
    + return -EBUSY;
    +}
    +
    +void __hash_remove(struct hash_cell *hc)
    +{
    + /* remove from the dev hash */
    + list_del(&hc->uuid_list);
    + list_del(&hc->name_list);
    + unregister_with_devfs(hc);
    + dm_put(hc->md);
    + if (hc->new_map)
    + dm_table_put(hc->new_map);
    + free_cell(hc);
    +}
    +
    +void dm_hash_remove_all(void)
    +{
    + int i;
    + struct hash_cell *hc;
    + struct list_head *tmp, *n;
    +
    + down_write(&_hash_lock);
    + for (i = 0; i < NUM_BUCKETS; i++) {
    + list_for_each_safe (tmp, n, _name_buckets + i) {
    + hc = list_entry(tmp, struct hash_cell, name_list);
    + __hash_remove(hc);
    + }
    + }
    + up_write(&_hash_lock);
    +}
    +
    +int dm_hash_rename(const char *old, const char *new)
    +{
    + char *new_name, *old_name;
    + struct hash_cell *hc;
    +
    + /*
    + * duplicate new.
    + */
    + new_name = kstrdup(new);
    + if (!new_name)
    + return -ENOMEM;
    +
    + down_write(&_hash_lock);
    +
    + /*
    + * Is new free ?
    + */
    + hc = __get_name_cell(new);
    + if (hc) {
    + DMWARN("asked to rename to an already existing name %s -> %s",
    + old, new);
    + up_write(&_hash_lock);
    + kfree(new_name);
    + return -EBUSY;
    + }
    +
    + /*
    + * Is there such a device as 'old' ?
    + */
    + hc = __get_name_cell(old);
    + if (!hc) {
    + DMWARN("asked to rename a non existent device %s -> %s",
    + old, new);
    + up_write(&_hash_lock);
    + kfree(new_name);
    + return -ENXIO;
    + }
    +
    + /*
    + * rename and move the name cell.
    + */
    + list_del(&hc->name_list);
    + old_name = hc->name;
    + hc->name = new_name;
    + list_add(&hc->name_list, _name_buckets + hash_str(new_name));
    +
    + /* rename the device node in devfs */
    + unregister_with_devfs(hc);
    + register_with_devfs(hc);
    +
    + up_write(&_hash_lock);
    + kfree(old_name);
    + return 0;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Implementation of the ioctl commands
    + *---------------------------------------------------------------*/
    +/*
    + * All the ioctl commands get dispatched to functions with this
    + * prototype.
    + */
    +typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
    +
    +static int remove_all(struct dm_ioctl *param, size_t param_size)
    +{
    + dm_hash_remove_all();
    + param->data_size = 0;
    + return 0;
    +}
    +
    +/*
    + * Round up the ptr to an 8-byte boundary.
    + */
    +#define ALIGN_MASK 7
    +static inline void *align_ptr(void *ptr)
    +{
    + return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
    +}
    +
    +/*
    + * Retrieves the data payload buffer from an already allocated
    + * struct dm_ioctl.
    + */
    +static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
    + size_t *len)
    +{
    + param->data_start = align_ptr(param + 1) - (void *) param;
    +
    + if (param->data_start < param_size)
    + *len = param_size - param->data_start;
    + else
    + *len = 0;
    +
    + return ((void *) param) + param->data_start;
    +}
    +
    +static int list_devices(struct dm_ioctl *param, size_t param_size)
    +{
    + unsigned int i;
    + struct hash_cell *hc;
    + size_t len, needed = 0;
    + struct gendisk *disk;
    + struct dm_name_list *nl, *old_nl = NULL;
    +
    + down_write(&_hash_lock);
    +
    + /*
    + * Loop through all the devices working out how much
    + * space we need.
    + */
    + for (i = 0; i < NUM_BUCKETS; i++) {
    + list_for_each_entry (hc, _name_buckets + i, name_list) {
    + needed += sizeof(struct dm_name_list);
    + needed += strlen(hc->name);
    + needed += ALIGN_MASK;
    + }
    + }
    +
    + /*
    + * Grab our output buffer.
    + */
    + nl = get_result_buffer(param, param_size, &len);
    + if (len < needed) {
    + param->flags |= DM_BUFFER_FULL_FLAG;
    + goto out;
    + }
    + param->data_size = param->data_start + needed;
    +
    + nl->dev = 0; /* Flags no data */
    +
    + /*
    + * Now loop through filling out the names.
    + */
    + for (i = 0; i < NUM_BUCKETS; i++) {
    + list_for_each_entry (hc, _name_buckets + i, name_list) {
    + if (old_nl)
    + old_nl->next = (__u32) ((void *) nl -
    + (void *) old_nl);
    + disk = dm_disk(hc->md);
    + nl->dev = MKDEV(disk->major, disk->first_minor);
    + nl->next = 0;
    + strcpy(nl->name, hc->name);
    +
    + old_nl = nl;
    + nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
    + }
    + }
    +
    + out:
    + up_write(&_hash_lock);
    + return 0;
    +}
    +
    +static int check_name(const char *name)
    +{
    + if (strchr(name, '/')) {
    + DMWARN("invalid device name");
    + return -EINVAL;
    + }
    +
    + return 0;
    +}
    +
    +/*
    + * Fills in a dm_ioctl structure, ready for sending back to
    + * userland.
    + */
    +static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
    +{
    + struct gendisk *disk = dm_disk(md);
    + struct dm_table *table;
    + struct block_device *bdev;
    +
    + param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
    + DM_ACTIVE_PRESENT_FLAG);
    +
    + if (dm_suspended(md))
    + param->flags |= DM_SUSPEND_FLAG;
    +
    + bdev = bdget_disk(disk, 0);
    + if (!bdev)
    + return -ENXIO;
    +
    + param->dev = MKDEV(disk->major, disk->first_minor);
    +
    + /*
    + * Yes, this will be out of date by the time it gets back
    + * to userland, but it is still very useful ofr
    + * debugging.
    + */
    + param->open_count = bdev->bd_openers;
    + bdput(bdev);
    +
    + if (disk->policy)
    + param->flags |= DM_READONLY_FLAG;
    +
    + param->event_nr = dm_get_event_nr(md);
    +
    + table = dm_get_table(md);
    + if (table) {
    + param->flags |= DM_ACTIVE_PRESENT_FLAG;
    + param->target_count = dm_table_get_num_targets(table);
    + dm_table_put(table);
    + } else
    + param->target_count = 0;
    +
    + return 0;
    +}
    +
    +static int dev_create(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct mapped_device *md;
    +
    + r = check_name(param->name);
    + if (r)
    + return r;
    +
    + if (param->flags & DM_PERSISTENT_DEV_FLAG)
    + r = dm_create_with_minor(minor(to_kdev_t(param->dev)), &md);
    + else
    + r = dm_create(&md);
    +
    + if (r)
    + return r;
    +
    + r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
    + if (r) {
    + dm_put(md);
    + return r;
    + }
    +
    + param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
    +
    + r = __dev_status(md, param);
    + dm_put(md);
    +
    + return r;
    +}
    +
    +/*
    + * Always use UUID for lookups if it's present, otherwise use name.
    + */
    +static inline struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
    +{
    + return *param->uuid ?
    + __get_uuid_cell(param->uuid) : __get_name_cell(param->name);
    +}
    +
    +static inline struct mapped_device *find_device(struct dm_ioctl *param)
    +{
    + struct hash_cell *hc;
    + struct mapped_device *md = NULL;
    +
    + down_read(&_hash_lock);
    + hc = __find_device_hash_cell(param);
    + if (hc) {
    + md = hc->md;
    +
    + /*
    + * Sneakily write in both the name and the uuid
    + * while we have the cell.
    + */
    + strncpy(param->name, hc->name, sizeof(param->name));
    + if (hc->uuid)
    + strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1);
    + else
    + param->uuid[0] = '\0';
    +
    + if (hc->new_map)
    + param->flags |= DM_INACTIVE_PRESENT_FLAG;
    + else
    + param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
    +
    + dm_get(md);
    + }
    + up_read(&_hash_lock);
    +
    + return md;
    +}
    +
    +static int dev_remove(struct dm_ioctl *param, size_t param_size)
    +{
    + struct hash_cell *hc;
    +
    + down_write(&_hash_lock);
    + hc = __find_device_hash_cell(param);
    +
    + if (!hc) {
    + DMWARN("device doesn't appear to be in the dev hash table.");
    + up_write(&_hash_lock);
    + return -ENXIO;
    + }
    +
    + __hash_remove(hc);
    + up_write(&_hash_lock);
    + param->data_size = 0;
    + return 0;
    +}
    +
    +/*
    + * Check a string doesn't overrun the chunk of
    + * memory we copied from userland.
    + */
    +static int invalid_str(char *str, void *end)
    +{
    + while ((void *) str < end)
    + if (!*str++)
    + return 0;
    +
    + return -EINVAL;
    +}
    +
    +static int dev_rename(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + char *new_name = (char *) param + param->data_start;
    +
    + if (new_name < (char *) (param + 1) ||
    + invalid_str(new_name, (void *) param + param_size)) {
    + DMWARN("Invalid new logical volume name supplied.");
    + return -EINVAL;
    + }
    +
    + r = check_name(new_name);
    + if (r)
    + return r;
    +
    + param->data_size = 0;
    + return dm_hash_rename(param->name, new_name);
    +}
    +
    +static int suspend(struct dm_ioctl *param)
    +{
    + int r = 0;
    + struct mapped_device *md;
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + if (!dm_suspended(md))
    + r = dm_suspend(md);
    +
    + if (!r)
    + r = __dev_status(md, param);
    +
    + dm_put(md);
    + return r;
    +}
    +
    +static int resume(struct dm_ioctl *param)
    +{
    + int r = 0;
    + struct hash_cell *hc;
    + struct mapped_device *md;
    + struct dm_table *new_map;
    +
    + down_write(&_hash_lock);
    +
    + hc = __find_device_hash_cell(param);
    + if (!hc) {
    + DMWARN("device doesn't appear to be in the dev hash table.");
    + up_write(&_hash_lock);
    + return -ENXIO;
    + }
    +
    + md = hc->md;
    + dm_get(md);
    +
    + new_map = hc->new_map;
    + hc->new_map = NULL;
    + param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
    +
    + up_write(&_hash_lock);
    +
    + /* Do we need to load a new map ? */
    + if (new_map) {
    + /* Suspend if it isn't already suspended */
    + if (!dm_suspended(md))
    + dm_suspend(md);
    +
    + r = dm_swap_table(md, new_map);
    + if (r) {
    + dm_put(md);
    + dm_table_put(new_map);
    + return r;
    + }
    +
    + if (!(dm_table_get_mode(new_map) & FMODE_WRITE))
    + set_disk_ro(dm_disk(md), 1);
    +
    + dm_table_put(new_map);
    + }
    +
    + if (dm_suspended(md))
    + r = dm_resume(md);
    +
    + if (!r)
    + r = __dev_status(md, param);
    +
    + dm_put(md);
    + return r;
    +}
    +
    +/*
    + * Set or unset the suspension state of a device.
    + * If the device already is in the requested state we just return its status.
    + */
    +static int dev_suspend(struct dm_ioctl *param, size_t param_size)
    +{
    + if (param->flags & DM_SUSPEND_FLAG)
    + return suspend(param);
    +
    + return resume(param);
    +}
    +
    +/*
    + * Copies device info back to user space, used by
    + * the create and info ioctls.
    + */
    +static int dev_status(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct mapped_device *md;
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + r = __dev_status(md, param);
    + dm_put(md);
    + return r;
    +}
    +
    +/*
    + * Wait for a device to report an event
    + */
    +static int dev_wait(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct mapped_device *md;
    + DECLARE_WAITQUEUE(wq, current);
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + /*
    + * Wait for a notification event
    + */
    + set_current_state(TASK_INTERRUPTIBLE);
    + if (!dm_add_wait_queue(md, &wq, param->event_nr)) {
    + schedule();
    + dm_remove_wait_queue(md, &wq);
    + }
    +
    + /*
    + * The userland program is going to want to know what
    + * changed to trigger the event, so we may as well tell
    + * him and save an ioctl.
    + */
    + r = __dev_status(md, param);
    +
    + dm_put(md);
    + return r;
    +}
    +
    +static inline int get_mode(struct dm_ioctl *param)
    +{
    + int mode = FMODE_READ | FMODE_WRITE;
    +
    + if (param->flags & DM_READONLY_FLAG)
    + mode = FMODE_READ;
    +
    + return mode;
    +}
    +
    +static int next_target(struct dm_target_spec *last, __u32 next, void *end,
    + struct dm_target_spec **spec, char **target_params)
    +{
    + *spec = (struct dm_target_spec *) ((unsigned char *) last + next);
    + *target_params = (char *) (*spec + 1);
    +
    + if (*spec < (last + 1))
    + return -EINVAL;
    +
    + return invalid_str(*target_params, end);
    +}
    +
    +static int populate_table(struct dm_table *table,
    + struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + unsigned int i = 0;
    + struct dm_target_spec *spec = (struct dm_target_spec *) param;
    + __u32 next = param->data_start;
    + void *end = (void *) param + param_size;
    + char *target_params;
    +
    + if (!param->target_count) {
    + DMWARN("populate_table: no targets specified");
    + return -EINVAL;
    + }
    +
    + for (i = 0; i < param->target_count; i++) {
    +
    + r = next_target(spec, next, end, &spec, &target_params);
    + if (r) {
    + DMWARN("unable to find target");
    + return r;
    + }
    +
    + r = dm_table_add_target(table, spec->target_type,
    + (sector_t) spec->sector_start,
    + (sector_t) spec->length,
    + target_params);
    + if (r) {
    + DMWARN("error adding target to table");
    + return r;
    + }
    +
    + next = spec->next;
    + }
    +
    + return dm_table_complete(table);
    +}
    +
    +static int table_load(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct hash_cell *hc;
    + struct dm_table *t;
    +
    + r = dm_table_create(&t, get_mode(param));
    + if (r)
    + return r;
    +
    + r = populate_table(t, param, param_size);
    + if (r) {
    + dm_table_put(t);
    + return r;
    + }
    +
    + down_write(&_hash_lock);
    + hc = __find_device_hash_cell(param);
    + if (!hc) {
    + DMWARN("device doesn't appear to be in the dev hash table.");
    + up_write(&_hash_lock);
    + return -ENXIO;
    + }
    +
    + hc->new_map = t;
    + param->flags |= DM_INACTIVE_PRESENT_FLAG;
    +
    + r = __dev_status(hc->md, param);
    + up_write(&_hash_lock);
    + return r;
    +}
    +
    +static int table_clear(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct hash_cell *hc;
    +
    + down_write(&_hash_lock);
    +
    + hc = __find_device_hash_cell(param);
    + if (!hc) {
    + DMWARN("device doesn't appear to be in the dev hash table.");
    + up_write(&_hash_lock);
    + return -ENXIO;
    + }
    +
    + if (hc->new_map) {
    + dm_table_put(hc->new_map);
    + hc->new_map = NULL;
    + }
    +
    + param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
    +
    + r = __dev_status(hc->md, param);
    + up_write(&_hash_lock);
    + return r;
    +}
    +
    +/*
    + * Retrieves a list of devices used by a particular dm device.
    + */
    +static void retrieve_deps(struct dm_table *table,
    + struct dm_ioctl *param, size_t param_size)
    +{
    + unsigned int count = 0;
    + struct list_head *tmp;
    + size_t len, needed;
    + struct dm_target_deps *deps;
    +
    + deps = get_result_buffer(param, param_size, &len);
    +
    + /*
    + * Count the devices.
    + */
    + list_for_each(tmp, dm_table_get_devices(table))
    + count++;
    +
    + /*
    + * Check we have enough space.
    + */
    + needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
    + if (len < needed) {
    + param->flags |= DM_BUFFER_FULL_FLAG;
    + return;
    + }
    +
    + /*
    + * Fill in the devices.
    + */
    + deps->count = count;
    + count = 0;
    + list_for_each(tmp, dm_table_get_devices(table)) {
    + struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
    + deps->dev[count++] = dd->bdev->bd_dev;
    + }
    +
    + param->data_size = param->data_start + needed;
    +}
    +
    +static int table_deps(struct dm_ioctl *param, size_t param_size)
    +{
    + int r = 0;
    + struct mapped_device *md;
    + struct dm_table *table;
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + r = __dev_status(md, param);
    + if (r)
    + goto out;
    +
    + table = dm_get_table(md);
    + if (table) {
    + retrieve_deps(table, param, param_size);
    + dm_table_put(table);
    + }
    +
    + out:
    + dm_put(md);
    + return r;
    +}
    +
    +/*
    + * Build up the status struct for each target
    + */
    +static void retrieve_status(struct dm_table *table,
    + struct dm_ioctl *param, size_t param_size)
    +{
    + unsigned int i, num_targets;
    + struct dm_target_spec *spec;
    + char *outbuf, *outptr;
    + status_type_t type;
    + size_t remaining, len, used = 0;
    +
    + outptr = outbuf = get_result_buffer(param, param_size, &len);
    +
    + if (param->flags & DM_STATUS_TABLE_FLAG)
    + type = STATUSTYPE_TABLE;
    + else
    + type = STATUSTYPE_INFO;
    +
    + /* Get all the target info */
    + num_targets = dm_table_get_num_targets(table);
    + for (i = 0; i < num_targets; i++) {
    + struct dm_target *ti = dm_table_get_target(table, i);
    +
    + remaining = len - (outptr - outbuf);
    + if (remaining < sizeof(struct dm_target_spec)) {
    + param->flags |= DM_BUFFER_FULL_FLAG;
    + break;
    + }
    +
    + spec = (struct dm_target_spec *) outptr;
    +
    + spec->status = 0;
    + spec->sector_start = ti->begin;
    + spec->length = ti->len;
    + strncpy(spec->target_type, ti->type->name,
    + sizeof(spec->target_type));
    +
    + outptr += sizeof(struct dm_target_spec);
    + remaining = len - (outptr - outbuf);
    +
    + /* Get the status/table string from the target driver */
    + if (ti->type->status) {
    + if (ti->type->status(ti, type, outptr, remaining)) {
    + param->flags |= DM_BUFFER_FULL_FLAG;
    + break;
    + }
    + } else
    + outptr[0] = '\0';
    +
    + outptr += strlen(outptr) + 1;
    + used = param->data_start + (outptr - outbuf);
    +
    + align_ptr(outptr);
    + spec->next = outptr - outbuf;
    + }
    +
    + if (used)
    + param->data_size = used;
    +
    + param->target_count = num_targets;
    +}
    +
    +/*
    + * Return the status of a device as a text string for each
    + * target.
    + */
    +static int table_status(struct dm_ioctl *param, size_t param_size)
    +{
    + int r;
    + struct mapped_device *md;
    + struct dm_table *table;
    +
    + md = find_device(param);
    + if (!md)
    + return -ENXIO;
    +
    + r = __dev_status(md, param);
    + if (r)
    + goto out;
    +
    + table = dm_get_table(md);
    + if (table) {
    + retrieve_status(table, param, param_size);
    + dm_table_put(table);
    + }
    +
    + out:
    + dm_put(md);
    + return r;
    +}
    +
    +/*-----------------------------------------------------------------
    + * Implementation of open/close/ioctl on the special char
    + * device.
    + *---------------------------------------------------------------*/
    +static ioctl_fn lookup_ioctl(unsigned int cmd)
    +{
    + static struct {
    + int cmd;
    + ioctl_fn fn;
    + } _ioctls[] = {
    + {DM_VERSION_CMD, NULL}, /* version is dealt with elsewhere */
    + {DM_REMOVE_ALL_CMD, remove_all},
    + {DM_LIST_DEVICES_CMD, list_devices},
    +
    + {DM_DEV_CREATE_CMD, dev_create},
    + {DM_DEV_REMOVE_CMD, dev_remove},
    + {DM_DEV_RENAME_CMD, dev_rename},
    + {DM_DEV_SUSPEND_CMD, dev_suspend},
    + {DM_DEV_STATUS_CMD, dev_status},
    + {DM_DEV_WAIT_CMD, dev_wait},
    +
    + {DM_TABLE_LOAD_CMD, table_load},
    + {DM_TABLE_CLEAR_CMD, table_clear},
    + {DM_TABLE_DEPS_CMD, table_deps},
    + {DM_TABLE_STATUS_CMD, table_status}
    + };
    +
    + return (cmd >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[cmd].fn;
    +}
    +
    +/*
    + * As well as checking the version compatibility this always
    + * copies the kernel interface version out.
    + */
    +static int check_version(unsigned int cmd, struct dm_ioctl *user)
    +{
    + __u32 version[3];
    + int r = 0;
    +
    + if (copy_from_user(version, user->version, sizeof(version)))
    + return -EFAULT;
    +
    + if ((DM_VERSION_MAJOR != version[0]) ||
    + (DM_VERSION_MINOR < version[1])) {
    + DMWARN("ioctl interface mismatch: "
    + "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
    + DM_VERSION_MAJOR, DM_VERSION_MINOR,
    + DM_VERSION_PATCHLEVEL,
    + version[0], version[1], version[2], cmd);
    + r = -EINVAL;
    + }
    +
    + /*
    + * Fill in the kernel version.
    + */
    + version[0] = DM_VERSION_MAJOR;
    + version[1] = DM_VERSION_MINOR;
    + version[2] = DM_VERSION_PATCHLEVEL;
    + if (copy_to_user(user->version, version, sizeof(version)))
    + return -EFAULT;
    +
    + return r;
    +}
    +
    +static void free_params(struct dm_ioctl *param)
    +{
    + vfree(param);
    +}
    +
    +static int copy_params(struct dm_ioctl *user, struct dm_ioctl **param)
    +{
    + struct dm_ioctl tmp, *dmi;
    +
    + if (copy_from_user(&tmp, user, sizeof(tmp)))
    + return -EFAULT;
    +
    + if (tmp.data_size < sizeof(tmp))
    + return -EINVAL;
    +
    + dmi = (struct dm_ioctl *) vmalloc(tmp.data_size);
    + if (!dmi)
    + return -ENOMEM;
    +
    + if (copy_from_user(dmi, user, tmp.data_size)) {
    + vfree(dmi);
    + return -EFAULT;
    + }
    +
    + *param = dmi;
    + return 0;
    +}
    +
    +static int validate_params(uint cmd, struct dm_ioctl *param)
    +{
    + /* Always clear this flag */
    + param->flags &= ~DM_BUFFER_FULL_FLAG;
    +
    + /* Ignores parameters */
    + if (cmd == DM_REMOVE_ALL_CMD || cmd == DM_LIST_DEVICES_CMD)
    + return 0;
    +
    + /* Unless creating, either name or uuid but not both */
    + if (cmd != DM_DEV_CREATE_CMD) {
    + if ((!*param->uuid && !*param->name) ||
    + (*param->uuid && *param->name)) {
    + DMWARN("one of name or uuid must be supplied, cmd(%u)",
    + cmd);
    + return -EINVAL;
    + }
    + }
    +
    + /* Ensure strings are terminated */
    + param->name[DM_NAME_LEN - 1] = '\0';
    + param->uuid[DM_UUID_LEN - 1] = '\0';
    +
    + return 0;
    +}
    +
    +static int ctl_ioctl(struct inode *inode, struct file *file,
    + uint command, ulong u)
    +{
    + int r = 0;
    + unsigned int cmd;
    + struct dm_ioctl *param;
    + struct dm_ioctl *user = (struct dm_ioctl *) u;
    + ioctl_fn fn = NULL;
    + size_t param_size;
    +
    + /* only root can play with this */
    + if (!capable(CAP_SYS_ADMIN))
    + return -EACCES;
    +
    + if (_IOC_TYPE(command) != DM_IOCTL)
    + return -ENOTTY;
    +
    + cmd = _IOC_NR(command);
    +
    + /*
    + * Check the interface version passed in. This also
    + * writes out the kernel's interface version.
    + */
    + r = check_version(cmd, user);
    + if (r)
    + return r;
    +
    + /*
    + * Nothing more to do for the version command.
    + */
    + if (cmd == DM_VERSION_CMD)
    + return 0;
    +
    + fn = lookup_ioctl(cmd);
    + if (!fn) {
    + DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
    + return -ENOTTY;
    + }
    +
    + /*
    + * Trying to avoid low memory issues when a device is
    + * suspended.
    + */
    + current->flags |= PF_MEMALLOC;
    +
    + /*
    + * Copy the parameters into kernel space.
    + */
    + r = copy_params(user, &param);
    + if (r) {
    + current->flags &= ~PF_MEMALLOC;
    + return r;
    + }
    +
    + /*
    + * FIXME: eventually we will remove the PF_MEMALLOC flag
    + * here. However the tools still do nasty things like
    + * 'load' while a device is suspended.
    + */
    +
    + r = validate_params(cmd, param);
    + if (r)
    + goto out;
    +
    + param_size = param->data_size;
    + param->data_size = sizeof(*param);
    + r = fn(param, param_size);
    +
    + /*
    + * Copy the results back to userland.
    + */
    + if (!r && copy_to_user(user, param, param->data_size))
    + r = -EFAULT;
    +
    + out:
    + free_params(param);
    + current->flags &= ~PF_MEMALLOC;
    + return r;
    +}
    +
    +static struct file_operations _ctl_fops = {
    + .ioctl = ctl_ioctl,
    + .owner = THIS_MODULE,
    +};
    +
    +static struct miscdevice _dm_misc = {
    + .minor = MISC_DYNAMIC_MINOR,
    + .name = DM_NAME,
    + .devfs_name = "mapper/control",
    + .fops = &_ctl_fops
    +};
    +
    +/*
    + * Create misc character device and link to DM_DIR/control.
    + */
    +int __init dm_interface_init(void)
    +{
    + int r;
    +
    + r = dm_hash_init();
    + if (r)
    + return r;
    +
    + r = misc_register(&_dm_misc);
    + if (r) {
    + DMERR("misc_register failed for control device");
    + dm_hash_exit();
    + return r;
    + }
    +
    + DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
    + DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
    + DM_DRIVER_EMAIL);
    + return 0;
    +}
    +
    +void dm_interface_exit(void)
    +{
    + if (misc_deregister(&_dm_misc) < 0)
    + DMERR("misc_deregister failed for control device");
    +
    + dm_hash_exit();
    +}
    --- diff/include/linux/dm-ioctl-v1.h 1970-01-01 01:00:00.000000000 +0100
    +++ source/include/linux/dm-ioctl-v1.h 2003-07-01 15:36:42.000000000 +0100
    @@ -0,0 +1,149 @@
    +/*
    + * Copyright (C) 2001 Sistina Software (UK) Limited.
    + *
    + * This file is released under the LGPL.
    + */
    +
    +#ifndef _LINUX_DM_IOCTL_V1_H
    +#define _LINUX_DM_IOCTL_V1_H
    +
    +#include <linux/types.h>
    +
    +#define DM_DIR "mapper" /* Slashes not supported */
    +#define DM_MAX_TYPE_NAME 16
    +#define DM_NAME_LEN 128
    +#define DM_UUID_LEN 129
    +
    +/*
    + * Implements a traditional ioctl interface to the device mapper.
    + */
    +
    +/*
    + * All ioctl arguments consist of a single chunk of memory, with
    + * this structure at the start. If a uuid is specified any
    + * lookup (eg. for a DM_INFO) will be done on that, *not* the
    + * name.
    + */
    +struct dm_ioctl {
    + /*
    + * The version number is made up of three parts:
    + * major - no backward or forward compatibility,
    + * minor - only backwards compatible,
    + * patch - both backwards and forwards compatible.
    + *
    + * All clients of the ioctl interface should fill in the
    + * version number of the interface that they were
    + * compiled with.
    + *
    + * All recognised ioctl commands (ie. those that don't
    + * return -ENOTTY) fill out this field, even if the
    + * command failed.
    + */
    + 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 */
    +};
    +
    +/*
    + * Used to specify tables. These structures appear after the
    + * dm_ioctl.
    + */
    +struct dm_target_spec {
    + int32_t status; /* used when reading from kernel only */
    + uint64_t sector_start;
    + uint32_t length;
    +
    + /*
    + * Offset in bytes (from the start of this struct) to
    + * next target_spec.
    + */
    + uint32_t next;
    +
    + char target_type[DM_MAX_TYPE_NAME];
    +
    + /*
    + * Parameter string starts immediately after this object.
    + * Be careful to add padding after string to ensure correct
    + * alignment of subsequent dm_target_spec.
    + */
    +};
    +
    +/*
    + * Used to retrieve the target dependencies.
    + */
    +struct dm_target_deps {
    + uint32_t count;
    +
    + __kernel_dev_t dev[0]; /* out */
    +};
    +
    +/*
    + * If you change this make sure you make the corresponding change
    + * to dm-ioctl.c:lookup_ioctl()
    + */
    +enum {
    + /* Top level cmds */
    + DM_VERSION_CMD = 0,
    + DM_REMOVE_ALL_CMD,
    +
    + /* device level cmds */
    + DM_DEV_CREATE_CMD,
    + DM_DEV_REMOVE_CMD,
    + DM_DEV_RELOAD_CMD,
    + DM_DEV_RENAME_CMD,
    + DM_DEV_SUSPEND_CMD,
    + DM_DEV_DEPS_CMD,
    + DM_DEV_STATUS_CMD,
    +
    + /* target level cmds */
    + DM_TARGET_STATUS_CMD,
    + DM_TARGET_WAIT_CMD
    +};
    +
    +#define DM_IOCTL 0xfd
    +
    +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
    +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
    +
    +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
    +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
    +#define DM_DEV_RELOAD _IOWR(DM_IOCTL, DM_DEV_RELOAD_CMD, struct dm_ioctl)
    +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
    +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
    +#define DM_DEV_DEPS _IOWR(DM_IOCTL, DM_DEV_DEPS_CMD, struct dm_ioctl)
    +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
    +
    +#define DM_TARGET_STATUS _IOWR(DM_IOCTL, DM_TARGET_STATUS_CMD, struct dm_ioctl)
    +#define DM_TARGET_WAIT _IOWR(DM_IOCTL, DM_TARGET_WAIT_CMD, struct dm_ioctl)
    +
    +#define DM_VERSION_MAJOR 1
    +#define DM_VERSION_MINOR 0
    +#define DM_VERSION_PATCHLEVEL 6
    +#define DM_VERSION_EXTRA "-ioctl (2002-10-15)"
    +
    +/* Status bits */
    +#define DM_READONLY_FLAG 0x00000001
    +#define DM_SUSPEND_FLAG 0x00000002
    +#define DM_EXISTS_FLAG 0x00000004
    +#define DM_PERSISTENT_DEV_FLAG 0x00000008
    +
    +/*
    + * Flag passed into ioctl STATUS command to get table information
    + * rather than current status.
    + */
    +#define DM_STATUS_TABLE_FLAG 0x00000010
    +
    +#endif /* _LINUX_DM_IOCTL_H */
    --- diff/include/linux/dm-ioctl-v4.h 1970-01-01 01:00:00.000000000 +0100
    +++ source/include/linux/dm-ioctl-v4.h 2003-07-01 15:36:42.000000000 +0100
    @@ -0,0 +1,237 @@
    +/*
    + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited.
    + *
    + * This file is released under the LGPL.
    + */
    +
    +#ifndef _LINUX_DM_IOCTL_V4_H
    +#define _LINUX_DM_IOCTL_V4_H
    +
    +#include <linux/types.h>
    +
    +#define DM_DIR "mapper" /* Slashes not supported */
    +#define DM_MAX_TYPE_NAME 16
    +#define DM_NAME_LEN 128
    +#define DM_UUID_LEN 129
    +
    +/*
    + * A traditional ioctl interface for the device mapper.
    + *
    + * Each device can have two tables associated with it, an
    + * 'active' table which is the one currently used by io passing
    + * through the device, and an 'inactive' one which is a table
    + * that is being prepared as a replacement for the 'active' one.
    + *
    + * DM_VERSION:
    + * Just get the version information for the ioctl interface.
    + *
    + * DM_REMOVE_ALL:
    + * Remove all dm devices, destroy all tables. Only really used
    + * for debug.
    + *
    + * DM_LIST_DEVICES:
    + * Get a list of all the dm device names.
    + *
    + * DM_DEV_CREATE:
    + * Create a new device, neither the 'active' or 'inactive' table
    + * slots will be filled. The device will be in suspended state
    + * after creation, however any io to the device will get errored
    + * since it will be out-of-bounds.
    + *
    + * DM_DEV_REMOVE:
    + * Remove a device, destroy any tables.
    + *
    + * DM_DEV_RENAME:
    + * Rename a device.
    + *
    + * DM_SUSPEND:
    + * This performs both suspend and resume, depending which flag is
    + * passed in.
    + * Suspend: This command will not return until all pending io to
    + * the device has completed. Further io will be deferred until
    + * the device is resumed.
    + * Resume: It is no longer an error to issue this command on an
    + * unsuspended device. If a table is present in the 'inactive'
    + * slot, it will be moved to the active slot, then the old table
    + * from the active slot will be _destroyed_. Finally the device
    + * is resumed.
    + *
    + * DM_DEV_STATUS:
    + * Retrieves the status for the table in the 'active' slot.
    + *
    + * DM_DEV_WAIT:
    + * Wait for a significant event to occur to the device. This
    + * could either be caused by an event triggered by one of the
    + * targets of the table in the 'active' slot, or a table change.
    + *
    + * DM_TABLE_LOAD:
    + * Load a table into the 'inactive' slot for the device. The
    + * device does _not_ need to be suspended prior to this command.
    + *
    + * DM_TABLE_CLEAR:
    + * Destroy any table in the 'inactive' slot (ie. abort).
    + *
    + * DM_TABLE_DEPS:
    + * Return a set of device dependencies for the 'active' table.
    + *
    + * DM_TABLE_STATUS:
    + * Return the targets status for the 'active' table.
    + */
    +
    +/*
    + * All ioctl arguments consist of a single chunk of memory, with
    + * this structure at the start. If a uuid is specified any
    + * lookup (eg. for a DM_INFO) will be done on that, *not* the
    + * name.
    + */
    +struct dm_ioctl {
    + /*
    + * The version number is made up of three parts:
    + * major - no backward or forward compatibility,
    + * minor - only backwards compatible,
    + * patch - both backwards and forwards compatible.
    + *
    + * All clients of the ioctl interface should fill in the
    + * version number of the interface that they were
    + * compiled with.
    + *
    + * All recognised ioctl commands (ie. those that don't
    + * return -ENOTTY) fill out this field, even if the
    + * command failed.
    + */
    + __u32 version[3]; /* in/out */
    + __u32 data_size; /* total size of data passed in
    + * including this struct */
    +
    + __u32 data_start; /* offset to start of data
    + * relative to start of this struct */
    +
    + __u32 target_count; /* in/out */
    + __s32 open_count; /* out */
    + __u32 flags; /* in/out */
    + __u32 event_nr; /* in/out */
    + __u32 padding;
    +
    + __u64 dev; /* in/out */
    +
    + char name[DM_NAME_LEN]; /* device name */
    + char uuid[DM_UUID_LEN]; /* unique identifier for
    + * the block device */
    +};
    +
    +/*
    + * Used to specify tables. These structures appear after the
    + * dm_ioctl.
    + */
    +struct dm_target_spec {
    + __u64 sector_start;
    + __u64 length;
    + __s32 status; /* used when reading from kernel only */
    +
    + /*
    + * Offset in bytes (from the start of this struct) to
    + * next target_spec.
    + */
    + __u32 next;
    +
    + char target_type[DM_MAX_TYPE_NAME];
    +
    + /*
    + * Parameter string starts immediately after this object.
    + * Be careful to add padding after string to ensure correct
    + * alignment of subsequent dm_target_spec.
    + */
    +};
    +
    +/*
    + * Used to retrieve the target dependencies.
    + */
    +struct dm_target_deps {
    + __u32 count; /* Array size */
    + __u32 padding; /* unused */
    + __u64 dev[0]; /* out */
    +};
    +
    +/*
    + * Used to get a list of all dm devices.
    + */
    +struct dm_name_list {
    + __u64 dev;
    + __u32 next; /* offset to the next record from
    + the _start_ of this */
    + char name[0];
    +};
    +
    +/*
    + * If you change this make sure you make the corresponding change
    + * to dm-ioctl.c:lookup_ioctl()
    + */
    +enum {
    + /* Top level cmds */
    + DM_VERSION_CMD = 0,
    + DM_REMOVE_ALL_CMD,
    + DM_LIST_DEVICES_CMD,
    +
    + /* device level cmds */
    + DM_DEV_CREATE_CMD,
    + DM_DEV_REMOVE_CMD,
    + DM_DEV_RENAME_CMD,
    + DM_DEV_SUSPEND_CMD,
    + DM_DEV_STATUS_CMD,
    + DM_DEV_WAIT_CMD,
    +
    + /* Table level cmds */
    + DM_TABLE_LOAD_CMD,
    + DM_TABLE_CLEAR_CMD,
    + DM_TABLE_DEPS_CMD,
    + DM_TABLE_STATUS_CMD,
    +};
    +
    +#define DM_IOCTL 0xfd
    +
    +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl)
    +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl)
    +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl)
    +
    +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl)
    +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl)
    +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl)
    +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl)
    +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl)
    +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl)
    +
    +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl)
    +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl)
    +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl)
    +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl)
    +
    +#define DM_VERSION_MAJOR 4
    +#define DM_VERSION_MINOR 0
    +#define DM_VERSION_PATCHLEVEL 0
    +#define DM_VERSION_EXTRA "-ioctl (2003-06-04)"
    +
    +/* Status bits */
    +#define DM_READONLY_FLAG (1 << 0) /* In/Out */
    +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */
    +#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 */
    +
    +#endif /* _LINUX_DM_IOCTL_H */
    -
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/
    \
     
     \ /
      Last update: 2005-03-22 13:46    [W:0.251 / U:0.268 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site