📄 dm-ioctl.c
字号:
{ 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 do_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), 0); else 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 do_suspend(param); return do_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;}/* * 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); if (remaining <= 0) { param->flags |= DM_BUFFER_FULL_FLAG; break; } /* 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); outptr = align_ptr(outptr); spec->next = outptr - outbuf; } if (used) param->data_size = used; param->target_count = num_targets;}/* * 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; struct dm_table *table; md = find_device(param); if (!md) return -ENXIO; /* * Wait for a notification event */ if (dm_wait_event(md, param->event_nr)) { r = -ERESTARTSYS; goto out; } /* * 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); 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;}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, uint32_t 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; uint32_t 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), param->target_count); 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; } if (hc->new_map) dm_table_put(hc->new_map); 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_dev *dd; 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_entry (dd, dm_table_get_devices(table), list) deps->dev[count++] = huge_encode_dev(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;}/* * 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}, {DM_LIST_VERSIONS_CMD, list_versions} }; 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 *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 *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 || cmd == DM_LIST_VERSIONS_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 *user = (struct dm_ioctl __user *) 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, ¶m); 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();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -