xref: /kernel/linux/linux-6.6/drivers/md/dm-ioctl.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
462306a36Sopenharmony_ci * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This file is released under the GPL.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "dm-core.h"
1062306a36Sopenharmony_ci#include "dm-ima.h"
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/vmalloc.h>
1362306a36Sopenharmony_ci#include <linux/miscdevice.h>
1462306a36Sopenharmony_ci#include <linux/sched/mm.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/wait.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/rbtree.h>
1962306a36Sopenharmony_ci#include <linux/dm-ioctl.h>
2062306a36Sopenharmony_ci#include <linux/hdreg.h>
2162306a36Sopenharmony_ci#include <linux/compat.h>
2262306a36Sopenharmony_ci#include <linux/nospec.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci#include <linux/ima.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DM_MSG_PREFIX "ioctl"
2862306a36Sopenharmony_ci#define DM_DRIVER_EMAIL "dm-devel@redhat.com"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct dm_file {
3162306a36Sopenharmony_ci	/*
3262306a36Sopenharmony_ci	 * poll will wait until the global event number is greater than
3362306a36Sopenharmony_ci	 * this value.
3462306a36Sopenharmony_ci	 */
3562306a36Sopenharmony_ci	volatile unsigned int global_event_nr;
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci *---------------------------------------------------------------
4062306a36Sopenharmony_ci * The ioctl interface needs to be able to look up devices by
4162306a36Sopenharmony_ci * name or uuid.
4262306a36Sopenharmony_ci *---------------------------------------------------------------
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_cistruct hash_cell {
4562306a36Sopenharmony_ci	struct rb_node name_node;
4662306a36Sopenharmony_ci	struct rb_node uuid_node;
4762306a36Sopenharmony_ci	bool name_set;
4862306a36Sopenharmony_ci	bool uuid_set;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	char *name;
5162306a36Sopenharmony_ci	char *uuid;
5262306a36Sopenharmony_ci	struct mapped_device *md;
5362306a36Sopenharmony_ci	struct dm_table *new_map;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct vers_iter {
5762306a36Sopenharmony_ci	size_t param_size;
5862306a36Sopenharmony_ci	struct dm_target_versions *vers, *old_vers;
5962306a36Sopenharmony_ci	char *end;
6062306a36Sopenharmony_ci	uint32_t flags;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct rb_root name_rb_tree = RB_ROOT;
6562306a36Sopenharmony_cistatic struct rb_root uuid_rb_tree = RB_ROOT;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/*
7062306a36Sopenharmony_ci * Guards access to both hash tables.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_cistatic DECLARE_RWSEM(_hash_lock);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cistatic DEFINE_MUTEX(dm_hash_cells_mutex);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void dm_hash_exit(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	dm_hash_remove_all(false, false, false);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci *---------------------------------------------------------------
8662306a36Sopenharmony_ci * Code for looking up a device by name
8762306a36Sopenharmony_ci *---------------------------------------------------------------
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_cistatic struct hash_cell *__get_name_cell(const char *str)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct rb_node *n = name_rb_tree.rb_node;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	while (n) {
9462306a36Sopenharmony_ci		struct hash_cell *hc = container_of(n, struct hash_cell, name_node);
9562306a36Sopenharmony_ci		int c;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		c = strcmp(hc->name, str);
9862306a36Sopenharmony_ci		if (!c) {
9962306a36Sopenharmony_ci			dm_get(hc->md);
10062306a36Sopenharmony_ci			return hc;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		n = c >= 0 ? n->rb_left : n->rb_right;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return NULL;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct hash_cell *__get_uuid_cell(const char *str)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct rb_node *n = uuid_rb_tree.rb_node;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	while (n) {
11362306a36Sopenharmony_ci		struct hash_cell *hc = container_of(n, struct hash_cell, uuid_node);
11462306a36Sopenharmony_ci		int c;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		c = strcmp(hc->uuid, str);
11762306a36Sopenharmony_ci		if (!c) {
11862306a36Sopenharmony_ci			dm_get(hc->md);
11962306a36Sopenharmony_ci			return hc;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci		n = c >= 0 ? n->rb_left : n->rb_right;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return NULL;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void __unlink_name(struct hash_cell *hc)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (hc->name_set) {
13062306a36Sopenharmony_ci		hc->name_set = false;
13162306a36Sopenharmony_ci		rb_erase(&hc->name_node, &name_rb_tree);
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void __unlink_uuid(struct hash_cell *hc)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	if (hc->uuid_set) {
13862306a36Sopenharmony_ci		hc->uuid_set = false;
13962306a36Sopenharmony_ci		rb_erase(&hc->uuid_node, &uuid_rb_tree);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic void __link_name(struct hash_cell *new_hc)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	struct rb_node **n, *parent;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	__unlink_name(new_hc);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	new_hc->name_set = true;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	n = &name_rb_tree.rb_node;
15262306a36Sopenharmony_ci	parent = NULL;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	while (*n) {
15562306a36Sopenharmony_ci		struct hash_cell *hc = container_of(*n, struct hash_cell, name_node);
15662306a36Sopenharmony_ci		int c;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		c = strcmp(hc->name, new_hc->name);
15962306a36Sopenharmony_ci		BUG_ON(!c);
16062306a36Sopenharmony_ci		parent = *n;
16162306a36Sopenharmony_ci		n = c >= 0 ? &hc->name_node.rb_left : &hc->name_node.rb_right;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	rb_link_node(&new_hc->name_node, parent, n);
16562306a36Sopenharmony_ci	rb_insert_color(&new_hc->name_node, &name_rb_tree);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void __link_uuid(struct hash_cell *new_hc)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct rb_node **n, *parent;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	__unlink_uuid(new_hc);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	new_hc->uuid_set = true;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	n = &uuid_rb_tree.rb_node;
17762306a36Sopenharmony_ci	parent = NULL;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	while (*n) {
18062306a36Sopenharmony_ci		struct hash_cell *hc = container_of(*n, struct hash_cell, uuid_node);
18162306a36Sopenharmony_ci		int c;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		c = strcmp(hc->uuid, new_hc->uuid);
18462306a36Sopenharmony_ci		BUG_ON(!c);
18562306a36Sopenharmony_ci		parent = *n;
18662306a36Sopenharmony_ci		n = c > 0 ? &hc->uuid_node.rb_left : &hc->uuid_node.rb_right;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	rb_link_node(&new_hc->uuid_node, parent, n);
19062306a36Sopenharmony_ci	rb_insert_color(&new_hc->uuid_node, &uuid_rb_tree);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic struct hash_cell *__get_dev_cell(uint64_t dev)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct mapped_device *md;
19662306a36Sopenharmony_ci	struct hash_cell *hc;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	md = dm_get_md(huge_decode_dev(dev));
19962306a36Sopenharmony_ci	if (!md)
20062306a36Sopenharmony_ci		return NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	hc = dm_get_mdptr(md);
20362306a36Sopenharmony_ci	if (!hc) {
20462306a36Sopenharmony_ci		dm_put(md);
20562306a36Sopenharmony_ci		return NULL;
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return hc;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci/*
21262306a36Sopenharmony_ci *---------------------------------------------------------------
21362306a36Sopenharmony_ci * Inserting, removing and renaming a device.
21462306a36Sopenharmony_ci *---------------------------------------------------------------
21562306a36Sopenharmony_ci */
21662306a36Sopenharmony_cistatic struct hash_cell *alloc_cell(const char *name, const char *uuid,
21762306a36Sopenharmony_ci				    struct mapped_device *md)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct hash_cell *hc;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	hc = kmalloc(sizeof(*hc), GFP_KERNEL);
22262306a36Sopenharmony_ci	if (!hc)
22362306a36Sopenharmony_ci		return NULL;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	hc->name = kstrdup(name, GFP_KERNEL);
22662306a36Sopenharmony_ci	if (!hc->name) {
22762306a36Sopenharmony_ci		kfree(hc);
22862306a36Sopenharmony_ci		return NULL;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (!uuid)
23262306a36Sopenharmony_ci		hc->uuid = NULL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	else {
23562306a36Sopenharmony_ci		hc->uuid = kstrdup(uuid, GFP_KERNEL);
23662306a36Sopenharmony_ci		if (!hc->uuid) {
23762306a36Sopenharmony_ci			kfree(hc->name);
23862306a36Sopenharmony_ci			kfree(hc);
23962306a36Sopenharmony_ci			return NULL;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	hc->name_set = hc->uuid_set = false;
24462306a36Sopenharmony_ci	hc->md = md;
24562306a36Sopenharmony_ci	hc->new_map = NULL;
24662306a36Sopenharmony_ci	return hc;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void free_cell(struct hash_cell *hc)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	if (hc) {
25262306a36Sopenharmony_ci		kfree(hc->name);
25362306a36Sopenharmony_ci		kfree(hc->uuid);
25462306a36Sopenharmony_ci		kfree(hc);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/*
25962306a36Sopenharmony_ci * The kdev_t and uuid of a device can never change once it is
26062306a36Sopenharmony_ci * initially inserted.
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_cistatic int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct hash_cell *cell, *hc;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	/*
26762306a36Sopenharmony_ci	 * Allocate the new cells.
26862306a36Sopenharmony_ci	 */
26962306a36Sopenharmony_ci	cell = alloc_cell(name, uuid, md);
27062306a36Sopenharmony_ci	if (!cell)
27162306a36Sopenharmony_ci		return -ENOMEM;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * Insert the cell into both hash tables.
27562306a36Sopenharmony_ci	 */
27662306a36Sopenharmony_ci	down_write(&_hash_lock);
27762306a36Sopenharmony_ci	hc = __get_name_cell(name);
27862306a36Sopenharmony_ci	if (hc) {
27962306a36Sopenharmony_ci		dm_put(hc->md);
28062306a36Sopenharmony_ci		goto bad;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	__link_name(cell);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (uuid) {
28662306a36Sopenharmony_ci		hc = __get_uuid_cell(uuid);
28762306a36Sopenharmony_ci		if (hc) {
28862306a36Sopenharmony_ci			__unlink_name(cell);
28962306a36Sopenharmony_ci			dm_put(hc->md);
29062306a36Sopenharmony_ci			goto bad;
29162306a36Sopenharmony_ci		}
29262306a36Sopenharmony_ci		__link_uuid(cell);
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci	dm_get(md);
29562306a36Sopenharmony_ci	mutex_lock(&dm_hash_cells_mutex);
29662306a36Sopenharmony_ci	dm_set_mdptr(md, cell);
29762306a36Sopenharmony_ci	mutex_unlock(&dm_hash_cells_mutex);
29862306a36Sopenharmony_ci	up_write(&_hash_lock);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci bad:
30362306a36Sopenharmony_ci	up_write(&_hash_lock);
30462306a36Sopenharmony_ci	free_cell(cell);
30562306a36Sopenharmony_ci	return -EBUSY;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic struct dm_table *__hash_remove(struct hash_cell *hc)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct dm_table *table;
31162306a36Sopenharmony_ci	int srcu_idx;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	lockdep_assert_held(&_hash_lock);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* remove from the dev trees */
31662306a36Sopenharmony_ci	__unlink_name(hc);
31762306a36Sopenharmony_ci	__unlink_uuid(hc);
31862306a36Sopenharmony_ci	mutex_lock(&dm_hash_cells_mutex);
31962306a36Sopenharmony_ci	dm_set_mdptr(hc->md, NULL);
32062306a36Sopenharmony_ci	mutex_unlock(&dm_hash_cells_mutex);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	table = dm_get_live_table(hc->md, &srcu_idx);
32362306a36Sopenharmony_ci	if (table)
32462306a36Sopenharmony_ci		dm_table_event(table);
32562306a36Sopenharmony_ci	dm_put_live_table(hc->md, srcu_idx);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	table = NULL;
32862306a36Sopenharmony_ci	if (hc->new_map)
32962306a36Sopenharmony_ci		table = hc->new_map;
33062306a36Sopenharmony_ci	dm_put(hc->md);
33162306a36Sopenharmony_ci	free_cell(hc);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	return table;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	int dev_skipped;
33962306a36Sopenharmony_ci	struct rb_node *n;
34062306a36Sopenharmony_ci	struct hash_cell *hc;
34162306a36Sopenharmony_ci	struct mapped_device *md;
34262306a36Sopenharmony_ci	struct dm_table *t;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ciretry:
34562306a36Sopenharmony_ci	dev_skipped = 0;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	down_write(&_hash_lock);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
35062306a36Sopenharmony_ci		hc = container_of(n, struct hash_cell, name_node);
35162306a36Sopenharmony_ci		md = hc->md;
35262306a36Sopenharmony_ci		dm_get(md);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		if (keep_open_devices &&
35562306a36Sopenharmony_ci		    dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
35662306a36Sopenharmony_ci			dm_put(md);
35762306a36Sopenharmony_ci			dev_skipped++;
35862306a36Sopenharmony_ci			continue;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		t = __hash_remove(hc);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci		up_write(&_hash_lock);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (t) {
36662306a36Sopenharmony_ci			dm_sync_table(md);
36762306a36Sopenharmony_ci			dm_table_destroy(t);
36862306a36Sopenharmony_ci		}
36962306a36Sopenharmony_ci		dm_ima_measure_on_device_remove(md, true);
37062306a36Sopenharmony_ci		dm_put(md);
37162306a36Sopenharmony_ci		if (likely(keep_open_devices))
37262306a36Sopenharmony_ci			dm_destroy(md);
37362306a36Sopenharmony_ci		else
37462306a36Sopenharmony_ci			dm_destroy_immediate(md);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		/*
37762306a36Sopenharmony_ci		 * Some mapped devices may be using other mapped
37862306a36Sopenharmony_ci		 * devices, so repeat until we make no further
37962306a36Sopenharmony_ci		 * progress.  If a new mapped device is created
38062306a36Sopenharmony_ci		 * here it will also get removed.
38162306a36Sopenharmony_ci		 */
38262306a36Sopenharmony_ci		goto retry;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	up_write(&_hash_lock);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (dev_skipped)
38862306a36Sopenharmony_ci		DMWARN("remove_all left %d open device(s)", dev_skipped);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/*
39262306a36Sopenharmony_ci * Set the uuid of a hash_cell that isn't already set.
39362306a36Sopenharmony_ci */
39462306a36Sopenharmony_cistatic void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	mutex_lock(&dm_hash_cells_mutex);
39762306a36Sopenharmony_ci	hc->uuid = new_uuid;
39862306a36Sopenharmony_ci	mutex_unlock(&dm_hash_cells_mutex);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	__link_uuid(hc);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci/*
40462306a36Sopenharmony_ci * Changes the name of a hash_cell and returns the old name for
40562306a36Sopenharmony_ci * the caller to free.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_cistatic char *__change_cell_name(struct hash_cell *hc, char *new_name)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	char *old_name;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/*
41262306a36Sopenharmony_ci	 * Rename and move the name cell.
41362306a36Sopenharmony_ci	 */
41462306a36Sopenharmony_ci	__unlink_name(hc);
41562306a36Sopenharmony_ci	old_name = hc->name;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	mutex_lock(&dm_hash_cells_mutex);
41862306a36Sopenharmony_ci	hc->name = new_name;
41962306a36Sopenharmony_ci	mutex_unlock(&dm_hash_cells_mutex);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	__link_name(hc);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return old_name;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
42762306a36Sopenharmony_ci					    const char *new)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	char *new_data, *old_name = NULL;
43062306a36Sopenharmony_ci	struct hash_cell *hc;
43162306a36Sopenharmony_ci	struct dm_table *table;
43262306a36Sopenharmony_ci	struct mapped_device *md;
43362306a36Sopenharmony_ci	unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
43462306a36Sopenharmony_ci	int srcu_idx;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	/*
43762306a36Sopenharmony_ci	 * duplicate new.
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci	new_data = kstrdup(new, GFP_KERNEL);
44062306a36Sopenharmony_ci	if (!new_data)
44162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	down_write(&_hash_lock);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/*
44662306a36Sopenharmony_ci	 * Is new free ?
44762306a36Sopenharmony_ci	 */
44862306a36Sopenharmony_ci	if (change_uuid)
44962306a36Sopenharmony_ci		hc = __get_uuid_cell(new);
45062306a36Sopenharmony_ci	else
45162306a36Sopenharmony_ci		hc = __get_name_cell(new);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (hc) {
45462306a36Sopenharmony_ci		DMERR("Unable to change %s on mapped device %s to one that already exists: %s",
45562306a36Sopenharmony_ci		      change_uuid ? "uuid" : "name",
45662306a36Sopenharmony_ci		      param->name, new);
45762306a36Sopenharmony_ci		dm_put(hc->md);
45862306a36Sopenharmony_ci		up_write(&_hash_lock);
45962306a36Sopenharmony_ci		kfree(new_data);
46062306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	/*
46462306a36Sopenharmony_ci	 * Is there such a device as 'old' ?
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	hc = __get_name_cell(param->name);
46762306a36Sopenharmony_ci	if (!hc) {
46862306a36Sopenharmony_ci		DMERR("Unable to rename non-existent device, %s to %s%s",
46962306a36Sopenharmony_ci		      param->name, change_uuid ? "uuid " : "", new);
47062306a36Sopenharmony_ci		up_write(&_hash_lock);
47162306a36Sopenharmony_ci		kfree(new_data);
47262306a36Sopenharmony_ci		return ERR_PTR(-ENXIO);
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/*
47662306a36Sopenharmony_ci	 * Does this device already have a uuid?
47762306a36Sopenharmony_ci	 */
47862306a36Sopenharmony_ci	if (change_uuid && hc->uuid) {
47962306a36Sopenharmony_ci		DMERR("Unable to change uuid of mapped device %s to %s "
48062306a36Sopenharmony_ci		      "because uuid is already set to %s",
48162306a36Sopenharmony_ci		      param->name, new, hc->uuid);
48262306a36Sopenharmony_ci		dm_put(hc->md);
48362306a36Sopenharmony_ci		up_write(&_hash_lock);
48462306a36Sopenharmony_ci		kfree(new_data);
48562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (change_uuid)
48962306a36Sopenharmony_ci		__set_cell_uuid(hc, new_data);
49062306a36Sopenharmony_ci	else
49162306a36Sopenharmony_ci		old_name = __change_cell_name(hc, new_data);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/*
49462306a36Sopenharmony_ci	 * Wake up any dm event waiters.
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci	table = dm_get_live_table(hc->md, &srcu_idx);
49762306a36Sopenharmony_ci	if (table)
49862306a36Sopenharmony_ci		dm_table_event(table);
49962306a36Sopenharmony_ci	dm_put_live_table(hc->md, srcu_idx);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false))
50262306a36Sopenharmony_ci		param->flags |= DM_UEVENT_GENERATED_FLAG;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	md = hc->md;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	dm_ima_measure_on_device_rename(md);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	up_write(&_hash_lock);
50962306a36Sopenharmony_ci	kfree(old_name);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return md;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_civoid dm_deferred_remove(void)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	dm_hash_remove_all(true, false, true);
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/*
52062306a36Sopenharmony_ci *---------------------------------------------------------------
52162306a36Sopenharmony_ci * Implementation of the ioctl commands
52262306a36Sopenharmony_ci *---------------------------------------------------------------
52362306a36Sopenharmony_ci */
52462306a36Sopenharmony_ci/*
52562306a36Sopenharmony_ci * All the ioctl commands get dispatched to functions with this
52662306a36Sopenharmony_ci * prototype.
52762306a36Sopenharmony_ci */
52862306a36Sopenharmony_citypedef int (*ioctl_fn)(struct file *filp, struct dm_ioctl *param, size_t param_size);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cistatic int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_size)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false);
53362306a36Sopenharmony_ci	param->data_size = 0;
53462306a36Sopenharmony_ci	return 0;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/*
53862306a36Sopenharmony_ci * Round up the ptr to an 8-byte boundary.
53962306a36Sopenharmony_ci */
54062306a36Sopenharmony_ci#define ALIGN_MASK 7
54162306a36Sopenharmony_cistatic inline size_t align_val(size_t val)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	return (val + ALIGN_MASK) & ~ALIGN_MASK;
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_cistatic inline void *align_ptr(void *ptr)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	return (void *)align_val((size_t)ptr);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci * Retrieves the data payload buffer from an already allocated
55262306a36Sopenharmony_ci * struct dm_ioctl.
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_cistatic void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
55562306a36Sopenharmony_ci			       size_t *len)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	param->data_start = align_ptr(param + 1) - (void *) param;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (param->data_start < param_size)
56062306a36Sopenharmony_ci		*len = param_size - param->data_start;
56162306a36Sopenharmony_ci	else
56262306a36Sopenharmony_ci		*len = 0;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return ((void *) param) + param->data_start;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic bool filter_device(struct hash_cell *hc, const char *pfx_name, const char *pfx_uuid)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	const char *val;
57062306a36Sopenharmony_ci	size_t val_len, pfx_len;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	val = hc->name;
57362306a36Sopenharmony_ci	val_len = strlen(val);
57462306a36Sopenharmony_ci	pfx_len = strnlen(pfx_name, DM_NAME_LEN);
57562306a36Sopenharmony_ci	if (pfx_len > val_len)
57662306a36Sopenharmony_ci		return false;
57762306a36Sopenharmony_ci	if (memcmp(val, pfx_name, pfx_len))
57862306a36Sopenharmony_ci		return false;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	val = hc->uuid ? hc->uuid : "";
58162306a36Sopenharmony_ci	val_len = strlen(val);
58262306a36Sopenharmony_ci	pfx_len = strnlen(pfx_uuid, DM_UUID_LEN);
58362306a36Sopenharmony_ci	if (pfx_len > val_len)
58462306a36Sopenharmony_ci		return false;
58562306a36Sopenharmony_ci	if (memcmp(val, pfx_uuid, pfx_len))
58662306a36Sopenharmony_ci		return false;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return true;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_size)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct rb_node *n;
59462306a36Sopenharmony_ci	struct hash_cell *hc;
59562306a36Sopenharmony_ci	size_t len, needed = 0;
59662306a36Sopenharmony_ci	struct gendisk *disk;
59762306a36Sopenharmony_ci	struct dm_name_list *orig_nl, *nl, *old_nl = NULL;
59862306a36Sopenharmony_ci	uint32_t *event_nr;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	down_write(&_hash_lock);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * Loop through all the devices working out how much
60462306a36Sopenharmony_ci	 * space we need.
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
60762306a36Sopenharmony_ci		hc = container_of(n, struct hash_cell, name_node);
60862306a36Sopenharmony_ci		if (!filter_device(hc, param->name, param->uuid))
60962306a36Sopenharmony_ci			continue;
61062306a36Sopenharmony_ci		needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1);
61162306a36Sopenharmony_ci		needed += align_val(sizeof(uint32_t) * 2);
61262306a36Sopenharmony_ci		if (param->flags & DM_UUID_FLAG && hc->uuid)
61362306a36Sopenharmony_ci			needed += align_val(strlen(hc->uuid) + 1);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/*
61762306a36Sopenharmony_ci	 * Grab our output buffer.
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	nl = orig_nl = get_result_buffer(param, param_size, &len);
62062306a36Sopenharmony_ci	if (len < needed || len < sizeof(nl->dev)) {
62162306a36Sopenharmony_ci		param->flags |= DM_BUFFER_FULL_FLAG;
62262306a36Sopenharmony_ci		goto out;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci	param->data_size = param->data_start + needed;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	nl->dev = 0;	/* Flags no data */
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/*
62962306a36Sopenharmony_ci	 * Now loop through filling out the names.
63062306a36Sopenharmony_ci	 */
63162306a36Sopenharmony_ci	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
63262306a36Sopenharmony_ci		void *uuid_ptr;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		hc = container_of(n, struct hash_cell, name_node);
63562306a36Sopenharmony_ci		if (!filter_device(hc, param->name, param->uuid))
63662306a36Sopenharmony_ci			continue;
63762306a36Sopenharmony_ci		if (old_nl)
63862306a36Sopenharmony_ci			old_nl->next = (uint32_t) ((void *) nl -
63962306a36Sopenharmony_ci						   (void *) old_nl);
64062306a36Sopenharmony_ci		disk = dm_disk(hc->md);
64162306a36Sopenharmony_ci		nl->dev = huge_encode_dev(disk_devt(disk));
64262306a36Sopenharmony_ci		nl->next = 0;
64362306a36Sopenharmony_ci		strcpy(nl->name, hc->name);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		old_nl = nl;
64662306a36Sopenharmony_ci		event_nr = align_ptr(nl->name + strlen(hc->name) + 1);
64762306a36Sopenharmony_ci		event_nr[0] = dm_get_event_nr(hc->md);
64862306a36Sopenharmony_ci		event_nr[1] = 0;
64962306a36Sopenharmony_ci		uuid_ptr = align_ptr(event_nr + 2);
65062306a36Sopenharmony_ci		if (param->flags & DM_UUID_FLAG) {
65162306a36Sopenharmony_ci			if (hc->uuid) {
65262306a36Sopenharmony_ci				event_nr[1] |= DM_NAME_LIST_FLAG_HAS_UUID;
65362306a36Sopenharmony_ci				strcpy(uuid_ptr, hc->uuid);
65462306a36Sopenharmony_ci				uuid_ptr = align_ptr(uuid_ptr + strlen(hc->uuid) + 1);
65562306a36Sopenharmony_ci			} else {
65662306a36Sopenharmony_ci				event_nr[1] |= DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID;
65762306a36Sopenharmony_ci			}
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci		nl = uuid_ptr;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci	/*
66262306a36Sopenharmony_ci	 * If mismatch happens, security may be compromised due to buffer
66362306a36Sopenharmony_ci	 * overflow, so it's better to crash.
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	BUG_ON((char *)nl - (char *)orig_nl != needed);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci out:
66862306a36Sopenharmony_ci	up_write(&_hash_lock);
66962306a36Sopenharmony_ci	return 0;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic void list_version_get_needed(struct target_type *tt, void *needed_param)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	size_t *needed = needed_param;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	*needed += sizeof(struct dm_target_versions);
67762306a36Sopenharmony_ci	*needed += strlen(tt->name) + 1;
67862306a36Sopenharmony_ci	*needed += ALIGN_MASK;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic void list_version_get_info(struct target_type *tt, void *param)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct vers_iter *info = param;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* Check space - it might have changed since the first iteration */
68662306a36Sopenharmony_ci	if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) {
68762306a36Sopenharmony_ci		info->flags = DM_BUFFER_FULL_FLAG;
68862306a36Sopenharmony_ci		return;
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (info->old_vers)
69262306a36Sopenharmony_ci		info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	info->vers->version[0] = tt->version[0];
69562306a36Sopenharmony_ci	info->vers->version[1] = tt->version[1];
69662306a36Sopenharmony_ci	info->vers->version[2] = tt->version[2];
69762306a36Sopenharmony_ci	info->vers->next = 0;
69862306a36Sopenharmony_ci	strcpy(info->vers->name, tt->name);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	info->old_vers = info->vers;
70162306a36Sopenharmony_ci	info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	size_t len, needed = 0;
70762306a36Sopenharmony_ci	struct dm_target_versions *vers;
70862306a36Sopenharmony_ci	struct vers_iter iter_info;
70962306a36Sopenharmony_ci	struct target_type *tt = NULL;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (name) {
71262306a36Sopenharmony_ci		tt = dm_get_target_type(name);
71362306a36Sopenharmony_ci		if (!tt)
71462306a36Sopenharmony_ci			return -EINVAL;
71562306a36Sopenharmony_ci	}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/*
71862306a36Sopenharmony_ci	 * Loop through all the devices working out how much
71962306a36Sopenharmony_ci	 * space we need.
72062306a36Sopenharmony_ci	 */
72162306a36Sopenharmony_ci	if (!tt)
72262306a36Sopenharmony_ci		dm_target_iterate(list_version_get_needed, &needed);
72362306a36Sopenharmony_ci	else
72462306a36Sopenharmony_ci		list_version_get_needed(tt, &needed);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * Grab our output buffer.
72862306a36Sopenharmony_ci	 */
72962306a36Sopenharmony_ci	vers = get_result_buffer(param, param_size, &len);
73062306a36Sopenharmony_ci	if (len < needed) {
73162306a36Sopenharmony_ci		param->flags |= DM_BUFFER_FULL_FLAG;
73262306a36Sopenharmony_ci		goto out;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	param->data_size = param->data_start + needed;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	iter_info.param_size = param_size;
73762306a36Sopenharmony_ci	iter_info.old_vers = NULL;
73862306a36Sopenharmony_ci	iter_info.vers = vers;
73962306a36Sopenharmony_ci	iter_info.flags = 0;
74062306a36Sopenharmony_ci	iter_info.end = (char *)vers + needed;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	/*
74362306a36Sopenharmony_ci	 * Now loop through filling out the names & versions.
74462306a36Sopenharmony_ci	 */
74562306a36Sopenharmony_ci	if (!tt)
74662306a36Sopenharmony_ci		dm_target_iterate(list_version_get_info, &iter_info);
74762306a36Sopenharmony_ci	else
74862306a36Sopenharmony_ci		list_version_get_info(tt, &iter_info);
74962306a36Sopenharmony_ci	param->flags |= iter_info.flags;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci out:
75262306a36Sopenharmony_ci	if (tt)
75362306a36Sopenharmony_ci		dm_put_target_type(tt);
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_cistatic int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	return __list_versions(param, param_size, NULL);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	return __list_versions(param, param_size, param->name);
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int check_name(const char *name)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	if (strchr(name, '/')) {
77062306a36Sopenharmony_ci		DMERR("device name cannot contain '/'");
77162306a36Sopenharmony_ci		return -EINVAL;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (strcmp(name, DM_CONTROL_NODE) == 0 ||
77562306a36Sopenharmony_ci	    strcmp(name, ".") == 0 ||
77662306a36Sopenharmony_ci	    strcmp(name, "..") == 0) {
77762306a36Sopenharmony_ci		DMERR("device name cannot be \"%s\", \".\", or \"..\"", DM_CONTROL_NODE);
77862306a36Sopenharmony_ci		return -EINVAL;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	return 0;
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci/*
78562306a36Sopenharmony_ci * On successful return, the caller must not attempt to acquire
78662306a36Sopenharmony_ci * _hash_lock without first calling dm_put_live_table, because dm_table_destroy
78762306a36Sopenharmony_ci * waits for this dm_put_live_table and could be called under this lock.
78862306a36Sopenharmony_ci */
78962306a36Sopenharmony_cistatic struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	struct hash_cell *hc;
79262306a36Sopenharmony_ci	struct dm_table *table = NULL;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	/* increment rcu count, we don't care about the table pointer */
79562306a36Sopenharmony_ci	dm_get_live_table(md, srcu_idx);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	down_read(&_hash_lock);
79862306a36Sopenharmony_ci	hc = dm_get_mdptr(md);
79962306a36Sopenharmony_ci	if (!hc) {
80062306a36Sopenharmony_ci		DMERR("device has been removed from the dev hash table.");
80162306a36Sopenharmony_ci		goto out;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	table = hc->new_map;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ciout:
80762306a36Sopenharmony_ci	up_read(&_hash_lock);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return table;
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
81362306a36Sopenharmony_ci						      struct dm_ioctl *param,
81462306a36Sopenharmony_ci						      int *srcu_idx)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
81762306a36Sopenharmony_ci		dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx);
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci/*
82162306a36Sopenharmony_ci * Fills in a dm_ioctl structure, ready for sending back to
82262306a36Sopenharmony_ci * userland.
82362306a36Sopenharmony_ci */
82462306a36Sopenharmony_cistatic void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	struct gendisk *disk = dm_disk(md);
82762306a36Sopenharmony_ci	struct dm_table *table;
82862306a36Sopenharmony_ci	int srcu_idx;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
83162306a36Sopenharmony_ci			  DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG);
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (dm_suspended_md(md))
83462306a36Sopenharmony_ci		param->flags |= DM_SUSPEND_FLAG;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (dm_suspended_internally_md(md))
83762306a36Sopenharmony_ci		param->flags |= DM_INTERNAL_SUSPEND_FLAG;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (dm_test_deferred_remove_flag(md))
84062306a36Sopenharmony_ci		param->flags |= DM_DEFERRED_REMOVE;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	param->dev = huge_encode_dev(disk_devt(disk));
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	/*
84562306a36Sopenharmony_ci	 * Yes, this will be out of date by the time it gets back
84662306a36Sopenharmony_ci	 * to userland, but it is still very useful for
84762306a36Sopenharmony_ci	 * debugging.
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci	param->open_count = dm_open_count(md);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	param->event_nr = dm_get_event_nr(md);
85262306a36Sopenharmony_ci	param->target_count = 0;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	table = dm_get_live_table(md, &srcu_idx);
85562306a36Sopenharmony_ci	if (table) {
85662306a36Sopenharmony_ci		if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
85762306a36Sopenharmony_ci			if (get_disk_ro(disk))
85862306a36Sopenharmony_ci				param->flags |= DM_READONLY_FLAG;
85962306a36Sopenharmony_ci			param->target_count = table->num_targets;
86062306a36Sopenharmony_ci		}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci		param->flags |= DM_ACTIVE_PRESENT_FLAG;
86362306a36Sopenharmony_ci	}
86462306a36Sopenharmony_ci	dm_put_live_table(md, srcu_idx);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
86762306a36Sopenharmony_ci		int srcu_idx;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci		table = dm_get_inactive_table(md, &srcu_idx);
87062306a36Sopenharmony_ci		if (table) {
87162306a36Sopenharmony_ci			if (!(dm_table_get_mode(table) & BLK_OPEN_WRITE))
87262306a36Sopenharmony_ci				param->flags |= DM_READONLY_FLAG;
87362306a36Sopenharmony_ci			param->target_count = table->num_targets;
87462306a36Sopenharmony_ci		}
87562306a36Sopenharmony_ci		dm_put_live_table(md, srcu_idx);
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	int r, m = DM_ANY_MINOR;
88262306a36Sopenharmony_ci	struct mapped_device *md;
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	r = check_name(param->name);
88562306a36Sopenharmony_ci	if (r)
88662306a36Sopenharmony_ci		return r;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (param->flags & DM_PERSISTENT_DEV_FLAG)
88962306a36Sopenharmony_ci		m = MINOR(huge_decode_dev(param->dev));
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	r = dm_create(m, &md);
89262306a36Sopenharmony_ci	if (r)
89362306a36Sopenharmony_ci		return r;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
89662306a36Sopenharmony_ci	if (r) {
89762306a36Sopenharmony_ci		dm_put(md);
89862306a36Sopenharmony_ci		dm_destroy(md);
89962306a36Sopenharmony_ci		return r;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	__dev_status(md, param);
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	dm_put(md);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	return 0;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci/*
91262306a36Sopenharmony_ci * Always use UUID for lookups if it's present, otherwise use name or dev.
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct hash_cell *hc = NULL;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (*param->uuid) {
91962306a36Sopenharmony_ci		if (*param->name || param->dev) {
92062306a36Sopenharmony_ci			DMERR("Invalid ioctl structure: uuid %s, name %s, dev %llx",
92162306a36Sopenharmony_ci			      param->uuid, param->name, (unsigned long long)param->dev);
92262306a36Sopenharmony_ci			return NULL;
92362306a36Sopenharmony_ci		}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		hc = __get_uuid_cell(param->uuid);
92662306a36Sopenharmony_ci		if (!hc)
92762306a36Sopenharmony_ci			return NULL;
92862306a36Sopenharmony_ci	} else if (*param->name) {
92962306a36Sopenharmony_ci		if (param->dev) {
93062306a36Sopenharmony_ci			DMERR("Invalid ioctl structure: name %s, dev %llx",
93162306a36Sopenharmony_ci			      param->name, (unsigned long long)param->dev);
93262306a36Sopenharmony_ci			return NULL;
93362306a36Sopenharmony_ci		}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci		hc = __get_name_cell(param->name);
93662306a36Sopenharmony_ci		if (!hc)
93762306a36Sopenharmony_ci			return NULL;
93862306a36Sopenharmony_ci	} else if (param->dev) {
93962306a36Sopenharmony_ci		hc = __get_dev_cell(param->dev);
94062306a36Sopenharmony_ci		if (!hc)
94162306a36Sopenharmony_ci			return NULL;
94262306a36Sopenharmony_ci	} else
94362306a36Sopenharmony_ci		return NULL;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	/*
94662306a36Sopenharmony_ci	 * Sneakily write in both the name and the uuid
94762306a36Sopenharmony_ci	 * while we have the cell.
94862306a36Sopenharmony_ci	 */
94962306a36Sopenharmony_ci	strscpy(param->name, hc->name, sizeof(param->name));
95062306a36Sopenharmony_ci	if (hc->uuid)
95162306a36Sopenharmony_ci		strscpy(param->uuid, hc->uuid, sizeof(param->uuid));
95262306a36Sopenharmony_ci	else
95362306a36Sopenharmony_ci		param->uuid[0] = '\0';
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (hc->new_map)
95662306a36Sopenharmony_ci		param->flags |= DM_INACTIVE_PRESENT_FLAG;
95762306a36Sopenharmony_ci	else
95862306a36Sopenharmony_ci		param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	return hc;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_cistatic struct mapped_device *find_device(struct dm_ioctl *param)
96462306a36Sopenharmony_ci{
96562306a36Sopenharmony_ci	struct hash_cell *hc;
96662306a36Sopenharmony_ci	struct mapped_device *md = NULL;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	down_read(&_hash_lock);
96962306a36Sopenharmony_ci	hc = __find_device_hash_cell(param);
97062306a36Sopenharmony_ci	if (hc)
97162306a36Sopenharmony_ci		md = hc->md;
97262306a36Sopenharmony_ci	up_read(&_hash_lock);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return md;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_size)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct hash_cell *hc;
98062306a36Sopenharmony_ci	struct mapped_device *md;
98162306a36Sopenharmony_ci	int r;
98262306a36Sopenharmony_ci	struct dm_table *t;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	down_write(&_hash_lock);
98562306a36Sopenharmony_ci	hc = __find_device_hash_cell(param);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (!hc) {
98862306a36Sopenharmony_ci		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
98962306a36Sopenharmony_ci		up_write(&_hash_lock);
99062306a36Sopenharmony_ci		return -ENXIO;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	md = hc->md;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/*
99662306a36Sopenharmony_ci	 * Ensure the device is not open and nothing further can open it.
99762306a36Sopenharmony_ci	 */
99862306a36Sopenharmony_ci	r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
99962306a36Sopenharmony_ci	if (r) {
100062306a36Sopenharmony_ci		if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
100162306a36Sopenharmony_ci			up_write(&_hash_lock);
100262306a36Sopenharmony_ci			dm_put(md);
100362306a36Sopenharmony_ci			return 0;
100462306a36Sopenharmony_ci		}
100562306a36Sopenharmony_ci		DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
100662306a36Sopenharmony_ci		up_write(&_hash_lock);
100762306a36Sopenharmony_ci		dm_put(md);
100862306a36Sopenharmony_ci		return r;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	t = __hash_remove(hc);
101262306a36Sopenharmony_ci	up_write(&_hash_lock);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (t) {
101562306a36Sopenharmony_ci		dm_sync_table(md);
101662306a36Sopenharmony_ci		dm_table_destroy(t);
101762306a36Sopenharmony_ci	}
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	param->flags &= ~DM_DEFERRED_REMOVE;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	dm_ima_measure_on_device_remove(md, false);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
102462306a36Sopenharmony_ci		param->flags |= DM_UEVENT_GENERATED_FLAG;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	dm_put(md);
102762306a36Sopenharmony_ci	dm_destroy(md);
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci/*
103262306a36Sopenharmony_ci * Check a string doesn't overrun the chunk of
103362306a36Sopenharmony_ci * memory we copied from userland.
103462306a36Sopenharmony_ci */
103562306a36Sopenharmony_cistatic int invalid_str(char *str, void *end)
103662306a36Sopenharmony_ci{
103762306a36Sopenharmony_ci	while ((void *) str < end)
103862306a36Sopenharmony_ci		if (!*str++)
103962306a36Sopenharmony_ci			return 0;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	return -EINVAL;
104262306a36Sopenharmony_ci}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_size)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	int r;
104762306a36Sopenharmony_ci	char *new_data = (char *) param + param->data_start;
104862306a36Sopenharmony_ci	struct mapped_device *md;
104962306a36Sopenharmony_ci	unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (new_data < param->data ||
105262306a36Sopenharmony_ci	    invalid_str(new_data, (void *) param + param_size) || !*new_data ||
105362306a36Sopenharmony_ci	    strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
105462306a36Sopenharmony_ci		DMERR("Invalid new mapped device name or uuid string supplied.");
105562306a36Sopenharmony_ci		return -EINVAL;
105662306a36Sopenharmony_ci	}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (!change_uuid) {
105962306a36Sopenharmony_ci		r = check_name(new_data);
106062306a36Sopenharmony_ci		if (r)
106162306a36Sopenharmony_ci			return r;
106262306a36Sopenharmony_ci	}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	md = dm_hash_rename(param, new_data);
106562306a36Sopenharmony_ci	if (IS_ERR(md))
106662306a36Sopenharmony_ci		return PTR_ERR(md);
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	__dev_status(md, param);
106962306a36Sopenharmony_ci	dm_put(md);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	return 0;
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t param_size)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	int r = -EINVAL, x;
107762306a36Sopenharmony_ci	struct mapped_device *md;
107862306a36Sopenharmony_ci	struct hd_geometry geometry;
107962306a36Sopenharmony_ci	unsigned long indata[4];
108062306a36Sopenharmony_ci	char *geostr = (char *) param + param->data_start;
108162306a36Sopenharmony_ci	char dummy;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	md = find_device(param);
108462306a36Sopenharmony_ci	if (!md)
108562306a36Sopenharmony_ci		return -ENXIO;
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (geostr < param->data ||
108862306a36Sopenharmony_ci	    invalid_str(geostr, (void *) param + param_size)) {
108962306a36Sopenharmony_ci		DMERR("Invalid geometry supplied.");
109062306a36Sopenharmony_ci		goto out;
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	x = sscanf(geostr, "%lu %lu %lu %lu%c", indata,
109462306a36Sopenharmony_ci		   indata + 1, indata + 2, indata + 3, &dummy);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	if (x != 4) {
109762306a36Sopenharmony_ci		DMERR("Unable to interpret geometry settings.");
109862306a36Sopenharmony_ci		goto out;
109962306a36Sopenharmony_ci	}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	if (indata[0] > 65535 || indata[1] > 255 || indata[2] > 255) {
110262306a36Sopenharmony_ci		DMERR("Geometry exceeds range limits.");
110362306a36Sopenharmony_ci		goto out;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	geometry.cylinders = indata[0];
110762306a36Sopenharmony_ci	geometry.heads = indata[1];
110862306a36Sopenharmony_ci	geometry.sectors = indata[2];
110962306a36Sopenharmony_ci	geometry.start = indata[3];
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	r = dm_set_geometry(md, &geometry);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	param->data_size = 0;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ciout:
111662306a36Sopenharmony_ci	dm_put(md);
111762306a36Sopenharmony_ci	return r;
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic int do_suspend(struct dm_ioctl *param)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	int r = 0;
112362306a36Sopenharmony_ci	unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
112462306a36Sopenharmony_ci	struct mapped_device *md;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	md = find_device(param);
112762306a36Sopenharmony_ci	if (!md)
112862306a36Sopenharmony_ci		return -ENXIO;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (param->flags & DM_SKIP_LOCKFS_FLAG)
113162306a36Sopenharmony_ci		suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
113262306a36Sopenharmony_ci	if (param->flags & DM_NOFLUSH_FLAG)
113362306a36Sopenharmony_ci		suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (!dm_suspended_md(md)) {
113662306a36Sopenharmony_ci		r = dm_suspend(md, suspend_flags);
113762306a36Sopenharmony_ci		if (r)
113862306a36Sopenharmony_ci			goto out;
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	__dev_status(md, param);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ciout:
114462306a36Sopenharmony_ci	dm_put(md);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	return r;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int do_resume(struct dm_ioctl *param)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	int r = 0;
115262306a36Sopenharmony_ci	unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
115362306a36Sopenharmony_ci	struct hash_cell *hc;
115462306a36Sopenharmony_ci	struct mapped_device *md;
115562306a36Sopenharmony_ci	struct dm_table *new_map, *old_map = NULL;
115662306a36Sopenharmony_ci	bool need_resize_uevent = false;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	down_write(&_hash_lock);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	hc = __find_device_hash_cell(param);
116162306a36Sopenharmony_ci	if (!hc) {
116262306a36Sopenharmony_ci		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
116362306a36Sopenharmony_ci		up_write(&_hash_lock);
116462306a36Sopenharmony_ci		return -ENXIO;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	md = hc->md;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	new_map = hc->new_map;
117062306a36Sopenharmony_ci	hc->new_map = NULL;
117162306a36Sopenharmony_ci	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	up_write(&_hash_lock);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/* Do we need to load a new map ? */
117662306a36Sopenharmony_ci	if (new_map) {
117762306a36Sopenharmony_ci		sector_t old_size, new_size;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci		/* Suspend if it isn't already suspended */
118062306a36Sopenharmony_ci		if (param->flags & DM_SKIP_LOCKFS_FLAG)
118162306a36Sopenharmony_ci			suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
118262306a36Sopenharmony_ci		if (param->flags & DM_NOFLUSH_FLAG)
118362306a36Sopenharmony_ci			suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
118462306a36Sopenharmony_ci		if (!dm_suspended_md(md))
118562306a36Sopenharmony_ci			dm_suspend(md, suspend_flags);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		old_size = dm_get_size(md);
118862306a36Sopenharmony_ci		old_map = dm_swap_table(md, new_map);
118962306a36Sopenharmony_ci		if (IS_ERR(old_map)) {
119062306a36Sopenharmony_ci			dm_sync_table(md);
119162306a36Sopenharmony_ci			dm_table_destroy(new_map);
119262306a36Sopenharmony_ci			dm_put(md);
119362306a36Sopenharmony_ci			return PTR_ERR(old_map);
119462306a36Sopenharmony_ci		}
119562306a36Sopenharmony_ci		new_size = dm_get_size(md);
119662306a36Sopenharmony_ci		if (old_size && new_size && old_size != new_size)
119762306a36Sopenharmony_ci			need_resize_uevent = true;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		if (dm_table_get_mode(new_map) & BLK_OPEN_WRITE)
120062306a36Sopenharmony_ci			set_disk_ro(dm_disk(md), 0);
120162306a36Sopenharmony_ci		else
120262306a36Sopenharmony_ci			set_disk_ro(dm_disk(md), 1);
120362306a36Sopenharmony_ci	}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	if (dm_suspended_md(md)) {
120662306a36Sopenharmony_ci		r = dm_resume(md);
120762306a36Sopenharmony_ci		if (!r) {
120862306a36Sopenharmony_ci			dm_ima_measure_on_device_resume(md, new_map ? true : false);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci			if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
121162306a36Sopenharmony_ci				param->flags |= DM_UEVENT_GENERATED_FLAG;
121262306a36Sopenharmony_ci		}
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/*
121662306a36Sopenharmony_ci	 * Since dm_swap_table synchronizes RCU, nobody should be in
121762306a36Sopenharmony_ci	 * read-side critical section already.
121862306a36Sopenharmony_ci	 */
121962306a36Sopenharmony_ci	if (old_map)
122062306a36Sopenharmony_ci		dm_table_destroy(old_map);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	if (!r)
122362306a36Sopenharmony_ci		__dev_status(md, param);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	dm_put(md);
122662306a36Sopenharmony_ci	return r;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci/*
123062306a36Sopenharmony_ci * Set or unset the suspension state of a device.
123162306a36Sopenharmony_ci * If the device already is in the requested state we just return its status.
123262306a36Sopenharmony_ci */
123362306a36Sopenharmony_cistatic int dev_suspend(struct file *filp, struct dm_ioctl *param, size_t param_size)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	if (param->flags & DM_SUSPEND_FLAG)
123662306a36Sopenharmony_ci		return do_suspend(param);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	return do_resume(param);
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci/*
124262306a36Sopenharmony_ci * Copies device info back to user space, used by
124362306a36Sopenharmony_ci * the create and info ioctls.
124462306a36Sopenharmony_ci */
124562306a36Sopenharmony_cistatic int dev_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct mapped_device *md;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	md = find_device(param);
125062306a36Sopenharmony_ci	if (!md)
125162306a36Sopenharmony_ci		return -ENXIO;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	__dev_status(md, param);
125462306a36Sopenharmony_ci	dm_put(md);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return 0;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci/*
126062306a36Sopenharmony_ci * Build up the status struct for each target
126162306a36Sopenharmony_ci */
126262306a36Sopenharmony_cistatic void retrieve_status(struct dm_table *table,
126362306a36Sopenharmony_ci			    struct dm_ioctl *param, size_t param_size)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	unsigned int i, num_targets;
126662306a36Sopenharmony_ci	struct dm_target_spec *spec;
126762306a36Sopenharmony_ci	char *outbuf, *outptr;
126862306a36Sopenharmony_ci	status_type_t type;
126962306a36Sopenharmony_ci	size_t remaining, len, used = 0;
127062306a36Sopenharmony_ci	unsigned int status_flags = 0;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	outptr = outbuf = get_result_buffer(param, param_size, &len);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	if (param->flags & DM_STATUS_TABLE_FLAG)
127562306a36Sopenharmony_ci		type = STATUSTYPE_TABLE;
127662306a36Sopenharmony_ci	else if (param->flags & DM_IMA_MEASUREMENT_FLAG)
127762306a36Sopenharmony_ci		type = STATUSTYPE_IMA;
127862306a36Sopenharmony_ci	else
127962306a36Sopenharmony_ci		type = STATUSTYPE_INFO;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	/* Get all the target info */
128262306a36Sopenharmony_ci	num_targets = table->num_targets;
128362306a36Sopenharmony_ci	for (i = 0; i < num_targets; i++) {
128462306a36Sopenharmony_ci		struct dm_target *ti = dm_table_get_target(table, i);
128562306a36Sopenharmony_ci		size_t l;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci		remaining = len - (outptr - outbuf);
128862306a36Sopenharmony_ci		if (remaining <= sizeof(struct dm_target_spec)) {
128962306a36Sopenharmony_ci			param->flags |= DM_BUFFER_FULL_FLAG;
129062306a36Sopenharmony_ci			break;
129162306a36Sopenharmony_ci		}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		spec = (struct dm_target_spec *) outptr;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci		spec->status = 0;
129662306a36Sopenharmony_ci		spec->sector_start = ti->begin;
129762306a36Sopenharmony_ci		spec->length = ti->len;
129862306a36Sopenharmony_ci		strncpy(spec->target_type, ti->type->name,
129962306a36Sopenharmony_ci			sizeof(spec->target_type) - 1);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci		outptr += sizeof(struct dm_target_spec);
130262306a36Sopenharmony_ci		remaining = len - (outptr - outbuf);
130362306a36Sopenharmony_ci		if (remaining <= 0) {
130462306a36Sopenharmony_ci			param->flags |= DM_BUFFER_FULL_FLAG;
130562306a36Sopenharmony_ci			break;
130662306a36Sopenharmony_ci		}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci		/* Get the status/table string from the target driver */
130962306a36Sopenharmony_ci		if (ti->type->status) {
131062306a36Sopenharmony_ci			if (param->flags & DM_NOFLUSH_FLAG)
131162306a36Sopenharmony_ci				status_flags |= DM_STATUS_NOFLUSH_FLAG;
131262306a36Sopenharmony_ci			ti->type->status(ti, type, status_flags, outptr, remaining);
131362306a36Sopenharmony_ci		} else
131462306a36Sopenharmony_ci			outptr[0] = '\0';
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci		l = strlen(outptr) + 1;
131762306a36Sopenharmony_ci		if (l == remaining) {
131862306a36Sopenharmony_ci			param->flags |= DM_BUFFER_FULL_FLAG;
131962306a36Sopenharmony_ci			break;
132062306a36Sopenharmony_ci		}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci		outptr += l;
132362306a36Sopenharmony_ci		used = param->data_start + (outptr - outbuf);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci		outptr = align_ptr(outptr);
132662306a36Sopenharmony_ci		spec->next = outptr - outbuf;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (used)
133062306a36Sopenharmony_ci		param->data_size = used;
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	param->target_count = num_targets;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/*
133662306a36Sopenharmony_ci * Wait for a device to report an event
133762306a36Sopenharmony_ci */
133862306a36Sopenharmony_cistatic int dev_wait(struct file *filp, struct dm_ioctl *param, size_t param_size)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	int r = 0;
134162306a36Sopenharmony_ci	struct mapped_device *md;
134262306a36Sopenharmony_ci	struct dm_table *table;
134362306a36Sopenharmony_ci	int srcu_idx;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	md = find_device(param);
134662306a36Sopenharmony_ci	if (!md)
134762306a36Sopenharmony_ci		return -ENXIO;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/*
135062306a36Sopenharmony_ci	 * Wait for a notification event
135162306a36Sopenharmony_ci	 */
135262306a36Sopenharmony_ci	if (dm_wait_event(md, param->event_nr)) {
135362306a36Sopenharmony_ci		r = -ERESTARTSYS;
135462306a36Sopenharmony_ci		goto out;
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/*
135862306a36Sopenharmony_ci	 * The userland program is going to want to know what
135962306a36Sopenharmony_ci	 * changed to trigger the event, so we may as well tell
136062306a36Sopenharmony_ci	 * him and save an ioctl.
136162306a36Sopenharmony_ci	 */
136262306a36Sopenharmony_ci	__dev_status(md, param);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
136562306a36Sopenharmony_ci	if (table)
136662306a36Sopenharmony_ci		retrieve_status(table, param, param_size);
136762306a36Sopenharmony_ci	dm_put_live_table(md, srcu_idx);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ciout:
137062306a36Sopenharmony_ci	dm_put(md);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	return r;
137362306a36Sopenharmony_ci}
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci/*
137662306a36Sopenharmony_ci * Remember the global event number and make it possible to poll
137762306a36Sopenharmony_ci * for further events.
137862306a36Sopenharmony_ci */
137962306a36Sopenharmony_cistatic int dev_arm_poll(struct file *filp, struct dm_ioctl *param, size_t param_size)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	struct dm_file *priv = filp->private_data;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	priv->global_event_nr = atomic_read(&dm_global_event_nr);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	return 0;
138662306a36Sopenharmony_ci}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic inline blk_mode_t get_mode(struct dm_ioctl *param)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	if (param->flags & DM_READONLY_FLAG)
139362306a36Sopenharmony_ci		mode = BLK_OPEN_READ;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	return mode;
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_cistatic int next_target(struct dm_target_spec *last, uint32_t next, const char *end,
139962306a36Sopenharmony_ci		       struct dm_target_spec **spec, char **target_params)
140062306a36Sopenharmony_ci{
140162306a36Sopenharmony_ci	static_assert(__alignof__(struct dm_target_spec) <= 8,
140262306a36Sopenharmony_ci		"struct dm_target_spec must not require more than 8-byte alignment");
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	/*
140562306a36Sopenharmony_ci	 * Number of bytes remaining, starting with last. This is always
140662306a36Sopenharmony_ci	 * sizeof(struct dm_target_spec) or more, as otherwise *last was
140762306a36Sopenharmony_ci	 * out of bounds already.
140862306a36Sopenharmony_ci	 */
140962306a36Sopenharmony_ci	size_t remaining = end - (char *)last;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	/*
141262306a36Sopenharmony_ci	 * There must be room for both the next target spec and the
141362306a36Sopenharmony_ci	 * NUL-terminator of the target itself.
141462306a36Sopenharmony_ci	 */
141562306a36Sopenharmony_ci	if (remaining - sizeof(struct dm_target_spec) <= next) {
141662306a36Sopenharmony_ci		DMERR("Target spec extends beyond end of parameters");
141762306a36Sopenharmony_ci		return -EINVAL;
141862306a36Sopenharmony_ci	}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	if (next % __alignof__(struct dm_target_spec)) {
142162306a36Sopenharmony_ci		DMERR("Next dm_target_spec (offset %u) is not %zu-byte aligned",
142262306a36Sopenharmony_ci		      next, __alignof__(struct dm_target_spec));
142362306a36Sopenharmony_ci		return -EINVAL;
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);
142762306a36Sopenharmony_ci	*target_params = (char *) (*spec + 1);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	return 0;
143062306a36Sopenharmony_ci}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic int populate_table(struct dm_table *table,
143362306a36Sopenharmony_ci			  struct dm_ioctl *param, size_t param_size)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	int r;
143662306a36Sopenharmony_ci	unsigned int i = 0;
143762306a36Sopenharmony_ci	struct dm_target_spec *spec = (struct dm_target_spec *) param;
143862306a36Sopenharmony_ci	uint32_t next = param->data_start;
143962306a36Sopenharmony_ci	const char *const end = (const char *) param + param_size;
144062306a36Sopenharmony_ci	char *target_params;
144162306a36Sopenharmony_ci	size_t min_size = sizeof(struct dm_ioctl);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (!param->target_count) {
144462306a36Sopenharmony_ci		DMERR("%s: no targets specified", __func__);
144562306a36Sopenharmony_ci		return -EINVAL;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci	for (i = 0; i < param->target_count; i++) {
144962306a36Sopenharmony_ci		const char *nul_terminator;
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci		if (next < min_size) {
145262306a36Sopenharmony_ci			DMERR("%s: next target spec (offset %u) overlaps %s",
145362306a36Sopenharmony_ci			      __func__, next, i ? "previous target" : "'struct dm_ioctl'");
145462306a36Sopenharmony_ci			return -EINVAL;
145562306a36Sopenharmony_ci		}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		r = next_target(spec, next, end, &spec, &target_params);
145862306a36Sopenharmony_ci		if (r) {
145962306a36Sopenharmony_ci			DMERR("unable to find target");
146062306a36Sopenharmony_ci			return r;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		nul_terminator = memchr(target_params, 0, (size_t)(end - target_params));
146462306a36Sopenharmony_ci		if (nul_terminator == NULL) {
146562306a36Sopenharmony_ci			DMERR("%s: target parameters not NUL-terminated", __func__);
146662306a36Sopenharmony_ci			return -EINVAL;
146762306a36Sopenharmony_ci		}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		/* Add 1 for NUL terminator */
147062306a36Sopenharmony_ci		min_size = (size_t)(nul_terminator - (const char *)spec) + 1;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci		r = dm_table_add_target(table, spec->target_type,
147362306a36Sopenharmony_ci					(sector_t) spec->sector_start,
147462306a36Sopenharmony_ci					(sector_t) spec->length,
147562306a36Sopenharmony_ci					target_params);
147662306a36Sopenharmony_ci		if (r) {
147762306a36Sopenharmony_ci			DMERR("error adding target to table");
147862306a36Sopenharmony_ci			return r;
147962306a36Sopenharmony_ci		}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		next = spec->next;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	return dm_table_complete(table);
148562306a36Sopenharmony_ci}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_cistatic bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
148862306a36Sopenharmony_ci{
148962306a36Sopenharmony_ci	if (cur == new ||
149062306a36Sopenharmony_ci	    (cur == DM_TYPE_BIO_BASED && new == DM_TYPE_DAX_BIO_BASED))
149162306a36Sopenharmony_ci		return true;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return false;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	int r;
149962306a36Sopenharmony_ci	struct hash_cell *hc;
150062306a36Sopenharmony_ci	struct dm_table *t, *old_map = NULL;
150162306a36Sopenharmony_ci	struct mapped_device *md;
150262306a36Sopenharmony_ci	struct target_type *immutable_target_type;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	md = find_device(param);
150562306a36Sopenharmony_ci	if (!md)
150662306a36Sopenharmony_ci		return -ENXIO;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	r = dm_table_create(&t, get_mode(param), param->target_count, md);
150962306a36Sopenharmony_ci	if (r)
151062306a36Sopenharmony_ci		goto err;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	/* Protect md->type and md->queue against concurrent table loads. */
151362306a36Sopenharmony_ci	dm_lock_md_type(md);
151462306a36Sopenharmony_ci	r = populate_table(t, param, param_size);
151562306a36Sopenharmony_ci	if (r)
151662306a36Sopenharmony_ci		goto err_unlock_md_type;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	immutable_target_type = dm_get_immutable_target_type(md);
152162306a36Sopenharmony_ci	if (immutable_target_type &&
152262306a36Sopenharmony_ci	    (immutable_target_type != dm_table_get_immutable_target_type(t)) &&
152362306a36Sopenharmony_ci	    !dm_table_get_wildcard_target(t)) {
152462306a36Sopenharmony_ci		DMERR("can't replace immutable target type %s",
152562306a36Sopenharmony_ci		      immutable_target_type->name);
152662306a36Sopenharmony_ci		r = -EINVAL;
152762306a36Sopenharmony_ci		goto err_unlock_md_type;
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (dm_get_md_type(md) == DM_TYPE_NONE) {
153162306a36Sopenharmony_ci		/* setup md->queue to reflect md's type (may block) */
153262306a36Sopenharmony_ci		r = dm_setup_md_queue(md, t);
153362306a36Sopenharmony_ci		if (r) {
153462306a36Sopenharmony_ci			DMERR("unable to set up device queue for new table.");
153562306a36Sopenharmony_ci			goto err_unlock_md_type;
153662306a36Sopenharmony_ci		}
153762306a36Sopenharmony_ci	} else if (!is_valid_type(dm_get_md_type(md), dm_table_get_type(t))) {
153862306a36Sopenharmony_ci		DMERR("can't change device type (old=%u vs new=%u) after initial table load.",
153962306a36Sopenharmony_ci		      dm_get_md_type(md), dm_table_get_type(t));
154062306a36Sopenharmony_ci		r = -EINVAL;
154162306a36Sopenharmony_ci		goto err_unlock_md_type;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	dm_unlock_md_type(md);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	/* stage inactive table */
154762306a36Sopenharmony_ci	down_write(&_hash_lock);
154862306a36Sopenharmony_ci	hc = dm_get_mdptr(md);
154962306a36Sopenharmony_ci	if (!hc) {
155062306a36Sopenharmony_ci		DMERR("device has been removed from the dev hash table.");
155162306a36Sopenharmony_ci		up_write(&_hash_lock);
155262306a36Sopenharmony_ci		r = -ENXIO;
155362306a36Sopenharmony_ci		goto err_destroy_table;
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	if (hc->new_map)
155762306a36Sopenharmony_ci		old_map = hc->new_map;
155862306a36Sopenharmony_ci	hc->new_map = t;
155962306a36Sopenharmony_ci	up_write(&_hash_lock);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	param->flags |= DM_INACTIVE_PRESENT_FLAG;
156262306a36Sopenharmony_ci	__dev_status(md, param);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	if (old_map) {
156562306a36Sopenharmony_ci		dm_sync_table(md);
156662306a36Sopenharmony_ci		dm_table_destroy(old_map);
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	dm_put(md);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	return 0;
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_cierr_unlock_md_type:
157462306a36Sopenharmony_ci	dm_unlock_md_type(md);
157562306a36Sopenharmony_cierr_destroy_table:
157662306a36Sopenharmony_ci	dm_table_destroy(t);
157762306a36Sopenharmony_cierr:
157862306a36Sopenharmony_ci	dm_put(md);
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	return r;
158162306a36Sopenharmony_ci}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_cistatic int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_size)
158462306a36Sopenharmony_ci{
158562306a36Sopenharmony_ci	struct hash_cell *hc;
158662306a36Sopenharmony_ci	struct mapped_device *md;
158762306a36Sopenharmony_ci	struct dm_table *old_map = NULL;
158862306a36Sopenharmony_ci	bool has_new_map = false;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	down_write(&_hash_lock);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	hc = __find_device_hash_cell(param);
159362306a36Sopenharmony_ci	if (!hc) {
159462306a36Sopenharmony_ci		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
159562306a36Sopenharmony_ci		up_write(&_hash_lock);
159662306a36Sopenharmony_ci		return -ENXIO;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	if (hc->new_map) {
160062306a36Sopenharmony_ci		old_map = hc->new_map;
160162306a36Sopenharmony_ci		hc->new_map = NULL;
160262306a36Sopenharmony_ci		has_new_map = true;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	md = hc->md;
160662306a36Sopenharmony_ci	up_write(&_hash_lock);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
160962306a36Sopenharmony_ci	__dev_status(md, param);
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	if (old_map) {
161262306a36Sopenharmony_ci		dm_sync_table(md);
161362306a36Sopenharmony_ci		dm_table_destroy(old_map);
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci	dm_ima_measure_on_table_clear(md, has_new_map);
161662306a36Sopenharmony_ci	dm_put(md);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	return 0;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci/*
162262306a36Sopenharmony_ci * Retrieves a list of devices used by a particular dm device.
162362306a36Sopenharmony_ci */
162462306a36Sopenharmony_cistatic void retrieve_deps(struct dm_table *table,
162562306a36Sopenharmony_ci			  struct dm_ioctl *param, size_t param_size)
162662306a36Sopenharmony_ci{
162762306a36Sopenharmony_ci	unsigned int count = 0;
162862306a36Sopenharmony_ci	struct list_head *tmp;
162962306a36Sopenharmony_ci	size_t len, needed;
163062306a36Sopenharmony_ci	struct dm_dev_internal *dd;
163162306a36Sopenharmony_ci	struct dm_target_deps *deps;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	down_read(&table->devices_lock);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	deps = get_result_buffer(param, param_size, &len);
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/*
163862306a36Sopenharmony_ci	 * Count the devices.
163962306a36Sopenharmony_ci	 */
164062306a36Sopenharmony_ci	list_for_each(tmp, dm_table_get_devices(table))
164162306a36Sopenharmony_ci		count++;
164262306a36Sopenharmony_ci
164362306a36Sopenharmony_ci	/*
164462306a36Sopenharmony_ci	 * Check we have enough space.
164562306a36Sopenharmony_ci	 */
164662306a36Sopenharmony_ci	needed = struct_size(deps, dev, count);
164762306a36Sopenharmony_ci	if (len < needed) {
164862306a36Sopenharmony_ci		param->flags |= DM_BUFFER_FULL_FLAG;
164962306a36Sopenharmony_ci		goto out;
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	/*
165362306a36Sopenharmony_ci	 * Fill in the devices.
165462306a36Sopenharmony_ci	 */
165562306a36Sopenharmony_ci	deps->count = count;
165662306a36Sopenharmony_ci	count = 0;
165762306a36Sopenharmony_ci	list_for_each_entry(dd, dm_table_get_devices(table), list)
165862306a36Sopenharmony_ci		deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev);
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	param->data_size = param->data_start + needed;
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ciout:
166362306a36Sopenharmony_ci	up_read(&table->devices_lock);
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	struct mapped_device *md;
166962306a36Sopenharmony_ci	struct dm_table *table;
167062306a36Sopenharmony_ci	int srcu_idx;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	md = find_device(param);
167362306a36Sopenharmony_ci	if (!md)
167462306a36Sopenharmony_ci		return -ENXIO;
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	__dev_status(md, param);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
167962306a36Sopenharmony_ci	if (table)
168062306a36Sopenharmony_ci		retrieve_deps(table, param, param_size);
168162306a36Sopenharmony_ci	dm_put_live_table(md, srcu_idx);
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	dm_put(md);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return 0;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci/*
168962306a36Sopenharmony_ci * Return the status of a device as a text string for each
169062306a36Sopenharmony_ci * target.
169162306a36Sopenharmony_ci */
169262306a36Sopenharmony_cistatic int table_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	struct mapped_device *md;
169562306a36Sopenharmony_ci	struct dm_table *table;
169662306a36Sopenharmony_ci	int srcu_idx;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	md = find_device(param);
169962306a36Sopenharmony_ci	if (!md)
170062306a36Sopenharmony_ci		return -ENXIO;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	__dev_status(md, param);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
170562306a36Sopenharmony_ci	if (table)
170662306a36Sopenharmony_ci		retrieve_status(table, param, param_size);
170762306a36Sopenharmony_ci	dm_put_live_table(md, srcu_idx);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	dm_put(md);
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	return 0;
171262306a36Sopenharmony_ci}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci/*
171562306a36Sopenharmony_ci * Process device-mapper dependent messages.  Messages prefixed with '@'
171662306a36Sopenharmony_ci * are processed by the DM core.  All others are delivered to the target.
171762306a36Sopenharmony_ci * Returns a number <= 1 if message was processed by device mapper.
171862306a36Sopenharmony_ci * Returns 2 if message should be delivered to the target.
171962306a36Sopenharmony_ci */
172062306a36Sopenharmony_cistatic int message_for_md(struct mapped_device *md, unsigned int argc, char **argv,
172162306a36Sopenharmony_ci			  char *result, unsigned int maxlen)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	int r;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (**argv != '@')
172662306a36Sopenharmony_ci		return 2; /* no '@' prefix, deliver to target */
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	if (!strcasecmp(argv[0], "@cancel_deferred_remove")) {
172962306a36Sopenharmony_ci		if (argc != 1) {
173062306a36Sopenharmony_ci			DMERR("Invalid arguments for @cancel_deferred_remove");
173162306a36Sopenharmony_ci			return -EINVAL;
173262306a36Sopenharmony_ci		}
173362306a36Sopenharmony_ci		return dm_cancel_deferred_remove(md);
173462306a36Sopenharmony_ci	}
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	r = dm_stats_message(md, argc, argv, result, maxlen);
173762306a36Sopenharmony_ci	if (r < 2)
173862306a36Sopenharmony_ci		return r;
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	DMERR("Unsupported message sent to DM core: %s", argv[0]);
174162306a36Sopenharmony_ci	return -EINVAL;
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci/*
174562306a36Sopenharmony_ci * Pass a message to the target that's at the supplied device offset.
174662306a36Sopenharmony_ci */
174762306a36Sopenharmony_cistatic int target_message(struct file *filp, struct dm_ioctl *param, size_t param_size)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	int r, argc;
175062306a36Sopenharmony_ci	char **argv;
175162306a36Sopenharmony_ci	struct mapped_device *md;
175262306a36Sopenharmony_ci	struct dm_table *table;
175362306a36Sopenharmony_ci	struct dm_target *ti;
175462306a36Sopenharmony_ci	struct dm_target_msg *tmsg = (void *) param + param->data_start;
175562306a36Sopenharmony_ci	size_t maxlen;
175662306a36Sopenharmony_ci	char *result = get_result_buffer(param, param_size, &maxlen);
175762306a36Sopenharmony_ci	int srcu_idx;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	md = find_device(param);
176062306a36Sopenharmony_ci	if (!md)
176162306a36Sopenharmony_ci		return -ENXIO;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	if (tmsg < (struct dm_target_msg *) param->data ||
176462306a36Sopenharmony_ci	    invalid_str(tmsg->message, (void *) param + param_size)) {
176562306a36Sopenharmony_ci		DMERR("Invalid target message parameters.");
176662306a36Sopenharmony_ci		r = -EINVAL;
176762306a36Sopenharmony_ci		goto out;
176862306a36Sopenharmony_ci	}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	r = dm_split_args(&argc, &argv, tmsg->message);
177162306a36Sopenharmony_ci	if (r) {
177262306a36Sopenharmony_ci		DMERR("Failed to split target message parameters");
177362306a36Sopenharmony_ci		goto out;
177462306a36Sopenharmony_ci	}
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	if (!argc) {
177762306a36Sopenharmony_ci		DMERR("Empty message received.");
177862306a36Sopenharmony_ci		r = -EINVAL;
177962306a36Sopenharmony_ci		goto out_argv;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	r = message_for_md(md, argc, argv, result, maxlen);
178362306a36Sopenharmony_ci	if (r <= 1)
178462306a36Sopenharmony_ci		goto out_argv;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	table = dm_get_live_table(md, &srcu_idx);
178762306a36Sopenharmony_ci	if (!table)
178862306a36Sopenharmony_ci		goto out_table;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (dm_deleting_md(md)) {
179162306a36Sopenharmony_ci		r = -ENXIO;
179262306a36Sopenharmony_ci		goto out_table;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	ti = dm_table_find_target(table, tmsg->sector);
179662306a36Sopenharmony_ci	if (!ti) {
179762306a36Sopenharmony_ci		DMERR("Target message sector outside device.");
179862306a36Sopenharmony_ci		r = -EINVAL;
179962306a36Sopenharmony_ci	} else if (ti->type->message)
180062306a36Sopenharmony_ci		r = ti->type->message(ti, argc, argv, result, maxlen);
180162306a36Sopenharmony_ci	else {
180262306a36Sopenharmony_ci		DMERR("Target type does not support messages");
180362306a36Sopenharmony_ci		r = -EINVAL;
180462306a36Sopenharmony_ci	}
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci out_table:
180762306a36Sopenharmony_ci	dm_put_live_table(md, srcu_idx);
180862306a36Sopenharmony_ci out_argv:
180962306a36Sopenharmony_ci	kfree(argv);
181062306a36Sopenharmony_ci out:
181162306a36Sopenharmony_ci	if (r >= 0)
181262306a36Sopenharmony_ci		__dev_status(md, param);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	if (r == 1) {
181562306a36Sopenharmony_ci		param->flags |= DM_DATA_OUT_FLAG;
181662306a36Sopenharmony_ci		if (dm_message_test_buffer_overflow(result, maxlen))
181762306a36Sopenharmony_ci			param->flags |= DM_BUFFER_FULL_FLAG;
181862306a36Sopenharmony_ci		else
181962306a36Sopenharmony_ci			param->data_size = param->data_start + strlen(result) + 1;
182062306a36Sopenharmony_ci		r = 0;
182162306a36Sopenharmony_ci	}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	dm_put(md);
182462306a36Sopenharmony_ci	return r;
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci/*
182862306a36Sopenharmony_ci * The ioctl parameter block consists of two parts, a dm_ioctl struct
182962306a36Sopenharmony_ci * followed by a data buffer.  This flag is set if the second part,
183062306a36Sopenharmony_ci * which has a variable size, is not used by the function processing
183162306a36Sopenharmony_ci * the ioctl.
183262306a36Sopenharmony_ci */
183362306a36Sopenharmony_ci#define IOCTL_FLAGS_NO_PARAMS		1
183462306a36Sopenharmony_ci#define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT	2
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci/*
183762306a36Sopenharmony_ci *---------------------------------------------------------------
183862306a36Sopenharmony_ci * Implementation of open/close/ioctl on the special char device.
183962306a36Sopenharmony_ci *---------------------------------------------------------------
184062306a36Sopenharmony_ci */
184162306a36Sopenharmony_cistatic ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
184262306a36Sopenharmony_ci{
184362306a36Sopenharmony_ci	static const struct {
184462306a36Sopenharmony_ci		int cmd;
184562306a36Sopenharmony_ci		int flags;
184662306a36Sopenharmony_ci		ioctl_fn fn;
184762306a36Sopenharmony_ci	} _ioctls[] = {
184862306a36Sopenharmony_ci		{DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */
184962306a36Sopenharmony_ci		{DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all},
185062306a36Sopenharmony_ci		{DM_LIST_DEVICES_CMD, 0, list_devices},
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci		{DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create},
185362306a36Sopenharmony_ci		{DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove},
185462306a36Sopenharmony_ci		{DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename},
185562306a36Sopenharmony_ci		{DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},
185662306a36Sopenharmony_ci		{DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},
185762306a36Sopenharmony_ci		{DM_DEV_WAIT_CMD, 0, dev_wait},
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci		{DM_TABLE_LOAD_CMD, 0, table_load},
186062306a36Sopenharmony_ci		{DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear},
186162306a36Sopenharmony_ci		{DM_TABLE_DEPS_CMD, 0, table_deps},
186262306a36Sopenharmony_ci		{DM_TABLE_STATUS_CMD, 0, table_status},
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci		{DM_LIST_VERSIONS_CMD, 0, list_versions},
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci		{DM_TARGET_MSG_CMD, 0, target_message},
186762306a36Sopenharmony_ci		{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
186862306a36Sopenharmony_ci		{DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
186962306a36Sopenharmony_ci		{DM_GET_TARGET_VERSION_CMD, 0, get_target_version},
187062306a36Sopenharmony_ci	};
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
187362306a36Sopenharmony_ci		return NULL;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	cmd = array_index_nospec(cmd, ARRAY_SIZE(_ioctls));
187662306a36Sopenharmony_ci	*ioctl_flags = _ioctls[cmd].flags;
187762306a36Sopenharmony_ci	return _ioctls[cmd].fn;
187862306a36Sopenharmony_ci}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci/*
188162306a36Sopenharmony_ci * As well as checking the version compatibility this always
188262306a36Sopenharmony_ci * copies the kernel interface version out.
188362306a36Sopenharmony_ci */
188462306a36Sopenharmony_cistatic int check_version(unsigned int cmd, struct dm_ioctl __user *user,
188562306a36Sopenharmony_ci			 struct dm_ioctl *kernel_params)
188662306a36Sopenharmony_ci{
188762306a36Sopenharmony_ci	int r = 0;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	/* Make certain version is first member of dm_ioctl struct */
189062306a36Sopenharmony_ci	BUILD_BUG_ON(offsetof(struct dm_ioctl, version) != 0);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (copy_from_user(kernel_params->version, user->version, sizeof(kernel_params->version)))
189362306a36Sopenharmony_ci		return -EFAULT;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	if ((kernel_params->version[0] != DM_VERSION_MAJOR) ||
189662306a36Sopenharmony_ci	    (kernel_params->version[1] > DM_VERSION_MINOR)) {
189762306a36Sopenharmony_ci		DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
189862306a36Sopenharmony_ci		      DM_VERSION_MAJOR, DM_VERSION_MINOR,
189962306a36Sopenharmony_ci		      DM_VERSION_PATCHLEVEL,
190062306a36Sopenharmony_ci		      kernel_params->version[0],
190162306a36Sopenharmony_ci		      kernel_params->version[1],
190262306a36Sopenharmony_ci		      kernel_params->version[2],
190362306a36Sopenharmony_ci		      cmd);
190462306a36Sopenharmony_ci		r = -EINVAL;
190562306a36Sopenharmony_ci	}
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	/*
190862306a36Sopenharmony_ci	 * Fill in the kernel version.
190962306a36Sopenharmony_ci	 */
191062306a36Sopenharmony_ci	kernel_params->version[0] = DM_VERSION_MAJOR;
191162306a36Sopenharmony_ci	kernel_params->version[1] = DM_VERSION_MINOR;
191262306a36Sopenharmony_ci	kernel_params->version[2] = DM_VERSION_PATCHLEVEL;
191362306a36Sopenharmony_ci	if (copy_to_user(user->version, kernel_params->version, sizeof(kernel_params->version)))
191462306a36Sopenharmony_ci		return -EFAULT;
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	return r;
191762306a36Sopenharmony_ci}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci#define DM_PARAMS_MALLOC	0x0001	/* Params allocated with kvmalloc() */
192062306a36Sopenharmony_ci#define DM_WIPE_BUFFER		0x0010	/* Wipe input buffer before returning from ioctl */
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_cistatic void free_params(struct dm_ioctl *param, size_t param_size, int param_flags)
192362306a36Sopenharmony_ci{
192462306a36Sopenharmony_ci	if (param_flags & DM_WIPE_BUFFER)
192562306a36Sopenharmony_ci		memset(param, 0, param_size);
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci	if (param_flags & DM_PARAMS_MALLOC)
192862306a36Sopenharmony_ci		kvfree(param);
192962306a36Sopenharmony_ci}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_cistatic int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
193262306a36Sopenharmony_ci		       int ioctl_flags, struct dm_ioctl **param, int *param_flags)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	struct dm_ioctl *dmi;
193562306a36Sopenharmony_ci	int secure_data;
193662306a36Sopenharmony_ci	const size_t minimum_data_size = offsetof(struct dm_ioctl, data);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	/* check_version() already copied version from userspace, avoid TOCTOU */
193962306a36Sopenharmony_ci	if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version),
194062306a36Sopenharmony_ci			   (char __user *)user + sizeof(param_kernel->version),
194162306a36Sopenharmony_ci			   minimum_data_size - sizeof(param_kernel->version)))
194262306a36Sopenharmony_ci		return -EFAULT;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	if (unlikely(param_kernel->data_size < minimum_data_size) ||
194562306a36Sopenharmony_ci	    unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) {
194662306a36Sopenharmony_ci		DMERR("Invalid data size in the ioctl structure: %u",
194762306a36Sopenharmony_ci		      param_kernel->data_size);
194862306a36Sopenharmony_ci		return -EINVAL;
194962306a36Sopenharmony_ci	}
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	*param_flags = secure_data ? DM_WIPE_BUFFER : 0;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
195662306a36Sopenharmony_ci		dmi = param_kernel;
195762306a36Sopenharmony_ci		dmi->data_size = minimum_data_size;
195862306a36Sopenharmony_ci		goto data_copied;
195962306a36Sopenharmony_ci	}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	/*
196262306a36Sopenharmony_ci	 * Use __GFP_HIGH to avoid low memory issues when a device is
196362306a36Sopenharmony_ci	 * suspended and the ioctl is needed to resume it.
196462306a36Sopenharmony_ci	 * Use kmalloc() rather than vmalloc() when we can.
196562306a36Sopenharmony_ci	 */
196662306a36Sopenharmony_ci	dmi = NULL;
196762306a36Sopenharmony_ci	dmi = kvmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH);
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	if (!dmi) {
197062306a36Sopenharmony_ci		if (secure_data && clear_user(user, param_kernel->data_size))
197162306a36Sopenharmony_ci			return -EFAULT;
197262306a36Sopenharmony_ci		return -ENOMEM;
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	*param_flags |= DM_PARAMS_MALLOC;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	/* Copy from param_kernel (which was already copied from user) */
197862306a36Sopenharmony_ci	memcpy(dmi, param_kernel, minimum_data_size);
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size,
198162306a36Sopenharmony_ci			   param_kernel->data_size - minimum_data_size))
198262306a36Sopenharmony_ci		goto bad;
198362306a36Sopenharmony_cidata_copied:
198462306a36Sopenharmony_ci	/* Wipe the user buffer so we do not return it to userspace */
198562306a36Sopenharmony_ci	if (secure_data && clear_user(user, param_kernel->data_size))
198662306a36Sopenharmony_ci		goto bad;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	*param = dmi;
198962306a36Sopenharmony_ci	return 0;
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_cibad:
199262306a36Sopenharmony_ci	free_params(dmi, param_kernel->data_size, *param_flags);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	return -EFAULT;
199562306a36Sopenharmony_ci}
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_cistatic int validate_params(uint cmd, struct dm_ioctl *param)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	/* Always clear this flag */
200062306a36Sopenharmony_ci	param->flags &= ~DM_BUFFER_FULL_FLAG;
200162306a36Sopenharmony_ci	param->flags &= ~DM_UEVENT_GENERATED_FLAG;
200262306a36Sopenharmony_ci	param->flags &= ~DM_SECURE_DATA_FLAG;
200362306a36Sopenharmony_ci	param->flags &= ~DM_DATA_OUT_FLAG;
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/* Ignores parameters */
200662306a36Sopenharmony_ci	if (cmd == DM_REMOVE_ALL_CMD ||
200762306a36Sopenharmony_ci	    cmd == DM_LIST_DEVICES_CMD ||
200862306a36Sopenharmony_ci	    cmd == DM_LIST_VERSIONS_CMD)
200962306a36Sopenharmony_ci		return 0;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if (cmd == DM_DEV_CREATE_CMD) {
201262306a36Sopenharmony_ci		if (!*param->name) {
201362306a36Sopenharmony_ci			DMERR("name not supplied when creating device");
201462306a36Sopenharmony_ci			return -EINVAL;
201562306a36Sopenharmony_ci		}
201662306a36Sopenharmony_ci	} else if (*param->uuid && *param->name) {
201762306a36Sopenharmony_ci		DMERR("only supply one of name or uuid, cmd(%u)", cmd);
201862306a36Sopenharmony_ci		return -EINVAL;
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	/* Ensure strings are terminated */
202262306a36Sopenharmony_ci	param->name[DM_NAME_LEN - 1] = '\0';
202362306a36Sopenharmony_ci	param->uuid[DM_UUID_LEN - 1] = '\0';
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	return 0;
202662306a36Sopenharmony_ci}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_cistatic int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *user)
202962306a36Sopenharmony_ci{
203062306a36Sopenharmony_ci	int r = 0;
203162306a36Sopenharmony_ci	int ioctl_flags;
203262306a36Sopenharmony_ci	int param_flags;
203362306a36Sopenharmony_ci	unsigned int cmd;
203462306a36Sopenharmony_ci	struct dm_ioctl *param;
203562306a36Sopenharmony_ci	ioctl_fn fn = NULL;
203662306a36Sopenharmony_ci	size_t input_param_size;
203762306a36Sopenharmony_ci	struct dm_ioctl param_kernel;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	/* only root can play with this */
204062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
204162306a36Sopenharmony_ci		return -EACCES;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (_IOC_TYPE(command) != DM_IOCTL)
204462306a36Sopenharmony_ci		return -ENOTTY;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	cmd = _IOC_NR(command);
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	/*
204962306a36Sopenharmony_ci	 * Check the interface version passed in.  This also
205062306a36Sopenharmony_ci	 * writes out the kernel's interface version.
205162306a36Sopenharmony_ci	 */
205262306a36Sopenharmony_ci	r = check_version(cmd, user, &param_kernel);
205362306a36Sopenharmony_ci	if (r)
205462306a36Sopenharmony_ci		return r;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	/*
205762306a36Sopenharmony_ci	 * Nothing more to do for the version command.
205862306a36Sopenharmony_ci	 */
205962306a36Sopenharmony_ci	if (cmd == DM_VERSION_CMD)
206062306a36Sopenharmony_ci		return 0;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	fn = lookup_ioctl(cmd, &ioctl_flags);
206362306a36Sopenharmony_ci	if (!fn) {
206462306a36Sopenharmony_ci		DMERR("dm_ctl_ioctl: unknown command 0x%x", command);
206562306a36Sopenharmony_ci		return -ENOTTY;
206662306a36Sopenharmony_ci	}
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	/*
206962306a36Sopenharmony_ci	 * Copy the parameters into kernel space.
207062306a36Sopenharmony_ci	 */
207162306a36Sopenharmony_ci	r = copy_params(user, &param_kernel, ioctl_flags, &param, &param_flags);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	if (r)
207462306a36Sopenharmony_ci		return r;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	input_param_size = param->data_size;
207762306a36Sopenharmony_ci	r = validate_params(cmd, param);
207862306a36Sopenharmony_ci	if (r)
207962306a36Sopenharmony_ci		goto out;
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	param->data_size = offsetof(struct dm_ioctl, data);
208262306a36Sopenharmony_ci	r = fn(file, param, input_param_size);
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) &&
208562306a36Sopenharmony_ci	    unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
208662306a36Sopenharmony_ci		DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
208762306a36Sopenharmony_ci
208862306a36Sopenharmony_ci	if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT)
208962306a36Sopenharmony_ci		dm_issue_global_event();
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	/*
209262306a36Sopenharmony_ci	 * Copy the results back to userland.
209362306a36Sopenharmony_ci	 */
209462306a36Sopenharmony_ci	if (!r && copy_to_user(user, param, param->data_size))
209562306a36Sopenharmony_ci		r = -EFAULT;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ciout:
209862306a36Sopenharmony_ci	free_params(param, input_param_size, param_flags);
209962306a36Sopenharmony_ci	return r;
210062306a36Sopenharmony_ci}
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_cistatic long dm_ctl_ioctl(struct file *file, uint command, ulong u)
210362306a36Sopenharmony_ci{
210462306a36Sopenharmony_ci	return (long)ctl_ioctl(file, command, (struct dm_ioctl __user *)u);
210562306a36Sopenharmony_ci}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
210862306a36Sopenharmony_cistatic long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci#else
211362306a36Sopenharmony_ci#define dm_compat_ctl_ioctl NULL
211462306a36Sopenharmony_ci#endif
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_cistatic int dm_open(struct inode *inode, struct file *filp)
211762306a36Sopenharmony_ci{
211862306a36Sopenharmony_ci	int r;
211962306a36Sopenharmony_ci	struct dm_file *priv;
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	r = nonseekable_open(inode, filp);
212262306a36Sopenharmony_ci	if (unlikely(r))
212362306a36Sopenharmony_ci		return r;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	priv = filp->private_data = kmalloc(sizeof(struct dm_file), GFP_KERNEL);
212662306a36Sopenharmony_ci	if (!priv)
212762306a36Sopenharmony_ci		return -ENOMEM;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	priv->global_event_nr = atomic_read(&dm_global_event_nr);
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	return 0;
213262306a36Sopenharmony_ci}
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_cistatic int dm_release(struct inode *inode, struct file *filp)
213562306a36Sopenharmony_ci{
213662306a36Sopenharmony_ci	kfree(filp->private_data);
213762306a36Sopenharmony_ci	return 0;
213862306a36Sopenharmony_ci}
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_cistatic __poll_t dm_poll(struct file *filp, poll_table *wait)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	struct dm_file *priv = filp->private_data;
214362306a36Sopenharmony_ci	__poll_t mask = 0;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	poll_wait(filp, &dm_global_eventq, wait);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	if ((int)(atomic_read(&dm_global_event_nr) - priv->global_event_nr) > 0)
214862306a36Sopenharmony_ci		mask |= EPOLLIN;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	return mask;
215162306a36Sopenharmony_ci}
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_cistatic const struct file_operations _ctl_fops = {
215462306a36Sopenharmony_ci	.open    = dm_open,
215562306a36Sopenharmony_ci	.release = dm_release,
215662306a36Sopenharmony_ci	.poll    = dm_poll,
215762306a36Sopenharmony_ci	.unlocked_ioctl	 = dm_ctl_ioctl,
215862306a36Sopenharmony_ci	.compat_ioctl = dm_compat_ctl_ioctl,
215962306a36Sopenharmony_ci	.owner	 = THIS_MODULE,
216062306a36Sopenharmony_ci	.llseek  = noop_llseek,
216162306a36Sopenharmony_ci};
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_cistatic struct miscdevice _dm_misc = {
216462306a36Sopenharmony_ci	.minor		= MAPPER_CTRL_MINOR,
216562306a36Sopenharmony_ci	.name		= DM_NAME,
216662306a36Sopenharmony_ci	.nodename	= DM_DIR "/" DM_CONTROL_NODE,
216762306a36Sopenharmony_ci	.fops		= &_ctl_fops
216862306a36Sopenharmony_ci};
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ciMODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR);
217162306a36Sopenharmony_ciMODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci/*
217462306a36Sopenharmony_ci * Create misc character device and link to DM_DIR/control.
217562306a36Sopenharmony_ci */
217662306a36Sopenharmony_ciint __init dm_interface_init(void)
217762306a36Sopenharmony_ci{
217862306a36Sopenharmony_ci	int r;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	r = misc_register(&_dm_misc);
218162306a36Sopenharmony_ci	if (r) {
218262306a36Sopenharmony_ci		DMERR("misc_register failed for control device");
218362306a36Sopenharmony_ci		return r;
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
218762306a36Sopenharmony_ci	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
218862306a36Sopenharmony_ci	       DM_DRIVER_EMAIL);
218962306a36Sopenharmony_ci	return 0;
219062306a36Sopenharmony_ci}
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_civoid dm_interface_exit(void)
219362306a36Sopenharmony_ci{
219462306a36Sopenharmony_ci	misc_deregister(&_dm_misc);
219562306a36Sopenharmony_ci	dm_hash_exit();
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci/**
219962306a36Sopenharmony_ci * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
220062306a36Sopenharmony_ci * @md: Pointer to mapped_device
220162306a36Sopenharmony_ci * @name: Buffer (size DM_NAME_LEN) for name
220262306a36Sopenharmony_ci * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined
220362306a36Sopenharmony_ci */
220462306a36Sopenharmony_ciint dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
220562306a36Sopenharmony_ci{
220662306a36Sopenharmony_ci	int r = 0;
220762306a36Sopenharmony_ci	struct hash_cell *hc;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	if (!md)
221062306a36Sopenharmony_ci		return -ENXIO;
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	mutex_lock(&dm_hash_cells_mutex);
221362306a36Sopenharmony_ci	hc = dm_get_mdptr(md);
221462306a36Sopenharmony_ci	if (!hc) {
221562306a36Sopenharmony_ci		r = -ENXIO;
221662306a36Sopenharmony_ci		goto out;
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	if (name)
222062306a36Sopenharmony_ci		strcpy(name, hc->name);
222162306a36Sopenharmony_ci	if (uuid)
222262306a36Sopenharmony_ci		strcpy(uuid, hc->uuid ? : "");
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ciout:
222562306a36Sopenharmony_ci	mutex_unlock(&dm_hash_cells_mutex);
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return r;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dm_copy_name_and_uuid);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci/**
223262306a36Sopenharmony_ci * dm_early_create - create a mapped device in early boot.
223362306a36Sopenharmony_ci *
223462306a36Sopenharmony_ci * @dmi: Contains main information of the device mapping to be created.
223562306a36Sopenharmony_ci * @spec_array: array of pointers to struct dm_target_spec. Describes the
223662306a36Sopenharmony_ci * mapping table of the device.
223762306a36Sopenharmony_ci * @target_params_array: array of strings with the parameters to a specific
223862306a36Sopenharmony_ci * target.
223962306a36Sopenharmony_ci *
224062306a36Sopenharmony_ci * Instead of having the struct dm_target_spec and the parameters for every
224162306a36Sopenharmony_ci * target embedded at the end of struct dm_ioctl (as performed in a normal
224262306a36Sopenharmony_ci * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
224362306a36Sopenharmony_ci * The size of the spec_array and target_params_array is given by
224462306a36Sopenharmony_ci * @dmi->target_count.
224562306a36Sopenharmony_ci * This function is supposed to be called in early boot, so locking mechanisms
224662306a36Sopenharmony_ci * to protect against concurrent loads are not required.
224762306a36Sopenharmony_ci */
224862306a36Sopenharmony_ciint __init dm_early_create(struct dm_ioctl *dmi,
224962306a36Sopenharmony_ci			   struct dm_target_spec **spec_array,
225062306a36Sopenharmony_ci			   char **target_params_array)
225162306a36Sopenharmony_ci{
225262306a36Sopenharmony_ci	int r, m = DM_ANY_MINOR;
225362306a36Sopenharmony_ci	struct dm_table *t, *old_map;
225462306a36Sopenharmony_ci	struct mapped_device *md;
225562306a36Sopenharmony_ci	unsigned int i;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	if (!dmi->target_count)
225862306a36Sopenharmony_ci		return -EINVAL;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	r = check_name(dmi->name);
226162306a36Sopenharmony_ci	if (r)
226262306a36Sopenharmony_ci		return r;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
226562306a36Sopenharmony_ci		m = MINOR(huge_decode_dev(dmi->dev));
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_ci	/* alloc dm device */
226862306a36Sopenharmony_ci	r = dm_create(m, &md);
226962306a36Sopenharmony_ci	if (r)
227062306a36Sopenharmony_ci		return r;
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	/* hash insert */
227362306a36Sopenharmony_ci	r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
227462306a36Sopenharmony_ci	if (r)
227562306a36Sopenharmony_ci		goto err_destroy_dm;
227662306a36Sopenharmony_ci
227762306a36Sopenharmony_ci	/* alloc table */
227862306a36Sopenharmony_ci	r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
227962306a36Sopenharmony_ci	if (r)
228062306a36Sopenharmony_ci		goto err_hash_remove;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	/* add targets */
228362306a36Sopenharmony_ci	for (i = 0; i < dmi->target_count; i++) {
228462306a36Sopenharmony_ci		r = dm_table_add_target(t, spec_array[i]->target_type,
228562306a36Sopenharmony_ci					(sector_t) spec_array[i]->sector_start,
228662306a36Sopenharmony_ci					(sector_t) spec_array[i]->length,
228762306a36Sopenharmony_ci					target_params_array[i]);
228862306a36Sopenharmony_ci		if (r) {
228962306a36Sopenharmony_ci			DMERR("error adding target to table");
229062306a36Sopenharmony_ci			goto err_destroy_table;
229162306a36Sopenharmony_ci		}
229262306a36Sopenharmony_ci	}
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	/* finish table */
229562306a36Sopenharmony_ci	r = dm_table_complete(t);
229662306a36Sopenharmony_ci	if (r)
229762306a36Sopenharmony_ci		goto err_destroy_table;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	/* setup md->queue to reflect md's type (may block) */
230062306a36Sopenharmony_ci	r = dm_setup_md_queue(md, t);
230162306a36Sopenharmony_ci	if (r) {
230262306a36Sopenharmony_ci		DMERR("unable to set up device queue for new table.");
230362306a36Sopenharmony_ci		goto err_destroy_table;
230462306a36Sopenharmony_ci	}
230562306a36Sopenharmony_ci
230662306a36Sopenharmony_ci	/* Set new map */
230762306a36Sopenharmony_ci	dm_suspend(md, 0);
230862306a36Sopenharmony_ci	old_map = dm_swap_table(md, t);
230962306a36Sopenharmony_ci	if (IS_ERR(old_map)) {
231062306a36Sopenharmony_ci		r = PTR_ERR(old_map);
231162306a36Sopenharmony_ci		goto err_destroy_table;
231262306a36Sopenharmony_ci	}
231362306a36Sopenharmony_ci	set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci	/* resume device */
231662306a36Sopenharmony_ci	r = dm_resume(md);
231762306a36Sopenharmony_ci	if (r)
231862306a36Sopenharmony_ci		goto err_destroy_table;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_ci	DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
232162306a36Sopenharmony_ci	dm_put(md);
232262306a36Sopenharmony_ci	return 0;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_cierr_destroy_table:
232562306a36Sopenharmony_ci	dm_table_destroy(t);
232662306a36Sopenharmony_cierr_hash_remove:
232762306a36Sopenharmony_ci	down_write(&_hash_lock);
232862306a36Sopenharmony_ci	(void) __hash_remove(__get_name_cell(dmi->name));
232962306a36Sopenharmony_ci	up_write(&_hash_lock);
233062306a36Sopenharmony_ci	/* release reference from __get_name_cell */
233162306a36Sopenharmony_ci	dm_put(md);
233262306a36Sopenharmony_cierr_destroy_dm:
233362306a36Sopenharmony_ci	dm_put(md);
233462306a36Sopenharmony_ci	dm_destroy(md);
233562306a36Sopenharmony_ci	return r;
233662306a36Sopenharmony_ci}
2337