xref: /openbmc/linux/drivers/md/dm-ioctl.c (revision 438d1949)
13bd94003SHeinz Mauelshagen // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
42b06cfffSAlasdair G Kergon  * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This file is released under the GPL.
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
94cc96131SMike Snitzer #include "dm-core.h"
1091ccbbacSTushar Sugandhi #include "dm-ima.h"
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/vmalloc.h>
131da177e4SLinus Torvalds #include <linux/miscdevice.h>
145b3cc15aSIngo Molnar #include <linux/sched/mm.h>
151da177e4SLinus Torvalds #include <linux/init.h>
161da177e4SLinus Torvalds #include <linux/wait.h>
171da177e4SLinus Torvalds #include <linux/slab.h>
18b82096afSMikulas Patocka #include <linux/rbtree.h>
191da177e4SLinus Torvalds #include <linux/dm-ioctl.h>
203ac51e74SDarrick J. Wong #include <linux/hdreg.h>
2176c072b4SMilan Broz #include <linux/compat.h>
22cd9c88daSJordy Zomer #include <linux/nospec.h>
231da177e4SLinus Torvalds 
247c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
2591ccbbacSTushar Sugandhi #include <linux/ima.h>
261da177e4SLinus Torvalds 
2772d94861SAlasdair G Kergon #define DM_MSG_PREFIX "ioctl"
281da177e4SLinus Torvalds #define DM_DRIVER_EMAIL "dm-devel@redhat.com"
291da177e4SLinus Torvalds 
3093e6442cSMikulas Patocka struct dm_file {
3193e6442cSMikulas Patocka 	/*
3293e6442cSMikulas Patocka 	 * poll will wait until the global event number is greater than
3393e6442cSMikulas Patocka 	 * this value.
3493e6442cSMikulas Patocka 	 */
3586a3238cSHeinz Mauelshagen 	volatile unsigned int global_event_nr;
3693e6442cSMikulas Patocka };
3793e6442cSMikulas Patocka 
38a4a82ce3SHeinz Mauelshagen /*
39a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
401da177e4SLinus Torvalds  * The ioctl interface needs to be able to look up devices by
411da177e4SLinus Torvalds  * name or uuid.
42a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
43a4a82ce3SHeinz Mauelshagen  */
441da177e4SLinus Torvalds struct hash_cell {
45b82096afSMikulas Patocka 	struct rb_node name_node;
46b82096afSMikulas Patocka 	struct rb_node uuid_node;
47b82096afSMikulas Patocka 	bool name_set;
48b82096afSMikulas Patocka 	bool uuid_set;
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds 	char *name;
511da177e4SLinus Torvalds 	char *uuid;
521da177e4SLinus Torvalds 	struct mapped_device *md;
531da177e4SLinus Torvalds 	struct dm_table *new_map;
541da177e4SLinus Torvalds };
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds struct vers_iter {
571da177e4SLinus Torvalds 	size_t param_size;
581da177e4SLinus Torvalds 	struct dm_target_versions *vers, *old_vers;
591da177e4SLinus Torvalds 	char *end;
601da177e4SLinus Torvalds 	uint32_t flags;
611da177e4SLinus Torvalds };
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 
64b82096afSMikulas Patocka static struct rb_root name_rb_tree = RB_ROOT;
65b82096afSMikulas Patocka static struct rb_root uuid_rb_tree = RB_ROOT;
661da177e4SLinus Torvalds 
672c140a24SMikulas Patocka static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred);
681da177e4SLinus Torvalds 
691da177e4SLinus Torvalds /*
701da177e4SLinus Torvalds  * Guards access to both hash tables.
711da177e4SLinus Torvalds  */
721da177e4SLinus Torvalds static DECLARE_RWSEM(_hash_lock);
731da177e4SLinus Torvalds 
746076905bSMikulas Patocka /*
756076905bSMikulas Patocka  * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
766076905bSMikulas Patocka  */
776076905bSMikulas Patocka static DEFINE_MUTEX(dm_hash_cells_mutex);
786076905bSMikulas Patocka 
dm_hash_exit(void)791da177e4SLinus Torvalds static void dm_hash_exit(void)
801da177e4SLinus Torvalds {
812c140a24SMikulas Patocka 	dm_hash_remove_all(false, false, false);
821da177e4SLinus Torvalds }
831da177e4SLinus Torvalds 
84a4a82ce3SHeinz Mauelshagen /*
85a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
861da177e4SLinus Torvalds  * Code for looking up a device by name
87a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
88a4a82ce3SHeinz Mauelshagen  */
__get_name_cell(const char * str)891da177e4SLinus Torvalds static struct hash_cell *__get_name_cell(const char *str)
901da177e4SLinus Torvalds {
91b82096afSMikulas Patocka 	struct rb_node *n = name_rb_tree.rb_node;
921da177e4SLinus Torvalds 
93b82096afSMikulas Patocka 	while (n) {
94b82096afSMikulas Patocka 		struct hash_cell *hc = container_of(n, struct hash_cell, name_node);
950ef0b471SHeinz Mauelshagen 		int c;
960ef0b471SHeinz Mauelshagen 
970ef0b471SHeinz Mauelshagen 		c = strcmp(hc->name, str);
98b82096afSMikulas Patocka 		if (!c) {
997ec75f25SJeff Mahoney 			dm_get(hc->md);
1001da177e4SLinus Torvalds 			return hc;
1017ec75f25SJeff Mahoney 		}
102b82096afSMikulas Patocka 		n = c >= 0 ? n->rb_left : n->rb_right;
103b82096afSMikulas Patocka 	}
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds 	return NULL;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
__get_uuid_cell(const char * str)1081da177e4SLinus Torvalds static struct hash_cell *__get_uuid_cell(const char *str)
1091da177e4SLinus Torvalds {
110b82096afSMikulas Patocka 	struct rb_node *n = uuid_rb_tree.rb_node;
1111da177e4SLinus Torvalds 
112b82096afSMikulas Patocka 	while (n) {
113b82096afSMikulas Patocka 		struct hash_cell *hc = container_of(n, struct hash_cell, uuid_node);
1140ef0b471SHeinz Mauelshagen 		int c;
1150ef0b471SHeinz Mauelshagen 
1160ef0b471SHeinz Mauelshagen 		c = strcmp(hc->uuid, str);
117b82096afSMikulas Patocka 		if (!c) {
1187ec75f25SJeff Mahoney 			dm_get(hc->md);
1191da177e4SLinus Torvalds 			return hc;
1207ec75f25SJeff Mahoney 		}
121b82096afSMikulas Patocka 		n = c >= 0 ? n->rb_left : n->rb_right;
122b82096afSMikulas Patocka 	}
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds 	return NULL;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
__unlink_name(struct hash_cell * hc)127b82096afSMikulas Patocka static void __unlink_name(struct hash_cell *hc)
128b82096afSMikulas Patocka {
129b82096afSMikulas Patocka 	if (hc->name_set) {
130b82096afSMikulas Patocka 		hc->name_set = false;
131b82096afSMikulas Patocka 		rb_erase(&hc->name_node, &name_rb_tree);
132b82096afSMikulas Patocka 	}
133b82096afSMikulas Patocka }
134b82096afSMikulas Patocka 
__unlink_uuid(struct hash_cell * hc)135b82096afSMikulas Patocka static void __unlink_uuid(struct hash_cell *hc)
136b82096afSMikulas Patocka {
137b82096afSMikulas Patocka 	if (hc->uuid_set) {
138b82096afSMikulas Patocka 		hc->uuid_set = false;
139b82096afSMikulas Patocka 		rb_erase(&hc->uuid_node, &uuid_rb_tree);
140b82096afSMikulas Patocka 	}
141b82096afSMikulas Patocka }
142b82096afSMikulas Patocka 
__link_name(struct hash_cell * new_hc)143b82096afSMikulas Patocka static void __link_name(struct hash_cell *new_hc)
144b82096afSMikulas Patocka {
145b82096afSMikulas Patocka 	struct rb_node **n, *parent;
146b82096afSMikulas Patocka 
147b82096afSMikulas Patocka 	__unlink_name(new_hc);
148b82096afSMikulas Patocka 
149b82096afSMikulas Patocka 	new_hc->name_set = true;
150b82096afSMikulas Patocka 
151b82096afSMikulas Patocka 	n = &name_rb_tree.rb_node;
152b82096afSMikulas Patocka 	parent = NULL;
153b82096afSMikulas Patocka 
154b82096afSMikulas Patocka 	while (*n) {
155b82096afSMikulas Patocka 		struct hash_cell *hc = container_of(*n, struct hash_cell, name_node);
1560ef0b471SHeinz Mauelshagen 		int c;
1570ef0b471SHeinz Mauelshagen 
1580ef0b471SHeinz Mauelshagen 		c = strcmp(hc->name, new_hc->name);
159b82096afSMikulas Patocka 		BUG_ON(!c);
160b82096afSMikulas Patocka 		parent = *n;
161b82096afSMikulas Patocka 		n = c >= 0 ? &hc->name_node.rb_left : &hc->name_node.rb_right;
162b82096afSMikulas Patocka 	}
163b82096afSMikulas Patocka 
164b82096afSMikulas Patocka 	rb_link_node(&new_hc->name_node, parent, n);
165b82096afSMikulas Patocka 	rb_insert_color(&new_hc->name_node, &name_rb_tree);
166b82096afSMikulas Patocka }
167b82096afSMikulas Patocka 
__link_uuid(struct hash_cell * new_hc)168b82096afSMikulas Patocka static void __link_uuid(struct hash_cell *new_hc)
169b82096afSMikulas Patocka {
170b82096afSMikulas Patocka 	struct rb_node **n, *parent;
171b82096afSMikulas Patocka 
172b82096afSMikulas Patocka 	__unlink_uuid(new_hc);
173b82096afSMikulas Patocka 
174b82096afSMikulas Patocka 	new_hc->uuid_set = true;
175b82096afSMikulas Patocka 
176b82096afSMikulas Patocka 	n = &uuid_rb_tree.rb_node;
177b82096afSMikulas Patocka 	parent = NULL;
178b82096afSMikulas Patocka 
179b82096afSMikulas Patocka 	while (*n) {
180b82096afSMikulas Patocka 		struct hash_cell *hc = container_of(*n, struct hash_cell, uuid_node);
1810ef0b471SHeinz Mauelshagen 		int c;
1820ef0b471SHeinz Mauelshagen 
1830ef0b471SHeinz Mauelshagen 		c = strcmp(hc->uuid, new_hc->uuid);
184b82096afSMikulas Patocka 		BUG_ON(!c);
185b82096afSMikulas Patocka 		parent = *n;
186b82096afSMikulas Patocka 		n = c > 0 ? &hc->uuid_node.rb_left : &hc->uuid_node.rb_right;
187b82096afSMikulas Patocka 	}
188b82096afSMikulas Patocka 
189b82096afSMikulas Patocka 	rb_link_node(&new_hc->uuid_node, parent, n);
190b82096afSMikulas Patocka 	rb_insert_color(&new_hc->uuid_node, &uuid_rb_tree);
191b82096afSMikulas Patocka }
192b82096afSMikulas Patocka 
__get_dev_cell(uint64_t dev)193ba2e19b0SMikulas Patocka static struct hash_cell *__get_dev_cell(uint64_t dev)
194ba2e19b0SMikulas Patocka {
195ba2e19b0SMikulas Patocka 	struct mapped_device *md;
196ba2e19b0SMikulas Patocka 	struct hash_cell *hc;
197ba2e19b0SMikulas Patocka 
198ba2e19b0SMikulas Patocka 	md = dm_get_md(huge_decode_dev(dev));
199ba2e19b0SMikulas Patocka 	if (!md)
200ba2e19b0SMikulas Patocka 		return NULL;
201ba2e19b0SMikulas Patocka 
202ba2e19b0SMikulas Patocka 	hc = dm_get_mdptr(md);
203ba2e19b0SMikulas Patocka 	if (!hc) {
204ba2e19b0SMikulas Patocka 		dm_put(md);
205ba2e19b0SMikulas Patocka 		return NULL;
206ba2e19b0SMikulas Patocka 	}
207ba2e19b0SMikulas Patocka 
208ba2e19b0SMikulas Patocka 	return hc;
209ba2e19b0SMikulas Patocka }
210ba2e19b0SMikulas Patocka 
211a4a82ce3SHeinz Mauelshagen /*
212a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
2131da177e4SLinus Torvalds  * Inserting, removing and renaming a device.
214a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
215a4a82ce3SHeinz Mauelshagen  */
alloc_cell(const char * name,const char * uuid,struct mapped_device * md)2161da177e4SLinus Torvalds static struct hash_cell *alloc_cell(const char *name, const char *uuid,
2171da177e4SLinus Torvalds 				    struct mapped_device *md)
2181da177e4SLinus Torvalds {
2191da177e4SLinus Torvalds 	struct hash_cell *hc;
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds 	hc = kmalloc(sizeof(*hc), GFP_KERNEL);
2221da177e4SLinus Torvalds 	if (!hc)
2231da177e4SLinus Torvalds 		return NULL;
2241da177e4SLinus Torvalds 
225543537bdSPaulo Marques 	hc->name = kstrdup(name, GFP_KERNEL);
2261da177e4SLinus Torvalds 	if (!hc->name) {
2271da177e4SLinus Torvalds 		kfree(hc);
2281da177e4SLinus Torvalds 		return NULL;
2291da177e4SLinus Torvalds 	}
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds 	if (!uuid)
2321da177e4SLinus Torvalds 		hc->uuid = NULL;
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds 	else {
235543537bdSPaulo Marques 		hc->uuid = kstrdup(uuid, GFP_KERNEL);
2361da177e4SLinus Torvalds 		if (!hc->uuid) {
2371da177e4SLinus Torvalds 			kfree(hc->name);
2381da177e4SLinus Torvalds 			kfree(hc);
2391da177e4SLinus Torvalds 			return NULL;
2401da177e4SLinus Torvalds 		}
2411da177e4SLinus Torvalds 	}
2421da177e4SLinus Torvalds 
243b82096afSMikulas Patocka 	hc->name_set = hc->uuid_set = false;
2441da177e4SLinus Torvalds 	hc->md = md;
2451da177e4SLinus Torvalds 	hc->new_map = NULL;
2461da177e4SLinus Torvalds 	return hc;
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds 
free_cell(struct hash_cell * hc)2491da177e4SLinus Torvalds static void free_cell(struct hash_cell *hc)
2501da177e4SLinus Torvalds {
2511da177e4SLinus Torvalds 	if (hc) {
2521da177e4SLinus Torvalds 		kfree(hc->name);
2531da177e4SLinus Torvalds 		kfree(hc->uuid);
2541da177e4SLinus Torvalds 		kfree(hc);
2551da177e4SLinus Torvalds 	}
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds /*
2591da177e4SLinus Torvalds  * The kdev_t and uuid of a device can never change once it is
2601da177e4SLinus Torvalds  * initially inserted.
2611da177e4SLinus Torvalds  */
dm_hash_insert(const char * name,const char * uuid,struct mapped_device * md)2621da177e4SLinus Torvalds static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
2631da177e4SLinus Torvalds {
2647ec75f25SJeff Mahoney 	struct hash_cell *cell, *hc;
2651da177e4SLinus Torvalds 
2661da177e4SLinus Torvalds 	/*
2671da177e4SLinus Torvalds 	 * Allocate the new cells.
2681da177e4SLinus Torvalds 	 */
2691da177e4SLinus Torvalds 	cell = alloc_cell(name, uuid, md);
2701da177e4SLinus Torvalds 	if (!cell)
2711da177e4SLinus Torvalds 		return -ENOMEM;
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	/*
2741da177e4SLinus Torvalds 	 * Insert the cell into both hash tables.
2751da177e4SLinus Torvalds 	 */
2761da177e4SLinus Torvalds 	down_write(&_hash_lock);
2777ec75f25SJeff Mahoney 	hc = __get_name_cell(name);
2787ec75f25SJeff Mahoney 	if (hc) {
2797ec75f25SJeff Mahoney 		dm_put(hc->md);
2801da177e4SLinus Torvalds 		goto bad;
2817ec75f25SJeff Mahoney 	}
2821da177e4SLinus Torvalds 
283b82096afSMikulas Patocka 	__link_name(cell);
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	if (uuid) {
2867ec75f25SJeff Mahoney 		hc = __get_uuid_cell(uuid);
2877ec75f25SJeff Mahoney 		if (hc) {
288b82096afSMikulas Patocka 			__unlink_name(cell);
2897ec75f25SJeff Mahoney 			dm_put(hc->md);
2901da177e4SLinus Torvalds 			goto bad;
2911da177e4SLinus Torvalds 		}
292b82096afSMikulas Patocka 		__link_uuid(cell);
2931da177e4SLinus Torvalds 	}
2941da177e4SLinus Torvalds 	dm_get(md);
2956076905bSMikulas Patocka 	mutex_lock(&dm_hash_cells_mutex);
2961da177e4SLinus Torvalds 	dm_set_mdptr(md, cell);
2976076905bSMikulas Patocka 	mutex_unlock(&dm_hash_cells_mutex);
2981da177e4SLinus Torvalds 	up_write(&_hash_lock);
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	return 0;
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds  bad:
3031da177e4SLinus Torvalds 	up_write(&_hash_lock);
3041da177e4SLinus Torvalds 	free_cell(cell);
3051da177e4SLinus Torvalds 	return -EBUSY;
3061da177e4SLinus Torvalds }
3071da177e4SLinus Torvalds 
__hash_remove(struct hash_cell * hc)30883d5e5b0SMikulas Patocka static struct dm_table *__hash_remove(struct hash_cell *hc)
3091da177e4SLinus Torvalds {
310269fd2a6Sgoggin, edward 	struct dm_table *table;
31183d5e5b0SMikulas Patocka 	int srcu_idx;
312269fd2a6Sgoggin, edward 
31369868bebSMike Snitzer 	lockdep_assert_held(&_hash_lock);
31469868bebSMike Snitzer 
315b82096afSMikulas Patocka 	/* remove from the dev trees */
316b82096afSMikulas Patocka 	__unlink_name(hc);
317b82096afSMikulas Patocka 	__unlink_uuid(hc);
3186076905bSMikulas Patocka 	mutex_lock(&dm_hash_cells_mutex);
3191da177e4SLinus Torvalds 	dm_set_mdptr(hc->md, NULL);
3206076905bSMikulas Patocka 	mutex_unlock(&dm_hash_cells_mutex);
321269fd2a6Sgoggin, edward 
32283d5e5b0SMikulas Patocka 	table = dm_get_live_table(hc->md, &srcu_idx);
32383d5e5b0SMikulas Patocka 	if (table)
324269fd2a6Sgoggin, edward 		dm_table_event(table);
32583d5e5b0SMikulas Patocka 	dm_put_live_table(hc->md, srcu_idx);
326269fd2a6Sgoggin, edward 
32783d5e5b0SMikulas Patocka 	table = NULL;
3281da177e4SLinus Torvalds 	if (hc->new_map)
32983d5e5b0SMikulas Patocka 		table = hc->new_map;
3301134e5aeSMike Anderson 	dm_put(hc->md);
3311da177e4SLinus Torvalds 	free_cell(hc);
33283d5e5b0SMikulas Patocka 
33383d5e5b0SMikulas Patocka 	return table;
3341da177e4SLinus Torvalds }
3351da177e4SLinus Torvalds 
dm_hash_remove_all(bool keep_open_devices,bool mark_deferred,bool only_deferred)3362c140a24SMikulas Patocka static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred)
3371da177e4SLinus Torvalds {
338b82096afSMikulas Patocka 	int dev_skipped;
339b82096afSMikulas Patocka 	struct rb_node *n;
3401da177e4SLinus Torvalds 	struct hash_cell *hc;
34198f33285SKiyoshi Ueda 	struct mapped_device *md;
34283d5e5b0SMikulas Patocka 	struct dm_table *t;
34398f33285SKiyoshi Ueda 
34498f33285SKiyoshi Ueda retry:
34598f33285SKiyoshi Ueda 	dev_skipped = 0;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	down_write(&_hash_lock);
3485c6bd75dSAlasdair G Kergon 
349b82096afSMikulas Patocka 	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
350b82096afSMikulas Patocka 		hc = container_of(n, struct hash_cell, name_node);
35198f33285SKiyoshi Ueda 		md = hc->md;
35298f33285SKiyoshi Ueda 		dm_get(md);
3535c6bd75dSAlasdair G Kergon 
3542c140a24SMikulas Patocka 		if (keep_open_devices &&
3552c140a24SMikulas Patocka 		    dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
35698f33285SKiyoshi Ueda 			dm_put(md);
3575c6bd75dSAlasdair G Kergon 			dev_skipped++;
3585c6bd75dSAlasdair G Kergon 			continue;
3595c6bd75dSAlasdair G Kergon 		}
36098f33285SKiyoshi Ueda 
36183d5e5b0SMikulas Patocka 		t = __hash_remove(hc);
36298f33285SKiyoshi Ueda 
36398f33285SKiyoshi Ueda 		up_write(&_hash_lock);
36498f33285SKiyoshi Ueda 
36583d5e5b0SMikulas Patocka 		if (t) {
36683d5e5b0SMikulas Patocka 			dm_sync_table(md);
36783d5e5b0SMikulas Patocka 			dm_table_destroy(t);
36883d5e5b0SMikulas Patocka 		}
36984010e51STushar Sugandhi 		dm_ima_measure_on_device_remove(md, true);
37098f33285SKiyoshi Ueda 		dm_put(md);
3713f77316dSKiyoshi Ueda 		if (likely(keep_open_devices))
3723f77316dSKiyoshi Ueda 			dm_destroy(md);
3733f77316dSKiyoshi Ueda 		else
3743f77316dSKiyoshi Ueda 			dm_destroy_immediate(md);
3755c6bd75dSAlasdair G Kergon 
3765c6bd75dSAlasdair G Kergon 		/*
37798f33285SKiyoshi Ueda 		 * Some mapped devices may be using other mapped
37898f33285SKiyoshi Ueda 		 * devices, so repeat until we make no further
37998f33285SKiyoshi Ueda 		 * progress.  If a new mapped device is created
38098f33285SKiyoshi Ueda 		 * here it will also get removed.
3815c6bd75dSAlasdair G Kergon 		 */
3825c6bd75dSAlasdair G Kergon 		goto retry;
38398f33285SKiyoshi Ueda 	}
3845c6bd75dSAlasdair G Kergon 
3851da177e4SLinus Torvalds 	up_write(&_hash_lock);
38698f33285SKiyoshi Ueda 
38798f33285SKiyoshi Ueda 	if (dev_skipped)
38898f33285SKiyoshi Ueda 		DMWARN("remove_all left %d open device(s)", dev_skipped);
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds 
39184c89557SPeter Jones /*
39284c89557SPeter Jones  * Set the uuid of a hash_cell that isn't already set.
39384c89557SPeter Jones  */
__set_cell_uuid(struct hash_cell * hc,char * new_uuid)39484c89557SPeter Jones static void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
39584c89557SPeter Jones {
39684c89557SPeter Jones 	mutex_lock(&dm_hash_cells_mutex);
39784c89557SPeter Jones 	hc->uuid = new_uuid;
39884c89557SPeter Jones 	mutex_unlock(&dm_hash_cells_mutex);
39984c89557SPeter Jones 
400b82096afSMikulas Patocka 	__link_uuid(hc);
40184c89557SPeter Jones }
40284c89557SPeter Jones 
40384c89557SPeter Jones /*
40484c89557SPeter Jones  * Changes the name of a hash_cell and returns the old name for
40584c89557SPeter Jones  * the caller to free.
40684c89557SPeter Jones  */
__change_cell_name(struct hash_cell * hc,char * new_name)40784c89557SPeter Jones static char *__change_cell_name(struct hash_cell *hc, char *new_name)
40884c89557SPeter Jones {
40984c89557SPeter Jones 	char *old_name;
41084c89557SPeter Jones 
41184c89557SPeter Jones 	/*
41284c89557SPeter Jones 	 * Rename and move the name cell.
41384c89557SPeter Jones 	 */
414b82096afSMikulas Patocka 	__unlink_name(hc);
41584c89557SPeter Jones 	old_name = hc->name;
41684c89557SPeter Jones 
41784c89557SPeter Jones 	mutex_lock(&dm_hash_cells_mutex);
41884c89557SPeter Jones 	hc->name = new_name;
41984c89557SPeter Jones 	mutex_unlock(&dm_hash_cells_mutex);
42084c89557SPeter Jones 
421b82096afSMikulas Patocka 	__link_name(hc);
42284c89557SPeter Jones 
42384c89557SPeter Jones 	return old_name;
42484c89557SPeter Jones }
42584c89557SPeter Jones 
dm_hash_rename(struct dm_ioctl * param,const char * new)426856a6f1dSPeter Rajnoha static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
4273abf85b5SPeter Rajnoha 					    const char *new)
4281da177e4SLinus Torvalds {
42984c89557SPeter Jones 	char *new_data, *old_name = NULL;
4301da177e4SLinus Torvalds 	struct hash_cell *hc;
43181f1777aSgoggin, edward 	struct dm_table *table;
432856a6f1dSPeter Rajnoha 	struct mapped_device *md;
43386a3238cSHeinz Mauelshagen 	unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
43483d5e5b0SMikulas Patocka 	int srcu_idx;
4351da177e4SLinus Torvalds 
4361da177e4SLinus Torvalds 	/*
4371da177e4SLinus Torvalds 	 * duplicate new.
4381da177e4SLinus Torvalds 	 */
43984c89557SPeter Jones 	new_data = kstrdup(new, GFP_KERNEL);
44084c89557SPeter Jones 	if (!new_data)
441856a6f1dSPeter Rajnoha 		return ERR_PTR(-ENOMEM);
4421da177e4SLinus Torvalds 
4431da177e4SLinus Torvalds 	down_write(&_hash_lock);
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	/*
4461da177e4SLinus Torvalds 	 * Is new free ?
4471da177e4SLinus Torvalds 	 */
44884c89557SPeter Jones 	if (change_uuid)
44984c89557SPeter Jones 		hc = __get_uuid_cell(new);
45084c89557SPeter Jones 	else
4511da177e4SLinus Torvalds 		hc = __get_name_cell(new);
45284c89557SPeter Jones 
4531da177e4SLinus Torvalds 	if (hc) {
4542e84fecfSHeinz Mauelshagen 		DMERR("Unable to change %s on mapped device %s to one that already exists: %s",
45584c89557SPeter Jones 		      change_uuid ? "uuid" : "name",
456856a6f1dSPeter Rajnoha 		      param->name, new);
4577ec75f25SJeff Mahoney 		dm_put(hc->md);
4581da177e4SLinus Torvalds 		up_write(&_hash_lock);
45984c89557SPeter Jones 		kfree(new_data);
460856a6f1dSPeter Rajnoha 		return ERR_PTR(-EBUSY);
4611da177e4SLinus Torvalds 	}
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	/*
4641da177e4SLinus Torvalds 	 * Is there such a device as 'old' ?
4651da177e4SLinus Torvalds 	 */
466856a6f1dSPeter Rajnoha 	hc = __get_name_cell(param->name);
4671da177e4SLinus Torvalds 	if (!hc) {
46843e6c111SMikulas Patocka 		DMERR("Unable to rename non-existent device, %s to %s%s",
46984c89557SPeter Jones 		      param->name, change_uuid ? "uuid " : "", new);
4701da177e4SLinus Torvalds 		up_write(&_hash_lock);
47184c89557SPeter Jones 		kfree(new_data);
472856a6f1dSPeter Rajnoha 		return ERR_PTR(-ENXIO);
4731da177e4SLinus Torvalds 	}
4741da177e4SLinus Torvalds 
4751da177e4SLinus Torvalds 	/*
47684c89557SPeter Jones 	 * Does this device already have a uuid?
4771da177e4SLinus Torvalds 	 */
47884c89557SPeter Jones 	if (change_uuid && hc->uuid) {
47943e6c111SMikulas Patocka 		DMERR("Unable to change uuid of mapped device %s to %s "
48084c89557SPeter Jones 		      "because uuid is already set to %s",
48184c89557SPeter Jones 		      param->name, new, hc->uuid);
48284c89557SPeter Jones 		dm_put(hc->md);
48384c89557SPeter Jones 		up_write(&_hash_lock);
48484c89557SPeter Jones 		kfree(new_data);
48584c89557SPeter Jones 		return ERR_PTR(-EINVAL);
48684c89557SPeter Jones 	}
48784c89557SPeter Jones 
48884c89557SPeter Jones 	if (change_uuid)
48984c89557SPeter Jones 		__set_cell_uuid(hc, new_data);
49084c89557SPeter Jones 	else
49184c89557SPeter Jones 		old_name = __change_cell_name(hc, new_data);
4921da177e4SLinus Torvalds 
49381f1777aSgoggin, edward 	/*
49481f1777aSgoggin, edward 	 * Wake up any dm event waiters.
49581f1777aSgoggin, edward 	 */
49683d5e5b0SMikulas Patocka 	table = dm_get_live_table(hc->md, &srcu_idx);
49783d5e5b0SMikulas Patocka 	if (table)
49881f1777aSgoggin, edward 		dm_table_event(table);
49983d5e5b0SMikulas Patocka 	dm_put_live_table(hc->md, srcu_idx);
50081f1777aSgoggin, edward 
5017533afa1SMikulas Patocka 	if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false))
502856a6f1dSPeter Rajnoha 		param->flags |= DM_UEVENT_GENERATED_FLAG;
50369267a30SAlasdair G Kergon 
504856a6f1dSPeter Rajnoha 	md = hc->md;
5057d1d1df8STushar Sugandhi 
5067d1d1df8STushar Sugandhi 	dm_ima_measure_on_device_rename(md);
5077d1d1df8STushar Sugandhi 
5081da177e4SLinus Torvalds 	up_write(&_hash_lock);
5091da177e4SLinus Torvalds 	kfree(old_name);
510856a6f1dSPeter Rajnoha 
511856a6f1dSPeter Rajnoha 	return md;
5121da177e4SLinus Torvalds }
5131da177e4SLinus Torvalds 
dm_deferred_remove(void)5142c140a24SMikulas Patocka void dm_deferred_remove(void)
5152c140a24SMikulas Patocka {
5162c140a24SMikulas Patocka 	dm_hash_remove_all(true, false, true);
5172c140a24SMikulas Patocka }
5182c140a24SMikulas Patocka 
519a4a82ce3SHeinz Mauelshagen /*
520a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
5211da177e4SLinus Torvalds  * Implementation of the ioctl commands
522a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
523a4a82ce3SHeinz Mauelshagen  */
5241da177e4SLinus Torvalds /*
5251da177e4SLinus Torvalds  * All the ioctl commands get dispatched to functions with this
5261da177e4SLinus Torvalds  * prototype.
5271da177e4SLinus Torvalds  */
528fc1841e1SMikulas Patocka typedef int (*ioctl_fn)(struct file *filp, struct dm_ioctl *param, size_t param_size);
5291da177e4SLinus Torvalds 
remove_all(struct file * filp,struct dm_ioctl * param,size_t param_size)530fc1841e1SMikulas Patocka static int remove_all(struct file *filp, struct dm_ioctl *param, size_t param_size)
5311da177e4SLinus Torvalds {
5322c140a24SMikulas Patocka 	dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false);
5331da177e4SLinus Torvalds 	param->data_size = 0;
5341da177e4SLinus Torvalds 	return 0;
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds /*
5381da177e4SLinus Torvalds  * Round up the ptr to an 8-byte boundary.
5391da177e4SLinus Torvalds  */
5401da177e4SLinus Torvalds #define ALIGN_MASK 7
align_val(size_t val)54162e08243SMikulas Patocka static inline size_t align_val(size_t val)
54262e08243SMikulas Patocka {
54362e08243SMikulas Patocka 	return (val + ALIGN_MASK) & ~ALIGN_MASK;
54462e08243SMikulas Patocka }
align_ptr(void * ptr)5451da177e4SLinus Torvalds static inline void *align_ptr(void *ptr)
5461da177e4SLinus Torvalds {
54762e08243SMikulas Patocka 	return (void *)align_val((size_t)ptr);
5481da177e4SLinus Torvalds }
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds /*
5511da177e4SLinus Torvalds  * Retrieves the data payload buffer from an already allocated
5521da177e4SLinus Torvalds  * struct dm_ioctl.
5531da177e4SLinus Torvalds  */
get_result_buffer(struct dm_ioctl * param,size_t param_size,size_t * len)5541da177e4SLinus Torvalds static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
5551da177e4SLinus Torvalds 			       size_t *len)
5561da177e4SLinus Torvalds {
5571da177e4SLinus Torvalds 	param->data_start = align_ptr(param + 1) - (void *) param;
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	if (param->data_start < param_size)
5601da177e4SLinus Torvalds 		*len = param_size - param->data_start;
5611da177e4SLinus Torvalds 	else
5621da177e4SLinus Torvalds 		*len = 0;
5631da177e4SLinus Torvalds 
5641da177e4SLinus Torvalds 	return ((void *) param) + param->data_start;
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds 
filter_device(struct hash_cell * hc,const char * pfx_name,const char * pfx_uuid)567c909085bSMikulas Patocka static bool filter_device(struct hash_cell *hc, const char *pfx_name, const char *pfx_uuid)
568c909085bSMikulas Patocka {
569c909085bSMikulas Patocka 	const char *val;
570c909085bSMikulas Patocka 	size_t val_len, pfx_len;
571c909085bSMikulas Patocka 
572c909085bSMikulas Patocka 	val = hc->name;
573c909085bSMikulas Patocka 	val_len = strlen(val);
574c909085bSMikulas Patocka 	pfx_len = strnlen(pfx_name, DM_NAME_LEN);
575c909085bSMikulas Patocka 	if (pfx_len > val_len)
576c909085bSMikulas Patocka 		return false;
577c909085bSMikulas Patocka 	if (memcmp(val, pfx_name, pfx_len))
578c909085bSMikulas Patocka 		return false;
579c909085bSMikulas Patocka 
580c909085bSMikulas Patocka 	val = hc->uuid ? hc->uuid : "";
581c909085bSMikulas Patocka 	val_len = strlen(val);
582c909085bSMikulas Patocka 	pfx_len = strnlen(pfx_uuid, DM_UUID_LEN);
583c909085bSMikulas Patocka 	if (pfx_len > val_len)
584c909085bSMikulas Patocka 		return false;
585c909085bSMikulas Patocka 	if (memcmp(val, pfx_uuid, pfx_len))
586c909085bSMikulas Patocka 		return false;
587c909085bSMikulas Patocka 
588c909085bSMikulas Patocka 	return true;
589c909085bSMikulas Patocka }
590c909085bSMikulas Patocka 
list_devices(struct file * filp,struct dm_ioctl * param,size_t param_size)591fc1841e1SMikulas Patocka static int list_devices(struct file *filp, struct dm_ioctl *param, size_t param_size)
5921da177e4SLinus Torvalds {
593b82096afSMikulas Patocka 	struct rb_node *n;
5941da177e4SLinus Torvalds 	struct hash_cell *hc;
5951da177e4SLinus Torvalds 	size_t len, needed = 0;
5961da177e4SLinus Torvalds 	struct gendisk *disk;
59762e08243SMikulas Patocka 	struct dm_name_list *orig_nl, *nl, *old_nl = NULL;
59823d70c5eSMikulas Patocka 	uint32_t *event_nr;
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 	down_write(&_hash_lock);
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	/*
6031da177e4SLinus Torvalds 	 * Loop through all the devices working out how much
6041da177e4SLinus Torvalds 	 * space we need.
6051da177e4SLinus Torvalds 	 */
606b82096afSMikulas Patocka 	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
607b82096afSMikulas Patocka 		hc = container_of(n, struct hash_cell, name_node);
608c909085bSMikulas Patocka 		if (!filter_device(hc, param->name, param->uuid))
609c909085bSMikulas Patocka 			continue;
61062e08243SMikulas Patocka 		needed += align_val(offsetof(struct dm_name_list, name) + strlen(hc->name) + 1);
6118b638081SMikulas Patocka 		needed += align_val(sizeof(uint32_t) * 2);
6128b638081SMikulas Patocka 		if (param->flags & DM_UUID_FLAG && hc->uuid)
6138b638081SMikulas Patocka 			needed += align_val(strlen(hc->uuid) + 1);
6141da177e4SLinus Torvalds 	}
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	/*
6171da177e4SLinus Torvalds 	 * Grab our output buffer.
6181da177e4SLinus Torvalds 	 */
61962e08243SMikulas Patocka 	nl = orig_nl = get_result_buffer(param, param_size, &len);
6204edbe1d7SMikulas Patocka 	if (len < needed || len < sizeof(nl->dev)) {
6211da177e4SLinus Torvalds 		param->flags |= DM_BUFFER_FULL_FLAG;
6221da177e4SLinus Torvalds 		goto out;
6231da177e4SLinus Torvalds 	}
6241da177e4SLinus Torvalds 	param->data_size = param->data_start + needed;
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds 	nl->dev = 0;	/* Flags no data */
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	/*
6291da177e4SLinus Torvalds 	 * Now loop through filling out the names.
6301da177e4SLinus Torvalds 	 */
631b82096afSMikulas Patocka 	for (n = rb_first(&name_rb_tree); n; n = rb_next(n)) {
6328b638081SMikulas Patocka 		void *uuid_ptr;
6330ef0b471SHeinz Mauelshagen 
634b82096afSMikulas Patocka 		hc = container_of(n, struct hash_cell, name_node);
635c909085bSMikulas Patocka 		if (!filter_device(hc, param->name, param->uuid))
636c909085bSMikulas Patocka 			continue;
6371da177e4SLinus Torvalds 		if (old_nl)
6381da177e4SLinus Torvalds 			old_nl->next = (uint32_t) ((void *) nl -
6391da177e4SLinus Torvalds 						   (void *) old_nl);
6401da177e4SLinus Torvalds 		disk = dm_disk(hc->md);
641f331c029STejun Heo 		nl->dev = huge_encode_dev(disk_devt(disk));
6421da177e4SLinus Torvalds 		nl->next = 0;
6431da177e4SLinus Torvalds 		strcpy(nl->name, hc->name);
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds 		old_nl = nl;
64662e08243SMikulas Patocka 		event_nr = align_ptr(nl->name + strlen(hc->name) + 1);
6478b638081SMikulas Patocka 		event_nr[0] = dm_get_event_nr(hc->md);
6488b638081SMikulas Patocka 		event_nr[1] = 0;
6498b638081SMikulas Patocka 		uuid_ptr = align_ptr(event_nr + 2);
6508b638081SMikulas Patocka 		if (param->flags & DM_UUID_FLAG) {
6518b638081SMikulas Patocka 			if (hc->uuid) {
6528b638081SMikulas Patocka 				event_nr[1] |= DM_NAME_LIST_FLAG_HAS_UUID;
6538b638081SMikulas Patocka 				strcpy(uuid_ptr, hc->uuid);
6548b638081SMikulas Patocka 				uuid_ptr = align_ptr(uuid_ptr + strlen(hc->uuid) + 1);
6558b638081SMikulas Patocka 			} else {
6568b638081SMikulas Patocka 				event_nr[1] |= DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID;
6578b638081SMikulas Patocka 			}
6588b638081SMikulas Patocka 		}
6598b638081SMikulas Patocka 		nl = uuid_ptr;
6601da177e4SLinus Torvalds 	}
66162e08243SMikulas Patocka 	/*
66262e08243SMikulas Patocka 	 * If mismatch happens, security may be compromised due to buffer
66362e08243SMikulas Patocka 	 * overflow, so it's better to crash.
66462e08243SMikulas Patocka 	 */
66562e08243SMikulas Patocka 	BUG_ON((char *)nl - (char *)orig_nl != needed);
6661da177e4SLinus Torvalds 
6671da177e4SLinus Torvalds  out:
6681da177e4SLinus Torvalds 	up_write(&_hash_lock);
6691da177e4SLinus Torvalds 	return 0;
6701da177e4SLinus Torvalds }
6711da177e4SLinus Torvalds 
list_version_get_needed(struct target_type * tt,void * needed_param)6721da177e4SLinus Torvalds static void list_version_get_needed(struct target_type *tt, void *needed_param)
6731da177e4SLinus Torvalds {
6741da177e4SLinus Torvalds 	size_t *needed = needed_param;
6751da177e4SLinus Torvalds 
676c4cc6635SAlasdair G Kergon 	*needed += sizeof(struct dm_target_versions);
6774fe1ec99SMikulas Patocka 	*needed += strlen(tt->name) + 1;
6781da177e4SLinus Torvalds 	*needed += ALIGN_MASK;
6791da177e4SLinus Torvalds }
6801da177e4SLinus Torvalds 
list_version_get_info(struct target_type * tt,void * param)6811da177e4SLinus Torvalds static void list_version_get_info(struct target_type *tt, void *param)
6821da177e4SLinus Torvalds {
6831da177e4SLinus Torvalds 	struct vers_iter *info = param;
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 	/* Check space - it might have changed since the first iteration */
6868ca817c4SHeinz Mauelshagen 	if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 > info->end) {
6871da177e4SLinus Torvalds 		info->flags = DM_BUFFER_FULL_FLAG;
6881da177e4SLinus Torvalds 		return;
6891da177e4SLinus Torvalds 	}
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 	if (info->old_vers)
6928ca817c4SHeinz Mauelshagen 		info->old_vers->next = (uint32_t) ((void *)info->vers - (void *)info->old_vers);
6938ca817c4SHeinz Mauelshagen 
6941da177e4SLinus Torvalds 	info->vers->version[0] = tt->version[0];
6951da177e4SLinus Torvalds 	info->vers->version[1] = tt->version[1];
6961da177e4SLinus Torvalds 	info->vers->version[2] = tt->version[2];
6971da177e4SLinus Torvalds 	info->vers->next = 0;
6981da177e4SLinus Torvalds 	strcpy(info->vers->name, tt->name);
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 	info->old_vers = info->vers;
701d043f9a1SMikulas Patocka 	info->vers = align_ptr((void *)(info->vers + 1) + strlen(tt->name) + 1);
7021da177e4SLinus Torvalds }
7031da177e4SLinus Torvalds 
__list_versions(struct dm_ioctl * param,size_t param_size,const char * name)704afa179ebSMikulas Patocka static int __list_versions(struct dm_ioctl *param, size_t param_size, const char *name)
7051da177e4SLinus Torvalds {
7061da177e4SLinus Torvalds 	size_t len, needed = 0;
7071da177e4SLinus Torvalds 	struct dm_target_versions *vers;
7081da177e4SLinus Torvalds 	struct vers_iter iter_info;
709afa179ebSMikulas Patocka 	struct target_type *tt = NULL;
710afa179ebSMikulas Patocka 
711afa179ebSMikulas Patocka 	if (name) {
712afa179ebSMikulas Patocka 		tt = dm_get_target_type(name);
713afa179ebSMikulas Patocka 		if (!tt)
714afa179ebSMikulas Patocka 			return -EINVAL;
715afa179ebSMikulas Patocka 	}
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 	/*
7181da177e4SLinus Torvalds 	 * Loop through all the devices working out how much
7191da177e4SLinus Torvalds 	 * space we need.
7201da177e4SLinus Torvalds 	 */
721afa179ebSMikulas Patocka 	if (!tt)
7221da177e4SLinus Torvalds 		dm_target_iterate(list_version_get_needed, &needed);
723afa179ebSMikulas Patocka 	else
724afa179ebSMikulas Patocka 		list_version_get_needed(tt, &needed);
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	/*
7271da177e4SLinus Torvalds 	 * Grab our output buffer.
7281da177e4SLinus Torvalds 	 */
7291da177e4SLinus Torvalds 	vers = get_result_buffer(param, param_size, &len);
7301da177e4SLinus Torvalds 	if (len < needed) {
7311da177e4SLinus Torvalds 		param->flags |= DM_BUFFER_FULL_FLAG;
7321da177e4SLinus Torvalds 		goto out;
7331da177e4SLinus Torvalds 	}
7341da177e4SLinus Torvalds 	param->data_size = param->data_start + needed;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	iter_info.param_size = param_size;
7371da177e4SLinus Torvalds 	iter_info.old_vers = NULL;
7381da177e4SLinus Torvalds 	iter_info.vers = vers;
7391da177e4SLinus Torvalds 	iter_info.flags = 0;
7404fe1ec99SMikulas Patocka 	iter_info.end = (char *)vers + needed;
7411da177e4SLinus Torvalds 
7421da177e4SLinus Torvalds 	/*
7431da177e4SLinus Torvalds 	 * Now loop through filling out the names & versions.
7441da177e4SLinus Torvalds 	 */
745afa179ebSMikulas Patocka 	if (!tt)
7461da177e4SLinus Torvalds 		dm_target_iterate(list_version_get_info, &iter_info);
747afa179ebSMikulas Patocka 	else
748afa179ebSMikulas Patocka 		list_version_get_info(tt, &iter_info);
7491da177e4SLinus Torvalds 	param->flags |= iter_info.flags;
7501da177e4SLinus Torvalds 
7511da177e4SLinus Torvalds  out:
752afa179ebSMikulas Patocka 	if (tt)
753afa179ebSMikulas Patocka 		dm_put_target_type(tt);
7541da177e4SLinus Torvalds 	return 0;
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds 
list_versions(struct file * filp,struct dm_ioctl * param,size_t param_size)757afa179ebSMikulas Patocka static int list_versions(struct file *filp, struct dm_ioctl *param, size_t param_size)
758afa179ebSMikulas Patocka {
759afa179ebSMikulas Patocka 	return __list_versions(param, param_size, NULL);
760afa179ebSMikulas Patocka }
761afa179ebSMikulas Patocka 
get_target_version(struct file * filp,struct dm_ioctl * param,size_t param_size)762afa179ebSMikulas Patocka static int get_target_version(struct file *filp, struct dm_ioctl *param, size_t param_size)
763afa179ebSMikulas Patocka {
764afa179ebSMikulas Patocka 	return __list_versions(param, param_size, param->name);
765afa179ebSMikulas Patocka }
766afa179ebSMikulas Patocka 
check_name(const char * name)7671da177e4SLinus Torvalds static int check_name(const char *name)
7681da177e4SLinus Torvalds {
7691da177e4SLinus Torvalds 	if (strchr(name, '/')) {
770a85f1a9dSDemi Marie Obenour 		DMERR("device name cannot contain '/'");
771a85f1a9dSDemi Marie Obenour 		return -EINVAL;
772a85f1a9dSDemi Marie Obenour 	}
773a85f1a9dSDemi Marie Obenour 
77481ca2dbeSDemi Marie Obenour 	if (strcmp(name, DM_CONTROL_NODE) == 0 ||
77581ca2dbeSDemi Marie Obenour 	    strcmp(name, ".") == 0 ||
77681ca2dbeSDemi Marie Obenour 	    strcmp(name, "..") == 0) {
77781ca2dbeSDemi Marie Obenour 		DMERR("device name cannot be \"%s\", \".\", or \"..\"", DM_CONTROL_NODE);
7781da177e4SLinus Torvalds 		return -EINVAL;
7791da177e4SLinus Torvalds 	}
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	return 0;
7821da177e4SLinus Torvalds }
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds /*
7851d0f3ce8SMike Snitzer  * On successful return, the caller must not attempt to acquire
78688e2f901SJunxiao Bi  * _hash_lock without first calling dm_put_live_table, because dm_table_destroy
78788e2f901SJunxiao Bi  * waits for this dm_put_live_table and could be called under this lock.
7881d0f3ce8SMike Snitzer  */
dm_get_inactive_table(struct mapped_device * md,int * srcu_idx)78983d5e5b0SMikulas Patocka static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx)
7901d0f3ce8SMike Snitzer {
7911d0f3ce8SMike Snitzer 	struct hash_cell *hc;
7921d0f3ce8SMike Snitzer 	struct dm_table *table = NULL;
7931d0f3ce8SMike Snitzer 
79483d5e5b0SMikulas Patocka 	/* increment rcu count, we don't care about the table pointer */
79583d5e5b0SMikulas Patocka 	dm_get_live_table(md, srcu_idx);
79683d5e5b0SMikulas Patocka 
7971d0f3ce8SMike Snitzer 	down_read(&_hash_lock);
7981d0f3ce8SMike Snitzer 	hc = dm_get_mdptr(md);
799a2f998a7SHou Tao 	if (!hc) {
80043e6c111SMikulas Patocka 		DMERR("device has been removed from the dev hash table.");
8011d0f3ce8SMike Snitzer 		goto out;
8021d0f3ce8SMike Snitzer 	}
8031d0f3ce8SMike Snitzer 
8041d0f3ce8SMike Snitzer 	table = hc->new_map;
8051d0f3ce8SMike Snitzer 
8061d0f3ce8SMike Snitzer out:
8071d0f3ce8SMike Snitzer 	up_read(&_hash_lock);
8081d0f3ce8SMike Snitzer 
8091d0f3ce8SMike Snitzer 	return table;
8101d0f3ce8SMike Snitzer }
8111d0f3ce8SMike Snitzer 
dm_get_live_or_inactive_table(struct mapped_device * md,struct dm_ioctl * param,int * srcu_idx)8121d0f3ce8SMike Snitzer static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
81383d5e5b0SMikulas Patocka 						      struct dm_ioctl *param,
81483d5e5b0SMikulas Patocka 						      int *srcu_idx)
8151d0f3ce8SMike Snitzer {
8161d0f3ce8SMike Snitzer 	return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
81783d5e5b0SMikulas Patocka 		dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx);
8181d0f3ce8SMike Snitzer }
8191d0f3ce8SMike Snitzer 
8201d0f3ce8SMike Snitzer /*
8211da177e4SLinus Torvalds  * Fills in a dm_ioctl structure, ready for sending back to
8221da177e4SLinus Torvalds  * userland.
8231da177e4SLinus Torvalds  */
__dev_status(struct mapped_device * md,struct dm_ioctl * param)824094ea9a0SAlasdair G Kergon static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
8251da177e4SLinus Torvalds {
8261da177e4SLinus Torvalds 	struct gendisk *disk = dm_disk(md);
8271da177e4SLinus Torvalds 	struct dm_table *table;
82883d5e5b0SMikulas Patocka 	int srcu_idx;
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds 	param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
831ffcc3936SMike Snitzer 			  DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG);
8321da177e4SLinus Torvalds 
8334f186f8bSKiyoshi Ueda 	if (dm_suspended_md(md))
8341da177e4SLinus Torvalds 		param->flags |= DM_SUSPEND_FLAG;
8351da177e4SLinus Torvalds 
836ffcc3936SMike Snitzer 	if (dm_suspended_internally_md(md))
837ffcc3936SMike Snitzer 		param->flags |= DM_INTERNAL_SUSPEND_FLAG;
838ffcc3936SMike Snitzer 
8392c140a24SMikulas Patocka 	if (dm_test_deferred_remove_flag(md))
8402c140a24SMikulas Patocka 		param->flags |= DM_DEFERRED_REMOVE;
8412c140a24SMikulas Patocka 
842f331c029STejun Heo 	param->dev = huge_encode_dev(disk_devt(disk));
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds 	/*
8451da177e4SLinus Torvalds 	 * Yes, this will be out of date by the time it gets back
8461da177e4SLinus Torvalds 	 * to userland, but it is still very useful for
8471da177e4SLinus Torvalds 	 * debugging.
8481da177e4SLinus Torvalds 	 */
8495c6bd75dSAlasdair G Kergon 	param->open_count = dm_open_count(md);
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 	param->event_nr = dm_get_event_nr(md);
8521d0f3ce8SMike Snitzer 	param->target_count = 0;
8531da177e4SLinus Torvalds 
85483d5e5b0SMikulas Patocka 	table = dm_get_live_table(md, &srcu_idx);
8551da177e4SLinus Torvalds 	if (table) {
8561d0f3ce8SMike Snitzer 		if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
8571d0f3ce8SMike Snitzer 			if (get_disk_ro(disk))
8581d0f3ce8SMike Snitzer 				param->flags |= DM_READONLY_FLAG;
8592aec377aSMike Snitzer 			param->target_count = table->num_targets;
8601d0f3ce8SMike Snitzer 		}
8611d0f3ce8SMike Snitzer 
8621da177e4SLinus Torvalds 		param->flags |= DM_ACTIVE_PRESENT_FLAG;
8631d0f3ce8SMike Snitzer 	}
86483d5e5b0SMikulas Patocka 	dm_put_live_table(md, srcu_idx);
8651d0f3ce8SMike Snitzer 
8661d0f3ce8SMike Snitzer 	if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
86783d5e5b0SMikulas Patocka 		int srcu_idx;
8680ef0b471SHeinz Mauelshagen 
86983d5e5b0SMikulas Patocka 		table = dm_get_inactive_table(md, &srcu_idx);
8701d0f3ce8SMike Snitzer 		if (table) {
87105bdb996SChristoph Hellwig 			if (!(dm_table_get_mode(table) & BLK_OPEN_WRITE))
8721d0f3ce8SMike Snitzer 				param->flags |= DM_READONLY_FLAG;
8732aec377aSMike Snitzer 			param->target_count = table->num_targets;
8741d0f3ce8SMike Snitzer 		}
87583d5e5b0SMikulas Patocka 		dm_put_live_table(md, srcu_idx);
8761d0f3ce8SMike Snitzer 	}
8771da177e4SLinus Torvalds }
8781da177e4SLinus Torvalds 
dev_create(struct file * filp,struct dm_ioctl * param,size_t param_size)879fc1841e1SMikulas Patocka static int dev_create(struct file *filp, struct dm_ioctl *param, size_t param_size)
8801da177e4SLinus Torvalds {
8812b06cfffSAlasdair G Kergon 	int r, m = DM_ANY_MINOR;
8821da177e4SLinus Torvalds 	struct mapped_device *md;
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds 	r = check_name(param->name);
8851da177e4SLinus Torvalds 	if (r)
8861da177e4SLinus Torvalds 		return r;
8871da177e4SLinus Torvalds 
8881da177e4SLinus Torvalds 	if (param->flags & DM_PERSISTENT_DEV_FLAG)
8892b06cfffSAlasdair G Kergon 		m = MINOR(huge_decode_dev(param->dev));
8901da177e4SLinus Torvalds 
8912b06cfffSAlasdair G Kergon 	r = dm_create(m, &md);
8921da177e4SLinus Torvalds 	if (r)
8931da177e4SLinus Torvalds 		return r;
8941da177e4SLinus Torvalds 
8951da177e4SLinus Torvalds 	r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
8963f77316dSKiyoshi Ueda 	if (r) {
8973f77316dSKiyoshi Ueda 		dm_put(md);
8983f77316dSKiyoshi Ueda 		dm_destroy(md);
8993f77316dSKiyoshi Ueda 		return r;
9003f77316dSKiyoshi Ueda 	}
9011da177e4SLinus Torvalds 
9021da177e4SLinus Torvalds 	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
9031da177e4SLinus Torvalds 
904094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
905094ea9a0SAlasdair G Kergon 
9061da177e4SLinus Torvalds 	dm_put(md);
9071da177e4SLinus Torvalds 
9083f77316dSKiyoshi Ueda 	return 0;
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds 
9111da177e4SLinus Torvalds /*
9121da177e4SLinus Torvalds  * Always use UUID for lookups if it's present, otherwise use name or dev.
9131da177e4SLinus Torvalds  */
__find_device_hash_cell(struct dm_ioctl * param)914858119e1SArjan van de Ven static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
9151da177e4SLinus Torvalds {
9160ddf9644SMikulas Patocka 	struct hash_cell *hc = NULL;
9179ade92a9SAlasdair G Kergon 
9180ddf9644SMikulas Patocka 	if (*param->uuid) {
919dbdcc906SMikulas Patocka 		if (*param->name || param->dev) {
920dbdcc906SMikulas Patocka 			DMERR("Invalid ioctl structure: uuid %s, name %s, dev %llx",
921dbdcc906SMikulas Patocka 			      param->uuid, param->name, (unsigned long long)param->dev);
922759dea20SMikulas Patocka 			return NULL;
923dbdcc906SMikulas Patocka 		}
924759dea20SMikulas Patocka 
9250ddf9644SMikulas Patocka 		hc = __get_uuid_cell(param->uuid);
9260ddf9644SMikulas Patocka 		if (!hc)
9270ddf9644SMikulas Patocka 			return NULL;
928ba2e19b0SMikulas Patocka 	} else if (*param->name) {
929dbdcc906SMikulas Patocka 		if (param->dev) {
930dbdcc906SMikulas Patocka 			DMERR("Invalid ioctl structure: name %s, dev %llx",
931dbdcc906SMikulas Patocka 			      param->name, (unsigned long long)param->dev);
932759dea20SMikulas Patocka 			return NULL;
933dbdcc906SMikulas Patocka 		}
934759dea20SMikulas Patocka 
9350ddf9644SMikulas Patocka 		hc = __get_name_cell(param->name);
9360ddf9644SMikulas Patocka 		if (!hc)
9370ddf9644SMikulas Patocka 			return NULL;
938ba2e19b0SMikulas Patocka 	} else if (param->dev) {
939ba2e19b0SMikulas Patocka 		hc = __get_dev_cell(param->dev);
940ba2e19b0SMikulas Patocka 		if (!hc)
941ba2e19b0SMikulas Patocka 			return NULL;
942ba2e19b0SMikulas Patocka 	} else
9430ddf9644SMikulas Patocka 		return NULL;
9449ade92a9SAlasdair G Kergon 
9451da177e4SLinus Torvalds 	/*
9461da177e4SLinus Torvalds 	 * Sneakily write in both the name and the uuid
9471da177e4SLinus Torvalds 	 * while we have the cell.
9481da177e4SLinus Torvalds 	 */
94922a8b849SHeinz Mauelshagen 	strscpy(param->name, hc->name, sizeof(param->name));
9501da177e4SLinus Torvalds 	if (hc->uuid)
95122a8b849SHeinz Mauelshagen 		strscpy(param->uuid, hc->uuid, sizeof(param->uuid));
9521da177e4SLinus Torvalds 	else
9531da177e4SLinus Torvalds 		param->uuid[0] = '\0';
9541da177e4SLinus Torvalds 
9551da177e4SLinus Torvalds 	if (hc->new_map)
9561da177e4SLinus Torvalds 		param->flags |= DM_INACTIVE_PRESENT_FLAG;
9571da177e4SLinus Torvalds 	else
9581da177e4SLinus Torvalds 		param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
9590ddf9644SMikulas Patocka 
9600ddf9644SMikulas Patocka 	return hc;
9611da177e4SLinus Torvalds }
9620ddf9644SMikulas Patocka 
find_device(struct dm_ioctl * param)9630ddf9644SMikulas Patocka static struct mapped_device *find_device(struct dm_ioctl *param)
9640ddf9644SMikulas Patocka {
9650ddf9644SMikulas Patocka 	struct hash_cell *hc;
9660ddf9644SMikulas Patocka 	struct mapped_device *md = NULL;
9670ddf9644SMikulas Patocka 
9680ddf9644SMikulas Patocka 	down_read(&_hash_lock);
9690ddf9644SMikulas Patocka 	hc = __find_device_hash_cell(param);
9700ddf9644SMikulas Patocka 	if (hc)
9710ddf9644SMikulas Patocka 		md = hc->md;
9721da177e4SLinus Torvalds 	up_read(&_hash_lock);
9731da177e4SLinus Torvalds 
9741da177e4SLinus Torvalds 	return md;
9751da177e4SLinus Torvalds }
9761da177e4SLinus Torvalds 
dev_remove(struct file * filp,struct dm_ioctl * param,size_t param_size)977fc1841e1SMikulas Patocka static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_size)
9781da177e4SLinus Torvalds {
9791da177e4SLinus Torvalds 	struct hash_cell *hc;
9807ec75f25SJeff Mahoney 	struct mapped_device *md;
9815c6bd75dSAlasdair G Kergon 	int r;
98283d5e5b0SMikulas Patocka 	struct dm_table *t;
9831da177e4SLinus Torvalds 
9841da177e4SLinus Torvalds 	down_write(&_hash_lock);
9851da177e4SLinus Torvalds 	hc = __find_device_hash_cell(param);
9861da177e4SLinus Torvalds 
9871da177e4SLinus Torvalds 	if (!hc) {
988810b4923SMilan Broz 		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
9891da177e4SLinus Torvalds 		up_write(&_hash_lock);
9901da177e4SLinus Torvalds 		return -ENXIO;
9911da177e4SLinus Torvalds 	}
9921da177e4SLinus Torvalds 
9937ec75f25SJeff Mahoney 	md = hc->md;
9947ec75f25SJeff Mahoney 
9955c6bd75dSAlasdair G Kergon 	/*
9965c6bd75dSAlasdair G Kergon 	 * Ensure the device is not open and nothing further can open it.
9975c6bd75dSAlasdair G Kergon 	 */
9982c140a24SMikulas Patocka 	r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
9995c6bd75dSAlasdair G Kergon 	if (r) {
10002c140a24SMikulas Patocka 		if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
10012c140a24SMikulas Patocka 			up_write(&_hash_lock);
10022c140a24SMikulas Patocka 			dm_put(md);
10032c140a24SMikulas Patocka 			return 0;
10042c140a24SMikulas Patocka 		}
1005810b4923SMilan Broz 		DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
10065c6bd75dSAlasdair G Kergon 		up_write(&_hash_lock);
10075c6bd75dSAlasdair G Kergon 		dm_put(md);
10085c6bd75dSAlasdair G Kergon 		return r;
10095c6bd75dSAlasdair G Kergon 	}
10105c6bd75dSAlasdair G Kergon 
101183d5e5b0SMikulas Patocka 	t = __hash_remove(hc);
10121da177e4SLinus Torvalds 	up_write(&_hash_lock);
101360935eb2SMilan Broz 
101483d5e5b0SMikulas Patocka 	if (t) {
101583d5e5b0SMikulas Patocka 		dm_sync_table(md);
101683d5e5b0SMikulas Patocka 		dm_table_destroy(t);
101783d5e5b0SMikulas Patocka 	}
101883d5e5b0SMikulas Patocka 
10192c140a24SMikulas Patocka 	param->flags &= ~DM_DEFERRED_REMOVE;
10202c140a24SMikulas Patocka 
102184010e51STushar Sugandhi 	dm_ima_measure_on_device_remove(md, false);
102284010e51STushar Sugandhi 
10237533afa1SMikulas Patocka 	if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
10243abf85b5SPeter Rajnoha 		param->flags |= DM_UEVENT_GENERATED_FLAG;
102560935eb2SMilan Broz 
10267ec75f25SJeff Mahoney 	dm_put(md);
10273f77316dSKiyoshi Ueda 	dm_destroy(md);
10281da177e4SLinus Torvalds 	return 0;
10291da177e4SLinus Torvalds }
10301da177e4SLinus Torvalds 
10311da177e4SLinus Torvalds /*
10321da177e4SLinus Torvalds  * Check a string doesn't overrun the chunk of
10331da177e4SLinus Torvalds  * memory we copied from userland.
10341da177e4SLinus Torvalds  */
invalid_str(char * str,void * end)10351da177e4SLinus Torvalds static int invalid_str(char *str, void *end)
10361da177e4SLinus Torvalds {
10371da177e4SLinus Torvalds 	while ((void *) str < end)
10381da177e4SLinus Torvalds 		if (!*str++)
10391da177e4SLinus Torvalds 			return 0;
10401da177e4SLinus Torvalds 
10411da177e4SLinus Torvalds 	return -EINVAL;
10421da177e4SLinus Torvalds }
10431da177e4SLinus Torvalds 
dev_rename(struct file * filp,struct dm_ioctl * param,size_t param_size)1044fc1841e1SMikulas Patocka static int dev_rename(struct file *filp, struct dm_ioctl *param, size_t param_size)
10451da177e4SLinus Torvalds {
10461da177e4SLinus Torvalds 	int r;
104784c89557SPeter Jones 	char *new_data = (char *) param + param->data_start;
1048856a6f1dSPeter Rajnoha 	struct mapped_device *md;
104986a3238cSHeinz Mauelshagen 	unsigned int change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
10501da177e4SLinus Torvalds 
105184c89557SPeter Jones 	if (new_data < param->data ||
1052c2b04824SAlasdair Kergon 	    invalid_str(new_data, (void *) param + param_size) || !*new_data ||
105384c89557SPeter Jones 	    strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
105443e6c111SMikulas Patocka 		DMERR("Invalid new mapped device name or uuid string supplied.");
10551da177e4SLinus Torvalds 		return -EINVAL;
10561da177e4SLinus Torvalds 	}
10571da177e4SLinus Torvalds 
105884c89557SPeter Jones 	if (!change_uuid) {
105984c89557SPeter Jones 		r = check_name(new_data);
10601da177e4SLinus Torvalds 		if (r)
10611da177e4SLinus Torvalds 			return r;
106284c89557SPeter Jones 	}
10631da177e4SLinus Torvalds 
106484c89557SPeter Jones 	md = dm_hash_rename(param, new_data);
1065856a6f1dSPeter Rajnoha 	if (IS_ERR(md))
1066856a6f1dSPeter Rajnoha 		return PTR_ERR(md);
10673abf85b5SPeter Rajnoha 
1068856a6f1dSPeter Rajnoha 	__dev_status(md, param);
1069856a6f1dSPeter Rajnoha 	dm_put(md);
1070856a6f1dSPeter Rajnoha 
1071856a6f1dSPeter Rajnoha 	return 0;
10721da177e4SLinus Torvalds }
10731da177e4SLinus Torvalds 
dev_set_geometry(struct file * filp,struct dm_ioctl * param,size_t param_size)1074fc1841e1SMikulas Patocka static int dev_set_geometry(struct file *filp, struct dm_ioctl *param, size_t param_size)
10753ac51e74SDarrick J. Wong {
10763ac51e74SDarrick J. Wong 	int r = -EINVAL, x;
10773ac51e74SDarrick J. Wong 	struct mapped_device *md;
10783ac51e74SDarrick J. Wong 	struct hd_geometry geometry;
10793ac51e74SDarrick J. Wong 	unsigned long indata[4];
10803ac51e74SDarrick J. Wong 	char *geostr = (char *) param + param->data_start;
108131998ef1SMikulas Patocka 	char dummy;
10823ac51e74SDarrick J. Wong 
10833ac51e74SDarrick J. Wong 	md = find_device(param);
10843ac51e74SDarrick J. Wong 	if (!md)
10853ac51e74SDarrick J. Wong 		return -ENXIO;
10863ac51e74SDarrick J. Wong 
108727238b2bSAlasdair G Kergon 	if (geostr < param->data ||
10883ac51e74SDarrick J. Wong 	    invalid_str(geostr, (void *) param + param_size)) {
108943e6c111SMikulas Patocka 		DMERR("Invalid geometry supplied.");
10903ac51e74SDarrick J. Wong 		goto out;
10913ac51e74SDarrick J. Wong 	}
10923ac51e74SDarrick J. Wong 
109331998ef1SMikulas Patocka 	x = sscanf(geostr, "%lu %lu %lu %lu%c", indata,
109431998ef1SMikulas Patocka 		   indata + 1, indata + 2, indata + 3, &dummy);
10953ac51e74SDarrick J. Wong 
10963ac51e74SDarrick J. Wong 	if (x != 4) {
109743e6c111SMikulas Patocka 		DMERR("Unable to interpret geometry settings.");
10983ac51e74SDarrick J. Wong 		goto out;
10993ac51e74SDarrick J. Wong 	}
11003ac51e74SDarrick J. Wong 
1101151d8122SSergey Shtylyov 	if (indata[0] > 65535 || indata[1] > 255 || indata[2] > 255) {
110243e6c111SMikulas Patocka 		DMERR("Geometry exceeds range limits.");
11033ac51e74SDarrick J. Wong 		goto out;
11043ac51e74SDarrick J. Wong 	}
11053ac51e74SDarrick J. Wong 
11063ac51e74SDarrick J. Wong 	geometry.cylinders = indata[0];
11073ac51e74SDarrick J. Wong 	geometry.heads = indata[1];
11083ac51e74SDarrick J. Wong 	geometry.sectors = indata[2];
11093ac51e74SDarrick J. Wong 	geometry.start = indata[3];
11103ac51e74SDarrick J. Wong 
11113ac51e74SDarrick J. Wong 	r = dm_set_geometry(md, &geometry);
11123ac51e74SDarrick J. Wong 
11133ac51e74SDarrick J. Wong 	param->data_size = 0;
11143ac51e74SDarrick J. Wong 
11153ac51e74SDarrick J. Wong out:
11163ac51e74SDarrick J. Wong 	dm_put(md);
11173ac51e74SDarrick J. Wong 	return r;
11183ac51e74SDarrick J. Wong }
11193ac51e74SDarrick J. Wong 
do_suspend(struct dm_ioctl * param)11201da177e4SLinus Torvalds static int do_suspend(struct dm_ioctl *param)
11211da177e4SLinus Torvalds {
11221da177e4SLinus Torvalds 	int r = 0;
112386a3238cSHeinz Mauelshagen 	unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
11241da177e4SLinus Torvalds 	struct mapped_device *md;
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	md = find_device(param);
11271da177e4SLinus Torvalds 	if (!md)
11281da177e4SLinus Torvalds 		return -ENXIO;
11291da177e4SLinus Torvalds 
11306da487dcSAlasdair G Kergon 	if (param->flags & DM_SKIP_LOCKFS_FLAG)
1131a3d77d35SKiyoshi Ueda 		suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
113281fdb096SKiyoshi Ueda 	if (param->flags & DM_NOFLUSH_FLAG)
113381fdb096SKiyoshi Ueda 		suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
11346da487dcSAlasdair G Kergon 
1135094ea9a0SAlasdair G Kergon 	if (!dm_suspended_md(md)) {
1136a3d77d35SKiyoshi Ueda 		r = dm_suspend(md, suspend_flags);
1137094ea9a0SAlasdair G Kergon 		if (r)
1138094ea9a0SAlasdair G Kergon 			goto out;
1139094ea9a0SAlasdair G Kergon 	}
11401da177e4SLinus Torvalds 
1141094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
11421da177e4SLinus Torvalds 
1143094ea9a0SAlasdair G Kergon out:
11441da177e4SLinus Torvalds 	dm_put(md);
1145094ea9a0SAlasdair G Kergon 
11461da177e4SLinus Torvalds 	return r;
11471da177e4SLinus Torvalds }
11481da177e4SLinus Torvalds 
do_resume(struct dm_ioctl * param)11491da177e4SLinus Torvalds static int do_resume(struct dm_ioctl *param)
11501da177e4SLinus Torvalds {
11511da177e4SLinus Torvalds 	int r = 0;
115286a3238cSHeinz Mauelshagen 	unsigned int suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
11531da177e4SLinus Torvalds 	struct hash_cell *hc;
11541da177e4SLinus Torvalds 	struct mapped_device *md;
1155042d2a9bSAlasdair G Kergon 	struct dm_table *new_map, *old_map = NULL;
11567533afa1SMikulas Patocka 	bool need_resize_uevent = false;
11571da177e4SLinus Torvalds 
11581da177e4SLinus Torvalds 	down_write(&_hash_lock);
11591da177e4SLinus Torvalds 
11601da177e4SLinus Torvalds 	hc = __find_device_hash_cell(param);
11611da177e4SLinus Torvalds 	if (!hc) {
1162810b4923SMilan Broz 		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
11631da177e4SLinus Torvalds 		up_write(&_hash_lock);
11641da177e4SLinus Torvalds 		return -ENXIO;
11651da177e4SLinus Torvalds 	}
11661da177e4SLinus Torvalds 
11671da177e4SLinus Torvalds 	md = hc->md;
11681da177e4SLinus Torvalds 
11691da177e4SLinus Torvalds 	new_map = hc->new_map;
11701da177e4SLinus Torvalds 	hc->new_map = NULL;
11711da177e4SLinus Torvalds 	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
11721da177e4SLinus Torvalds 
11731da177e4SLinus Torvalds 	up_write(&_hash_lock);
11741da177e4SLinus Torvalds 
11751da177e4SLinus Torvalds 	/* Do we need to load a new map ? */
11761da177e4SLinus Torvalds 	if (new_map) {
11777533afa1SMikulas Patocka 		sector_t old_size, new_size;
11787533afa1SMikulas Patocka 
11791da177e4SLinus Torvalds 		/* Suspend if it isn't already suspended */
11802760904dSLi Lingfeng 		if (param->flags & DM_SKIP_LOCKFS_FLAG)
1181a3d77d35SKiyoshi Ueda 			suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
118281fdb096SKiyoshi Ueda 		if (param->flags & DM_NOFLUSH_FLAG)
118381fdb096SKiyoshi Ueda 			suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
11844f186f8bSKiyoshi Ueda 		if (!dm_suspended_md(md))
1185a3d77d35SKiyoshi Ueda 			dm_suspend(md, suspend_flags);
11861da177e4SLinus Torvalds 
11877533afa1SMikulas Patocka 		old_size = dm_get_size(md);
1188042d2a9bSAlasdair G Kergon 		old_map = dm_swap_table(md, new_map);
1189042d2a9bSAlasdair G Kergon 		if (IS_ERR(old_map)) {
119083d5e5b0SMikulas Patocka 			dm_sync_table(md);
1191d5816876SMikulas Patocka 			dm_table_destroy(new_map);
11921da177e4SLinus Torvalds 			dm_put(md);
1193042d2a9bSAlasdair G Kergon 			return PTR_ERR(old_map);
11941da177e4SLinus Torvalds 		}
11957533afa1SMikulas Patocka 		new_size = dm_get_size(md);
11967533afa1SMikulas Patocka 		if (old_size && new_size && old_size != new_size)
11977533afa1SMikulas Patocka 			need_resize_uevent = true;
11981da177e4SLinus Torvalds 
119905bdb996SChristoph Hellwig 		if (dm_table_get_mode(new_map) & BLK_OPEN_WRITE)
12001da177e4SLinus Torvalds 			set_disk_ro(dm_disk(md), 0);
12011da177e4SLinus Torvalds 		else
12021da177e4SLinus Torvalds 			set_disk_ro(dm_disk(md), 1);
12031da177e4SLinus Torvalds 	}
12041da177e4SLinus Torvalds 
12050f3649a9SMike Snitzer 	if (dm_suspended_md(md)) {
12061da177e4SLinus Torvalds 		r = dm_resume(md);
12078eb6fab4STushar Sugandhi 		if (!r) {
12088eb6fab4STushar Sugandhi 			dm_ima_measure_on_device_resume(md, new_map ? true : false);
12098eb6fab4STushar Sugandhi 
12107533afa1SMikulas Patocka 			if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
12113abf85b5SPeter Rajnoha 				param->flags |= DM_UEVENT_GENERATED_FLAG;
12120f3649a9SMike Snitzer 		}
12138eb6fab4STushar Sugandhi 	}
12141da177e4SLinus Torvalds 
121583d5e5b0SMikulas Patocka 	/*
121683d5e5b0SMikulas Patocka 	 * Since dm_swap_table synchronizes RCU, nobody should be in
121783d5e5b0SMikulas Patocka 	 * read-side critical section already.
121883d5e5b0SMikulas Patocka 	 */
1219042d2a9bSAlasdair G Kergon 	if (old_map)
1220042d2a9bSAlasdair G Kergon 		dm_table_destroy(old_map);
122160935eb2SMilan Broz 
12220f3649a9SMike Snitzer 	if (!r)
1223094ea9a0SAlasdair G Kergon 		__dev_status(md, param);
12241da177e4SLinus Torvalds 
12251da177e4SLinus Torvalds 	dm_put(md);
12261da177e4SLinus Torvalds 	return r;
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds /*
12301da177e4SLinus Torvalds  * Set or unset the suspension state of a device.
12311da177e4SLinus Torvalds  * If the device already is in the requested state we just return its status.
12321da177e4SLinus Torvalds  */
dev_suspend(struct file * filp,struct dm_ioctl * param,size_t param_size)1233fc1841e1SMikulas Patocka static int dev_suspend(struct file *filp, struct dm_ioctl *param, size_t param_size)
12341da177e4SLinus Torvalds {
12351da177e4SLinus Torvalds 	if (param->flags & DM_SUSPEND_FLAG)
12361da177e4SLinus Torvalds 		return do_suspend(param);
12371da177e4SLinus Torvalds 
12381da177e4SLinus Torvalds 	return do_resume(param);
12391da177e4SLinus Torvalds }
12401da177e4SLinus Torvalds 
12411da177e4SLinus Torvalds /*
12421da177e4SLinus Torvalds  * Copies device info back to user space, used by
12431da177e4SLinus Torvalds  * the create and info ioctls.
12441da177e4SLinus Torvalds  */
dev_status(struct file * filp,struct dm_ioctl * param,size_t param_size)1245fc1841e1SMikulas Patocka static int dev_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
12461da177e4SLinus Torvalds {
12471da177e4SLinus Torvalds 	struct mapped_device *md;
12481da177e4SLinus Torvalds 
12491da177e4SLinus Torvalds 	md = find_device(param);
12501da177e4SLinus Torvalds 	if (!md)
12511da177e4SLinus Torvalds 		return -ENXIO;
12521da177e4SLinus Torvalds 
1253094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
12541da177e4SLinus Torvalds 	dm_put(md);
1255094ea9a0SAlasdair G Kergon 
1256094ea9a0SAlasdair G Kergon 	return 0;
12571da177e4SLinus Torvalds }
12581da177e4SLinus Torvalds 
12591da177e4SLinus Torvalds /*
12601da177e4SLinus Torvalds  * Build up the status struct for each target
12611da177e4SLinus Torvalds  */
retrieve_status(struct dm_table * table,struct dm_ioctl * param,size_t param_size)12621da177e4SLinus Torvalds static void retrieve_status(struct dm_table *table,
12631da177e4SLinus Torvalds 			    struct dm_ioctl *param, size_t param_size)
12641da177e4SLinus Torvalds {
12651da177e4SLinus Torvalds 	unsigned int i, num_targets;
12661da177e4SLinus Torvalds 	struct dm_target_spec *spec;
12671da177e4SLinus Torvalds 	char *outbuf, *outptr;
12681da177e4SLinus Torvalds 	status_type_t type;
12691da177e4SLinus Torvalds 	size_t remaining, len, used = 0;
127086a3238cSHeinz Mauelshagen 	unsigned int status_flags = 0;
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds 	outptr = outbuf = get_result_buffer(param, param_size, &len);
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds 	if (param->flags & DM_STATUS_TABLE_FLAG)
12751da177e4SLinus Torvalds 		type = STATUSTYPE_TABLE;
127691ccbbacSTushar Sugandhi 	else if (param->flags & DM_IMA_MEASUREMENT_FLAG)
127791ccbbacSTushar Sugandhi 		type = STATUSTYPE_IMA;
12781da177e4SLinus Torvalds 	else
12791da177e4SLinus Torvalds 		type = STATUSTYPE_INFO;
12801da177e4SLinus Torvalds 
12811da177e4SLinus Torvalds 	/* Get all the target info */
12822aec377aSMike Snitzer 	num_targets = table->num_targets;
12831da177e4SLinus Torvalds 	for (i = 0; i < num_targets; i++) {
12841da177e4SLinus Torvalds 		struct dm_target *ti = dm_table_get_target(table, i);
1285fd7c092eSMikulas Patocka 		size_t l;
12861da177e4SLinus Torvalds 
12871da177e4SLinus Torvalds 		remaining = len - (outptr - outbuf);
12881da177e4SLinus Torvalds 		if (remaining <= sizeof(struct dm_target_spec)) {
12891da177e4SLinus Torvalds 			param->flags |= DM_BUFFER_FULL_FLAG;
12901da177e4SLinus Torvalds 			break;
12911da177e4SLinus Torvalds 		}
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 		spec = (struct dm_target_spec *) outptr;
12941da177e4SLinus Torvalds 
12951da177e4SLinus Torvalds 		spec->status = 0;
12961da177e4SLinus Torvalds 		spec->sector_start = ti->begin;
12971da177e4SLinus Torvalds 		spec->length = ti->len;
12981da177e4SLinus Torvalds 		strncpy(spec->target_type, ti->type->name,
12991aeb6e7cSDamien Le Moal 			sizeof(spec->target_type) - 1);
13001da177e4SLinus Torvalds 
13011da177e4SLinus Torvalds 		outptr += sizeof(struct dm_target_spec);
13021da177e4SLinus Torvalds 		remaining = len - (outptr - outbuf);
13031da177e4SLinus Torvalds 		if (remaining <= 0) {
13041da177e4SLinus Torvalds 			param->flags |= DM_BUFFER_FULL_FLAG;
13051da177e4SLinus Torvalds 			break;
13061da177e4SLinus Torvalds 		}
13071da177e4SLinus Torvalds 
13081da177e4SLinus Torvalds 		/* Get the status/table string from the target driver */
13091da177e4SLinus Torvalds 		if (ti->type->status) {
13101f4e0ff0SAlasdair G Kergon 			if (param->flags & DM_NOFLUSH_FLAG)
13111f4e0ff0SAlasdair G Kergon 				status_flags |= DM_STATUS_NOFLUSH_FLAG;
1312fd7c092eSMikulas Patocka 			ti->type->status(ti, type, status_flags, outptr, remaining);
13131da177e4SLinus Torvalds 		} else
13141da177e4SLinus Torvalds 			outptr[0] = '\0';
13151da177e4SLinus Torvalds 
1316fd7c092eSMikulas Patocka 		l = strlen(outptr) + 1;
1317fd7c092eSMikulas Patocka 		if (l == remaining) {
1318fd7c092eSMikulas Patocka 			param->flags |= DM_BUFFER_FULL_FLAG;
1319fd7c092eSMikulas Patocka 			break;
1320fd7c092eSMikulas Patocka 		}
1321fd7c092eSMikulas Patocka 
1322fd7c092eSMikulas Patocka 		outptr += l;
13231da177e4SLinus Torvalds 		used = param->data_start + (outptr - outbuf);
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds 		outptr = align_ptr(outptr);
13261da177e4SLinus Torvalds 		spec->next = outptr - outbuf;
13271da177e4SLinus Torvalds 	}
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds 	if (used)
13301da177e4SLinus Torvalds 		param->data_size = used;
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds 	param->target_count = num_targets;
13331da177e4SLinus Torvalds }
13341da177e4SLinus Torvalds 
13351da177e4SLinus Torvalds /*
13361da177e4SLinus Torvalds  * Wait for a device to report an event
13371da177e4SLinus Torvalds  */
dev_wait(struct file * filp,struct dm_ioctl * param,size_t param_size)1338fc1841e1SMikulas Patocka static int dev_wait(struct file *filp, struct dm_ioctl *param, size_t param_size)
13391da177e4SLinus Torvalds {
1340094ea9a0SAlasdair G Kergon 	int r = 0;
13411da177e4SLinus Torvalds 	struct mapped_device *md;
13421da177e4SLinus Torvalds 	struct dm_table *table;
134383d5e5b0SMikulas Patocka 	int srcu_idx;
13441da177e4SLinus Torvalds 
13451da177e4SLinus Torvalds 	md = find_device(param);
13461da177e4SLinus Torvalds 	if (!md)
13471da177e4SLinus Torvalds 		return -ENXIO;
13481da177e4SLinus Torvalds 
13491da177e4SLinus Torvalds 	/*
13501da177e4SLinus Torvalds 	 * Wait for a notification event
13511da177e4SLinus Torvalds 	 */
13521da177e4SLinus Torvalds 	if (dm_wait_event(md, param->event_nr)) {
13531da177e4SLinus Torvalds 		r = -ERESTARTSYS;
13541da177e4SLinus Torvalds 		goto out;
13551da177e4SLinus Torvalds 	}
13561da177e4SLinus Torvalds 
13571da177e4SLinus Torvalds 	/*
13581da177e4SLinus Torvalds 	 * The userland program is going to want to know what
13591da177e4SLinus Torvalds 	 * changed to trigger the event, so we may as well tell
13601da177e4SLinus Torvalds 	 * him and save an ioctl.
13611da177e4SLinus Torvalds 	 */
1362094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
13631da177e4SLinus Torvalds 
136483d5e5b0SMikulas Patocka 	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
136583d5e5b0SMikulas Patocka 	if (table)
13661da177e4SLinus Torvalds 		retrieve_status(table, param, param_size);
136783d5e5b0SMikulas Patocka 	dm_put_live_table(md, srcu_idx);
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds out:
13701da177e4SLinus Torvalds 	dm_put(md);
1371094ea9a0SAlasdair G Kergon 
13721da177e4SLinus Torvalds 	return r;
13731da177e4SLinus Torvalds }
13741da177e4SLinus Torvalds 
1375fc1841e1SMikulas Patocka /*
1376fc1841e1SMikulas Patocka  * Remember the global event number and make it possible to poll
1377fc1841e1SMikulas Patocka  * for further events.
1378fc1841e1SMikulas Patocka  */
dev_arm_poll(struct file * filp,struct dm_ioctl * param,size_t param_size)1379fc1841e1SMikulas Patocka static int dev_arm_poll(struct file *filp, struct dm_ioctl *param, size_t param_size)
1380fc1841e1SMikulas Patocka {
1381fc1841e1SMikulas Patocka 	struct dm_file *priv = filp->private_data;
1382fc1841e1SMikulas Patocka 
1383fc1841e1SMikulas Patocka 	priv->global_event_nr = atomic_read(&dm_global_event_nr);
1384fc1841e1SMikulas Patocka 
1385fc1841e1SMikulas Patocka 	return 0;
1386fc1841e1SMikulas Patocka }
1387fc1841e1SMikulas Patocka 
get_mode(struct dm_ioctl * param)138805bdb996SChristoph Hellwig static inline blk_mode_t get_mode(struct dm_ioctl *param)
13891da177e4SLinus Torvalds {
139005bdb996SChristoph Hellwig 	blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_WRITE;
13911da177e4SLinus Torvalds 
13921da177e4SLinus Torvalds 	if (param->flags & DM_READONLY_FLAG)
139305bdb996SChristoph Hellwig 		mode = BLK_OPEN_READ;
13941da177e4SLinus Torvalds 
13951da177e4SLinus Torvalds 	return mode;
13961da177e4SLinus Torvalds }
13971da177e4SLinus Torvalds 
next_target(struct dm_target_spec * last,uint32_t next,const char * end,struct dm_target_spec ** spec,char ** target_params)139810655c7aSDemi Marie Obenour static int next_target(struct dm_target_spec *last, uint32_t next, const char *end,
13991da177e4SLinus Torvalds 		       struct dm_target_spec **spec, char **target_params)
14001da177e4SLinus Torvalds {
1401b60528d9SDemi Marie Obenour 	static_assert(__alignof__(struct dm_target_spec) <= 8,
1402b60528d9SDemi Marie Obenour 		"struct dm_target_spec must not require more than 8-byte alignment");
1403b60528d9SDemi Marie Obenour 
140413f4a697SDemi Marie Obenour 	/*
140513f4a697SDemi Marie Obenour 	 * Number of bytes remaining, starting with last. This is always
140613f4a697SDemi Marie Obenour 	 * sizeof(struct dm_target_spec) or more, as otherwise *last was
140713f4a697SDemi Marie Obenour 	 * out of bounds already.
140813f4a697SDemi Marie Obenour 	 */
140910655c7aSDemi Marie Obenour 	size_t remaining = end - (char *)last;
141013f4a697SDemi Marie Obenour 
141113f4a697SDemi Marie Obenour 	/*
141213f4a697SDemi Marie Obenour 	 * There must be room for both the next target spec and the
141313f4a697SDemi Marie Obenour 	 * NUL-terminator of the target itself.
141413f4a697SDemi Marie Obenour 	 */
141513f4a697SDemi Marie Obenour 	if (remaining - sizeof(struct dm_target_spec) <= next) {
141613f4a697SDemi Marie Obenour 		DMERR("Target spec extends beyond end of parameters");
141713f4a697SDemi Marie Obenour 		return -EINVAL;
141813f4a697SDemi Marie Obenour 	}
141913f4a697SDemi Marie Obenour 
1420b60528d9SDemi Marie Obenour 	if (next % __alignof__(struct dm_target_spec)) {
1421b60528d9SDemi Marie Obenour 		DMERR("Next dm_target_spec (offset %u) is not %zu-byte aligned",
1422b60528d9SDemi Marie Obenour 		      next, __alignof__(struct dm_target_spec));
1423b60528d9SDemi Marie Obenour 		return -EINVAL;
1424b60528d9SDemi Marie Obenour 	}
1425b60528d9SDemi Marie Obenour 
14261da177e4SLinus Torvalds 	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);
14271da177e4SLinus Torvalds 	*target_params = (char *) (*spec + 1);
14281da177e4SLinus Torvalds 
142910655c7aSDemi Marie Obenour 	return 0;
14301da177e4SLinus Torvalds }
14311da177e4SLinus Torvalds 
populate_table(struct dm_table * table,struct dm_ioctl * param,size_t param_size)14321da177e4SLinus Torvalds static int populate_table(struct dm_table *table,
14331da177e4SLinus Torvalds 			  struct dm_ioctl *param, size_t param_size)
14341da177e4SLinus Torvalds {
14351da177e4SLinus Torvalds 	int r;
14361da177e4SLinus Torvalds 	unsigned int i = 0;
14371da177e4SLinus Torvalds 	struct dm_target_spec *spec = (struct dm_target_spec *) param;
14381da177e4SLinus Torvalds 	uint32_t next = param->data_start;
143910655c7aSDemi Marie Obenour 	const char *const end = (const char *) param + param_size;
14401da177e4SLinus Torvalds 	char *target_params;
144110655c7aSDemi Marie Obenour 	size_t min_size = sizeof(struct dm_ioctl);
14421da177e4SLinus Torvalds 
14431da177e4SLinus Torvalds 	if (!param->target_count) {
14441c131886SHeinz Mauelshagen 		DMERR("%s: no targets specified", __func__);
14451da177e4SLinus Torvalds 		return -EINVAL;
14461da177e4SLinus Torvalds 	}
14471da177e4SLinus Torvalds 
14481da177e4SLinus Torvalds 	for (i = 0; i < param->target_count; i++) {
144910655c7aSDemi Marie Obenour 		const char *nul_terminator;
145010655c7aSDemi Marie Obenour 
145110655c7aSDemi Marie Obenour 		if (next < min_size) {
145210655c7aSDemi Marie Obenour 			DMERR("%s: next target spec (offset %u) overlaps %s",
145310655c7aSDemi Marie Obenour 			      __func__, next, i ? "previous target" : "'struct dm_ioctl'");
145410655c7aSDemi Marie Obenour 			return -EINVAL;
145510655c7aSDemi Marie Obenour 		}
14561da177e4SLinus Torvalds 
14571da177e4SLinus Torvalds 		r = next_target(spec, next, end, &spec, &target_params);
14581da177e4SLinus Torvalds 		if (r) {
145943e6c111SMikulas Patocka 			DMERR("unable to find target");
14601da177e4SLinus Torvalds 			return r;
14611da177e4SLinus Torvalds 		}
14621da177e4SLinus Torvalds 
146310655c7aSDemi Marie Obenour 		nul_terminator = memchr(target_params, 0, (size_t)(end - target_params));
146410655c7aSDemi Marie Obenour 		if (nul_terminator == NULL) {
146510655c7aSDemi Marie Obenour 			DMERR("%s: target parameters not NUL-terminated", __func__);
146610655c7aSDemi Marie Obenour 			return -EINVAL;
146710655c7aSDemi Marie Obenour 		}
146810655c7aSDemi Marie Obenour 
146910655c7aSDemi Marie Obenour 		/* Add 1 for NUL terminator */
147010655c7aSDemi Marie Obenour 		min_size = (size_t)(nul_terminator - (const char *)spec) + 1;
147110655c7aSDemi Marie Obenour 
14721da177e4SLinus Torvalds 		r = dm_table_add_target(table, spec->target_type,
14731da177e4SLinus Torvalds 					(sector_t) spec->sector_start,
14741da177e4SLinus Torvalds 					(sector_t) spec->length,
14751da177e4SLinus Torvalds 					target_params);
14761da177e4SLinus Torvalds 		if (r) {
147743e6c111SMikulas Patocka 			DMERR("error adding target to table");
14781da177e4SLinus Torvalds 			return r;
14791da177e4SLinus Torvalds 		}
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds 		next = spec->next;
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 
14841da177e4SLinus Torvalds 	return dm_table_complete(table);
14851da177e4SLinus Torvalds }
14861da177e4SLinus Torvalds 
is_valid_type(enum dm_queue_mode cur,enum dm_queue_mode new)14877e0d574fSBart Van Assche static bool is_valid_type(enum dm_queue_mode cur, enum dm_queue_mode new)
1488b5ab4a9bSToshi Kani {
1489b5ab4a9bSToshi Kani 	if (cur == new ||
1490b5ab4a9bSToshi Kani 	    (cur == DM_TYPE_BIO_BASED && new == DM_TYPE_DAX_BIO_BASED))
1491b5ab4a9bSToshi Kani 		return true;
1492b5ab4a9bSToshi Kani 
1493b5ab4a9bSToshi Kani 	return false;
1494b5ab4a9bSToshi Kani }
1495b5ab4a9bSToshi Kani 
table_load(struct file * filp,struct dm_ioctl * param,size_t param_size)1496fc1841e1SMikulas Patocka static int table_load(struct file *filp, struct dm_ioctl *param, size_t param_size)
14971da177e4SLinus Torvalds {
14981da177e4SLinus Torvalds 	int r;
14991da177e4SLinus Torvalds 	struct hash_cell *hc;
150083d5e5b0SMikulas Patocka 	struct dm_table *t, *old_map = NULL;
15011134e5aeSMike Anderson 	struct mapped_device *md;
150236a0456fSAlasdair G Kergon 	struct target_type *immutable_target_type;
15031da177e4SLinus Torvalds 
15041134e5aeSMike Anderson 	md = find_device(param);
15051134e5aeSMike Anderson 	if (!md)
15061134e5aeSMike Anderson 		return -ENXIO;
15071134e5aeSMike Anderson 
15081134e5aeSMike Anderson 	r = dm_table_create(&t, get_mode(param), param->target_count, md);
15091da177e4SLinus Torvalds 	if (r)
1510f11c1c56SMike Snitzer 		goto err;
15111da177e4SLinus Torvalds 
151200c4fc3bSMike Snitzer 	/* Protect md->type and md->queue against concurrent table loads. */
151300c4fc3bSMike Snitzer 	dm_lock_md_type(md);
15141da177e4SLinus Torvalds 	r = populate_table(t, param, param_size);
1515f11c1c56SMike Snitzer 	if (r)
1516f11c1c56SMike Snitzer 		goto err_unlock_md_type;
15171da177e4SLinus Torvalds 
151891ccbbacSTushar Sugandhi 	dm_ima_measure_on_table_load(t, STATUSTYPE_IMA);
151991ccbbacSTushar Sugandhi 
152036a0456fSAlasdair G Kergon 	immutable_target_type = dm_get_immutable_target_type(md);
152136a0456fSAlasdair G Kergon 	if (immutable_target_type &&
1522f083b09bSMike Snitzer 	    (immutable_target_type != dm_table_get_immutable_target_type(t)) &&
1523f083b09bSMike Snitzer 	    !dm_table_get_wildcard_target(t)) {
152443e6c111SMikulas Patocka 		DMERR("can't replace immutable target type %s",
152536a0456fSAlasdair G Kergon 		      immutable_target_type->name);
152636a0456fSAlasdair G Kergon 		r = -EINVAL;
1527f11c1c56SMike Snitzer 		goto err_unlock_md_type;
152836a0456fSAlasdair G Kergon 	}
152936a0456fSAlasdair G Kergon 
15303e6180f0SChristoph Hellwig 	if (dm_get_md_type(md) == DM_TYPE_NONE) {
15314a0b4ddfSMike Snitzer 		/* setup md->queue to reflect md's type (may block) */
1532591ddcfcSMike Snitzer 		r = dm_setup_md_queue(md, t);
15334a0b4ddfSMike Snitzer 		if (r) {
153443e6c111SMikulas Patocka 			DMERR("unable to set up device queue for new table.");
1535f11c1c56SMike Snitzer 			goto err_unlock_md_type;
15364a0b4ddfSMike Snitzer 		}
1537b5ab4a9bSToshi Kani 	} else if (!is_valid_type(dm_get_md_type(md), dm_table_get_type(t))) {
153843e6c111SMikulas Patocka 		DMERR("can't change device type (old=%u vs new=%u) after initial table load.",
1539b2b04e7eSMike Snitzer 		      dm_get_md_type(md), dm_table_get_type(t));
15403e6180f0SChristoph Hellwig 		r = -EINVAL;
15413e6180f0SChristoph Hellwig 		goto err_unlock_md_type;
15423e6180f0SChristoph Hellwig 	}
15433e6180f0SChristoph Hellwig 
1544a5664dadSMike Snitzer 	dm_unlock_md_type(md);
1545a5664dadSMike Snitzer 
1546a5664dadSMike Snitzer 	/* stage inactive table */
15471da177e4SLinus Torvalds 	down_write(&_hash_lock);
15481134e5aeSMike Anderson 	hc = dm_get_mdptr(md);
1549a2f998a7SHou Tao 	if (!hc) {
155043e6c111SMikulas Patocka 		DMERR("device has been removed from the dev hash table.");
15511134e5aeSMike Anderson 		up_write(&_hash_lock);
15521134e5aeSMike Anderson 		r = -ENXIO;
1553f11c1c56SMike Snitzer 		goto err_destroy_table;
15541da177e4SLinus Torvalds 	}
15551da177e4SLinus Torvalds 
15561da177e4SLinus Torvalds 	if (hc->new_map)
155783d5e5b0SMikulas Patocka 		old_map = hc->new_map;
15581da177e4SLinus Torvalds 	hc->new_map = t;
15591da177e4SLinus Torvalds 	up_write(&_hash_lock);
15601134e5aeSMike Anderson 
15611134e5aeSMike Anderson 	param->flags |= DM_INACTIVE_PRESENT_FLAG;
1562094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
15631134e5aeSMike Anderson 
156483d5e5b0SMikulas Patocka 	if (old_map) {
156583d5e5b0SMikulas Patocka 		dm_sync_table(md);
156683d5e5b0SMikulas Patocka 		dm_table_destroy(old_map);
156783d5e5b0SMikulas Patocka 	}
156883d5e5b0SMikulas Patocka 
15691134e5aeSMike Anderson 	dm_put(md);
15701134e5aeSMike Anderson 
1571f11c1c56SMike Snitzer 	return 0;
1572f11c1c56SMike Snitzer 
1573f11c1c56SMike Snitzer err_unlock_md_type:
1574f11c1c56SMike Snitzer 	dm_unlock_md_type(md);
1575f11c1c56SMike Snitzer err_destroy_table:
1576f11c1c56SMike Snitzer 	dm_table_destroy(t);
1577f11c1c56SMike Snitzer err:
1578f11c1c56SMike Snitzer 	dm_put(md);
1579f11c1c56SMike Snitzer 
15801da177e4SLinus Torvalds 	return r;
15811da177e4SLinus Torvalds }
15821da177e4SLinus Torvalds 
table_clear(struct file * filp,struct dm_ioctl * param,size_t param_size)1583fc1841e1SMikulas Patocka static int table_clear(struct file *filp, struct dm_ioctl *param, size_t param_size)
15841da177e4SLinus Torvalds {
15851da177e4SLinus Torvalds 	struct hash_cell *hc;
15867ec75f25SJeff Mahoney 	struct mapped_device *md;
158783d5e5b0SMikulas Patocka 	struct dm_table *old_map = NULL;
158899169b93STushar Sugandhi 	bool has_new_map = false;
15891da177e4SLinus Torvalds 
15901da177e4SLinus Torvalds 	down_write(&_hash_lock);
15911da177e4SLinus Torvalds 
15921da177e4SLinus Torvalds 	hc = __find_device_hash_cell(param);
15931da177e4SLinus Torvalds 	if (!hc) {
1594810b4923SMilan Broz 		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
15951da177e4SLinus Torvalds 		up_write(&_hash_lock);
15961da177e4SLinus Torvalds 		return -ENXIO;
15971da177e4SLinus Torvalds 	}
15981da177e4SLinus Torvalds 
15991da177e4SLinus Torvalds 	if (hc->new_map) {
160083d5e5b0SMikulas Patocka 		old_map = hc->new_map;
16011da177e4SLinus Torvalds 		hc->new_map = NULL;
160299169b93STushar Sugandhi 		has_new_map = true;
16031da177e4SLinus Torvalds 	}
16041da177e4SLinus Torvalds 
16057ec75f25SJeff Mahoney 	md = hc->md;
16061da177e4SLinus Torvalds 	up_write(&_hash_lock);
16073d32aaa7SMike Snitzer 
16083d32aaa7SMike Snitzer 	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
16093d32aaa7SMike Snitzer 	__dev_status(md, param);
16103d32aaa7SMike Snitzer 
161183d5e5b0SMikulas Patocka 	if (old_map) {
161283d5e5b0SMikulas Patocka 		dm_sync_table(md);
161383d5e5b0SMikulas Patocka 		dm_table_destroy(old_map);
161483d5e5b0SMikulas Patocka 	}
161599169b93STushar Sugandhi 	dm_ima_measure_on_table_clear(md, has_new_map);
16167ec75f25SJeff Mahoney 	dm_put(md);
1617094ea9a0SAlasdair G Kergon 
1618094ea9a0SAlasdair G Kergon 	return 0;
16191da177e4SLinus Torvalds }
16201da177e4SLinus Torvalds 
16211da177e4SLinus Torvalds /*
16221da177e4SLinus Torvalds  * Retrieves a list of devices used by a particular dm device.
16231da177e4SLinus Torvalds  */
retrieve_deps(struct dm_table * table,struct dm_ioctl * param,size_t param_size)16241da177e4SLinus Torvalds static void retrieve_deps(struct dm_table *table,
16251da177e4SLinus Torvalds 			  struct dm_ioctl *param, size_t param_size)
16261da177e4SLinus Torvalds {
16271da177e4SLinus Torvalds 	unsigned int count = 0;
16281da177e4SLinus Torvalds 	struct list_head *tmp;
16291da177e4SLinus Torvalds 	size_t len, needed;
163082b1519bSMikulas Patocka 	struct dm_dev_internal *dd;
16311da177e4SLinus Torvalds 	struct dm_target_deps *deps;
16321da177e4SLinus Torvalds 
1633f6007dceSMikulas Patocka 	down_read(&table->devices_lock);
1634f6007dceSMikulas Patocka 
16351da177e4SLinus Torvalds 	deps = get_result_buffer(param, param_size, &len);
16361da177e4SLinus Torvalds 
16371da177e4SLinus Torvalds 	/*
16381da177e4SLinus Torvalds 	 * Count the devices.
16391da177e4SLinus Torvalds 	 */
16401da177e4SLinus Torvalds 	list_for_each(tmp, dm_table_get_devices(table))
16411da177e4SLinus Torvalds 		count++;
16421da177e4SLinus Torvalds 
16431da177e4SLinus Torvalds 	/*
16441da177e4SLinus Torvalds 	 * Check we have enough space.
16451da177e4SLinus Torvalds 	 */
1646da899625SGustavo A. R. Silva 	needed = struct_size(deps, dev, count);
16471da177e4SLinus Torvalds 	if (len < needed) {
16481da177e4SLinus Torvalds 		param->flags |= DM_BUFFER_FULL_FLAG;
1649f6007dceSMikulas Patocka 		goto out;
16501da177e4SLinus Torvalds 	}
16511da177e4SLinus Torvalds 
16521da177e4SLinus Torvalds 	/*
16531da177e4SLinus Torvalds 	 * Fill in the devices.
16541da177e4SLinus Torvalds 	 */
16551da177e4SLinus Torvalds 	deps->count = count;
16561da177e4SLinus Torvalds 	count = 0;
16571da177e4SLinus Torvalds 	list_for_each_entry(dd, dm_table_get_devices(table), list)
165886f1152bSBenjamin Marzinski 		deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev);
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 	param->data_size = param->data_start + needed;
1661f6007dceSMikulas Patocka 
1662f6007dceSMikulas Patocka out:
1663f6007dceSMikulas Patocka 	up_read(&table->devices_lock);
16641da177e4SLinus Torvalds }
16651da177e4SLinus Torvalds 
table_deps(struct file * filp,struct dm_ioctl * param,size_t param_size)1666fc1841e1SMikulas Patocka static int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size)
16671da177e4SLinus Torvalds {
16681da177e4SLinus Torvalds 	struct mapped_device *md;
16691da177e4SLinus Torvalds 	struct dm_table *table;
167083d5e5b0SMikulas Patocka 	int srcu_idx;
16711da177e4SLinus Torvalds 
16721da177e4SLinus Torvalds 	md = find_device(param);
16731da177e4SLinus Torvalds 	if (!md)
16741da177e4SLinus Torvalds 		return -ENXIO;
16751da177e4SLinus Torvalds 
1676094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
16771da177e4SLinus Torvalds 
167883d5e5b0SMikulas Patocka 	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
167983d5e5b0SMikulas Patocka 	if (table)
16801da177e4SLinus Torvalds 		retrieve_deps(table, param, param_size);
168183d5e5b0SMikulas Patocka 	dm_put_live_table(md, srcu_idx);
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds 	dm_put(md);
1684094ea9a0SAlasdair G Kergon 
1685094ea9a0SAlasdair G Kergon 	return 0;
16861da177e4SLinus Torvalds }
16871da177e4SLinus Torvalds 
16881da177e4SLinus Torvalds /*
16891da177e4SLinus Torvalds  * Return the status of a device as a text string for each
16901da177e4SLinus Torvalds  * target.
16911da177e4SLinus Torvalds  */
table_status(struct file * filp,struct dm_ioctl * param,size_t param_size)1692fc1841e1SMikulas Patocka static int table_status(struct file *filp, struct dm_ioctl *param, size_t param_size)
16931da177e4SLinus Torvalds {
16941da177e4SLinus Torvalds 	struct mapped_device *md;
16951da177e4SLinus Torvalds 	struct dm_table *table;
169683d5e5b0SMikulas Patocka 	int srcu_idx;
16971da177e4SLinus Torvalds 
16981da177e4SLinus Torvalds 	md = find_device(param);
16991da177e4SLinus Torvalds 	if (!md)
17001da177e4SLinus Torvalds 		return -ENXIO;
17011da177e4SLinus Torvalds 
1702094ea9a0SAlasdair G Kergon 	__dev_status(md, param);
17031da177e4SLinus Torvalds 
170483d5e5b0SMikulas Patocka 	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
170583d5e5b0SMikulas Patocka 	if (table)
17061da177e4SLinus Torvalds 		retrieve_status(table, param, param_size);
170783d5e5b0SMikulas Patocka 	dm_put_live_table(md, srcu_idx);
17081da177e4SLinus Torvalds 
17091da177e4SLinus Torvalds 	dm_put(md);
1710094ea9a0SAlasdair G Kergon 
1711094ea9a0SAlasdair G Kergon 	return 0;
17121da177e4SLinus Torvalds }
17131da177e4SLinus Torvalds 
1714a2606241SMikulas Patocka /*
1715fd2ed4d2SMikulas Patocka  * Process device-mapper dependent messages.  Messages prefixed with '@'
1716fd2ed4d2SMikulas Patocka  * are processed by the DM core.  All others are delivered to the target.
1717a2606241SMikulas Patocka  * Returns a number <= 1 if message was processed by device mapper.
1718a2606241SMikulas Patocka  * Returns 2 if message should be delivered to the target.
1719a2606241SMikulas Patocka  */
message_for_md(struct mapped_device * md,unsigned int argc,char ** argv,char * result,unsigned int maxlen)172086a3238cSHeinz Mauelshagen static int message_for_md(struct mapped_device *md, unsigned int argc, char **argv,
172186a3238cSHeinz Mauelshagen 			  char *result, unsigned int maxlen)
1722a2606241SMikulas Patocka {
1723fd2ed4d2SMikulas Patocka 	int r;
1724fd2ed4d2SMikulas Patocka 
1725fd2ed4d2SMikulas Patocka 	if (**argv != '@')
1726fd2ed4d2SMikulas Patocka 		return 2; /* no '@' prefix, deliver to target */
1727fd2ed4d2SMikulas Patocka 
17282c140a24SMikulas Patocka 	if (!strcasecmp(argv[0], "@cancel_deferred_remove")) {
17292c140a24SMikulas Patocka 		if (argc != 1) {
17302c140a24SMikulas Patocka 			DMERR("Invalid arguments for @cancel_deferred_remove");
17312c140a24SMikulas Patocka 			return -EINVAL;
17322c140a24SMikulas Patocka 		}
17332c140a24SMikulas Patocka 		return dm_cancel_deferred_remove(md);
17342c140a24SMikulas Patocka 	}
17352c140a24SMikulas Patocka 
1736fd2ed4d2SMikulas Patocka 	r = dm_stats_message(md, argc, argv, result, maxlen);
1737fd2ed4d2SMikulas Patocka 	if (r < 2)
1738fd2ed4d2SMikulas Patocka 		return r;
1739fd2ed4d2SMikulas Patocka 
1740fd2ed4d2SMikulas Patocka 	DMERR("Unsupported message sent to DM core: %s", argv[0]);
1741fd2ed4d2SMikulas Patocka 	return -EINVAL;
1742a2606241SMikulas Patocka }
1743a2606241SMikulas Patocka 
17441da177e4SLinus Torvalds /*
17451da177e4SLinus Torvalds  * Pass a message to the target that's at the supplied device offset.
17461da177e4SLinus Torvalds  */
target_message(struct file * filp,struct dm_ioctl * param,size_t param_size)1747fc1841e1SMikulas Patocka static int target_message(struct file *filp, struct dm_ioctl *param, size_t param_size)
17481da177e4SLinus Torvalds {
17491da177e4SLinus Torvalds 	int r, argc;
17501da177e4SLinus Torvalds 	char **argv;
17511da177e4SLinus Torvalds 	struct mapped_device *md;
17521da177e4SLinus Torvalds 	struct dm_table *table;
17531da177e4SLinus Torvalds 	struct dm_target *ti;
17541da177e4SLinus Torvalds 	struct dm_target_msg *tmsg = (void *) param + param->data_start;
1755a2606241SMikulas Patocka 	size_t maxlen;
1756a2606241SMikulas Patocka 	char *result = get_result_buffer(param, param_size, &maxlen);
175783d5e5b0SMikulas Patocka 	int srcu_idx;
17581da177e4SLinus Torvalds 
17591da177e4SLinus Torvalds 	md = find_device(param);
17601da177e4SLinus Torvalds 	if (!md)
17611da177e4SLinus Torvalds 		return -ENXIO;
17621da177e4SLinus Torvalds 
1763027d50f9SMilan Broz 	if (tmsg < (struct dm_target_msg *) param->data ||
17641da177e4SLinus Torvalds 	    invalid_str(tmsg->message, (void *) param + param_size)) {
176543e6c111SMikulas Patocka 		DMERR("Invalid target message parameters.");
17661da177e4SLinus Torvalds 		r = -EINVAL;
17671da177e4SLinus Torvalds 		goto out;
17681da177e4SLinus Torvalds 	}
17691da177e4SLinus Torvalds 
17701da177e4SLinus Torvalds 	r = dm_split_args(&argc, &argv, tmsg->message);
17711da177e4SLinus Torvalds 	if (r) {
177243e6c111SMikulas Patocka 		DMERR("Failed to split target message parameters");
17731da177e4SLinus Torvalds 		goto out;
17741da177e4SLinus Torvalds 	}
17751da177e4SLinus Torvalds 
17762ca4c92fSAlasdair G Kergon 	if (!argc) {
177743e6c111SMikulas Patocka 		DMERR("Empty message received.");
17784d7659bfSQinglang Miao 		r = -EINVAL;
1779902c6a96SJesper Juhl 		goto out_argv;
17802ca4c92fSAlasdair G Kergon 	}
17812ca4c92fSAlasdair G Kergon 
1782a2606241SMikulas Patocka 	r = message_for_md(md, argc, argv, result, maxlen);
1783a2606241SMikulas Patocka 	if (r <= 1)
1784a2606241SMikulas Patocka 		goto out_argv;
1785a2606241SMikulas Patocka 
178683d5e5b0SMikulas Patocka 	table = dm_get_live_table(md, &srcu_idx);
17871da177e4SLinus Torvalds 	if (!table)
178883d5e5b0SMikulas Patocka 		goto out_table;
17891da177e4SLinus Torvalds 
1790c50abeb3SMike Anderson 	if (dm_deleting_md(md)) {
1791c50abeb3SMike Anderson 		r = -ENXIO;
1792c50abeb3SMike Anderson 		goto out_table;
1793c50abeb3SMike Anderson 	}
1794c50abeb3SMike Anderson 
1795512875bdSJun'ichi Nomura 	ti = dm_table_find_target(table, tmsg->sector);
1796123d87d5SMikulas Patocka 	if (!ti) {
179743e6c111SMikulas Patocka 		DMERR("Target message sector outside device.");
17981da177e4SLinus Torvalds 		r = -EINVAL;
1799512875bdSJun'ichi Nomura 	} else if (ti->type->message)
18001eb5fa84SMike Snitzer 		r = ti->type->message(ti, argc, argv, result, maxlen);
18011da177e4SLinus Torvalds 	else {
180243e6c111SMikulas Patocka 		DMERR("Target type does not support messages");
18031da177e4SLinus Torvalds 		r = -EINVAL;
18041da177e4SLinus Torvalds 	}
18051da177e4SLinus Torvalds 
1806c50abeb3SMike Anderson  out_table:
180783d5e5b0SMikulas Patocka 	dm_put_live_table(md, srcu_idx);
18081da177e4SLinus Torvalds  out_argv:
18091da177e4SLinus Torvalds 	kfree(argv);
18101da177e4SLinus Torvalds  out:
1811a2606241SMikulas Patocka 	if (r >= 0)
1812a2606241SMikulas Patocka 		__dev_status(md, param);
1813a2606241SMikulas Patocka 
1814a2606241SMikulas Patocka 	if (r == 1) {
1815a2606241SMikulas Patocka 		param->flags |= DM_DATA_OUT_FLAG;
1816fd2ed4d2SMikulas Patocka 		if (dm_message_test_buffer_overflow(result, maxlen))
1817a2606241SMikulas Patocka 			param->flags |= DM_BUFFER_FULL_FLAG;
1818a2606241SMikulas Patocka 		else
1819a2606241SMikulas Patocka 			param->data_size = param->data_start + strlen(result) + 1;
1820a2606241SMikulas Patocka 		r = 0;
1821a2606241SMikulas Patocka 	}
1822a2606241SMikulas Patocka 
18231da177e4SLinus Torvalds 	dm_put(md);
18241da177e4SLinus Torvalds 	return r;
18251da177e4SLinus Torvalds }
18261da177e4SLinus Torvalds 
1827e2914cc2SMikulas Patocka /*
1828e2914cc2SMikulas Patocka  * The ioctl parameter block consists of two parts, a dm_ioctl struct
1829e2914cc2SMikulas Patocka  * followed by a data buffer.  This flag is set if the second part,
1830e2914cc2SMikulas Patocka  * which has a variable size, is not used by the function processing
1831e2914cc2SMikulas Patocka  * the ioctl.
1832e2914cc2SMikulas Patocka  */
1833e2914cc2SMikulas Patocka #define IOCTL_FLAGS_NO_PARAMS		1
183462e08243SMikulas Patocka #define IOCTL_FLAGS_ISSUE_GLOBAL_EVENT	2
1835e2914cc2SMikulas Patocka 
1836a4a82ce3SHeinz Mauelshagen /*
1837a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
1838a4a82ce3SHeinz Mauelshagen  * Implementation of open/close/ioctl on the special char device.
1839a4a82ce3SHeinz Mauelshagen  *---------------------------------------------------------------
1840a4a82ce3SHeinz Mauelshagen  */
lookup_ioctl(unsigned int cmd,int * ioctl_flags)1841e2914cc2SMikulas Patocka static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
18421da177e4SLinus Torvalds {
1843cf0dec66SEric Biggers 	static const struct {
18441da177e4SLinus Torvalds 		int cmd;
1845e2914cc2SMikulas Patocka 		int flags;
18461da177e4SLinus Torvalds 		ioctl_fn fn;
18471da177e4SLinus Torvalds 	} _ioctls[] = {
1848e2914cc2SMikulas Patocka 		{DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */
184962e08243SMikulas Patocka 		{DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, remove_all},
1850e2914cc2SMikulas Patocka 		{DM_LIST_DEVICES_CMD, 0, list_devices},
18511da177e4SLinus Torvalds 
185262e08243SMikulas Patocka 		{DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_create},
185362e08243SMikulas Patocka 		{DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS | IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_remove},
185462e08243SMikulas Patocka 		{DM_DEV_RENAME_CMD, IOCTL_FLAGS_ISSUE_GLOBAL_EVENT, dev_rename},
1855e2914cc2SMikulas Patocka 		{DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},
1856e2914cc2SMikulas Patocka 		{DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},
1857e2914cc2SMikulas Patocka 		{DM_DEV_WAIT_CMD, 0, dev_wait},
18581da177e4SLinus Torvalds 
1859e2914cc2SMikulas Patocka 		{DM_TABLE_LOAD_CMD, 0, table_load},
1860e2914cc2SMikulas Patocka 		{DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear},
1861e2914cc2SMikulas Patocka 		{DM_TABLE_DEPS_CMD, 0, table_deps},
1862e2914cc2SMikulas Patocka 		{DM_TABLE_STATUS_CMD, 0, table_status},
18631da177e4SLinus Torvalds 
1864e2914cc2SMikulas Patocka 		{DM_LIST_VERSIONS_CMD, 0, list_versions},
18651da177e4SLinus Torvalds 
1866e2914cc2SMikulas Patocka 		{DM_TARGET_MSG_CMD, 0, target_message},
1867fc1841e1SMikulas Patocka 		{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry},
1868b52c3de8SMikulas Patocka 		{DM_DEV_ARM_POLL_CMD, IOCTL_FLAGS_NO_PARAMS, dev_arm_poll},
1869b52c3de8SMikulas Patocka 		{DM_GET_TARGET_VERSION_CMD, 0, get_target_version},
18701da177e4SLinus Torvalds 	};
18711da177e4SLinus Torvalds 
1872e2914cc2SMikulas Patocka 	if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
1873e2914cc2SMikulas Patocka 		return NULL;
1874e2914cc2SMikulas Patocka 
1875cd9c88daSJordy Zomer 	cmd = array_index_nospec(cmd, ARRAY_SIZE(_ioctls));
1876e2914cc2SMikulas Patocka 	*ioctl_flags = _ioctls[cmd].flags;
1877e2914cc2SMikulas Patocka 	return _ioctls[cmd].fn;
18781da177e4SLinus Torvalds }
18791da177e4SLinus Torvalds 
18801da177e4SLinus Torvalds /*
18811da177e4SLinus Torvalds  * As well as checking the version compatibility this always
18821da177e4SLinus Torvalds  * copies the kernel interface version out.
18831da177e4SLinus Torvalds  */
check_version(unsigned int cmd,struct dm_ioctl __user * user,struct dm_ioctl * kernel_params)1884249bed82SDemi Marie Obenour static int check_version(unsigned int cmd, struct dm_ioctl __user *user,
1885249bed82SDemi Marie Obenour 			 struct dm_ioctl *kernel_params)
18861da177e4SLinus Torvalds {
18871da177e4SLinus Torvalds 	int r = 0;
18881da177e4SLinus Torvalds 
1889249bed82SDemi Marie Obenour 	/* Make certain version is first member of dm_ioctl struct */
1890249bed82SDemi Marie Obenour 	BUILD_BUG_ON(offsetof(struct dm_ioctl, version) != 0);
1891249bed82SDemi Marie Obenour 
1892249bed82SDemi Marie Obenour 	if (copy_from_user(kernel_params->version, user->version, sizeof(kernel_params->version)))
18931da177e4SLinus Torvalds 		return -EFAULT;
18941da177e4SLinus Torvalds 
1895249bed82SDemi Marie Obenour 	if ((kernel_params->version[0] != DM_VERSION_MAJOR) ||
1896249bed82SDemi Marie Obenour 	    (kernel_params->version[1] > DM_VERSION_MINOR)) {
18972e84fecfSHeinz Mauelshagen 		DMERR("ioctl interface mismatch: kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
18981da177e4SLinus Torvalds 		      DM_VERSION_MAJOR, DM_VERSION_MINOR,
18991da177e4SLinus Torvalds 		      DM_VERSION_PATCHLEVEL,
1900249bed82SDemi Marie Obenour 		      kernel_params->version[0],
1901249bed82SDemi Marie Obenour 		      kernel_params->version[1],
1902249bed82SDemi Marie Obenour 		      kernel_params->version[2],
1903249bed82SDemi Marie Obenour 		      cmd);
19041da177e4SLinus Torvalds 		r = -EINVAL;
19051da177e4SLinus Torvalds 	}
19061da177e4SLinus Torvalds 
19071da177e4SLinus Torvalds 	/*
19081da177e4SLinus Torvalds 	 * Fill in the kernel version.
19091da177e4SLinus Torvalds 	 */
1910249bed82SDemi Marie Obenour 	kernel_params->version[0] = DM_VERSION_MAJOR;
1911249bed82SDemi Marie Obenour 	kernel_params->version[1] = DM_VERSION_MINOR;
1912249bed82SDemi Marie Obenour 	kernel_params->version[2] = DM_VERSION_PATCHLEVEL;
1913249bed82SDemi Marie Obenour 	if (copy_to_user(user->version, kernel_params->version, sizeof(kernel_params->version)))
19141da177e4SLinus Torvalds 		return -EFAULT;
19151da177e4SLinus Torvalds 
19161da177e4SLinus Torvalds 	return r;
19171da177e4SLinus Torvalds }
19181da177e4SLinus Torvalds 
1919028b39e3SBart Van Assche #define DM_PARAMS_MALLOC	0x0001	/* Params allocated with kvmalloc() */
19209c5091f2SMikulas Patocka #define DM_WIPE_BUFFER		0x0010	/* Wipe input buffer before returning from ioctl */
19219c5091f2SMikulas Patocka 
free_params(struct dm_ioctl * param,size_t param_size,int param_flags)19229c5091f2SMikulas Patocka static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags)
19239c5091f2SMikulas Patocka {
19249c5091f2SMikulas Patocka 	if (param_flags & DM_WIPE_BUFFER)
19259c5091f2SMikulas Patocka 		memset(param, 0, param_size);
19269c5091f2SMikulas Patocka 
1927028b39e3SBart Van Assche 	if (param_flags & DM_PARAMS_MALLOC)
1928028b39e3SBart Van Assche 		kvfree(param);
19299c5091f2SMikulas Patocka }
19309c5091f2SMikulas Patocka 
copy_params(struct dm_ioctl __user * user,struct dm_ioctl * param_kernel,int ioctl_flags,struct dm_ioctl ** param,int * param_flags)193102cde50bSMikulas Patocka static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
1932800a7340SWenwen Wang 		       int ioctl_flags, struct dm_ioctl **param, int *param_flags)
19331da177e4SLinus Torvalds {
193402cde50bSMikulas Patocka 	struct dm_ioctl *dmi;
1935f8681205SMilan Broz 	int secure_data;
19366080758dSBart Van Assche 	const size_t minimum_data_size = offsetof(struct dm_ioctl, data);
19371da177e4SLinus Torvalds 
1938249bed82SDemi Marie Obenour 	/* check_version() already copied version from userspace, avoid TOCTOU */
1939249bed82SDemi Marie Obenour 	if (copy_from_user((char *)param_kernel + sizeof(param_kernel->version),
1940249bed82SDemi Marie Obenour 			   (char __user *)user + sizeof(param_kernel->version),
1941249bed82SDemi Marie Obenour 			   minimum_data_size - sizeof(param_kernel->version)))
19421da177e4SLinus Torvalds 		return -EFAULT;
19431da177e4SLinus Torvalds 
1944*438d1949SMikulas Patocka 	if (unlikely(param_kernel->data_size < minimum_data_size) ||
1945*438d1949SMikulas Patocka 	    unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) {
1946dbdcc906SMikulas Patocka 		DMERR("Invalid data size in the ioctl structure: %u",
1947dbdcc906SMikulas Patocka 		      param_kernel->data_size);
19481da177e4SLinus Torvalds 		return -EINVAL;
1949dbdcc906SMikulas Patocka 	}
19501da177e4SLinus Torvalds 
195102cde50bSMikulas Patocka 	secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
1952f8681205SMilan Broz 
19539c5091f2SMikulas Patocka 	*param_flags = secure_data ? DM_WIPE_BUFFER : 0;
19549c5091f2SMikulas Patocka 
195502cde50bSMikulas Patocka 	if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
195602cde50bSMikulas Patocka 		dmi = param_kernel;
195702cde50bSMikulas Patocka 		dmi->data_size = minimum_data_size;
195802cde50bSMikulas Patocka 		goto data_copied;
195902cde50bSMikulas Patocka 	}
196002cde50bSMikulas Patocka 
19615023e5cfSMikulas Patocka 	/*
19628c1e2162SJunaid Shahid 	 * Use __GFP_HIGH to avoid low memory issues when a device is
19638c1e2162SJunaid Shahid 	 * suspended and the ioctl is needed to resume it.
19649c5091f2SMikulas Patocka 	 * Use kmalloc() rather than vmalloc() when we can.
19655023e5cfSMikulas Patocka 	 */
19669c5091f2SMikulas Patocka 	dmi = NULL;
1967e2c789caSMikulas Patocka 	dmi = kvmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH);
19689c5091f2SMikulas Patocka 
1969f8681205SMilan Broz 	if (!dmi) {
197002cde50bSMikulas Patocka 		if (secure_data && clear_user(user, param_kernel->data_size))
1971f8681205SMilan Broz 			return -EFAULT;
19721da177e4SLinus Torvalds 		return -ENOMEM;
1973f8681205SMilan Broz 	}
19741da177e4SLinus Torvalds 
1975028b39e3SBart Van Assche 	*param_flags |= DM_PARAMS_MALLOC;
1976028b39e3SBart Van Assche 
1977800a7340SWenwen Wang 	/* Copy from param_kernel (which was already copied from user) */
1978800a7340SWenwen Wang 	memcpy(dmi, param_kernel, minimum_data_size);
19791da177e4SLinus Torvalds 
1980800a7340SWenwen Wang 	if (copy_from_user(&dmi->data, (char __user *)user + minimum_data_size,
1981800a7340SWenwen Wang 			   param_kernel->data_size - minimum_data_size))
1982800a7340SWenwen Wang 		goto bad;
198302cde50bSMikulas Patocka data_copied:
1984f8681205SMilan Broz 	/* Wipe the user buffer so we do not return it to userspace */
198502cde50bSMikulas Patocka 	if (secure_data && clear_user(user, param_kernel->data_size))
1986f8681205SMilan Broz 		goto bad;
1987f8681205SMilan Broz 
19881da177e4SLinus Torvalds 	*param = dmi;
19891da177e4SLinus Torvalds 	return 0;
19906bb43b5dSMilan Broz 
19916bb43b5dSMilan Broz bad:
199202cde50bSMikulas Patocka 	free_params(dmi, param_kernel->data_size, *param_flags);
19939c5091f2SMikulas Patocka 
19946bb43b5dSMilan Broz 	return -EFAULT;
19951da177e4SLinus Torvalds }
19961da177e4SLinus Torvalds 
validate_params(uint cmd,struct dm_ioctl * param)19971da177e4SLinus Torvalds static int validate_params(uint cmd, struct dm_ioctl *param)
19981da177e4SLinus Torvalds {
19991da177e4SLinus Torvalds 	/* Always clear this flag */
20001da177e4SLinus Torvalds 	param->flags &= ~DM_BUFFER_FULL_FLAG;
20013abf85b5SPeter Rajnoha 	param->flags &= ~DM_UEVENT_GENERATED_FLAG;
2002f8681205SMilan Broz 	param->flags &= ~DM_SECURE_DATA_FLAG;
2003a2606241SMikulas Patocka 	param->flags &= ~DM_DATA_OUT_FLAG;
20041da177e4SLinus Torvalds 
20051da177e4SLinus Torvalds 	/* Ignores parameters */
20061da177e4SLinus Torvalds 	if (cmd == DM_REMOVE_ALL_CMD ||
20071da177e4SLinus Torvalds 	    cmd == DM_LIST_DEVICES_CMD ||
20081da177e4SLinus Torvalds 	    cmd == DM_LIST_VERSIONS_CMD)
20091da177e4SLinus Torvalds 		return 0;
20101da177e4SLinus Torvalds 
2011e36215d8SMatthias Kaehlcke 	if (cmd == DM_DEV_CREATE_CMD) {
20121da177e4SLinus Torvalds 		if (!*param->name) {
201343e6c111SMikulas Patocka 			DMERR("name not supplied when creating device");
20141da177e4SLinus Torvalds 			return -EINVAL;
20151da177e4SLinus Torvalds 		}
2016e36215d8SMatthias Kaehlcke 	} else if (*param->uuid && *param->name) {
201743e6c111SMikulas Patocka 		DMERR("only supply one of name or uuid, cmd(%u)", cmd);
20181da177e4SLinus Torvalds 		return -EINVAL;
20191da177e4SLinus Torvalds 	}
20201da177e4SLinus Torvalds 
20211da177e4SLinus Torvalds 	/* Ensure strings are terminated */
20221da177e4SLinus Torvalds 	param->name[DM_NAME_LEN - 1] = '\0';
20231da177e4SLinus Torvalds 	param->uuid[DM_UUID_LEN - 1] = '\0';
20241da177e4SLinus Torvalds 
20251da177e4SLinus Torvalds 	return 0;
20261da177e4SLinus Torvalds }
20271da177e4SLinus Torvalds 
ctl_ioctl(struct file * file,uint command,struct dm_ioctl __user * user)2028fc1841e1SMikulas Patocka static int ctl_ioctl(struct file *file, uint command, struct dm_ioctl __user *user)
20291da177e4SLinus Torvalds {
20301da177e4SLinus Torvalds 	int r = 0;
2031e2914cc2SMikulas Patocka 	int ioctl_flags;
20329c5091f2SMikulas Patocka 	int param_flags;
20331da177e4SLinus Torvalds 	unsigned int cmd;
20343f649ab7SKees Cook 	struct dm_ioctl *param;
20351da177e4SLinus Torvalds 	ioctl_fn fn = NULL;
20366bb43b5dSMilan Broz 	size_t input_param_size;
203702cde50bSMikulas Patocka 	struct dm_ioctl param_kernel;
20381da177e4SLinus Torvalds 
20391da177e4SLinus Torvalds 	/* only root can play with this */
20401da177e4SLinus Torvalds 	if (!capable(CAP_SYS_ADMIN))
20411da177e4SLinus Torvalds 		return -EACCES;
20421da177e4SLinus Torvalds 
20431da177e4SLinus Torvalds 	if (_IOC_TYPE(command) != DM_IOCTL)
20441da177e4SLinus Torvalds 		return -ENOTTY;
20451da177e4SLinus Torvalds 
20461da177e4SLinus Torvalds 	cmd = _IOC_NR(command);
20471da177e4SLinus Torvalds 
20481da177e4SLinus Torvalds 	/*
20491da177e4SLinus Torvalds 	 * Check the interface version passed in.  This also
20501da177e4SLinus Torvalds 	 * writes out the kernel's interface version.
20511da177e4SLinus Torvalds 	 */
2052249bed82SDemi Marie Obenour 	r = check_version(cmd, user, &param_kernel);
20531da177e4SLinus Torvalds 	if (r)
20541da177e4SLinus Torvalds 		return r;
20551da177e4SLinus Torvalds 
20561da177e4SLinus Torvalds 	/*
20571da177e4SLinus Torvalds 	 * Nothing more to do for the version command.
20581da177e4SLinus Torvalds 	 */
20591da177e4SLinus Torvalds 	if (cmd == DM_VERSION_CMD)
20601da177e4SLinus Torvalds 		return 0;
20611da177e4SLinus Torvalds 
2062e2914cc2SMikulas Patocka 	fn = lookup_ioctl(cmd, &ioctl_flags);
20631da177e4SLinus Torvalds 	if (!fn) {
206443e6c111SMikulas Patocka 		DMERR("dm_ctl_ioctl: unknown command 0x%x", command);
20651da177e4SLinus Torvalds 		return -ENOTTY;
20661da177e4SLinus Torvalds 	}
20671da177e4SLinus Torvalds 
20681da177e4SLinus Torvalds 	/*
20691da177e4SLinus Torvalds 	 * Copy the parameters into kernel space.
20701da177e4SLinus Torvalds 	 */
207102cde50bSMikulas Patocka 	r = copy_params(user, &param_kernel, ioctl_flags, &param, &param_flags);
20721da177e4SLinus Torvalds 
2073dab6a429SAlasdair G Kergon 	if (r)
2074dab6a429SAlasdair G Kergon 		return r;
20751da177e4SLinus Torvalds 
2076f8681205SMilan Broz 	input_param_size = param->data_size;
20771da177e4SLinus Torvalds 	r = validate_params(cmd, param);
20781da177e4SLinus Torvalds 	if (r)
20791da177e4SLinus Torvalds 		goto out;
20801da177e4SLinus Torvalds 
20814617f564SAdrian Salido 	param->data_size = offsetof(struct dm_ioctl, data);
2082fc1841e1SMikulas Patocka 	r = fn(file, param, input_param_size);
20831da177e4SLinus Torvalds 
2084e2914cc2SMikulas Patocka 	if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) &&
2085e2914cc2SMikulas Patocka 	    unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
2086e2914cc2SMikulas Patocka 		DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
2087e2914cc2SMikulas Patocka 
208862e08243SMikulas Patocka 	if (!r && ioctl_flags & IOCTL_FLAGS_ISSUE_GLOBAL_EVENT)
208962e08243SMikulas Patocka 		dm_issue_global_event();
209062e08243SMikulas Patocka 
20911da177e4SLinus Torvalds 	/*
20921da177e4SLinus Torvalds 	 * Copy the results back to userland.
20931da177e4SLinus Torvalds 	 */
20941da177e4SLinus Torvalds 	if (!r && copy_to_user(user, param, param->data_size))
20951da177e4SLinus Torvalds 		r = -EFAULT;
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds out:
20989c5091f2SMikulas Patocka 	free_params(param, input_param_size, param_flags);
20991da177e4SLinus Torvalds 	return r;
21001da177e4SLinus Torvalds }
21011da177e4SLinus Torvalds 
dm_ctl_ioctl(struct file * file,uint command,ulong u)210227238b2bSAlasdair G Kergon static long dm_ctl_ioctl(struct file *file, uint command, ulong u)
210327238b2bSAlasdair G Kergon {
2104fc1841e1SMikulas Patocka 	return (long)ctl_ioctl(file, command, (struct dm_ioctl __user *)u);
210527238b2bSAlasdair G Kergon }
210627238b2bSAlasdair G Kergon 
210776c072b4SMilan Broz #ifdef CONFIG_COMPAT
dm_compat_ctl_ioctl(struct file * file,uint command,ulong u)210876c072b4SMilan Broz static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
210976c072b4SMilan Broz {
211076c072b4SMilan Broz 	return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
211176c072b4SMilan Broz }
211276c072b4SMilan Broz #else
211376c072b4SMilan Broz #define dm_compat_ctl_ioctl NULL
211476c072b4SMilan Broz #endif
211576c072b4SMilan Broz 
dm_open(struct inode * inode,struct file * filp)211693e6442cSMikulas Patocka static int dm_open(struct inode *inode, struct file *filp)
211793e6442cSMikulas Patocka {
211893e6442cSMikulas Patocka 	int r;
211993e6442cSMikulas Patocka 	struct dm_file *priv;
212093e6442cSMikulas Patocka 
212193e6442cSMikulas Patocka 	r = nonseekable_open(inode, filp);
212293e6442cSMikulas Patocka 	if (unlikely(r))
212393e6442cSMikulas Patocka 		return r;
212493e6442cSMikulas Patocka 
212593e6442cSMikulas Patocka 	priv = filp->private_data = kmalloc(sizeof(struct dm_file), GFP_KERNEL);
212693e6442cSMikulas Patocka 	if (!priv)
212793e6442cSMikulas Patocka 		return -ENOMEM;
212893e6442cSMikulas Patocka 
212993e6442cSMikulas Patocka 	priv->global_event_nr = atomic_read(&dm_global_event_nr);
213093e6442cSMikulas Patocka 
213193e6442cSMikulas Patocka 	return 0;
213293e6442cSMikulas Patocka }
213393e6442cSMikulas Patocka 
dm_release(struct inode * inode,struct file * filp)213493e6442cSMikulas Patocka static int dm_release(struct inode *inode, struct file *filp)
213593e6442cSMikulas Patocka {
213693e6442cSMikulas Patocka 	kfree(filp->private_data);
213793e6442cSMikulas Patocka 	return 0;
213893e6442cSMikulas Patocka }
213993e6442cSMikulas Patocka 
dm_poll(struct file * filp,poll_table * wait)2140afc9a42bSAl Viro static __poll_t dm_poll(struct file *filp, poll_table *wait)
214193e6442cSMikulas Patocka {
214293e6442cSMikulas Patocka 	struct dm_file *priv = filp->private_data;
2143afc9a42bSAl Viro 	__poll_t mask = 0;
214493e6442cSMikulas Patocka 
214593e6442cSMikulas Patocka 	poll_wait(filp, &dm_global_eventq, wait);
214693e6442cSMikulas Patocka 
214793e6442cSMikulas Patocka 	if ((int)(atomic_read(&dm_global_event_nr) - priv->global_event_nr) > 0)
2148a9a08845SLinus Torvalds 		mask |= EPOLLIN;
214993e6442cSMikulas Patocka 
215093e6442cSMikulas Patocka 	return mask;
215193e6442cSMikulas Patocka }
215293e6442cSMikulas Patocka 
2153fa027c2aSArjan van de Ven static const struct file_operations _ctl_fops = {
215493e6442cSMikulas Patocka 	.open    = dm_open,
215593e6442cSMikulas Patocka 	.release = dm_release,
215693e6442cSMikulas Patocka 	.poll    = dm_poll,
215727238b2bSAlasdair G Kergon 	.unlocked_ioctl	 = dm_ctl_ioctl,
215876c072b4SMilan Broz 	.compat_ioctl = dm_compat_ctl_ioctl,
21591da177e4SLinus Torvalds 	.owner	 = THIS_MODULE,
21606038f373SArnd Bergmann 	.llseek  = noop_llseek,
21611da177e4SLinus Torvalds };
21621da177e4SLinus Torvalds 
21631da177e4SLinus Torvalds static struct miscdevice _dm_misc = {
21647e507eb6SPeter Rajnoha 	.minor		= MAPPER_CTRL_MINOR,
21651da177e4SLinus Torvalds 	.name		= DM_NAME,
21667e507eb6SPeter Rajnoha 	.nodename	= DM_DIR "/" DM_CONTROL_NODE,
21671da177e4SLinus Torvalds 	.fops		= &_ctl_fops
21681da177e4SLinus Torvalds };
21691da177e4SLinus Torvalds 
21707e507eb6SPeter Rajnoha MODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR);
21717e507eb6SPeter Rajnoha MODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE);
21727e507eb6SPeter Rajnoha 
21731da177e4SLinus Torvalds /*
21741da177e4SLinus Torvalds  * Create misc character device and link to DM_DIR/control.
21751da177e4SLinus Torvalds  */
dm_interface_init(void)21761da177e4SLinus Torvalds int __init dm_interface_init(void)
21771da177e4SLinus Torvalds {
21781da177e4SLinus Torvalds 	int r;
21791da177e4SLinus Torvalds 
21801da177e4SLinus Torvalds 	r = misc_register(&_dm_misc);
21811da177e4SLinus Torvalds 	if (r) {
21821da177e4SLinus Torvalds 		DMERR("misc_register failed for control device");
21831da177e4SLinus Torvalds 		return r;
21841da177e4SLinus Torvalds 	}
21851da177e4SLinus Torvalds 
21861da177e4SLinus Torvalds 	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
21871da177e4SLinus Torvalds 	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
21881da177e4SLinus Torvalds 	       DM_DRIVER_EMAIL);
21891da177e4SLinus Torvalds 	return 0;
21901da177e4SLinus Torvalds }
21911da177e4SLinus Torvalds 
dm_interface_exit(void)21921da177e4SLinus Torvalds void dm_interface_exit(void)
21931da177e4SLinus Torvalds {
2194f368ed60SGreg Kroah-Hartman 	misc_deregister(&_dm_misc);
21951da177e4SLinus Torvalds 	dm_hash_exit();
21961da177e4SLinus Torvalds }
219796a1f7dbSMike Anderson 
219896a1f7dbSMike Anderson /**
219996a1f7dbSMike Anderson  * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
220096a1f7dbSMike Anderson  * @md: Pointer to mapped_device
220196a1f7dbSMike Anderson  * @name: Buffer (size DM_NAME_LEN) for name
220296a1f7dbSMike Anderson  * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined
220396a1f7dbSMike Anderson  */
dm_copy_name_and_uuid(struct mapped_device * md,char * name,char * uuid)220496a1f7dbSMike Anderson int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
220596a1f7dbSMike Anderson {
220696a1f7dbSMike Anderson 	int r = 0;
220796a1f7dbSMike Anderson 	struct hash_cell *hc;
220896a1f7dbSMike Anderson 
220996a1f7dbSMike Anderson 	if (!md)
221096a1f7dbSMike Anderson 		return -ENXIO;
221196a1f7dbSMike Anderson 
22126076905bSMikulas Patocka 	mutex_lock(&dm_hash_cells_mutex);
221396a1f7dbSMike Anderson 	hc = dm_get_mdptr(md);
2214a2f998a7SHou Tao 	if (!hc) {
221596a1f7dbSMike Anderson 		r = -ENXIO;
221696a1f7dbSMike Anderson 		goto out;
221796a1f7dbSMike Anderson 	}
221896a1f7dbSMike Anderson 
221923d39f63SMilan Broz 	if (name)
222096a1f7dbSMike Anderson 		strcpy(name, hc->name);
222123d39f63SMilan Broz 	if (uuid)
222296a1f7dbSMike Anderson 		strcpy(uuid, hc->uuid ? : "");
222396a1f7dbSMike Anderson 
222496a1f7dbSMike Anderson out:
22256076905bSMikulas Patocka 	mutex_unlock(&dm_hash_cells_mutex);
222696a1f7dbSMike Anderson 
222796a1f7dbSMike Anderson 	return r;
222896a1f7dbSMike Anderson }
222961931c0eSMike Snitzer EXPORT_SYMBOL_GPL(dm_copy_name_and_uuid);
22306bbc923dSHelen Koike 
22316bbc923dSHelen Koike /**
22326bbc923dSHelen Koike  * dm_early_create - create a mapped device in early boot.
22336bbc923dSHelen Koike  *
22346bbc923dSHelen Koike  * @dmi: Contains main information of the device mapping to be created.
22356bbc923dSHelen Koike  * @spec_array: array of pointers to struct dm_target_spec. Describes the
22366bbc923dSHelen Koike  * mapping table of the device.
22376bbc923dSHelen Koike  * @target_params_array: array of strings with the parameters to a specific
22386bbc923dSHelen Koike  * target.
22396bbc923dSHelen Koike  *
22406bbc923dSHelen Koike  * Instead of having the struct dm_target_spec and the parameters for every
22416bbc923dSHelen Koike  * target embedded at the end of struct dm_ioctl (as performed in a normal
22426bbc923dSHelen Koike  * ioctl), pass them as arguments, so the caller doesn't need to serialize them.
22436bbc923dSHelen Koike  * The size of the spec_array and target_params_array is given by
22446bbc923dSHelen Koike  * @dmi->target_count.
22456bbc923dSHelen Koike  * This function is supposed to be called in early boot, so locking mechanisms
22466bbc923dSHelen Koike  * to protect against concurrent loads are not required.
22476bbc923dSHelen Koike  */
dm_early_create(struct dm_ioctl * dmi,struct dm_target_spec ** spec_array,char ** target_params_array)22486bbc923dSHelen Koike int __init dm_early_create(struct dm_ioctl *dmi,
22496bbc923dSHelen Koike 			   struct dm_target_spec **spec_array,
22506bbc923dSHelen Koike 			   char **target_params_array)
22516bbc923dSHelen Koike {
22526bbc923dSHelen Koike 	int r, m = DM_ANY_MINOR;
22536bbc923dSHelen Koike 	struct dm_table *t, *old_map;
22546bbc923dSHelen Koike 	struct mapped_device *md;
22556bbc923dSHelen Koike 	unsigned int i;
22566bbc923dSHelen Koike 
22576bbc923dSHelen Koike 	if (!dmi->target_count)
22586bbc923dSHelen Koike 		return -EINVAL;
22596bbc923dSHelen Koike 
22606bbc923dSHelen Koike 	r = check_name(dmi->name);
22616bbc923dSHelen Koike 	if (r)
22626bbc923dSHelen Koike 		return r;
22636bbc923dSHelen Koike 
22646bbc923dSHelen Koike 	if (dmi->flags & DM_PERSISTENT_DEV_FLAG)
22656bbc923dSHelen Koike 		m = MINOR(huge_decode_dev(dmi->dev));
22666bbc923dSHelen Koike 
22676bbc923dSHelen Koike 	/* alloc dm device */
22686bbc923dSHelen Koike 	r = dm_create(m, &md);
22696bbc923dSHelen Koike 	if (r)
22706bbc923dSHelen Koike 		return r;
22716bbc923dSHelen Koike 
22726bbc923dSHelen Koike 	/* hash insert */
22736bbc923dSHelen Koike 	r = dm_hash_insert(dmi->name, *dmi->uuid ? dmi->uuid : NULL, md);
22746bbc923dSHelen Koike 	if (r)
22756bbc923dSHelen Koike 		goto err_destroy_dm;
22766bbc923dSHelen Koike 
22776bbc923dSHelen Koike 	/* alloc table */
22786bbc923dSHelen Koike 	r = dm_table_create(&t, get_mode(dmi), dmi->target_count, md);
22796bbc923dSHelen Koike 	if (r)
22800f41fcf7SHelen Koike 		goto err_hash_remove;
22816bbc923dSHelen Koike 
22826bbc923dSHelen Koike 	/* add targets */
22836bbc923dSHelen Koike 	for (i = 0; i < dmi->target_count; i++) {
22846bbc923dSHelen Koike 		r = dm_table_add_target(t, spec_array[i]->target_type,
22856bbc923dSHelen Koike 					(sector_t) spec_array[i]->sector_start,
22866bbc923dSHelen Koike 					(sector_t) spec_array[i]->length,
22876bbc923dSHelen Koike 					target_params_array[i]);
22886bbc923dSHelen Koike 		if (r) {
228943e6c111SMikulas Patocka 			DMERR("error adding target to table");
22906bbc923dSHelen Koike 			goto err_destroy_table;
22916bbc923dSHelen Koike 		}
22926bbc923dSHelen Koike 	}
22936bbc923dSHelen Koike 
22946bbc923dSHelen Koike 	/* finish table */
22956bbc923dSHelen Koike 	r = dm_table_complete(t);
22966bbc923dSHelen Koike 	if (r)
22976bbc923dSHelen Koike 		goto err_destroy_table;
22986bbc923dSHelen Koike 
22996bbc923dSHelen Koike 	/* setup md->queue to reflect md's type (may block) */
23006bbc923dSHelen Koike 	r = dm_setup_md_queue(md, t);
23016bbc923dSHelen Koike 	if (r) {
230243e6c111SMikulas Patocka 		DMERR("unable to set up device queue for new table.");
23036bbc923dSHelen Koike 		goto err_destroy_table;
23046bbc923dSHelen Koike 	}
23056bbc923dSHelen Koike 
23066bbc923dSHelen Koike 	/* Set new map */
23076bbc923dSHelen Koike 	dm_suspend(md, 0);
23086bbc923dSHelen Koike 	old_map = dm_swap_table(md, t);
23096bbc923dSHelen Koike 	if (IS_ERR(old_map)) {
23106bbc923dSHelen Koike 		r = PTR_ERR(old_map);
23116bbc923dSHelen Koike 		goto err_destroy_table;
23126bbc923dSHelen Koike 	}
23136bbc923dSHelen Koike 	set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG));
23146bbc923dSHelen Koike 
23156bbc923dSHelen Koike 	/* resume device */
23166bbc923dSHelen Koike 	r = dm_resume(md);
23176bbc923dSHelen Koike 	if (r)
23186bbc923dSHelen Koike 		goto err_destroy_table;
23196bbc923dSHelen Koike 
23206bbc923dSHelen Koike 	DMINFO("%s (%s) is ready", md->disk->disk_name, dmi->name);
23216bbc923dSHelen Koike 	dm_put(md);
23226bbc923dSHelen Koike 	return 0;
23236bbc923dSHelen Koike 
23246bbc923dSHelen Koike err_destroy_table:
23256bbc923dSHelen Koike 	dm_table_destroy(t);
23260f41fcf7SHelen Koike err_hash_remove:
232769868bebSMike Snitzer 	down_write(&_hash_lock);
23280f41fcf7SHelen Koike 	(void) __hash_remove(__get_name_cell(dmi->name));
232969868bebSMike Snitzer 	up_write(&_hash_lock);
23300f41fcf7SHelen Koike 	/* release reference from __get_name_cell */
23310f41fcf7SHelen Koike 	dm_put(md);
23326bbc923dSHelen Koike err_destroy_dm:
23336bbc923dSHelen Koike 	dm_put(md);
23346bbc923dSHelen Koike 	dm_destroy(md);
23356bbc923dSHelen Koike 	return r;
23366bbc923dSHelen Koike }
2337