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

📄 dm-ioctl.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved. * * 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/dm-ioctl.h>#include <linux/hdreg.h>#include <asm/uaccess.h>#define DM_MSG_PREFIX "ioctl"#define DM_DRIVER_EMAIL "dm-devel@redhat.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(int keep_open_devices);/* * 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);	return 0;}static void dm_hash_exit(void){	dm_hash_remove_all(0);}/*----------------------------------------------------------------- * 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)) {			dm_get(hc->md);			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)) {			dm_get(hc->md);			return hc;		}	return NULL;}/*----------------------------------------------------------------- * Inserting, removing and renaming a device. *---------------------------------------------------------------*/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, GFP_KERNEL);	if (!hc->name) {		kfree(hc);		return NULL;	}	if (!uuid)		hc->uuid = NULL;	else {		hc->uuid = kstrdup(uuid, GFP_KERNEL);		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);	}}/* * 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, *hc;	/*	 * 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);	hc = __get_name_cell(name);	if (hc) {		dm_put(hc->md);		goto bad;	}	list_add(&cell->name_list, _name_buckets + hash_str(name));	if (uuid) {		hc = __get_uuid_cell(uuid);		if (hc) {			list_del(&cell->name_list);			dm_put(hc->md);			goto bad;		}		list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));	}	dm_get(md);	dm_set_mdptr(md, cell);	up_write(&_hash_lock);	return 0; bad:	up_write(&_hash_lock);	free_cell(cell);	return -EBUSY;}static void __hash_remove(struct hash_cell *hc){	struct dm_table *table;	/* remove from the dev hash */	list_del(&hc->uuid_list);	list_del(&hc->name_list);	dm_set_mdptr(hc->md, NULL);	table = dm_get_table(hc->md);	if (table) {		dm_table_event(table);		dm_table_put(table);	}	if (hc->new_map)		dm_table_put(hc->new_map);	dm_put(hc->md);	free_cell(hc);}static void dm_hash_remove_all(int keep_open_devices){	int i, dev_skipped, dev_removed;	struct hash_cell *hc;	struct list_head *tmp, *n;	down_write(&_hash_lock);retry:	dev_skipped = dev_removed = 0;	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);			if (keep_open_devices &&			    dm_lock_for_deletion(hc->md)) {				dev_skipped++;				continue;			}			__hash_remove(hc);			dev_removed = 1;		}	}	/*	 * Some mapped devices may be using other mapped devices, so if any	 * still exist, repeat until we make no further progress.	 */	if (dev_skipped) {		if (dev_removed)			goto retry;		DMWARN("remove_all left %d open device(s)", dev_skipped);	}	up_write(&_hash_lock);}static int dm_hash_rename(const char *old, const char *new){	char *new_name, *old_name;	struct hash_cell *hc;	struct dm_table *table;	/*	 * duplicate new.	 */	new_name = kstrdup(new, GFP_KERNEL);	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);		dm_put(hc->md);		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));	/*	 * Wake up any dm event waiters.	 */	table = dm_get_table(hc->md);	if (table) {		dm_table_event(table);		dm_table_put(table);	}	dm_kobject_uevent(hc->md);	dm_put(hc->md);	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(1);	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 += sizeof(struct dm_target_versions);    *needed += strlen(tt->name);    *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)

⌨️ 快捷键说明

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