📄 dm-ioctl.c
字号:
/* * 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 + -