Blame view

drivers/md/dm-ioctl.c 42.4 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
2b06cfff1   Alasdair G Kergon   [PATCH] dm: conso...
3
   * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
   *
   * This file is released under the GPL.
   */
  
  #include "dm.h"
  
  #include <linux/module.h>
  #include <linux/vmalloc.h>
  #include <linux/miscdevice.h>
  #include <linux/init.h>
  #include <linux/wait.h>
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/dm-ioctl.h>
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
17
  #include <linux/hdreg.h>
76c072b48   Milan Broz   dm ioctl: move co...
18
  #include <linux/compat.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
20
  
  #include <asm/uaccess.h>
72d948616   Alasdair G Kergon   [PATCH] dm: impro...
21
  #define DM_MSG_PREFIX "ioctl"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  #define DM_DRIVER_EMAIL "dm-devel@redhat.com"
  
  /*-----------------------------------------------------------------
   * The ioctl interface needs to be able to look up devices by
   * name or uuid.
   *---------------------------------------------------------------*/
  struct hash_cell {
  	struct list_head name_list;
  	struct list_head uuid_list;
  
  	char *name;
  	char *uuid;
  	struct mapped_device *md;
  	struct dm_table *new_map;
  };
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
37
38
39
40
41
42
43
  /*
   * A dummy definition to make RCU happy.
   * struct dm_table should never be dereferenced in this file.
   */
  struct dm_table {
  	int undefined__;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
45
46
47
48
49
50
51
52
53
54
55
  struct vers_iter {
      size_t param_size;
      struct dm_target_versions *vers, *old_vers;
      char *end;
      uint32_t flags;
  };
  
  
  #define NUM_BUCKETS 64
  #define MASK_BUCKETS (NUM_BUCKETS - 1)
  static struct list_head _name_buckets[NUM_BUCKETS];
  static struct list_head _uuid_buckets[NUM_BUCKETS];
2c140a246   Mikulas Patocka   dm: allow remove ...
56
  static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
58
59
60
61
  
  /*
   * Guards access to both hash tables.
   */
  static DECLARE_RWSEM(_hash_lock);
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
62
63
64
65
  /*
   * Protects use of mdptr to obtain hash cell name and uuid from mapped device.
   */
  static DEFINE_MUTEX(dm_hash_cells_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
68
69
70
71
72
73
74
75
76
77
  static void init_buckets(struct list_head *buckets)
  {
  	unsigned int i;
  
  	for (i = 0; i < NUM_BUCKETS; i++)
  		INIT_LIST_HEAD(buckets + i);
  }
  
  static int dm_hash_init(void)
  {
  	init_buckets(_name_buckets);
  	init_buckets(_uuid_buckets);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
  	return 0;
  }
  
  static void dm_hash_exit(void)
  {
2c140a246   Mikulas Patocka   dm: allow remove ...
83
  	dm_hash_remove_all(false, false, false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  }
  
  /*-----------------------------------------------------------------
   * Hash function:
   * We're not really concerned with the str hash function being
   * fast since it's only used by the ioctl interface.
   *---------------------------------------------------------------*/
  static unsigned int hash_str(const char *str)
  {
  	const unsigned int hash_mult = 2654435387U;
  	unsigned int h = 0;
  
  	while (*str)
  		h = (h + (unsigned int) *str++) * hash_mult;
  
  	return h & MASK_BUCKETS;
  }
  
  /*-----------------------------------------------------------------
   * Code for looking up a device by name
   *---------------------------------------------------------------*/
  static struct hash_cell *__get_name_cell(const char *str)
  {
  	struct hash_cell *hc;
  	unsigned int h = hash_str(str);
  
  	list_for_each_entry (hc, _name_buckets + h, name_list)
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
111
112
  		if (!strcmp(hc->name, str)) {
  			dm_get(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  			return hc;
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
114
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
117
118
119
120
121
122
123
124
  
  	return NULL;
  }
  
  static struct hash_cell *__get_uuid_cell(const char *str)
  {
  	struct hash_cell *hc;
  	unsigned int h = hash_str(str);
  
  	list_for_each_entry (hc, _uuid_buckets + h, uuid_list)
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
125
126
  		if (!strcmp(hc->uuid, str)) {
  			dm_get(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  			return hc;
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
128
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  
  	return NULL;
  }
ba2e19b0f   Mikulas Patocka   dm ioctl: introdu...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  static struct hash_cell *__get_dev_cell(uint64_t dev)
  {
  	struct mapped_device *md;
  	struct hash_cell *hc;
  
  	md = dm_get_md(huge_decode_dev(dev));
  	if (!md)
  		return NULL;
  
  	hc = dm_get_mdptr(md);
  	if (!hc) {
  		dm_put(md);
  		return NULL;
  	}
  
  	return hc;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  /*-----------------------------------------------------------------
   * Inserting, removing and renaming a device.
   *---------------------------------------------------------------*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152
153
154
155
156
157
158
159
  static struct hash_cell *alloc_cell(const char *name, const char *uuid,
  				    struct mapped_device *md)
  {
  	struct hash_cell *hc;
  
  	hc = kmalloc(sizeof(*hc), GFP_KERNEL);
  	if (!hc)
  		return NULL;
543537bd9   Paulo Marques   [PATCH] create a ...
160
  	hc->name = kstrdup(name, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
163
164
165
166
167
168
169
  	if (!hc->name) {
  		kfree(hc);
  		return NULL;
  	}
  
  	if (!uuid)
  		hc->uuid = NULL;
  
  	else {
543537bd9   Paulo Marques   [PATCH] create a ...
170
  		hc->uuid = kstrdup(uuid, GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  		if (!hc->uuid) {
  			kfree(hc->name);
  			kfree(hc);
  			return NULL;
  		}
  	}
  
  	INIT_LIST_HEAD(&hc->name_list);
  	INIT_LIST_HEAD(&hc->uuid_list);
  	hc->md = md;
  	hc->new_map = NULL;
  	return hc;
  }
  
  static void free_cell(struct hash_cell *hc)
  {
  	if (hc) {
  		kfree(hc->name);
  		kfree(hc->uuid);
  		kfree(hc);
  	}
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
199
   * The kdev_t and uuid of a device can never change once it is
   * initially inserted.
   */
  static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
  {
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
200
  	struct hash_cell *cell, *hc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
205
206
207
208
209
210
211
212
  
  	/*
  	 * Allocate the new cells.
  	 */
  	cell = alloc_cell(name, uuid, md);
  	if (!cell)
  		return -ENOMEM;
  
  	/*
  	 * Insert the cell into both hash tables.
  	 */
  	down_write(&_hash_lock);
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
213
214
215
  	hc = __get_name_cell(name);
  	if (hc) {
  		dm_put(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
216
  		goto bad;
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
217
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
  
  	list_add(&cell->name_list, _name_buckets + hash_str(name));
  
  	if (uuid) {
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
222
223
  		hc = __get_uuid_cell(uuid);
  		if (hc) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
  			list_del(&cell->name_list);
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
225
  			dm_put(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
  			goto bad;
  		}
  		list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  	dm_get(md);
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
231
  	mutex_lock(&dm_hash_cells_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
  	dm_set_mdptr(md, cell);
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
233
  	mutex_unlock(&dm_hash_cells_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234
235
236
237
238
239
240
241
242
  	up_write(&_hash_lock);
  
  	return 0;
  
   bad:
  	up_write(&_hash_lock);
  	free_cell(cell);
  	return -EBUSY;
  }
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
243
  static struct dm_table *__hash_remove(struct hash_cell *hc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
  {
269fd2a6f   goggin, edward   [PATCH] device-ma...
245
  	struct dm_table *table;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
246
  	int srcu_idx;
269fd2a6f   goggin, edward   [PATCH] device-ma...
247

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
250
  	/* remove from the dev hash */
  	list_del(&hc->uuid_list);
  	list_del(&hc->name_list);
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
251
  	mutex_lock(&dm_hash_cells_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  	dm_set_mdptr(hc->md, NULL);
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
253
  	mutex_unlock(&dm_hash_cells_mutex);
269fd2a6f   goggin, edward   [PATCH] device-ma...
254

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
255
256
  	table = dm_get_live_table(hc->md, &srcu_idx);
  	if (table)
269fd2a6f   goggin, edward   [PATCH] device-ma...
257
  		dm_table_event(table);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
258
  	dm_put_live_table(hc->md, srcu_idx);
269fd2a6f   goggin, edward   [PATCH] device-ma...
259

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
260
  	table = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  	if (hc->new_map)
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
262
  		table = hc->new_map;
1134e5ae7   Mike Anderson   [PATCH] dm table:...
263
  	dm_put(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
  	free_cell(hc);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
265
266
  
  	return table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267
  }
2c140a246   Mikulas Patocka   dm: allow remove ...
268
  static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  {
98f332855   Kiyoshi Ueda   dm ioctl: release...
270
  	int i, dev_skipped;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  	struct hash_cell *hc;
98f332855   Kiyoshi Ueda   dm ioctl: release...
272
  	struct mapped_device *md;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
273
  	struct dm_table *t;
98f332855   Kiyoshi Ueda   dm ioctl: release...
274
275
276
  
  retry:
  	dev_skipped = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277
278
  
  	down_write(&_hash_lock);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
279

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
  	for (i = 0; i < NUM_BUCKETS; i++) {
98f332855   Kiyoshi Ueda   dm ioctl: release...
281
282
283
  		list_for_each_entry(hc, _name_buckets + i, name_list) {
  			md = hc->md;
  			dm_get(md);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
284

2c140a246   Mikulas Patocka   dm: allow remove ...
285
286
  			if (keep_open_devices &&
  			    dm_lock_for_deletion(md, mark_deferred, only_deferred)) {
98f332855   Kiyoshi Ueda   dm ioctl: release...
287
  				dm_put(md);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
288
289
290
  				dev_skipped++;
  				continue;
  			}
98f332855   Kiyoshi Ueda   dm ioctl: release...
291

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
292
  			t = __hash_remove(hc);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
293

98f332855   Kiyoshi Ueda   dm ioctl: release...
294
  			up_write(&_hash_lock);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
295

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
296
297
298
299
  			if (t) {
  				dm_sync_table(md);
  				dm_table_destroy(t);
  			}
98f332855   Kiyoshi Ueda   dm ioctl: release...
300
  			dm_put(md);
3f77316de   Kiyoshi Ueda   dm: separate devi...
301
302
303
304
  			if (likely(keep_open_devices))
  				dm_destroy(md);
  			else
  				dm_destroy_immediate(md);
98f332855   Kiyoshi Ueda   dm ioctl: release...
305
306
307
308
309
310
311
312
313
  
  			/*
  			 * Some mapped devices may be using other mapped
  			 * devices, so repeat until we make no further
  			 * progress.  If a new mapped device is created
  			 * here it will also get removed.
  			 */
  			goto retry;
  		}
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
314
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
  	up_write(&_hash_lock);
98f332855   Kiyoshi Ueda   dm ioctl: release...
316
317
318
  
  	if (dev_skipped)
  		DMWARN("remove_all left %d open device(s)", dev_skipped);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
  }
84c89557a   Peter Jones   dm ioctl: allow r...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  /*
   * Set the uuid of a hash_cell that isn't already set.
   */
  static void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
  {
  	mutex_lock(&dm_hash_cells_mutex);
  	hc->uuid = new_uuid;
  	mutex_unlock(&dm_hash_cells_mutex);
  
  	list_add(&hc->uuid_list, _uuid_buckets + hash_str(new_uuid));
  }
  
  /*
   * Changes the name of a hash_cell and returns the old name for
   * the caller to free.
   */
  static char *__change_cell_name(struct hash_cell *hc, char *new_name)
  {
  	char *old_name;
  
  	/*
  	 * Rename and move the name cell.
  	 */
  	list_del(&hc->name_list);
  	old_name = hc->name;
  
  	mutex_lock(&dm_hash_cells_mutex);
  	hc->name = new_name;
  	mutex_unlock(&dm_hash_cells_mutex);
  
  	list_add(&hc->name_list, _name_buckets + hash_str(new_name));
  
  	return old_name;
  }
856a6f1db   Peter Rajnoha   dm ioctl: return ...
354
355
  static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
  					    const char *new)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
  {
84c89557a   Peter Jones   dm ioctl: allow r...
357
  	char *new_data, *old_name = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  	struct hash_cell *hc;
81f1777a5   goggin, edward   [PATCH] device-ma...
359
  	struct dm_table *table;
856a6f1db   Peter Rajnoha   dm ioctl: return ...
360
  	struct mapped_device *md;
84c89557a   Peter Jones   dm ioctl: allow r...
361
  	unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
362
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
364
365
366
  
  	/*
  	 * duplicate new.
  	 */
84c89557a   Peter Jones   dm ioctl: allow r...
367
368
  	new_data = kstrdup(new, GFP_KERNEL);
  	if (!new_data)
856a6f1db   Peter Rajnoha   dm ioctl: return ...
369
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
370
371
372
373
374
375
  
  	down_write(&_hash_lock);
  
  	/*
  	 * Is new free ?
  	 */
84c89557a   Peter Jones   dm ioctl: allow r...
376
377
378
379
  	if (change_uuid)
  		hc = __get_uuid_cell(new);
  	else
  		hc = __get_name_cell(new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  	if (hc) {
84c89557a   Peter Jones   dm ioctl: allow r...
381
382
383
  		DMWARN("Unable to change %s on mapped device %s to one that "
  		       "already exists: %s",
  		       change_uuid ? "uuid" : "name",
856a6f1db   Peter Rajnoha   dm ioctl: return ...
384
  		       param->name, new);
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
385
  		dm_put(hc->md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
  		up_write(&_hash_lock);
84c89557a   Peter Jones   dm ioctl: allow r...
387
  		kfree(new_data);
856a6f1db   Peter Rajnoha   dm ioctl: return ...
388
  		return ERR_PTR(-EBUSY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
389
390
391
392
393
  	}
  
  	/*
  	 * Is there such a device as 'old' ?
  	 */
856a6f1db   Peter Rajnoha   dm ioctl: return ...
394
  	hc = __get_name_cell(param->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
395
  	if (!hc) {
84c89557a   Peter Jones   dm ioctl: allow r...
396
397
  		DMWARN("Unable to rename non-existent device, %s to %s%s",
  		       param->name, change_uuid ? "uuid " : "", new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398
  		up_write(&_hash_lock);
84c89557a   Peter Jones   dm ioctl: allow r...
399
  		kfree(new_data);
856a6f1db   Peter Rajnoha   dm ioctl: return ...
400
  		return ERR_PTR(-ENXIO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
401
402
403
  	}
  
  	/*
84c89557a   Peter Jones   dm ioctl: allow r...
404
  	 * Does this device already have a uuid?
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  	 */
84c89557a   Peter Jones   dm ioctl: allow r...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	if (change_uuid && hc->uuid) {
  		DMWARN("Unable to change uuid of mapped device %s to %s "
  		       "because uuid is already set to %s",
  		       param->name, new, hc->uuid);
  		dm_put(hc->md);
  		up_write(&_hash_lock);
  		kfree(new_data);
  		return ERR_PTR(-EINVAL);
  	}
  
  	if (change_uuid)
  		__set_cell_uuid(hc, new_data);
  	else
  		old_name = __change_cell_name(hc, new_data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420

81f1777a5   goggin, edward   [PATCH] device-ma...
421
422
423
  	/*
  	 * Wake up any dm event waiters.
  	 */
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
424
425
  	table = dm_get_live_table(hc->md, &srcu_idx);
  	if (table)
81f1777a5   goggin, edward   [PATCH] device-ma...
426
  		dm_table_event(table);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
427
  	dm_put_live_table(hc->md, srcu_idx);
81f1777a5   goggin, edward   [PATCH] device-ma...
428

856a6f1db   Peter Rajnoha   dm ioctl: return ...
429
430
  	if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
  		param->flags |= DM_UEVENT_GENERATED_FLAG;
69267a30b   Alasdair G Kergon   dm: trigger chang...
431

856a6f1db   Peter Rajnoha   dm ioctl: return ...
432
  	md = hc->md;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
433
434
  	up_write(&_hash_lock);
  	kfree(old_name);
856a6f1db   Peter Rajnoha   dm ioctl: return ...
435
436
  
  	return md;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
  }
2c140a246   Mikulas Patocka   dm: allow remove ...
438
439
440
441
  void dm_deferred_remove(void)
  {
  	dm_hash_remove_all(true, false, true);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442
443
444
445
446
447
448
449
450
451
452
  /*-----------------------------------------------------------------
   * Implementation of the ioctl commands
   *---------------------------------------------------------------*/
  /*
   * All the ioctl commands get dispatched to functions with this
   * prototype.
   */
  typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
  
  static int remove_all(struct dm_ioctl *param, size_t param_size)
  {
2c140a246   Mikulas Patocka   dm: allow remove ...
453
  	dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  	param->data_size = 0;
  	return 0;
  }
  
  /*
   * Round up the ptr to an 8-byte boundary.
   */
  #define ALIGN_MASK 7
  static inline void *align_ptr(void *ptr)
  {
  	return (void *) (((size_t) (ptr + ALIGN_MASK)) & ~ALIGN_MASK);
  }
  
  /*
   * Retrieves the data payload buffer from an already allocated
   * struct dm_ioctl.
   */
  static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
  			       size_t *len)
  {
  	param->data_start = align_ptr(param + 1) - (void *) param;
  
  	if (param->data_start < param_size)
  		*len = param_size - param->data_start;
  	else
  		*len = 0;
  
  	return ((void *) param) + param->data_start;
  }
  
  static int list_devices(struct dm_ioctl *param, size_t param_size)
  {
  	unsigned int i;
  	struct hash_cell *hc;
  	size_t len, needed = 0;
  	struct gendisk *disk;
  	struct dm_name_list *nl, *old_nl = NULL;
  
  	down_write(&_hash_lock);
  
  	/*
  	 * Loop through all the devices working out how much
  	 * space we need.
  	 */
  	for (i = 0; i < NUM_BUCKETS; i++) {
  		list_for_each_entry (hc, _name_buckets + i, name_list) {
  			needed += sizeof(struct dm_name_list);
  			needed += strlen(hc->name) + 1;
  			needed += ALIGN_MASK;
  		}
  	}
  
  	/*
  	 * Grab our output buffer.
  	 */
  	nl = get_result_buffer(param, param_size, &len);
  	if (len < needed) {
  		param->flags |= DM_BUFFER_FULL_FLAG;
  		goto out;
  	}
  	param->data_size = param->data_start + needed;
  
  	nl->dev = 0;	/* Flags no data */
  
  	/*
  	 * Now loop through filling out the names.
  	 */
  	for (i = 0; i < NUM_BUCKETS; i++) {
  		list_for_each_entry (hc, _name_buckets + i, name_list) {
  			if (old_nl)
  				old_nl->next = (uint32_t) ((void *) nl -
  							   (void *) old_nl);
  			disk = dm_disk(hc->md);
f331c0296   Tejun Heo   block: don't depe...
527
  			nl->dev = huge_encode_dev(disk_devt(disk));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
  			nl->next = 0;
  			strcpy(nl->name, hc->name);
  
  			old_nl = nl;
  			nl = align_ptr(((void *) ++nl) + strlen(hc->name) + 1);
  		}
  	}
  
   out:
  	up_write(&_hash_lock);
  	return 0;
  }
  
  static void list_version_get_needed(struct target_type *tt, void *needed_param)
  {
      size_t *needed = needed_param;
c4cc66351   Alasdair G Kergon   [PATCH] device-ma...
544
      *needed += sizeof(struct dm_target_versions);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
      *needed += strlen(tt->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
      *needed += ALIGN_MASK;
  }
  
  static void list_version_get_info(struct target_type *tt, void *param)
  {
      struct vers_iter *info = param;
  
      /* Check space - it might have changed since the first iteration */
      if ((char *)info->vers + sizeof(tt->version) + strlen(tt->name) + 1 >
  	info->end) {
  
  	info->flags = DM_BUFFER_FULL_FLAG;
  	return;
      }
  
      if (info->old_vers)
  	info->old_vers->next = (uint32_t) ((void *)info->vers -
  					   (void *)info->old_vers);
      info->vers->version[0] = tt->version[0];
      info->vers->version[1] = tt->version[1];
      info->vers->version[2] = tt->version[2];
      info->vers->next = 0;
      strcpy(info->vers->name, tt->name);
  
      info->old_vers = info->vers;
      info->vers = align_ptr(((void *) ++info->vers) + strlen(tt->name) + 1);
  }
  
  static int list_versions(struct dm_ioctl *param, size_t param_size)
  {
  	size_t len, needed = 0;
  	struct dm_target_versions *vers;
  	struct vers_iter iter_info;
  
  	/*
  	 * Loop through all the devices working out how much
  	 * space we need.
  	 */
  	dm_target_iterate(list_version_get_needed, &needed);
  
  	/*
  	 * Grab our output buffer.
  	 */
  	vers = get_result_buffer(param, param_size, &len);
  	if (len < needed) {
  		param->flags |= DM_BUFFER_FULL_FLAG;
  		goto out;
  	}
  	param->data_size = param->data_start + needed;
  
  	iter_info.param_size = param_size;
  	iter_info.old_vers = NULL;
  	iter_info.vers = vers;
  	iter_info.flags = 0;
  	iter_info.end = (char *)vers+len;
  
  	/*
  	 * Now loop through filling out the names & versions.
  	 */
  	dm_target_iterate(list_version_get_info, &iter_info);
  	param->flags |= iter_info.flags;
  
   out:
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
614
615
616
617
618
619
620
621
  static int check_name(const char *name)
  {
  	if (strchr(name, '/')) {
  		DMWARN("invalid device name");
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  /*
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
622
   * On successful return, the caller must not attempt to acquire
88e2f901e   Junxiao Bi   dm ioctl: fix sta...
623
624
   * _hash_lock without first calling dm_put_live_table, because dm_table_destroy
   * waits for this dm_put_live_table and could be called under this lock.
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
625
   */
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
626
  static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx)
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
627
628
629
  {
  	struct hash_cell *hc;
  	struct dm_table *table = NULL;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
630
631
  	/* increment rcu count, we don't care about the table pointer */
  	dm_get_live_table(md, srcu_idx);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
632
633
634
635
636
637
638
639
  	down_read(&_hash_lock);
  	hc = dm_get_mdptr(md);
  	if (!hc || hc->md != md) {
  		DMWARN("device has been removed from the dev hash table.");
  		goto out;
  	}
  
  	table = hc->new_map;
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
640
641
642
643
644
645
646
647
  
  out:
  	up_read(&_hash_lock);
  
  	return table;
  }
  
  static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md,
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
648
649
  						      struct dm_ioctl *param,
  						      int *srcu_idx)
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
650
651
  {
  	return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ?
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
652
  		dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
653
654
655
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
   * Fills in a dm_ioctl structure, ready for sending back to
   * userland.
   */
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
659
  static void __dev_status(struct mapped_device *md, struct dm_ioctl *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
662
  {
  	struct gendisk *disk = dm_disk(md);
  	struct dm_table *table;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
663
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
  
  	param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
ffcc39364   Mike Snitzer   dm: enhance inter...
666
  			  DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667

4f186f8bb   Kiyoshi Ueda   dm: rename dm_sus...
668
  	if (dm_suspended_md(md))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
669
  		param->flags |= DM_SUSPEND_FLAG;
ffcc39364   Mike Snitzer   dm: enhance inter...
670
671
  	if (dm_suspended_internally_md(md))
  		param->flags |= DM_INTERNAL_SUSPEND_FLAG;
2c140a246   Mikulas Patocka   dm: allow remove ...
672
673
  	if (dm_test_deferred_remove_flag(md))
  		param->flags |= DM_DEFERRED_REMOVE;
f331c0296   Tejun Heo   block: don't depe...
674
  	param->dev = huge_encode_dev(disk_devt(disk));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
675

5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
676
677
678
679
680
681
  	/*
  	 * Yes, this will be out of date by the time it gets back
  	 * to userland, but it is still very useful for
  	 * debugging.
  	 */
  	param->open_count = dm_open_count(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
682

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
  	param->event_nr = dm_get_event_nr(md);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
684
  	param->target_count = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
685

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
686
  	table = dm_get_live_table(md, &srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
  	if (table) {
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
688
689
690
691
692
  		if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) {
  			if (get_disk_ro(disk))
  				param->flags |= DM_READONLY_FLAG;
  			param->target_count = dm_table_get_num_targets(table);
  		}
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
693
694
695
  
  		param->flags |= DM_ACTIVE_PRESENT_FLAG;
  	}
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
696
  	dm_put_live_table(md, srcu_idx);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
697
698
  
  	if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) {
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
699
700
  		int srcu_idx;
  		table = dm_get_inactive_table(md, &srcu_idx);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
701
702
703
704
  		if (table) {
  			if (!(dm_table_get_mode(table) & FMODE_WRITE))
  				param->flags |= DM_READONLY_FLAG;
  			param->target_count = dm_table_get_num_targets(table);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
705
  		}
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
706
  		dm_put_live_table(md, srcu_idx);
1d0f3ce83   Mike Snitzer   dm ioctl: retriev...
707
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
708
709
710
711
  }
  
  static int dev_create(struct dm_ioctl *param, size_t param_size)
  {
2b06cfff1   Alasdair G Kergon   [PATCH] dm: conso...
712
  	int r, m = DM_ANY_MINOR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713
714
715
716
717
718
719
  	struct mapped_device *md;
  
  	r = check_name(param->name);
  	if (r)
  		return r;
  
  	if (param->flags & DM_PERSISTENT_DEV_FLAG)
2b06cfff1   Alasdair G Kergon   [PATCH] dm: conso...
720
  		m = MINOR(huge_decode_dev(param->dev));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
721

2b06cfff1   Alasdair G Kergon   [PATCH] dm: conso...
722
  	r = dm_create(m, &md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
723
724
725
726
  	if (r)
  		return r;
  
  	r = dm_hash_insert(param->name, *param->uuid ? param->uuid : NULL, md);
3f77316de   Kiyoshi Ueda   dm: separate devi...
727
728
729
730
731
  	if (r) {
  		dm_put(md);
  		dm_destroy(md);
  		return r;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
  
  	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
734
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
735
  	dm_put(md);
3f77316de   Kiyoshi Ueda   dm: separate devi...
736
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
737
738
739
740
741
  }
  
  /*
   * Always use UUID for lookups if it's present, otherwise use name or dev.
   */
858119e15   Arjan van de Ven   [PATCH] Unlinline...
742
  static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
  {
0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
744
  	struct hash_cell *hc = NULL;
9ade92a9a   Alasdair G Kergon   [PATCH] dm: tidy ...
745

0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
746
  	if (*param->uuid) {
759dea204   Mikulas Patocka   dm ioctl: forbid ...
747
748
  		if (*param->name || param->dev)
  			return NULL;
0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
749
750
751
  		hc = __get_uuid_cell(param->uuid);
  		if (!hc)
  			return NULL;
ba2e19b0f   Mikulas Patocka   dm ioctl: introdu...
752
  	} else if (*param->name) {
759dea204   Mikulas Patocka   dm ioctl: forbid ...
753
754
  		if (param->dev)
  			return NULL;
0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
755
756
757
  		hc = __get_name_cell(param->name);
  		if (!hc)
  			return NULL;
ba2e19b0f   Mikulas Patocka   dm ioctl: introdu...
758
759
760
761
762
  	} else if (param->dev) {
  		hc = __get_dev_cell(param->dev);
  		if (!hc)
  			return NULL;
  	} else
0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
763
  		return NULL;
9ade92a9a   Alasdair G Kergon   [PATCH] dm: tidy ...
764

0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  	/*
  	 * Sneakily write in both the name and the uuid
  	 * while we have the cell.
  	 */
  	strlcpy(param->name, hc->name, sizeof(param->name));
  	if (hc->uuid)
  		strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
  	else
  		param->uuid[0] = '\0';
  
  	if (hc->new_map)
  		param->flags |= DM_INACTIVE_PRESENT_FLAG;
  	else
  		param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
  
  	return hc;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
  }
858119e15   Arjan van de Ven   [PATCH] Unlinline...
782
  static struct mapped_device *find_device(struct dm_ioctl *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
784
785
786
787
788
  {
  	struct hash_cell *hc;
  	struct mapped_device *md = NULL;
  
  	down_read(&_hash_lock);
  	hc = __find_device_hash_cell(param);
0ddf9644c   Mikulas Patocka   dm ioctl: fill in...
789
  	if (hc)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
  		md = hc->md;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
793
794
795
796
797
798
  	up_read(&_hash_lock);
  
  	return md;
  }
  
  static int dev_remove(struct dm_ioctl *param, size_t param_size)
  {
  	struct hash_cell *hc;
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
799
  	struct mapped_device *md;
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
800
  	int r;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
801
  	struct dm_table *t;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
805
806
  
  	down_write(&_hash_lock);
  	hc = __find_device_hash_cell(param);
  
  	if (!hc) {
810b49237   Milan Broz   dm ioctl: suppres...
807
  		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
808
809
810
  		up_write(&_hash_lock);
  		return -ENXIO;
  	}
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
811
  	md = hc->md;
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
812
813
814
  	/*
  	 * Ensure the device is not open and nothing further can open it.
  	 */
2c140a246   Mikulas Patocka   dm: allow remove ...
815
  	r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
816
  	if (r) {
2c140a246   Mikulas Patocka   dm: allow remove ...
817
818
819
820
821
  		if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) {
  			up_write(&_hash_lock);
  			dm_put(md);
  			return 0;
  		}
810b49237   Milan Broz   dm ioctl: suppres...
822
  		DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
5c6bd75d0   Alasdair G Kergon   [PATCH] dm: preve...
823
824
825
826
  		up_write(&_hash_lock);
  		dm_put(md);
  		return r;
  	}
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
827
  	t = __hash_remove(hc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
  	up_write(&_hash_lock);
60935eb21   Milan Broz   dm ioctl: support...
829

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
830
831
832
833
  	if (t) {
  		dm_sync_table(md);
  		dm_table_destroy(t);
  	}
2c140a246   Mikulas Patocka   dm: allow remove ...
834
  	param->flags &= ~DM_DEFERRED_REMOVE;
3abf85b5b   Peter Rajnoha   dm ioctl: introdu...
835
836
  	if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr))
  		param->flags |= DM_UEVENT_GENERATED_FLAG;
60935eb21   Milan Broz   dm ioctl: support...
837

7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
838
  	dm_put(md);
3f77316de   Kiyoshi Ueda   dm: separate devi...
839
  	dm_destroy(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
  	return 0;
  }
  
  /*
   * Check a string doesn't overrun the chunk of
   * memory we copied from userland.
   */
  static int invalid_str(char *str, void *end)
  {
  	while ((void *) str < end)
  		if (!*str++)
  			return 0;
  
  	return -EINVAL;
  }
  
  static int dev_rename(struct dm_ioctl *param, size_t param_size)
  {
  	int r;
84c89557a   Peter Jones   dm ioctl: allow r...
859
  	char *new_data = (char *) param + param->data_start;
856a6f1db   Peter Rajnoha   dm ioctl: return ...
860
  	struct mapped_device *md;
84c89557a   Peter Jones   dm ioctl: allow r...
861
  	unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862

84c89557a   Peter Jones   dm ioctl: allow r...
863
  	if (new_data < param->data ||
c2b048246   Alasdair Kergon   dm ioctl: prevent...
864
  	    invalid_str(new_data, (void *) param + param_size) || !*new_data ||
84c89557a   Peter Jones   dm ioctl: allow r...
865
866
  	    strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
  		DMWARN("Invalid new mapped device name or uuid string supplied.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
867
868
  		return -EINVAL;
  	}
84c89557a   Peter Jones   dm ioctl: allow r...
869
870
871
872
873
  	if (!change_uuid) {
  		r = check_name(new_data);
  		if (r)
  			return r;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874

84c89557a   Peter Jones   dm ioctl: allow r...
875
  	md = dm_hash_rename(param, new_data);
856a6f1db   Peter Rajnoha   dm ioctl: return ...
876
877
  	if (IS_ERR(md))
  		return PTR_ERR(md);
3abf85b5b   Peter Rajnoha   dm ioctl: introdu...
878

856a6f1db   Peter Rajnoha   dm ioctl: return ...
879
880
881
882
  	__dev_status(md, param);
  	dm_put(md);
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
883
  }
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
884
885
886
887
888
889
890
  static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
  {
  	int r = -EINVAL, x;
  	struct mapped_device *md;
  	struct hd_geometry geometry;
  	unsigned long indata[4];
  	char *geostr = (char *) param + param->data_start;
31998ef19   Mikulas Patocka   dm: reject traili...
891
  	char dummy;
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
892
893
894
895
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
27238b2be   Alasdair G Kergon   dm ioctl: remove ...
896
  	if (geostr < param->data ||
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
897
898
899
900
  	    invalid_str(geostr, (void *) param + param_size)) {
  		DMWARN("Invalid geometry supplied.");
  		goto out;
  	}
31998ef19   Mikulas Patocka   dm: reject traili...
901
902
  	x = sscanf(geostr, "%lu %lu %lu %lu%c", indata,
  		   indata + 1, indata + 2, indata + 3, &dummy);
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
  
  	if (x != 4) {
  		DMWARN("Unable to interpret geometry settings.");
  		goto out;
  	}
  
  	if (indata[0] > 65535 || indata[1] > 255 ||
  	    indata[2] > 255 || indata[3] > ULONG_MAX) {
  		DMWARN("Geometry exceeds range limits.");
  		goto out;
  	}
  
  	geometry.cylinders = indata[0];
  	geometry.heads = indata[1];
  	geometry.sectors = indata[2];
  	geometry.start = indata[3];
  
  	r = dm_set_geometry(md, &geometry);
3ac51e741   Darrick J. Wong   [PATCH] dm store ...
921
922
923
924
925
926
927
  
  	param->data_size = 0;
  
  out:
  	dm_put(md);
  	return r;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928
929
930
  static int do_suspend(struct dm_ioctl *param)
  {
  	int r = 0;
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
931
  	unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
932
933
934
935
936
  	struct mapped_device *md;
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
6da487dcc   Alasdair G Kergon   [PATCH] device-ma...
937
  	if (param->flags & DM_SKIP_LOCKFS_FLAG)
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
938
  		suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
81fdb096d   Kiyoshi Ueda   [PATCH] dm: ioctl...
939
940
  	if (param->flags & DM_NOFLUSH_FLAG)
  		suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
6da487dcc   Alasdair G Kergon   [PATCH] device-ma...
941

094ea9a07   Alasdair G Kergon   dm ioctl: make __...
942
  	if (!dm_suspended_md(md)) {
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
943
  		r = dm_suspend(md, suspend_flags);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
944
945
946
  		if (r)
  			goto out;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
947

094ea9a07   Alasdair G Kergon   dm ioctl: make __...
948
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949

094ea9a07   Alasdair G Kergon   dm ioctl: make __...
950
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
951
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
952

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953
954
955
956
957
958
  	return r;
  }
  
  static int do_resume(struct dm_ioctl *param)
  {
  	int r = 0;
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
959
  	unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
960
961
  	struct hash_cell *hc;
  	struct mapped_device *md;
042d2a9bc   Alasdair G Kergon   dm: keep old tabl...
962
  	struct dm_table *new_map, *old_map = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
963
964
965
966
967
  
  	down_write(&_hash_lock);
  
  	hc = __find_device_hash_cell(param);
  	if (!hc) {
810b49237   Milan Broz   dm ioctl: suppres...
968
  		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
971
972
973
  		up_write(&_hash_lock);
  		return -ENXIO;
  	}
  
  	md = hc->md;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
974
975
976
977
978
979
980
981
982
983
  
  	new_map = hc->new_map;
  	hc->new_map = NULL;
  	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
  
  	up_write(&_hash_lock);
  
  	/* Do we need to load a new map ? */
  	if (new_map) {
  		/* Suspend if it isn't already suspended */
6da487dcc   Alasdair G Kergon   [PATCH] device-ma...
984
  		if (param->flags & DM_SKIP_LOCKFS_FLAG)
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
985
  			suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
81fdb096d   Kiyoshi Ueda   [PATCH] dm: ioctl...
986
987
  		if (param->flags & DM_NOFLUSH_FLAG)
  			suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG;
4f186f8bb   Kiyoshi Ueda   dm: rename dm_sus...
988
  		if (!dm_suspended_md(md))
a3d77d35b   Kiyoshi Ueda   [PATCH] dm: suspe...
989
  			dm_suspend(md, suspend_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
990

042d2a9bc   Alasdair G Kergon   dm: keep old tabl...
991
992
  		old_map = dm_swap_table(md, new_map);
  		if (IS_ERR(old_map)) {
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
993
  			dm_sync_table(md);
d58168763   Mikulas Patocka   dm table: rework ...
994
  			dm_table_destroy(new_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
995
  			dm_put(md);
042d2a9bc   Alasdair G Kergon   dm: keep old tabl...
996
  			return PTR_ERR(old_map);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
997
998
999
1000
1001
1002
  		}
  
  		if (dm_table_get_mode(new_map) & FMODE_WRITE)
  			set_disk_ro(dm_disk(md), 0);
  		else
  			set_disk_ro(dm_disk(md), 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1003
  	}
0f3649a9e   Mike Snitzer   dm ioctl: only is...
1004
  	if (dm_suspended_md(md)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1005
  		r = dm_resume(md);
3abf85b5b   Peter Rajnoha   dm ioctl: introdu...
1006
1007
  		if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
  			param->flags |= DM_UEVENT_GENERATED_FLAG;
0f3649a9e   Mike Snitzer   dm ioctl: only is...
1008
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1009

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1010
1011
1012
1013
  	/*
  	 * Since dm_swap_table synchronizes RCU, nobody should be in
  	 * read-side critical section already.
  	 */
042d2a9bc   Alasdair G Kergon   dm: keep old tabl...
1014
1015
  	if (old_map)
  		dm_table_destroy(old_map);
60935eb21   Milan Broz   dm ioctl: support...
1016

0f3649a9e   Mike Snitzer   dm ioctl: only is...
1017
  	if (!r)
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1018
  		__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  
  	dm_put(md);
  	return r;
  }
  
  /*
   * Set or unset the suspension state of a device.
   * If the device already is in the requested state we just return its status.
   */
  static int dev_suspend(struct dm_ioctl *param, size_t param_size)
  {
  	if (param->flags & DM_SUSPEND_FLAG)
  		return do_suspend(param);
  
  	return do_resume(param);
  }
  
  /*
   * Copies device info back to user space, used by
   * the create and info ioctls.
   */
  static int dev_status(struct dm_ioctl *param, size_t param_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
1044
1045
1046
  	struct mapped_device *md;
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1047
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1048
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1049
1050
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
  }
  
  /*
   * Build up the status struct for each target
   */
  static void retrieve_status(struct dm_table *table,
  			    struct dm_ioctl *param, size_t param_size)
  {
  	unsigned int i, num_targets;
  	struct dm_target_spec *spec;
  	char *outbuf, *outptr;
  	status_type_t type;
  	size_t remaining, len, used = 0;
1f4e0ff07   Alasdair G Kergon   dm thin: commit b...
1064
  	unsigned status_flags = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
  
  	outptr = outbuf = get_result_buffer(param, param_size, &len);
  
  	if (param->flags & DM_STATUS_TABLE_FLAG)
  		type = STATUSTYPE_TABLE;
  	else
  		type = STATUSTYPE_INFO;
  
  	/* Get all the target info */
  	num_targets = dm_table_get_num_targets(table);
  	for (i = 0; i < num_targets; i++) {
  		struct dm_target *ti = dm_table_get_target(table, i);
fd7c092e7   Mikulas Patocka   dm: fix truncated...
1077
  		size_t l;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
  
  		remaining = len - (outptr - outbuf);
  		if (remaining <= sizeof(struct dm_target_spec)) {
  			param->flags |= DM_BUFFER_FULL_FLAG;
  			break;
  		}
  
  		spec = (struct dm_target_spec *) outptr;
  
  		spec->status = 0;
  		spec->sector_start = ti->begin;
  		spec->length = ti->len;
  		strncpy(spec->target_type, ti->type->name,
  			sizeof(spec->target_type));
  
  		outptr += sizeof(struct dm_target_spec);
  		remaining = len - (outptr - outbuf);
  		if (remaining <= 0) {
  			param->flags |= DM_BUFFER_FULL_FLAG;
  			break;
  		}
  
  		/* Get the status/table string from the target driver */
  		if (ti->type->status) {
1f4e0ff07   Alasdair G Kergon   dm thin: commit b...
1102
1103
  			if (param->flags & DM_NOFLUSH_FLAG)
  				status_flags |= DM_STATUS_NOFLUSH_FLAG;
fd7c092e7   Mikulas Patocka   dm: fix truncated...
1104
  			ti->type->status(ti, type, status_flags, outptr, remaining);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1105
1106
  		} else
  			outptr[0] = '\0';
fd7c092e7   Mikulas Patocka   dm: fix truncated...
1107
1108
1109
1110
1111
1112
1113
  		l = strlen(outptr) + 1;
  		if (l == remaining) {
  			param->flags |= DM_BUFFER_FULL_FLAG;
  			break;
  		}
  
  		outptr += l;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
  		used = param->data_start + (outptr - outbuf);
  
  		outptr = align_ptr(outptr);
  		spec->next = outptr - outbuf;
  	}
  
  	if (used)
  		param->data_size = used;
  
  	param->target_count = num_targets;
  }
  
  /*
   * Wait for a device to report an event
   */
  static int dev_wait(struct dm_ioctl *param, size_t param_size)
  {
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1131
  	int r = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1132
1133
  	struct mapped_device *md;
  	struct dm_table *table;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1134
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
  
  	/*
  	 * Wait for a notification event
  	 */
  	if (dm_wait_event(md, param->event_nr)) {
  		r = -ERESTARTSYS;
  		goto out;
  	}
  
  	/*
  	 * The userland program is going to want to know what
  	 * changed to trigger the event, so we may as well tell
  	 * him and save an ioctl.
  	 */
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1153
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1154

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1155
1156
  	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
  	if (table)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1157
  		retrieve_status(table, param, param_size);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1158
  	dm_put_live_table(md, srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1159

094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1160
  out:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1161
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1162

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1163
1164
  	return r;
  }
aeb5d7270   Al Viro   [PATCH] introduce...
1165
  static inline fmode_t get_mode(struct dm_ioctl *param)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1166
  {
aeb5d7270   Al Viro   [PATCH] introduce...
1167
  	fmode_t mode = FMODE_READ | FMODE_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
  
  	if (param->flags & DM_READONLY_FLAG)
  		mode = FMODE_READ;
  
  	return mode;
  }
  
  static int next_target(struct dm_target_spec *last, uint32_t next, void *end,
  		       struct dm_target_spec **spec, char **target_params)
  {
  	*spec = (struct dm_target_spec *) ((unsigned char *) last + next);
  	*target_params = (char *) (*spec + 1);
  
  	if (*spec < (last + 1))
  		return -EINVAL;
  
  	return invalid_str(*target_params, end);
  }
  
  static int populate_table(struct dm_table *table,
  			  struct dm_ioctl *param, size_t param_size)
  {
  	int r;
  	unsigned int i = 0;
  	struct dm_target_spec *spec = (struct dm_target_spec *) param;
  	uint32_t next = param->data_start;
  	void *end = (void *) param + param_size;
  	char *target_params;
  
  	if (!param->target_count) {
  		DMWARN("populate_table: no targets specified");
  		return -EINVAL;
  	}
  
  	for (i = 0; i < param->target_count; i++) {
  
  		r = next_target(spec, next, end, &spec, &target_params);
  		if (r) {
  			DMWARN("unable to find target");
  			return r;
  		}
  
  		r = dm_table_add_target(table, spec->target_type,
  					(sector_t) spec->sector_start,
  					(sector_t) spec->length,
  					target_params);
  		if (r) {
  			DMWARN("error adding target to table");
  			return r;
  		}
  
  		next = spec->next;
  	}
  
  	return dm_table_complete(table);
  }
  
  static int table_load(struct dm_ioctl *param, size_t param_size)
  {
  	int r;
  	struct hash_cell *hc;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1229
  	struct dm_table *t, *old_map = NULL;
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1230
  	struct mapped_device *md;
36a0456fb   Alasdair G Kergon   dm table: add imm...
1231
  	struct target_type *immutable_target_type;
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1232
1233
1234
1235
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1236

1134e5ae7   Mike Anderson   [PATCH] dm table:...
1237
  	r = dm_table_create(&t, get_mode(param), param->target_count, md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1238
  	if (r)
f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1239
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1240

00c4fc3b1   Mike Snitzer   dm ioctl: increas...
1241
1242
  	/* Protect md->type and md->queue against concurrent table loads. */
  	dm_lock_md_type(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243
  	r = populate_table(t, param, param_size);
f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1244
1245
  	if (r)
  		goto err_unlock_md_type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246

36a0456fb   Alasdair G Kergon   dm table: add imm...
1247
1248
  	immutable_target_type = dm_get_immutable_target_type(md);
  	if (immutable_target_type &&
f083b09b7   Mike Snitzer   dm: set DM_TARGET...
1249
1250
  	    (immutable_target_type != dm_table_get_immutable_target_type(t)) &&
  	    !dm_table_get_wildcard_target(t)) {
36a0456fb   Alasdair G Kergon   dm table: add imm...
1251
1252
  		DMWARN("can't replace immutable target type %s",
  		       immutable_target_type->name);
36a0456fb   Alasdair G Kergon   dm table: add imm...
1253
  		r = -EINVAL;
f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1254
  		goto err_unlock_md_type;
36a0456fb   Alasdair G Kergon   dm table: add imm...
1255
  	}
3e6180f0c   Christoph Hellwig   dm: only initiali...
1256
  	if (dm_get_md_type(md) == DM_TYPE_NONE) {
a5664dad7   Mike Snitzer   dm ioctl: make bi...
1257
1258
  		/* Initial table load: acquire type of table. */
  		dm_set_md_type(md, dm_table_get_type(t));
3e6180f0c   Christoph Hellwig   dm: only initiali...
1259
1260
  
  		/* setup md->queue to reflect md's type (may block) */
591ddcfc4   Mike Snitzer   dm: allow immutab...
1261
  		r = dm_setup_md_queue(md, t);
3e6180f0c   Christoph Hellwig   dm: only initiali...
1262
1263
1264
1265
1266
  		if (r) {
  			DMWARN("unable to set up device queue for new table.");
  			goto err_unlock_md_type;
  		}
  	} else if (dm_get_md_type(md) != dm_table_get_type(t)) {
a5664dad7   Mike Snitzer   dm ioctl: make bi...
1267
  		DMWARN("can't change device type after initial table load.");
a5664dad7   Mike Snitzer   dm ioctl: make bi...
1268
  		r = -EINVAL;
f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1269
  		goto err_unlock_md_type;
a5664dad7   Mike Snitzer   dm ioctl: make bi...
1270
  	}
4a0b4ddf2   Mike Snitzer   dm: do not initia...
1271

a5664dad7   Mike Snitzer   dm ioctl: make bi...
1272
1273
1274
  	dm_unlock_md_type(md);
  
  	/* stage inactive table */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1275
  	down_write(&_hash_lock);
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1276
1277
1278
  	hc = dm_get_mdptr(md);
  	if (!hc || hc->md != md) {
  		DMWARN("device has been removed from the dev hash table.");
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1279
1280
  		up_write(&_hash_lock);
  		r = -ENXIO;
f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1281
  		goto err_destroy_table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1282
1283
1284
  	}
  
  	if (hc->new_map)
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1285
  		old_map = hc->new_map;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1286
  	hc->new_map = t;
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1287
  	up_write(&_hash_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1288
  	param->flags |= DM_INACTIVE_PRESENT_FLAG;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1289
  	__dev_status(md, param);
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1290

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1291
1292
1293
1294
  	if (old_map) {
  		dm_sync_table(md);
  		dm_table_destroy(old_map);
  	}
1134e5ae7   Mike Anderson   [PATCH] dm table:...
1295
  	dm_put(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1296

f11c1c569   Mike Snitzer   dm ioctl: cleanup...
1297
1298
1299
1300
1301
1302
1303
1304
  	return 0;
  
  err_unlock_md_type:
  	dm_unlock_md_type(md);
  err_destroy_table:
  	dm_table_destroy(t);
  err:
  	dm_put(md);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1305
1306
1307
1308
1309
  	return r;
  }
  
  static int table_clear(struct dm_ioctl *param, size_t param_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1310
  	struct hash_cell *hc;
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
1311
  	struct mapped_device *md;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1312
  	struct dm_table *old_map = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1313
1314
1315
1316
1317
  
  	down_write(&_hash_lock);
  
  	hc = __find_device_hash_cell(param);
  	if (!hc) {
810b49237   Milan Broz   dm ioctl: suppres...
1318
  		DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1319
1320
1321
1322
1323
  		up_write(&_hash_lock);
  		return -ENXIO;
  	}
  
  	if (hc->new_map) {
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1324
  		old_map = hc->new_map;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1325
1326
1327
1328
  		hc->new_map = NULL;
  	}
  
  	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1329
  	__dev_status(hc->md, param);
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
1330
  	md = hc->md;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1331
  	up_write(&_hash_lock);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1332
1333
1334
1335
  	if (old_map) {
  		dm_sync_table(md);
  		dm_table_destroy(old_map);
  	}
7ec75f254   Jeff Mahoney   [PATCH] dm: fix m...
1336
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1337
1338
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
  }
  
  /*
   * Retrieves a list of devices used by a particular dm device.
   */
  static void retrieve_deps(struct dm_table *table,
  			  struct dm_ioctl *param, size_t param_size)
  {
  	unsigned int count = 0;
  	struct list_head *tmp;
  	size_t len, needed;
82b1519b3   Mikulas Patocka   dm: export struct...
1350
  	struct dm_dev_internal *dd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
  	struct dm_target_deps *deps;
  
  	deps = get_result_buffer(param, param_size, &len);
  
  	/*
  	 * Count the devices.
  	 */
  	list_for_each (tmp, dm_table_get_devices(table))
  		count++;
  
  	/*
  	 * Check we have enough space.
  	 */
  	needed = sizeof(*deps) + (sizeof(*deps->dev) * count);
  	if (len < needed) {
  		param->flags |= DM_BUFFER_FULL_FLAG;
  		return;
  	}
  
  	/*
  	 * Fill in the devices.
  	 */
  	deps->count = count;
  	count = 0;
  	list_for_each_entry (dd, dm_table_get_devices(table), list)
86f1152b1   Benjamin Marzinski   dm: allow active ...
1376
  		deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1377
1378
1379
1380
1381
1382
  
  	param->data_size = param->data_start + needed;
  }
  
  static int table_deps(struct dm_ioctl *param, size_t param_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1383
1384
  	struct mapped_device *md;
  	struct dm_table *table;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1385
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1386
1387
1388
1389
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1390
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1391

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1392
1393
  	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
  	if (table)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1394
  		retrieve_deps(table, param, param_size);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1395
  	dm_put_live_table(md, srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1396

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1397
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1398
1399
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1400
1401
1402
1403
1404
1405
1406
1407
  }
  
  /*
   * Return the status of a device as a text string for each
   * target.
   */
  static int table_status(struct dm_ioctl *param, size_t param_size)
  {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1408
1409
  	struct mapped_device *md;
  	struct dm_table *table;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1410
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1411
1412
1413
1414
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1415
  	__dev_status(md, param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1416

83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1417
1418
  	table = dm_get_live_or_inactive_table(md, param, &srcu_idx);
  	if (table)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
  		retrieve_status(table, param, param_size);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1420
  	dm_put_live_table(md, srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1421

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1422
  	dm_put(md);
094ea9a07   Alasdair G Kergon   dm ioctl: make __...
1423
1424
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1425
  }
a26062416   Mikulas Patocka   dm ioctl: allow m...
1426
  /*
fd2ed4d25   Mikulas Patocka   dm: add statistic...
1427
1428
   * Process device-mapper dependent messages.  Messages prefixed with '@'
   * are processed by the DM core.  All others are delivered to the target.
a26062416   Mikulas Patocka   dm ioctl: allow m...
1429
1430
1431
1432
1433
1434
   * Returns a number <= 1 if message was processed by device mapper.
   * Returns 2 if message should be delivered to the target.
   */
  static int message_for_md(struct mapped_device *md, unsigned argc, char **argv,
  			  char *result, unsigned maxlen)
  {
fd2ed4d25   Mikulas Patocka   dm: add statistic...
1435
1436
1437
1438
  	int r;
  
  	if (**argv != '@')
  		return 2; /* no '@' prefix, deliver to target */
2c140a246   Mikulas Patocka   dm: allow remove ...
1439
1440
1441
1442
1443
1444
1445
  	if (!strcasecmp(argv[0], "@cancel_deferred_remove")) {
  		if (argc != 1) {
  			DMERR("Invalid arguments for @cancel_deferred_remove");
  			return -EINVAL;
  		}
  		return dm_cancel_deferred_remove(md);
  	}
fd2ed4d25   Mikulas Patocka   dm: add statistic...
1446
1447
1448
1449
1450
1451
  	r = dm_stats_message(md, argc, argv, result, maxlen);
  	if (r < 2)
  		return r;
  
  	DMERR("Unsupported message sent to DM core: %s", argv[0]);
  	return -EINVAL;
a26062416   Mikulas Patocka   dm ioctl: allow m...
1452
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
  /*
   * Pass a message to the target that's at the supplied device offset.
   */
  static int target_message(struct dm_ioctl *param, size_t param_size)
  {
  	int r, argc;
  	char **argv;
  	struct mapped_device *md;
  	struct dm_table *table;
  	struct dm_target *ti;
  	struct dm_target_msg *tmsg = (void *) param + param->data_start;
a26062416   Mikulas Patocka   dm ioctl: allow m...
1464
1465
  	size_t maxlen;
  	char *result = get_result_buffer(param, param_size, &maxlen);
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1466
  	int srcu_idx;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1467
1468
1469
1470
  
  	md = find_device(param);
  	if (!md)
  		return -ENXIO;
027d50f92   Milan Broz   dm io:ctl use con...
1471
  	if (tmsg < (struct dm_target_msg *) param->data ||
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
  	    invalid_str(tmsg->message, (void *) param + param_size)) {
  		DMWARN("Invalid target message parameters.");
  		r = -EINVAL;
  		goto out;
  	}
  
  	r = dm_split_args(&argc, &argv, tmsg->message);
  	if (r) {
  		DMWARN("Failed to split target message parameters");
  		goto out;
  	}
2ca4c92f5   Alasdair G Kergon   dm ioctl: prevent...
1483
1484
  	if (!argc) {
  		DMWARN("Empty message received.");
902c6a96a   Jesper Juhl   dm ioctl: do not ...
1485
  		goto out_argv;
2ca4c92f5   Alasdair G Kergon   dm ioctl: prevent...
1486
  	}
a26062416   Mikulas Patocka   dm ioctl: allow m...
1487
1488
1489
  	r = message_for_md(md, argc, argv, result, maxlen);
  	if (r <= 1)
  		goto out_argv;
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1490
  	table = dm_get_live_table(md, &srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1491
  	if (!table)
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1492
  		goto out_table;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1493

c50abeb38   Mike Anderson   dm ioctl: forbid ...
1494
1495
1496
1497
  	if (dm_deleting_md(md)) {
  		r = -ENXIO;
  		goto out_table;
  	}
512875bd9   Jun'ichi Nomura   dm: table detect ...
1498
1499
  	ti = dm_table_find_target(table, tmsg->sector);
  	if (!dm_target_is_valid(ti)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1500
1501
  		DMWARN("Target message sector outside device.");
  		r = -EINVAL;
512875bd9   Jun'ichi Nomura   dm: table detect ...
1502
  	} else if (ti->type->message)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1503
1504
1505
1506
1507
  		r = ti->type->message(ti, argc, argv);
  	else {
  		DMWARN("Target type does not support messages");
  		r = -EINVAL;
  	}
c50abeb38   Mike Anderson   dm ioctl: forbid ...
1508
   out_table:
83d5e5b0a   Mikulas Patocka   dm: optimize use ...
1509
  	dm_put_live_table(md, srcu_idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1510
1511
1512
   out_argv:
  	kfree(argv);
   out:
a26062416   Mikulas Patocka   dm ioctl: allow m...
1513
1514
1515
1516
1517
  	if (r >= 0)
  		__dev_status(md, param);
  
  	if (r == 1) {
  		param->flags |= DM_DATA_OUT_FLAG;
fd2ed4d25   Mikulas Patocka   dm: add statistic...
1518
  		if (dm_message_test_buffer_overflow(result, maxlen))
a26062416   Mikulas Patocka   dm ioctl: allow m...
1519
1520
1521
1522
1523
  			param->flags |= DM_BUFFER_FULL_FLAG;
  		else
  			param->data_size = param->data_start + strlen(result) + 1;
  		r = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1524
1525
1526
  	dm_put(md);
  	return r;
  }
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1527
1528
1529
1530
1531
1532
1533
  /*
   * The ioctl parameter block consists of two parts, a dm_ioctl struct
   * followed by a data buffer.  This flag is set if the second part,
   * which has a variable size, is not used by the function processing
   * the ioctl.
   */
  #define IOCTL_FLAGS_NO_PARAMS	1
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1534
1535
1536
1537
  /*-----------------------------------------------------------------
   * Implementation of open/close/ioctl on the special char
   * device.
   *---------------------------------------------------------------*/
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1538
  static ioctl_fn lookup_ioctl(unsigned int cmd, int *ioctl_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1539
1540
1541
  {
  	static struct {
  		int cmd;
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1542
  		int flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1543
1544
  		ioctl_fn fn;
  	} _ioctls[] = {
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
  		{DM_VERSION_CMD, 0, NULL}, /* version is dealt with elsewhere */
  		{DM_REMOVE_ALL_CMD, IOCTL_FLAGS_NO_PARAMS, remove_all},
  		{DM_LIST_DEVICES_CMD, 0, list_devices},
  
  		{DM_DEV_CREATE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_create},
  		{DM_DEV_REMOVE_CMD, IOCTL_FLAGS_NO_PARAMS, dev_remove},
  		{DM_DEV_RENAME_CMD, 0, dev_rename},
  		{DM_DEV_SUSPEND_CMD, IOCTL_FLAGS_NO_PARAMS, dev_suspend},
  		{DM_DEV_STATUS_CMD, IOCTL_FLAGS_NO_PARAMS, dev_status},
  		{DM_DEV_WAIT_CMD, 0, dev_wait},
  
  		{DM_TABLE_LOAD_CMD, 0, table_load},
  		{DM_TABLE_CLEAR_CMD, IOCTL_FLAGS_NO_PARAMS, table_clear},
  		{DM_TABLE_DEPS_CMD, 0, table_deps},
  		{DM_TABLE_STATUS_CMD, 0, table_status},
  
  		{DM_LIST_VERSIONS_CMD, 0, list_versions},
  
  		{DM_TARGET_MSG_CMD, 0, target_message},
  		{DM_DEV_SET_GEOMETRY_CMD, 0, dev_set_geometry}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1565
  	};
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1566
1567
1568
1569
1570
  	if (unlikely(cmd >= ARRAY_SIZE(_ioctls)))
  		return NULL;
  
  	*ioctl_flags = _ioctls[cmd].flags;
  	return _ioctls[cmd].fn;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
  }
  
  /*
   * As well as checking the version compatibility this always
   * copies the kernel interface version out.
   */
  static int check_version(unsigned int cmd, struct dm_ioctl __user *user)
  {
  	uint32_t version[3];
  	int r = 0;
  
  	if (copy_from_user(version, user->version, sizeof(version)))
  		return -EFAULT;
  
  	if ((DM_VERSION_MAJOR != version[0]) ||
  	    (DM_VERSION_MINOR < version[1])) {
  		DMWARN("ioctl interface mismatch: "
  		       "kernel(%u.%u.%u), user(%u.%u.%u), cmd(%d)",
  		       DM_VERSION_MAJOR, DM_VERSION_MINOR,
  		       DM_VERSION_PATCHLEVEL,
  		       version[0], version[1], version[2], cmd);
  		r = -EINVAL;
  	}
  
  	/*
  	 * Fill in the kernel version.
  	 */
  	version[0] = DM_VERSION_MAJOR;
  	version[1] = DM_VERSION_MINOR;
  	version[2] = DM_VERSION_PATCHLEVEL;
  	if (copy_to_user(user->version, version, sizeof(version)))
  		return -EFAULT;
  
  	return r;
  }
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1606
1607
  #define DM_PARAMS_KMALLOC	0x0001	/* Params alloced with kmalloc */
  #define DM_PARAMS_VMALLOC	0x0002	/* Params alloced with vmalloc */
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1608
1609
1610
1611
1612
1613
  #define DM_WIPE_BUFFER		0x0010	/* Wipe input buffer before returning from ioctl */
  
  static void free_params(struct dm_ioctl *param, size_t param_size, int param_flags)
  {
  	if (param_flags & DM_WIPE_BUFFER)
  		memset(param, 0, param_size);
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1614
1615
  	if (param_flags & DM_PARAMS_KMALLOC)
  		kfree(param);
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1616
1617
  	if (param_flags & DM_PARAMS_VMALLOC)
  		vfree(param);
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1618
  }
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1619
1620
1621
  static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kernel,
  		       int ioctl_flags,
  		       struct dm_ioctl **param, int *param_flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1622
  {
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1623
  	struct dm_ioctl *dmi;
f86812054   Milan Broz   dm ioctl: add fla...
1624
  	int secure_data;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1625
  	const size_t minimum_data_size = sizeof(*param_kernel) - sizeof(param_kernel->data);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1626

02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1627
  	if (copy_from_user(param_kernel, user, minimum_data_size))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1628
  		return -EFAULT;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1629
  	if (param_kernel->data_size < minimum_data_size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1630
  		return -EINVAL;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1631
  	secure_data = param_kernel->flags & DM_SECURE_DATA_FLAG;
f86812054   Milan Broz   dm ioctl: add fla...
1632

9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1633
  	*param_flags = secure_data ? DM_WIPE_BUFFER : 0;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1634
1635
1636
1637
1638
  	if (ioctl_flags & IOCTL_FLAGS_NO_PARAMS) {
  		dmi = param_kernel;
  		dmi->data_size = minimum_data_size;
  		goto data_copied;
  	}
5023e5cf5   Mikulas Patocka   dm ioctl: remove ...
1639
1640
  	/*
  	 * Try to avoid low memory issues when a device is suspended.
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1641
  	 * Use kmalloc() rather than vmalloc() when we can.
5023e5cf5   Mikulas Patocka   dm ioctl: remove ...
1642
  	 */
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1643
  	dmi = NULL;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1644
1645
1646
1647
1648
  	if (param_kernel->data_size <= KMALLOC_MAX_SIZE) {
  		dmi = kmalloc(param_kernel->data_size, GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
  		if (dmi)
  			*param_flags |= DM_PARAMS_KMALLOC;
  	}
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1649
1650
  
  	if (!dmi) {
1c0e883e8   Mikulas Patocka   dm ioctl: set noi...
1651
1652
  		unsigned noio_flag;
  		noio_flag = memalloc_noio_save();
72f6d8d8c   Michal Hocko   dm ioctl: drop us...
1653
  		dmi = __vmalloc(param_kernel->data_size, GFP_NOIO | __GFP_HIGH | __GFP_HIGHMEM, PAGE_KERNEL);
1c0e883e8   Mikulas Patocka   dm ioctl: set noi...
1654
  		memalloc_noio_restore(noio_flag);
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1655
1656
  		if (dmi)
  			*param_flags |= DM_PARAMS_VMALLOC;
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1657
  	}
f86812054   Milan Broz   dm ioctl: add fla...
1658
  	if (!dmi) {
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1659
  		if (secure_data && clear_user(user, param_kernel->data_size))
f86812054   Milan Broz   dm ioctl: add fla...
1660
  			return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1661
  		return -ENOMEM;
f86812054   Milan Broz   dm ioctl: add fla...
1662
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1663

02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1664
  	if (copy_from_user(dmi, user, param_kernel->data_size))
6bb43b5d1   Milan Broz   dm ioctl: prepare...
1665
  		goto bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1666

02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1667
  data_copied:
e910d7ebe   Alasdair G Kergon   dm ioctl: prevent...
1668
1669
1670
  	/*
  	 * Abort if something changed the ioctl data while it was being copied.
  	 */
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1671
  	if (dmi->data_size != param_kernel->data_size) {
e910d7ebe   Alasdair G Kergon   dm ioctl: prevent...
1672
1673
1674
  		DMERR("rejecting ioctl: data size modified while processing parameters");
  		goto bad;
  	}
f86812054   Milan Broz   dm ioctl: add fla...
1675
  	/* Wipe the user buffer so we do not return it to userspace */
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1676
  	if (secure_data && clear_user(user, param_kernel->data_size))
f86812054   Milan Broz   dm ioctl: add fla...
1677
  		goto bad;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1678
1679
  	*param = dmi;
  	return 0;
6bb43b5d1   Milan Broz   dm ioctl: prepare...
1680
1681
  
  bad:
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1682
  	free_params(dmi, param_kernel->data_size, *param_flags);
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1683

6bb43b5d1   Milan Broz   dm ioctl: prepare...
1684
  	return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1685
1686
1687
1688
1689
1690
  }
  
  static int validate_params(uint cmd, struct dm_ioctl *param)
  {
  	/* Always clear this flag */
  	param->flags &= ~DM_BUFFER_FULL_FLAG;
3abf85b5b   Peter Rajnoha   dm ioctl: introdu...
1691
  	param->flags &= ~DM_UEVENT_GENERATED_FLAG;
f86812054   Milan Broz   dm ioctl: add fla...
1692
  	param->flags &= ~DM_SECURE_DATA_FLAG;
a26062416   Mikulas Patocka   dm ioctl: allow m...
1693
  	param->flags &= ~DM_DATA_OUT_FLAG;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
  
  	/* Ignores parameters */
  	if (cmd == DM_REMOVE_ALL_CMD ||
  	    cmd == DM_LIST_DEVICES_CMD ||
  	    cmd == DM_LIST_VERSIONS_CMD)
  		return 0;
  
  	if ((cmd == DM_DEV_CREATE_CMD)) {
  		if (!*param->name) {
  			DMWARN("name not supplied when creating device");
  			return -EINVAL;
  		}
  	} else if ((*param->uuid && *param->name)) {
  		DMWARN("only supply one of name or uuid, cmd(%u)", cmd);
  		return -EINVAL;
  	}
  
  	/* Ensure strings are terminated */
  	param->name[DM_NAME_LEN - 1] = '\0';
  	param->uuid[DM_UUID_LEN - 1] = '\0';
  
  	return 0;
  }
27238b2be   Alasdair G Kergon   dm ioctl: remove ...
1717
  static int ctl_ioctl(uint command, struct dm_ioctl __user *user)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1718
1719
  {
  	int r = 0;
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1720
  	int ioctl_flags;
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1721
  	int param_flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1722
  	unsigned int cmd;
a26ffd4aa   Andrew Morton   dm ioctl: use uni...
1723
  	struct dm_ioctl *uninitialized_var(param);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1724
  	ioctl_fn fn = NULL;
6bb43b5d1   Milan Broz   dm ioctl: prepare...
1725
  	size_t input_param_size;
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1726
  	struct dm_ioctl param_kernel;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
  
  	/* only root can play with this */
  	if (!capable(CAP_SYS_ADMIN))
  		return -EACCES;
  
  	if (_IOC_TYPE(command) != DM_IOCTL)
  		return -ENOTTY;
  
  	cmd = _IOC_NR(command);
  
  	/*
  	 * Check the interface version passed in.  This also
  	 * writes out the kernel's interface version.
  	 */
  	r = check_version(cmd, user);
  	if (r)
  		return r;
  
  	/*
  	 * Nothing more to do for the version command.
  	 */
  	if (cmd == DM_VERSION_CMD)
  		return 0;
e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1750
  	fn = lookup_ioctl(cmd, &ioctl_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1751
1752
1753
1754
1755
1756
  	if (!fn) {
  		DMWARN("dm_ctl_ioctl: unknown command 0x%x", command);
  		return -ENOTTY;
  	}
  
  	/*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1757
1758
  	 * Copy the parameters into kernel space.
  	 */
02cde50b7   Mikulas Patocka   dm ioctl: optimiz...
1759
  	r = copy_params(user, &param_kernel, ioctl_flags, &param, &param_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1760

dab6a4291   Alasdair G Kergon   [PATCH] device-ma...
1761
1762
  	if (r)
  		return r;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1763

f86812054   Milan Broz   dm ioctl: add fla...
1764
  	input_param_size = param->data_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1765
1766
1767
  	r = validate_params(cmd, param);
  	if (r)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1768
  	param->data_size = sizeof(*param);
6bb43b5d1   Milan Broz   dm ioctl: prepare...
1769
  	r = fn(param, input_param_size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1770

e2914cc26   Mikulas Patocka   dm ioctl: introdu...
1771
1772
1773
  	if (unlikely(param->flags & DM_BUFFER_FULL_FLAG) &&
  	    unlikely(ioctl_flags & IOCTL_FLAGS_NO_PARAMS))
  		DMERR("ioctl %d tried to output some data but has IOCTL_FLAGS_NO_PARAMS set", cmd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1774
1775
1776
1777
1778
  	/*
  	 * Copy the results back to userland.
  	 */
  	if (!r && copy_to_user(user, param, param->data_size))
  		r = -EFAULT;
6bb43b5d1   Milan Broz   dm ioctl: prepare...
1779
  out:
9c5091f2e   Mikulas Patocka   dm ioctl: use kma...
1780
  	free_params(param, input_param_size, param_flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1781
1782
  	return r;
  }
27238b2be   Alasdair G Kergon   dm ioctl: remove ...
1783
1784
1785
1786
  static long dm_ctl_ioctl(struct file *file, uint command, ulong u)
  {
  	return (long)ctl_ioctl(command, (struct dm_ioctl __user *)u);
  }
76c072b48   Milan Broz   dm ioctl: move co...
1787
1788
1789
1790
1791
1792
1793
1794
  #ifdef CONFIG_COMPAT
  static long dm_compat_ctl_ioctl(struct file *file, uint command, ulong u)
  {
  	return (long)dm_ctl_ioctl(file, command, (ulong) compat_ptr(u));
  }
  #else
  #define dm_compat_ctl_ioctl NULL
  #endif
fa027c2a0   Arjan van de Ven   [PATCH] mark stru...
1795
  static const struct file_operations _ctl_fops = {
402ab352c   Arnd Bergmann   dm ioctl: use non...
1796
  	.open = nonseekable_open,
27238b2be   Alasdair G Kergon   dm ioctl: remove ...
1797
  	.unlocked_ioctl	 = dm_ctl_ioctl,
76c072b48   Milan Broz   dm ioctl: move co...
1798
  	.compat_ioctl = dm_compat_ctl_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1799
  	.owner	 = THIS_MODULE,
6038f373a   Arnd Bergmann   llseek: automatic...
1800
  	.llseek  = noop_llseek,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
1802
1803
  };
  
  static struct miscdevice _dm_misc = {
7e507eb64   Peter Rajnoha   dm: allow autoloa...
1804
  	.minor		= MAPPER_CTRL_MINOR,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1805
  	.name  		= DM_NAME,
7e507eb64   Peter Rajnoha   dm: allow autoloa...
1806
  	.nodename	= DM_DIR "/" DM_CONTROL_NODE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1807
1808
  	.fops  		= &_ctl_fops
  };
7e507eb64   Peter Rajnoha   dm: allow autoloa...
1809
1810
  MODULE_ALIAS_MISCDEV(MAPPER_CTRL_MINOR);
  MODULE_ALIAS("devname:" DM_DIR "/" DM_CONTROL_NODE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
  /*
   * Create misc character device and link to DM_DIR/control.
   */
  int __init dm_interface_init(void)
  {
  	int r;
  
  	r = dm_hash_init();
  	if (r)
  		return r;
  
  	r = misc_register(&_dm_misc);
  	if (r) {
  		DMERR("misc_register failed for control device");
  		dm_hash_exit();
  		return r;
  	}
  
  	DMINFO("%d.%d.%d%s initialised: %s", DM_VERSION_MAJOR,
  	       DM_VERSION_MINOR, DM_VERSION_PATCHLEVEL, DM_VERSION_EXTRA,
  	       DM_DRIVER_EMAIL);
  	return 0;
  }
  
  void dm_interface_exit(void)
  {
f368ed608   Greg Kroah-Hartman   char: make misc_d...
1837
  	misc_deregister(&_dm_misc);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1838
1839
  	dm_hash_exit();
  }
96a1f7dba   Mike Anderson   dm: export name a...
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
  
  /**
   * dm_copy_name_and_uuid - Copy mapped device name & uuid into supplied buffers
   * @md: Pointer to mapped_device
   * @name: Buffer (size DM_NAME_LEN) for name
   * @uuid: Buffer (size DM_UUID_LEN) for uuid or empty string if uuid not defined
   */
  int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid)
  {
  	int r = 0;
  	struct hash_cell *hc;
  
  	if (!md)
  		return -ENXIO;
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
1854
  	mutex_lock(&dm_hash_cells_mutex);
96a1f7dba   Mike Anderson   dm: export name a...
1855
1856
1857
1858
1859
  	hc = dm_get_mdptr(md);
  	if (!hc || hc->md != md) {
  		r = -ENXIO;
  		goto out;
  	}
23d39f63a   Milan Broz   dm ioctl: allow d...
1860
1861
1862
1863
  	if (name)
  		strcpy(name, hc->name);
  	if (uuid)
  		strcpy(uuid, hc->uuid ? : "");
96a1f7dba   Mike Anderson   dm: export name a...
1864
1865
  
  out:
6076905b5   Mikulas Patocka   dm: avoid _hash_l...
1866
  	mutex_unlock(&dm_hash_cells_mutex);
96a1f7dba   Mike Anderson   dm: export name a...
1867
1868
1869
  
  	return r;
  }