summaryrefslogtreecommitdiff
path: root/drivers/md/dm-ioctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r--drivers/md/dm-ioctl.c168
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: