📄 dm-ioctl.c
字号:
} 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; struct mapped_device *md; md = find_device(param); if (!md) return -ENXIO; r = dm_table_create(&t, get_mode(param), param->target_count, md); if (r) goto out; r = populate_table(t, param, param_size); if (r) { dm_table_put(t); goto out; } down_write(&_hash_lock); hc = dm_get_mdptr(md); if (!hc || hc->md != md) { DMWARN("device has been removed from the dev hash table."); dm_table_put(t); up_write(&_hash_lock); r = -ENXIO; goto out; } if (hc->new_map) dm_table_put(hc->new_map); hc->new_map = t; up_write(&_hash_lock); param->flags |= DM_INACTIVE_PRESENT_FLAG; r = __dev_status(md, param);out: dm_put(md); return r;}static int table_clear(struct dm_ioctl *param, size_t param_size){ int r; struct hash_cell *hc; struct mapped_device *md; 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); md = hc->md; up_write(&_hash_lock); dm_put(md); 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;}/* * Pass a message to the target that's at the supplied device offset. */static int target_message(struct dm_ioctl *param, size_t param_size){ int r, argc; char **argv; struct mapped_device *md; struct dm_table *table; struct dm_target *ti; struct dm_target_msg *tmsg = (void *) param + param->data_start; md = find_device(param); if (!md) return -ENXIO; r = __dev_status(md, param); if (r) goto out; if (tmsg < (struct dm_target_msg *) param->data || invalid_str(tmsg->message, (void *) param + param_size)) { DMWARN("Invalid target message parameters."); r = -EINVAL; goto out; } r = dm_split_args(&argc, &argv, tmsg->message); if (r) { DMWARN("Failed to split target message parameters"); goto out; } table = dm_get_table(md); if (!table) goto out_argv; ti = dm_table_find_target(table, tmsg->sector); if (!dm_target_is_valid(ti)) { DMWARN("Target message sector outside device."); r = -EINVAL; } else if (ti->type->message) r = ti->type->message(ti, argc, argv); else { DMWARN("Target type does not support messages"); r = -EINVAL; } dm_table_put(table); out_argv: kfree(argv); out: param->data_size = 0; 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}, {DM_TARGET_MSG_CMD, target_message}, {DM_DEV_SET_GEOMETRY_CMD, dev_set_geometry} }; 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 = 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; if ((cmd == DM_DEV_CREATE_CMD)) { if (!*param->name) { DMWARN("name not supplied when creating device"); return -EINVAL; } } else if ((*param->uuid && *param->name)) { DMWARN("only supply one of name or uuid, 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); current->flags &= ~PF_MEMALLOC; if (r) return r; 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); return r;}static const struct file_operations _ctl_fops = { .ioctl = ctl_ioctl, .owner = THIS_MODULE,};static struct miscdevice _dm_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DM_NAME, .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();}/** * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers * @md: Pointer to mapped_device * @name: Buffer (size DM_NAME_LEN) for name * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined */int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid){ int r = 0; struct hash_cell *hc; if (!md) return -ENXIO; dm_get(md); down_read(&_hash_lock); hc = dm_get_mdptr(md); if (!hc || hc->md != md) { r = -ENXIO; goto out; } strcpy(name, hc->name); strcpy(uuid, hc->uuid ? : "");out: up_read(&_hash_lock); dm_put(md); return r;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -