check.c 14.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
Linus Torvalds's avatar
Linus Torvalds committed
2 3
 *  fs/partitions/check.c
 *
Linus Torvalds's avatar
Linus Torvalds committed
4 5 6 7 8 9 10 11 12 13 14 15
 *  Code extracted from drivers/block/genhd.c
 *  Copyright (C) 1991-1998  Linus Torvalds
 *  Re-organised Feb 1998 Russell King
 *
 *  We now have independent partition support from the
 *  block drivers, which allows all the partition code to
 *  be grouped in one location, and it to be mostly self
 *  contained.
 *
 *  Added needed MAJORS for new pairs, {hdi,hdj}, {hdk,hdl}
 */

16
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
17 18
#include <linux/fs.h>
#include <linux/blk.h>
Mike Sullivan's avatar
Mike Sullivan committed
19
#include <linux/kmod.h>
Alexander Viro's avatar
Alexander Viro committed
20
#include <linux/ctype.h>
Linus Torvalds's avatar
Linus Torvalds committed
21 22 23 24 25 26

#include "check.h"

#include "acorn.h"
#include "amiga.h"
#include "atari.h"
Linus Torvalds's avatar
Linus Torvalds committed
27
#include "ldm.h"
Linus Torvalds's avatar
Linus Torvalds committed
28 29 30 31 32 33 34
#include "mac.h"
#include "msdos.h"
#include "osf.h"
#include "sgi.h"
#include "sun.h"
#include "ibm.h"
#include "ultrix.h"
35
#include "efi.h"
Linus Torvalds's avatar
Linus Torvalds committed
36

37
#if CONFIG_BLK_DEV_MD
38
extern void md_autodetect_dev(dev_t dev);
39 40
#endif

Linus Torvalds's avatar
Linus Torvalds committed
41 42
int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/

43
static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {
Linus Torvalds's avatar
Linus Torvalds committed
44 45 46
#ifdef CONFIG_ACORN_PARTITION
	acorn_partition,
#endif
47 48 49
#ifdef CONFIG_EFI_PARTITION
	efi_partition,		/* this must come before msdos */
#endif
Linus Torvalds's avatar
Linus Torvalds committed
50 51 52
#ifdef CONFIG_LDM_PARTITION
	ldm_partition,		/* this must come before msdos */
#endif
Linus Torvalds's avatar
Linus Torvalds committed
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
#ifdef CONFIG_MSDOS_PARTITION
	msdos_partition,
#endif
#ifdef CONFIG_OSF_PARTITION
	osf_partition,
#endif
#ifdef CONFIG_SUN_PARTITION
	sun_partition,
#endif
#ifdef CONFIG_AMIGA_PARTITION
	amiga_partition,
#endif
#ifdef CONFIG_ATARI_PARTITION
	atari_partition,
#endif
#ifdef CONFIG_MAC_PARTITION
	mac_partition,
#endif
#ifdef CONFIG_SGI_PARTITION
	sgi_partition,
#endif
#ifdef CONFIG_ULTRIX_PARTITION
	ultrix_partition,
#endif
#ifdef CONFIG_IBM_PARTITION
	ibm_partition,
#endif
	NULL
};
Linus Torvalds's avatar
Linus Torvalds committed
82
 
Linus Torvalds's avatar
Linus Torvalds committed
83
/*
Linus Torvalds's avatar
Linus Torvalds committed
84
 * disk_name() is used by partition check code and the md driver.
Linus Torvalds's avatar
Linus Torvalds committed
85 86 87 88
 * It formats the devicename of the indicated disk into
 * the supplied buffer (of size at least 32), and returns
 * a pointer to that same buffer (for convenience).
 */
Linus Torvalds's avatar
Linus Torvalds committed
89

90
char *disk_name(struct gendisk *hd, int part, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
91
{
92 93 94 95 96 97 98
	int pos;
	if (!part) {
		if (hd->disk_de) {
			pos = devfs_generate_path(hd->disk_de, buf, 64);
			if (pos >= 0)
				return buf + pos;
		}
99
		sprintf(buf, "%s", hd->major_name);
100 101 102 103 104 105 106 107 108 109 110
	} else {
		if (hd->part[part-1].de) {
			pos = devfs_generate_path(hd->part[part-1].de, buf, 64);
			if (pos >= 0)
				return buf + pos;
		}
		if (isdigit(hd->major_name[strlen(hd->major_name)-1]))
			sprintf(buf, "%sp%d", hd->major_name, part);
		else
			sprintf(buf, "%s%d", hd->major_name, part);
	}
Linus Torvalds's avatar
Linus Torvalds committed
111 112 113
	return buf;
}

Mike Sullivan's avatar
Mike Sullivan committed
114 115 116 117 118
/* Driverfs file support */
static ssize_t partition_device_kdev_read(struct device *driverfs_dev, 
			char *page, size_t count, loff_t off)
{
	kdev_t kdev; 
119
	kdev.value=(int)(long)driverfs_dev->driver_data;
Mike Sullivan's avatar
Mike Sullivan committed
120 121
	return off ? 0 : sprintf (page, "%x\n",kdev.value);
}
122
static DEVICE_ATTR(kdev,S_IRUGO,partition_device_kdev_read,NULL);
Mike Sullivan's avatar
Mike Sullivan committed
123 124 125 126 127 128

static ssize_t partition_device_type_read(struct device *driverfs_dev, 
			char *page, size_t count, loff_t off) 
{
	return off ? 0 : sprintf (page, "BLK\n");
}
129
static DEVICE_ATTR(type,S_IRUGO,partition_device_type_read,NULL);
Mike Sullivan's avatar
Mike Sullivan committed
130

131
static void driverfs_create_partitions(struct gendisk *hd)
Mike Sullivan's avatar
Mike Sullivan committed
132
{
133
	int max_p = 1<<hd->minor_shift;
134
	struct hd_struct *p = hd->part;
135 136 137 138 139
	char name[DEVICE_NAME_SIZE];
	char bus_id[BUS_ID_SIZE];
	struct device *dev, *parent;
	int part;

140 141
	/* if driverfs not supported by subsystem, skip partitions */
	if (!(hd->flags & GENHD_FL_DRIVERFS))
Mike Sullivan's avatar
Mike Sullivan committed
142
		return;
143

144 145
	parent = hd->driverfs_dev;

146 147 148 149 150
	if (parent)  {
		sprintf(name, "%s", parent->name);
		sprintf(bus_id, "%s:", parent->bus_id);
	} else {
		*name = *bus_id = '\0';
Mike Sullivan's avatar
Mike Sullivan committed
151
	}
152

153
	dev = &hd->disk_dev;
154 155 156 157
	dev->driver_data = (void *)(long)__mkdev(hd->major, hd->first_minor);
	sprintf(dev->name, "%sdisc", name);
	sprintf(dev->bus_id, "%sdisc", bus_id);
	for (part=1; part < max_p; part++) {
158
		dev = &p[part-1].hd_driverfs_dev;
159 160
		sprintf(dev->name, "%spart%d", name, part);
		sprintf(dev->bus_id, "%s:p%d", bus_id, part);
161
		if (!p[part-1].nr_sects)
162 163 164
			continue;
		dev->driver_data =
				(void *)(long)__mkdev(hd->major, hd->first_minor+part);
Mike Sullivan's avatar
Mike Sullivan committed
165
	}
166

167 168 169 170 171 172 173 174 175
	dev = &hd->disk_dev;
	dev->parent = parent;
	if (parent)
		dev->bus = parent->bus;
	device_register(dev);
	device_create_file(dev, &dev_attr_type);
	device_create_file(dev, &dev_attr_kdev);

	for (part=0; part < max_p-1; part++) {
176 177 178 179
		dev = &p[part].hd_driverfs_dev;
		dev->parent = parent;
		if (parent)
			dev->bus = parent->bus;
180 181
		if (!dev->driver_data)
			continue;
182 183 184
		device_register(dev);
		device_create_file(dev, &dev_attr_type);
		device_create_file(dev, &dev_attr_kdev);
Mike Sullivan's avatar
Mike Sullivan committed
185 186 187
	}
}

188
static void driverfs_remove_partitions(struct gendisk *hd)
Mike Sullivan's avatar
Mike Sullivan committed
189
{
190
	int max_p = 1<<hd->minor_shift;
191
	struct device *dev;
192
	struct hd_struct *p;
Mike Sullivan's avatar
Mike Sullivan committed
193
	int part;
194

195 196
	for (part=1, p = hd->part; part < max_p; part++, p++) {
		dev = &p->hd_driverfs_dev;
197 198 199 200 201
		if (dev->driver_data) {
			device_remove_file(dev, &dev_attr_type);
			device_remove_file(dev, &dev_attr_kdev);
			put_device(dev);	
			dev->driver_data = NULL;
Mike Sullivan's avatar
Mike Sullivan committed
202 203
		}
	}
204 205 206 207 208 209 210
	dev = &hd->disk_dev;
	if (dev->driver_data) {
		device_remove_file(dev, &dev_attr_type);
		device_remove_file(dev, &dev_attr_kdev);
		put_device(dev);	
		dev->driver_data = NULL;
	}
Mike Sullivan's avatar
Mike Sullivan committed
211 212
}

213
static void check_partition(struct gendisk *hd, struct block_device *bdev)
Linus Torvalds's avatar
Linus Torvalds committed
214 215 216
{
	devfs_handle_t de = NULL;
	char buf[64];
217
	struct parsed_partitions *state;
Linus Torvalds's avatar
Linus Torvalds committed
218 219
	int i;

220 221 222
	state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
	if (!state)
		return;
Linus Torvalds's avatar
Linus Torvalds committed
223

224 225
	if (hd->flags & GENHD_FL_DEVFS)
		de = hd->de;
Linus Torvalds's avatar
Linus Torvalds committed
226
	i = devfs_generate_path (de, buf, sizeof buf);
227
	if (i >= 0) {
Linus Torvalds's avatar
Linus Torvalds committed
228
		printk(KERN_INFO " /dev/%s:", buf + i);
229 230
		sprintf(state->name, "p");
	} else {
231
		disk_name(hd, 0, state->name);
232
		printk(KERN_INFO " %s:", state->name);
233
		if (isdigit(state->name[strlen(state->name)-1]))
234 235 236
			sprintf(state->name, "p");
	}
	state->limit = 1<<hd->minor_shift;
Linus Torvalds's avatar
Linus Torvalds committed
237
	for (i = 0; check_part[i]; i++) {
238
		int res, j;
239
		struct hd_struct *p;
240 241 242 243 244 245
		memset(&state->parts, 0, sizeof(state->parts));
		res = check_part[i](state, bdev);
		if (!res)
			continue;
		if (res < 0) {
			if (warn_no_part)
Linus Torvalds's avatar
Linus Torvalds committed
246
				printk(" unable to read partition table\n");
247
			return;
248
		} 
249
		p = hd->part;
250
		for (j = 1; j < state->limit; j++) {
251 252
			p[j-1].start_sect = state->parts[j].from;
			p[j-1].nr_sects = state->parts[j].size;
253
#if CONFIG_BLK_DEV_MD
254
			if (!state->parts[j].flags)
255
				continue;
256
			md_autodetect_dev(bdev->bd_dev+j);
257 258
#endif
		}
259
		return;
Linus Torvalds's avatar
Linus Torvalds committed
260
	}
Linus Torvalds's avatar
Linus Torvalds committed
261 262 263
	printk(" unknown partition table\n");
}

264
static void devfs_register_partition(struct gendisk *dev, int part)
Linus Torvalds's avatar
Linus Torvalds committed
265
{
266
#ifdef CONFIG_DEVFS_FS
Linus Torvalds's avatar
Linus Torvalds committed
267 268
	devfs_handle_t dir;
	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
269
	struct hd_struct *p = dev->part;
Linus Torvalds's avatar
Linus Torvalds committed
270 271
	char devname[16];

272
	if (p[part-1].de)
273
		return;
274
	dir = devfs_get_parent(dev->disk_de);
275 276
	if (!dir)
		return;
277
	if (dev->flags & GENHD_FL_REMOVABLE)
Linus Torvalds's avatar
Linus Torvalds committed
278
		devfs_flags |= DEVFS_FL_REMOVABLE;
279
	sprintf(devname, "part%d", part);
280
	p[part-1].de = devfs_register (dir, devname, devfs_flags,
281
				    dev->major, dev->first_minor + part,
282 283
				    S_IFBLK | S_IRUSR | S_IWUSR,
				    dev->fops, NULL);
284
#endif
Linus Torvalds's avatar
Linus Torvalds committed
285 286
}

287
#ifdef CONFIG_DEVFS_FS
Linus Torvalds's avatar
Linus Torvalds committed
288
static struct unique_numspace disc_numspace = UNIQUE_NUMBERSPACE_INITIALISER;
289 290
static devfs_handle_t cdroms;
static struct unique_numspace cdrom_numspace = UNIQUE_NUMBERSPACE_INITIALISER;
291
#endif
Linus Torvalds's avatar
Linus Torvalds committed
292

293
static void devfs_create_partitions(struct gendisk *dev)
Linus Torvalds's avatar
Linus Torvalds committed
294
{
295
#ifdef CONFIG_DEVFS_FS
Linus Torvalds's avatar
Linus Torvalds committed
296 297 298 299 300
	int pos = 0;
	devfs_handle_t dir, slave;
	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
	char dirname[64], symlink[16];
	static devfs_handle_t devfs_handle;
301
	int part, max_p = 1<<dev->minor_shift;
302
	struct hd_struct *p = dev->part;
Linus Torvalds's avatar
Linus Torvalds committed
303

304
	if (dev->flags & GENHD_FL_REMOVABLE)
Linus Torvalds's avatar
Linus Torvalds committed
305
		devfs_flags |= DEVFS_FL_REMOVABLE;
306 307
	if (dev->flags & GENHD_FL_DEVFS) {
		dir = dev->de;
Linus Torvalds's avatar
Linus Torvalds committed
308 309
		if (!dir)  /*  Aware driver wants to block disc management  */
			return;
310 311 312 313 314
		pos = devfs_generate_path(dir, dirname + 3, sizeof dirname-3);
		if (pos < 0)
			return;
		strncpy(dirname + pos, "../", 3);
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
315
		/*  Unaware driver: construct "real" directory  */
316
		sprintf(dirname, "../%s/disc%d", dev->major_name,
317
			dev->first_minor >> dev->minor_shift);
318
		dir = devfs_mk_dir(NULL, dirname + 3, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
319 320
	}
	if (!devfs_handle)
321
		devfs_handle = devfs_mk_dir(NULL, "discs", NULL);
322 323
	dev->number = devfs_alloc_unique_number (&disc_numspace);
	sprintf(symlink, "disc%d", dev->number);
Linus Torvalds's avatar
Linus Torvalds committed
324 325
	devfs_mk_symlink (devfs_handle, symlink, DEVFS_FL_DEFAULT,
			  dirname + pos, &slave, NULL);
326
	dev->disk_de = devfs_register(dir, "disc", devfs_flags,
327
			    dev->major, dev->first_minor,
Linus Torvalds's avatar
Linus Torvalds committed
328
			    S_IFBLK | S_IRUSR | S_IWUSR, dev->fops, NULL);
329
	devfs_auto_unregister(dev->disk_de, slave);
330
	if (!(dev->flags & GENHD_FL_DEVFS))
Linus Torvalds's avatar
Linus Torvalds committed
331
		devfs_auto_unregister (slave, dir);
332
	for (part = 1; part < max_p; part++, p++)
333 334 335
		if (p->nr_sects)
			devfs_register_partition(dev, part);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
336 337
}

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
static void devfs_create_cdrom(struct gendisk *dev)
{
#ifdef CONFIG_DEVFS_FS
	int pos = 0;
	devfs_handle_t dir, slave;
	unsigned int devfs_flags = DEVFS_FL_DEFAULT;
	char dirname[64], symlink[16];
	char vname[23];

	if (!cdroms)
		cdroms = devfs_mk_dir (NULL, "cdroms", NULL);

	dev->number = devfs_alloc_unique_number(&cdrom_numspace);
	sprintf(vname, "cdroms/cdrom%d", dev->number);
	if (dev->de) {
		int pos;
		devfs_handle_t slave;
		char rname[64];

		dev->disk_de = devfs_register(dev->de, "cd", DEVFS_FL_DEFAULT,
				     dev->major, dev->first_minor,
				     S_IFBLK | S_IRUGO | S_IWUGO,
				     dev->fops, NULL);

		pos = devfs_generate_path(dev->disk_de, rname+3, sizeof(rname)-3);
		if (pos >= 0) {
			strncpy(rname + pos, "../", 3);
Alexander Viro's avatar
Alexander Viro committed
365
			devfs_mk_symlink(cdroms, vname,
366 367 368 369 370 371 372 373 374 375 376 377 378
					 DEVFS_FL_DEFAULT,
					 rname + pos, &slave, NULL);
			devfs_auto_unregister(dev->de, slave);
		}
	} else {
		dev->disk_de = devfs_register (NULL, vname, DEVFS_FL_DEFAULT,
				    dev->major, dev->first_minor,
				    S_IFBLK | S_IRUGO | S_IWUGO,
				    dev->fops, NULL);
	}
#endif
}

379
static void devfs_remove_partitions(struct gendisk *dev)
Linus Torvalds's avatar
Linus Torvalds committed
380 381
{
#ifdef CONFIG_DEVFS_FS
382
	int part;
383
	for (part = (1<<dev->minor_shift)-1; part--; ) {
384 385
		devfs_unregister(dev->part[part].de);
		dev->part[part].de = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
386
	}
387 388
	devfs_unregister(dev->disk_de);
	dev->disk_de = NULL;
389 390 391 392
	if (dev->flags & GENHD_FL_CD)
		devfs_dealloc_unique_number(&cdrom_numspace, dev->number);
	else
		devfs_dealloc_unique_number(&disc_numspace, dev->number);
393
#endif
Linus Torvalds's avatar
Linus Torvalds committed
394 395 396 397 398 399 400 401 402 403 404 405
}

/*
 * This function will re-read the partition tables for a given device,
 * and set things back up again.  There are some important caveats,
 * however.  You must ensure that no one is using the device, and no one
 * can start using the device while this function is being executed.
 *
 * Much of the cleanup from the old partition tables should have already been
 * done
 */

406
void register_disk(struct gendisk *disk, kdev_t dev, unsigned minors,
Linus Torvalds's avatar
Linus Torvalds committed
407 408
	struct block_device_operations *ops, long size)
{
409
	struct block_device *bdev;
Linus Torvalds's avatar
Linus Torvalds committed
410

411
	if (!disk)
Linus Torvalds's avatar
Linus Torvalds committed
412 413
		return;

414
	set_capacity(disk, size);
Linus Torvalds's avatar
Linus Torvalds committed
415

416 417 418
	if (disk->flags & GENHD_FL_CD)
		devfs_create_cdrom(disk);

Andries E. Brouwer's avatar
Andries E. Brouwer committed
419
	/* No minors to use for partitions */
420
	if (!disk->minor_shift)
Linus Torvalds's avatar
Linus Torvalds committed
421 422
		return;

Andries E. Brouwer's avatar
Andries E. Brouwer committed
423 424 425 426
	/* No such device (e.g., media were just removed) */
	if (!size)
		return;

427 428 429
	bdev = bdget(kdev_t_to_nr(dev));
	if (blkdev_get(bdev, FMODE_READ, 0, BDEV_RAW) < 0)
		return;
430 431 432
	check_partition(disk, bdev);
	driverfs_create_partitions(disk);
	devfs_create_partitions(disk);
433
	blkdev_put(bdev, BDEV_RAW);
Linus Torvalds's avatar
Linus Torvalds committed
434
}
Linus Torvalds's avatar
Linus Torvalds committed
435

436 437
void update_partition(struct gendisk *disk, int part)
{
438
	struct hd_struct *p = disk->part + part - 1;
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
	struct device *dev = &p->hd_driverfs_dev;

	if (!p->nr_sects) {
		if (p->de) {
			devfs_unregister(p->de);
			p->de = NULL;
		}
		if (dev->driver_data) {
			device_remove_file(dev, &dev_attr_type);
			device_remove_file(dev, &dev_attr_kdev);
			put_device(dev);	
			dev->driver_data = NULL;
		}
		return;
	}
	if (!p->de)
		devfs_register_partition(disk, part);
	if (dev->driver_data || !(disk->flags & GENHD_FL_DRIVERFS))
		return;
	dev->driver_data =
		(void *)(long)__mkdev(disk->major, disk->first_minor+part);
	device_register(dev);
	device_create_file(dev, &dev_attr_type);
	device_create_file(dev, &dev_attr_kdev);
}

int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
	kdev_t dev = to_kdev_t(bdev->bd_dev);
	int p, res;
	if (!bdev->bd_invalidated)
		return 0;
	if (bdev->bd_part_count)
		return -EBUSY;
	res = invalidate_device(dev, 1);
	if (res)
		return res;
	bdev->bd_invalidated = 0;
477
	for (p = 0; p < (1<<disk->minor_shift) - 1; p++) {
478 479 480 481 482
		disk->part[p].start_sect = 0;
		disk->part[p].nr_sects = 0;
	}
	if (bdev->bd_op->revalidate)
		bdev->bd_op->revalidate(dev);
483
	if (get_capacity(disk))
484 485 486 487 488 489
		check_partition(disk, bdev);
	for (p = 1; p < (1<<disk->minor_shift); p++)
		update_partition(disk, p);
	return res;
}

Linus Torvalds's avatar
Linus Torvalds committed
490 491 492 493 494 495 496 497 498
unsigned char *read_dev_sector(struct block_device *bdev, unsigned long n, Sector *p)
{
	struct address_space *mapping = bdev->bd_inode->i_mapping;
	int sect = PAGE_CACHE_SIZE / 512;
	struct page *page;

	page = read_cache_page(mapping, n/sect,
			(filler_t *)mapping->a_ops->readpage, NULL);
	if (!IS_ERR(page)) {
499
		wait_on_page_locked(page);
Andrew Morton's avatar
Andrew Morton committed
500
		if (!PageUptodate(page))
Linus Torvalds's avatar
Linus Torvalds committed
501 502 503 504 505 506 507 508 509 510 511
			goto fail;
		if (PageError(page))
			goto fail;
		p->v = page;
		return (unsigned char *)page_address(page) + 512 * (n % sect);
fail:
		page_cache_release(page);
	}
	p->v = NULL;
	return NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed
512

513
static int wipe_partitions(struct gendisk *disk)
Linus Torvalds's avatar
Linus Torvalds committed
514
{
515
	int max_p = 1 << disk->minor_shift;
516 517
	kdev_t devp;
	int res;
518 519
	int p;

Linus Torvalds's avatar
Linus Torvalds committed
520
	/* invalidate stuff */
521 522
	for (p = max_p - 1; p > 0; p--) {
		devp = mk_kdev(disk->major,disk->first_minor + p);
Linus Torvalds's avatar
Linus Torvalds committed
523
#if 0					/* %%% superfluous? */
524
		if (disk->part[p-1].nr_sects == 0)
Linus Torvalds's avatar
Linus Torvalds committed
525 526 527 528 529
			continue;
#endif
		res = invalidate_device(devp, 1);
		if (res)
			return res;
530 531
		disk->part[p-1].start_sect = 0;
		disk->part[p-1].nr_sects = 0;
Linus Torvalds's avatar
Linus Torvalds committed
532
	}
533 534 535 536 537 538 539 540 541
	devp = mk_kdev(disk->major,disk->first_minor);
#if 0					/* %%% superfluous? */
	if (disk->part[p].nr_sects == 0)
		continue;
#endif
	res = invalidate_device(devp, 1);
	if (res)
		return res;
	disk->capacity = 0;
Linus Torvalds's avatar
Linus Torvalds committed
542 543
	return 0;
}
544 545 546 547 548 549 550

void del_gendisk(struct gendisk *disk)
{
	driverfs_remove_partitions(disk);
	wipe_partitions(disk);
	unlink_gendisk(disk);
	devfs_remove_partitions(disk);
551 552 553 554
	if (disk->part) {
		kfree(disk->part);
		disk->part = NULL;
	}
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

struct dev_name {
	struct list_head list;
	dev_t dev;
	char namebuf[64];
	char *name;
};

static LIST_HEAD(device_names);

char *partition_name(dev_t dev)
{
	struct gendisk *hd;
	static char nomem [] = "<nomem>";
	struct dev_name *dname;
	struct list_head *tmp;

	list_for_each(tmp, &device_names) {
		dname = list_entry(tmp, struct dev_name, list);
		if (dname->dev == dev)
			return dname->name;
	}

	dname = kmalloc(sizeof(*dname), GFP_KERNEL);

	if (!dname)
		return nomem;
	/*
	 * ok, add this new device name to the list
	 */
	hd = get_gendisk(to_kdev_t(dev));
	dname->name = NULL;
	if (hd)
		dname->name = disk_name(hd, MINOR(dev)-hd->first_minor, dname->namebuf);
	if (!dname->name) {
		sprintf(dname->namebuf, "[dev %s]", kdevname(to_kdev_t(dev)));
		dname->name = dname->namebuf;
	}

	dname->dev = dev;
	list_add(&dname->list, &device_names);

	return dname->name;
}