⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dm-ioctl.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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/slab.h>#include <linux/devfs_fs_kernel.h>#include <linux/dm-ioctl.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;};struct vers_iter {    size_t param_size;    struct dm_target_versions *vers, *old_vers;    char *end;    uint32_t flags;};#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];static void dm_hash_remove_all(void);/* * Guards access to both hash 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);}static int dm_hash_init(void){	init_buckets(_name_buckets);	init_buckets(_uuid_buckets);	devfs_mk_dir(DM_DIR);	return 0;}static 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 hash_cell *hc;	unsigned int h = hash_str(str);	list_for_each_entry (hc, _name_buckets + h, name_list)		if (!strcmp(hc->name, str))			return hc;	return NULL;}static struct hash_cell *__get_uuid_cell(const char *str){	struct hash_cell *hc;	unsigned int h = hash_str(str);	list_for_each_entry (hc, _uuid_buckets + h, 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. */static 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 both 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;}static 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);}static 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);}static 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, 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 7static 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) + 1;			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 = (uint32_t) ((void *) nl -							   (void *) old_nl);			disk = dm_disk(hc->md);			nl->dev = huge_encode_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 void list_version_get_needed(struct target_type *tt, void *needed_param){    size_t *needed = needed_param;    *needed += strlen(tt->name);    *needed += sizeof(tt->version);    *needed += ALIGN_MASK;}static void list_version_get_info(struct target_type *tt, void *param){    struct vers_iter *info = param;    /* Check space - it might have changed since the first iteration */    if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 >	info->end) {	info->flags = DM_BUFFER_FULL_FLAG;	return;    }    if (info->old_vers)	info->old_vers->next = (uint32_t) ((void *)info->vers -					   (void *)info->old_vers);    info->vers->version[0] = tt->version[0];    info->vers->version[1] = tt->version[1];    info->vers->version[2] = tt->version[2];    info->vers->next = 0;    strcpy(info->vers->name, tt->name);    info->old_vers = info->vers;    info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);}static int list_versions(struct dm_ioctl *param, size_t param_size){	size_t len, needed = 0;	struct dm_target_versions *vers;	struct vers_iter iter_info;	/*	 * Loop through all the devices working out how much	 * space we need.	 */	dm_target_iterate(list_version_get_needed, &needed);	/*	 * Grab our output buffer.	 */	vers = 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;	iter_info.param_size = param_size;	iter_info.old_vers = NULL;	iter_info.vers = vers;	iter_info.flags = 0;	iter_info.end = (char *)vers+len;	/*	 * Now loop through filling out the names & versions.	 */	dm_target_iterate(list_version_get_info, &iter_info);	param->flags |= iter_info.flags; out:	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 = huge_encode_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(huge_decode_dev(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 do_suspend(struct dm_ioctl *param)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -