diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 168 |
1 files changed, 97 insertions, 71 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 36fc6ae4737a..50a1259294d1 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved. @@ -31,13 +32,15 @@ struct dm_file { * poll will wait until the global event number is greater than * this value. */ - volatile unsigned global_event_nr; + volatile unsigned int global_event_nr; }; -/*----------------------------------------------------------------- +/* + *--------------------------------------------------------------- * The ioctl interface needs to be able to look up devices by * name or uuid. - *---------------------------------------------------------------*/ + *--------------------------------------------------------------- + */ struct hash_cell { struct rb_node name_node; struct rb_node uuid_node; @@ -51,10 +54,10 @@ struct hash_cell { }; struct vers_iter { - size_t param_size; - struct dm_target_versions *vers, *old_vers; - char *end; - uint32_t flags; + size_t param_size; + struct dm_target_versions *vers, *old_vers; + char *end; + uint32_t flags; }; @@ -78,16 +81,20 @@ static void dm_hash_exit(void) dm_hash_remove_all(false, false, false); } -/*----------------------------------------------------------------- +/* + *--------------------------------------------------------------- * Code for looking up a device by name - *---------------------------------------------------------------*/ + *--------------------------------------------------------------- + */ static struct hash_cell *__get_name_cell(const char *str) { struct rb_node *n = name_rb_tree.rb_node; while (n) { struct hash_cell *hc = container_of(n, struct hash_cell, name_node); - int c = strcmp(hc->name, str); + int c; + + c = strcmp(hc->name, str); if (!c) { dm_get(hc->md); return hc; @@ -104,7 +111,9 @@ static struct hash_cell *__get_uuid_cell(const char *str) while (n) { struct hash_cell *hc = container_of(n, struct hash_cell, uuid_node); - int c = strcmp(hc->uuid, str); + int c; + + c = strcmp(hc->uuid, str); if (!c) { dm_get(hc->md); return hc; @@ -144,7 +153,9 @@ static void __link_name(struct hash_cell *new_hc) while (*n) { struct hash_cell *hc = container_of(*n, struct hash_cell, name_node); - int c = strcmp(hc->name, new_hc->name); + int c; + + c = strcmp(hc->name, new_hc->name); BUG_ON(!c); parent = *n; n = c >= 0 ? &hc->name_node.rb_left : &hc->name_node.rb_right; @@ -167,7 +178,9 @@ static void __link_uuid(struct hash_cell *new_hc) while (*n) { struct hash_cell *hc = container_of(*n, struct hash_cell, uuid_node); - int c = strcmp(hc->uuid, new_hc->uuid); + int c; + + c = strcmp(hc->uuid, new_hc->uuid); BUG_ON(!c); parent = *n; n = c > 0 ? &hc->uuid_node.rb_left : &hc->uuid_node.rb_right; @@ -195,9 +208,11 @@ static struct hash_cell *__get_dev_cell(uint64_t dev) return hc; } -/*----------------------------------------------------------------- +/* + *--------------------------------------------------------------- * Inserting, removing and renaming a device. - *---------------------------------------------------------------*/ + *--------------------------------------------------------------- + */ static struct hash_cell *alloc_cell(const char *name, const char *uuid, struct mapped_device *md) { @@ -295,6 +310,8 @@ static struct dm_table *__hash_remove(struct hash_cell *hc) struct dm_table *table; int srcu_idx; + lockdep_assert_held(&_hash_lock); + /* remove from the dev trees */ __unlink_name(hc); __unlink_uuid(hc); @@ -413,7 +430,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, struct hash_cell *hc; struct dm_table *table; struct mapped_device *md; - unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; + unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; int srcu_idx; /* @@ -434,8 +451,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, hc = __get_name_cell(new); if (hc) { - DMERR("Unable to change %s on mapped device %s to one that " - "already exists: %s", + DMERR("Unable to change %s on mapped device %s to one that already exists: %s", change_uuid ? "uuid" : "name", param->name, new); dm_put(hc->md); @@ -482,7 +498,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, dm_table_event(table); dm_put_live_table(hc->md, srcu_idx); - if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr)) + if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false)) param->flags |= DM_UEVENT_GENERATED_FLAG; md = hc->md; @@ -500,9 +516,11 @@ void dm_deferred_remove(void) dm_hash_remove_all(true, false, true); } -/*----------------------------------------------------------------- +/* + *--------------------------------------------------------------- * Implementation of the ioctl commands - *---------------------------------------------------------------*/ + *--------------------------------------------------------------- + */ /* * All the ioctl commands get dispatched to functions with this * prototype. @@ -612,6 +630,7 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ */ for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) { void *uuid_ptr; + hc = container_of(n, struct hash_cell, name_node); if (!filter_device(hc, param->name, param->uuid)) continue; @@ -652,36 +671,34 @@ static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_ static void list_version_get_needed(struct target_type *tt, void *needed_param) { - size_t *needed = needed_param; + size_t *needed = needed_param; - *needed += sizeof(struct dm_target_versions); - *needed += strlen(tt->name) + 1; - *needed += ALIGN_MASK; + *needed += sizeof(struct dm_target_versions); + *needed += strlen(tt->name) + 1; + *needed += ALIGN_MASK; } static void list_version_get_info(struct target_type *tt, void *param) { - struct vers_iter *info = 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) { + /* 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; + } - info->flags = DM_BUFFER_FULL_FLAG; - return; - } + if (info->old_vers) + info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers); - 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->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 + 1) + strlen(tt->name) + 1); + info->old_vers = info->vers; + info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1); } static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name) @@ -772,7 +789,7 @@ static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *src down_read(&_hash_lock); hc = dm_get_mdptr(md); - if (!hc || hc->md != md) { + if (!hc) { DMERR("device has been removed from the dev hash table."); goto out; } @@ -841,6 +858,7 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param) if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) { int srcu_idx; + table = dm_get_inactive_table(md, &srcu_idx); if (table) { if (!(dm_table_get_mode(table) & FMODE_WRITE)) @@ -921,9 +939,9 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) * Sneakily write in both the name and the uuid * while we have the cell. */ - strlcpy(param->name, hc->name, sizeof(param->name)); + strscpy(param->name, hc->name, sizeof(param->name)); if (hc->uuid) - strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); + strscpy(param->uuid, hc->uuid, sizeof(param->uuid)); else param->uuid[0] = '\0'; @@ -995,7 +1013,7 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si dm_ima_measure_on_device_remove(md, false); - if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr)) + if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false)) param->flags |= DM_UEVENT_GENERATED_FLAG; dm_put(md); @@ -1021,7 +1039,7 @@ static int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_si int r; char *new_data = (char *) param + param->data_start; struct mapped_device *md; - unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; + unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; if (new_data < param->data || invalid_str(new_data, (void *) param + param_size) || !*new_data || @@ -1073,8 +1091,7 @@ static int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t pa goto out; } - if (indata[0] > 65535 || indata[1] > 255 || - indata[2] > 255 || indata[3] > ULONG_MAX) { + if (indata[0] > 65535 || indata[1] > 255 || indata[2] > 255) { DMERR("Geometry exceeds range limits."); goto out; } @@ -1096,7 +1113,7 @@ out: static int do_suspend(struct dm_ioctl *param) { int r = 0; - unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; + unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG; struct mapped_device *md; md = find_device(param); @@ -1125,10 +1142,11 @@ out: static int do_resume(struct dm_ioctl *param) { int r = 0; - unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; + unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG; struct hash_cell *hc; struct mapped_device *md; struct dm_table *new_map, *old_map = NULL; + bool need_resize_uevent = false; down_write(&_hash_lock); @@ -1149,6 +1167,8 @@ static int do_resume(struct dm_ioctl *param) /* Do we need to load a new map ? */ if (new_map) { + sector_t old_size, new_size; + /* Suspend if it isn't already suspended */ if (param->flags & DM_SKIP_LOCKFS_FLAG) suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; @@ -1157,6 +1177,7 @@ static int do_resume(struct dm_ioctl *param) if (!dm_suspended_md(md)) dm_suspend(md, suspend_flags); + old_size = dm_get_size(md); old_map = dm_swap_table(md, new_map); if (IS_ERR(old_map)) { dm_sync_table(md); @@ -1164,6 +1185,9 @@ static int do_resume(struct dm_ioctl *param) dm_put(md); return PTR_ERR(old_map); } + new_size = dm_get_size(md); + if (old_size && new_size && old_size != new_size) + need_resize_uevent = true; if (dm_table_get_mode(new_map) & FMODE_WRITE) set_disk_ro(dm_disk(md), 0); @@ -1176,7 +1200,7 @@ static int do_resume(struct dm_ioctl *param) if (!r) { dm_ima_measure_on_device_resume(md, new_map ? true : false); - if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr)) + if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent)) param->flags |= DM_UEVENT_GENERATED_FLAG; } } @@ -1236,7 +1260,7 @@ static void retrieve_status(struct dm_table *table, char *outbuf, *outptr; status_type_t type; size_t remaining, len, used = 0; - unsigned status_flags = 0; + unsigned int status_flags = 0; outptr = outbuf = get_result_buffer(param, param_size, &len); @@ -1387,7 +1411,7 @@ static int populate_table(struct dm_table *table, char *target_params; if (!param->target_count) { - DMERR("populate_table: no targets specified"); + DMERR("%s: no targets specified", __func__); return -EINVAL; } @@ -1476,7 +1500,7 @@ static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_si /* stage inactive table */ down_write(&_hash_lock); hc = dm_get_mdptr(md); - if (!hc || hc->md != md) { + if (!hc) { DMERR("device has been removed from the dev hash table."); up_write(&_hash_lock); r = -ENXIO; @@ -1564,7 +1588,7 @@ static void retrieve_deps(struct dm_table *table, /* * Count the devices. */ - list_for_each (tmp, dm_table_get_devices(table)) + list_for_each(tmp, dm_table_get_devices(table)) count++; /* @@ -1581,7 +1605,7 @@ static void retrieve_deps(struct dm_table *table, */ deps->count = count; count = 0; - list_for_each_entry (dd, dm_table_get_devices(table), list) + list_for_each_entry(dd, dm_table_get_devices(table), list) deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); param->data_size = param->data_start + needed; @@ -1641,8 +1665,8 @@ static int table_status(struct file *filp, struct dm_ioctl *param, size_t param_ * Returns a number <= 1 if message was processed by device mapper. * Returns 2 if message should be delivered to the target. */ -static int message_for_md(struct mapped_device *md, unsigned argc, char **argv, - char *result, unsigned maxlen) +static int message_for_md(struct mapped_device *md, unsigned int argc, char **argv, + char *result, unsigned int maxlen) { int r; @@ -1757,10 +1781,11 @@ static int target_message(struct file *filp, struct dm_ioctl *param, size_t para #define IOCTL_FLAGS_NO_PARAMS 1 #define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT 2 -/*----------------------------------------------------------------- - * Implementation of open/close/ioctl on the special char - * device. - *---------------------------------------------------------------*/ +/* + *--------------------------------------------------------------- + * Implementation of open/close/ioctl on the special char device. + *--------------------------------------------------------------- + */ static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags) { static const struct { @@ -1812,10 +1837,9 @@ static int check_version(unsigned int cmd, struct dm_ioctl __user *user) if (copy_from_user(version, user->version, sizeof(version))) return -EFAULT; - if ((DM_VERSION_MAJOR != version[0]) || - (DM_VERSION_MINOR < version[1])) { - DMERR("ioctl interface mismatch: " - "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)", + if ((version[0] != DM_VERSION_MAJOR) || + (version[1] > DM_VERSION_MINOR)) { + DMERR("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); @@ -1852,7 +1876,7 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern struct dm_ioctl *dmi; int secure_data; const size_t minimum_data_size = offsetof(struct dm_ioctl, data); - unsigned noio_flag; + unsigned int noio_flag; if (copy_from_user(param_kernel, user, minimum_data_size)) return -EFAULT; @@ -2079,9 +2103,9 @@ static const struct file_operations _ctl_fops = { static struct miscdevice _dm_misc = { .minor = MAPPER_CTRL_MINOR, - .name = DM_NAME, + .name = DM_NAME, .nodename = DM_DIR "/" DM_CONTROL_NODE, - .fops = &_ctl_fops + .fops = &_ctl_fops }; MODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR); @@ -2128,7 +2152,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) mutex_lock(&dm_hash_cells_mutex); hc = dm_get_mdptr(md); - if (!hc || hc->md != md) { + if (!hc) { r = -ENXIO; goto out; } @@ -2241,7 +2265,9 @@ int __init dm_early_create(struct dm_ioctl *dmi, err_destroy_table: dm_table_destroy(t); err_hash_remove: + down_write(&_hash_lock); (void) __hash_remove(__get_name_cell(dmi->name)); + up_write(&_hash_lock); /* release reference from __get_name_cell */ dm_put(md); err_destroy_dm: |