check.c 11.1 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>
17
#include <linux/module.h>
Linus Torvalds's avatar
Linus Torvalds committed
18 19
#include <linux/fs.h>
#include <linux/blk.h>
Mike Sullivan's avatar
Mike Sullivan committed
20
#include <linux/kmod.h>
Alexander Viro's avatar
Alexander Viro committed
21
#include <linux/ctype.h>
Linus Torvalds's avatar
Linus Torvalds committed
22 23 24 25 26 27

#include "check.h"

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

39
#if CONFIG_BLK_DEV_MD
40
extern void md_autodetect_dev(dev_t dev);
41 42
#endif

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

45
static int (*check_part[])(struct parsed_partitions *, struct block_device *) = {
Linus Torvalds's avatar
Linus Torvalds committed
46 47 48
#ifdef CONFIG_ACORN_PARTITION
	acorn_partition,
#endif
49 50 51
#ifdef CONFIG_EFI_PARTITION
	efi_partition,		/* this must come before msdos */
#endif
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54
#ifdef CONFIG_LDM_PARTITION
	ldm_partition,		/* this must come before msdos */
#endif
55 56 57
#ifdef CONFIG_NEC98_PARTITION
	nec98_partition,	/* must be come before `msdos_partition' */
#endif
Linus Torvalds's avatar
Linus Torvalds committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
#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
87
 
Linus Torvalds's avatar
Linus Torvalds committed
88
/*
Linus Torvalds's avatar
Linus Torvalds committed
89
 * disk_name() is used by partition check code and the md driver.
Linus Torvalds's avatar
Linus Torvalds committed
90 91 92 93
 * 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
94

95
char *disk_name(struct gendisk *hd, int part, char *buf)
Linus Torvalds's avatar
Linus Torvalds committed
96
{
97 98 99 100 101 102 103
	int pos;
	if (!part) {
		if (hd->disk_de) {
			pos = devfs_generate_path(hd->disk_de, buf, 64);
			if (pos >= 0)
				return buf + pos;
		}
104
		sprintf(buf, "%s", hd->disk_name);
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;
		}
111 112
		if (isdigit(hd->disk_name[strlen(hd->disk_name)-1]))
			sprintf(buf, "%sp%d", hd->disk_name, part);
113
		else
114
			sprintf(buf, "%s%d", hd->disk_name, part);
115
	}
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118
	return buf;
}

119 120
static struct parsed_partitions *
check_partition(struct gendisk *hd, struct block_device *bdev)
Linus Torvalds's avatar
Linus Torvalds committed
121
{
122
	struct parsed_partitions *state;
Linus Torvalds's avatar
Linus Torvalds committed
123 124
	devfs_handle_t de = NULL;
	char buf[64];
125
	int i, res;
Linus Torvalds's avatar
Linus Torvalds committed
126

127 128
	state = kmalloc(sizeof(struct parsed_partitions), GFP_KERNEL);
	if (!state)
129
		return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
130

131 132
	if (hd->flags & GENHD_FL_DEVFS)
		de = hd->de;
Linus Torvalds's avatar
Linus Torvalds committed
133
	i = devfs_generate_path (de, buf, sizeof buf);
134
	if (i >= 0) {
Linus Torvalds's avatar
Linus Torvalds committed
135
		printk(KERN_INFO " /dev/%s:", buf + i);
136 137
		sprintf(state->name, "p");
	} else {
138
		disk_name(hd, 0, state->name);
139
		printk(KERN_INFO " %s:", state->name);
140
		if (isdigit(state->name[strlen(state->name)-1]))
141 142
			sprintf(state->name, "p");
	}
143
	state->limit = hd->minors;
144 145
	i = res = 0;
	while (!res && check_part[i]) {
146
		memset(&state->parts, 0, sizeof(state->parts));
147
		res = check_part[i++](state, bdev);
Linus Torvalds's avatar
Linus Torvalds committed
148
	}
149 150 151 152 153 154 155 156
	if (res > 0)
		return state;
	if (!res)
		printk(" unknown partition table\n");
	else if (warn_no_part)
		printk(" unable to read partition table\n");
	kfree(state);
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
157 158
}

159
static void devfs_register_partition(struct gendisk *dev, int part)
Linus Torvalds's avatar
Linus Torvalds committed
160
{
161
#ifdef CONFIG_DEVFS_FS
Linus Torvalds's avatar
Linus Torvalds committed
162
	devfs_handle_t dir;
163
	struct hd_struct *p = dev->part;
Linus Torvalds's avatar
Linus Torvalds committed
164 165
	char devname[16];

166
	if (p[part-1].de)
167
		return;
168
	dir = dev->de;
169 170
	if (!dir)
		return;
171
	sprintf(devname, "part%d", part);
172
	p[part-1].de = devfs_register (dir, devname, 0,
173
				    dev->major, dev->first_minor + part,
174 175
				    S_IFBLK | S_IRUSR | S_IWUSR,
				    dev->fops, NULL);
176
#endif
Linus Torvalds's avatar
Linus Torvalds committed
177 178
}

179 180 181 182 183 184
/*
 * sysfs bindings for partitions
 */

struct part_attribute {
	struct attribute attr;
185
	ssize_t (*show)(struct hd_struct *,char *);
186 187
};

188 189
static ssize_t 
part_attr_show(struct kobject * kobj, struct attribute * attr, char * page)
190 191 192 193 194
{
	struct hd_struct * p = container_of(kobj,struct hd_struct,kobj);
	struct part_attribute * part_attr = container_of(attr,struct part_attribute,attr);
	ssize_t ret = 0;
	if (part_attr->show)
195
		ret = part_attr->show(p,page);
196 197 198 199
	return ret;
}

static struct sysfs_ops part_sysfs_ops = {
200
	.show	=	part_attr_show,
201 202
};

203
static ssize_t part_dev_read(struct hd_struct * p, char *page)
204
{
205
	struct gendisk *disk = container_of(p->kobj.parent,struct gendisk,kobj);
206 207
	int part = p - disk->part + 1;
	dev_t base = MKDEV(disk->major, disk->first_minor); 
208
	return sprintf(page, "%04x\n", (unsigned)(base + part));
209
}
210
static ssize_t part_start_read(struct hd_struct * p, char *page)
211
{
212
	return sprintf(page, "%llu\n",(unsigned long long)p->start_sect);
213
}
214
static ssize_t part_size_read(struct hd_struct * p, char *page)
215
{
216
	return sprintf(page, "%llu\n",(unsigned long long)p->nr_sects);
217
}
218
static ssize_t part_stat_read(struct hd_struct * p, char *page)
219
{
220 221 222
	return sprintf(page, "%8u %8llu %8u %8llu\n",
		       p->reads, (unsigned long long)p->read_sectors,
		       p->writes, (unsigned long long)p->write_sectors);
223
}
224
static struct part_attribute part_attr_dev = {
225 226 227
	.attr = {.name = "dev", .mode = S_IRUGO },
	.show	= part_dev_read
};
228
static struct part_attribute part_attr_start = {
229 230 231
	.attr = {.name = "start", .mode = S_IRUGO },
	.show	= part_start_read
};
232
static struct part_attribute part_attr_size = {
233 234 235
	.attr = {.name = "size", .mode = S_IRUGO },
	.show	= part_size_read
};
236
static struct part_attribute part_attr_stat = {
237 238 239
	.attr = {.name = "stat", .mode = S_IRUGO },
	.show	= part_stat_read
};
240

241 242 243 244 245 246 247 248 249 250
static struct attribute * default_attrs[] = {
	&part_attr_dev.attr,
	&part_attr_start.attr,
	&part_attr_size.attr,
	&part_attr_stat.attr,
	NULL,
};

extern struct subsystem block_subsys;

251
static struct kobj_type ktype_part = {
252 253 254 255
	.default_attrs	= default_attrs,
	.sysfs_ops	= &part_sysfs_ops,
};

256 257 258 259 260 261 262
void delete_partition(struct gendisk *disk, int part)
{
	struct hd_struct *p = disk->part + part - 1;
	if (!p->nr_sects)
		return;
	p->start_sect = 0;
	p->nr_sects = 0;
263
	p->reads = p->writes = p->read_sectors = p->write_sectors = 0;
264
	devfs_unregister(p->de);
265
	kobject_unregister(&p->kobj);
266 267 268 269 270 271 272 273 274
}

void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len)
{
	struct hd_struct *p = disk->part + part - 1;

	p->start_sect = start;
	p->nr_sects = len;
	devfs_register_partition(disk, part);
275
	snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part);
276
	p->kobj.parent = &disk->kobj;
277
	p->kobj.ktype = &ktype_part;
278
	kobject_register(&p->kobj);
279 280
}

281
static void disk_sysfs_symlinks(struct gendisk *disk)
282
{
283 284 285 286 287
	struct device *target = get_device(disk->driverfs_dev);
	if (target) {
		sysfs_create_link(&disk->kobj,&target->kobj,"device");
		sysfs_create_link(&target->kobj,&disk->kobj,"block");
	}
288 289
}

290 291
/* Not exported, helper to add_disk(). */
void register_disk(struct gendisk *disk)
Linus Torvalds's avatar
Linus Torvalds committed
292
{
293
	struct parsed_partitions *state;
294
	struct block_device *bdev;
295
	char *s;
296
	int j;
297
	int err;
298

299
	strncpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN);
300
	/* ewww... some of these buggers have / in name... */
301
	s = strchr(disk->kobj.name, '/');
302 303
	if (s)
		*s = '!';
304 305
	if ((err = kobject_add(&disk->kobj)))
		return;
306
	disk_sysfs_symlinks(disk);
307

308 309 310
	if (disk->flags & GENHD_FL_CD)
		devfs_create_cdrom(disk);

Andries E. Brouwer's avatar
Andries E. Brouwer committed
311
	/* No minors to use for partitions */
312
	if (disk->minors == 1)
Linus Torvalds's avatar
Linus Torvalds committed
313 314
		return;

Andries E. Brouwer's avatar
Andries E. Brouwer committed
315
	/* No such device (e.g., media were just removed) */
316
	if (!get_capacity(disk))
Andries E. Brouwer's avatar
Andries E. Brouwer committed
317 318
		return;

319
	bdev = bdget(MKDEV(disk->major, disk->first_minor));
320 321
	if (blkdev_get(bdev, FMODE_READ, 0, BDEV_RAW) < 0)
		return;
322
	state = check_partition(disk, bdev);
323
	devfs_create_partitions(disk);
324 325 326 327 328 329 330 331 332 333 334 335
	if (state) {
		for (j = 1; j < state->limit; j++) {
			sector_t size = state->parts[j].size;
			sector_t from = state->parts[j].from;
			if (!size)
				continue;
			add_partition(disk, j, from, size);
#if CONFIG_BLK_DEV_MD
			if (!state->parts[j].flags)
				continue;
			md_autodetect_dev(bdev->bd_dev+j);
#endif
336
		}
337
		kfree(state);
338
	}
339
	blkdev_put(bdev, BDEV_RAW);
340 341 342 343 344
}

int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
	kdev_t dev = to_kdev_t(bdev->bd_dev);
345
	struct parsed_partitions *state;
346
	int p, res;
347

348 349 350 351 352 353
	if (bdev->bd_part_count)
		return -EBUSY;
	res = invalidate_device(dev, 1);
	if (res)
		return res;
	bdev->bd_invalidated = 0;
354 355
	for (p = 1; p < disk->minors; p++)
		delete_partition(disk, p);
356 357
	if (disk->fops->revalidate_disk)
		disk->fops->revalidate_disk(disk);
358 359 360 361 362 363 364 365 366
	if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
		return res;
	for (p = 1; p < state->limit; p++) {
		sector_t size = state->parts[p].size;
		sector_t from = state->parts[p].from;
		if (!size)
			continue;
		add_partition(disk, p, from, size);
#if CONFIG_BLK_DEV_MD
367 368
		if (state->parts[p].flags)
			md_autodetect_dev(bdev->bd_dev+p);
369 370 371
#endif
	}
	kfree(state);
372 373 374
	return res;
}

375
unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p)
Linus Torvalds's avatar
Linus Torvalds committed
376 377 378 379
{
	struct address_space *mapping = bdev->bd_inode->i_mapping;
	struct page *page;

380
	page = read_cache_page(mapping, (pgoff_t)(n >> (PAGE_CACHE_SHIFT-9)),
Linus Torvalds's avatar
Linus Torvalds committed
381 382
			(filler_t *)mapping->a_ops->readpage, NULL);
	if (!IS_ERR(page)) {
383
		wait_on_page_locked(page);
Andrew Morton's avatar
Andrew Morton committed
384
		if (!PageUptodate(page))
Linus Torvalds's avatar
Linus Torvalds committed
385 386 387 388
			goto fail;
		if (PageError(page))
			goto fail;
		p->v = page;
389
		return (unsigned char *)page_address(page) +  ((n & ((1 << (PAGE_CACHE_SHIFT - 9)) - 1)) << 9);
Linus Torvalds's avatar
Linus Torvalds committed
390 391 392 393 394 395
fail:
		page_cache_release(page);
	}
	p->v = NULL;
	return NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed
396

397
void del_gendisk(struct gendisk *disk)
Linus Torvalds's avatar
Linus Torvalds committed
398
{
399
	int max_p = disk->minors;
400
	kdev_t devp;
401 402
	int p;

Linus Torvalds's avatar
Linus Torvalds committed
403
	/* invalidate stuff */
404 405
	for (p = max_p - 1; p > 0; p--) {
		devp = mk_kdev(disk->major,disk->first_minor + p);
406 407
		invalidate_device(devp, 1);
		delete_partition(disk, p);
Linus Torvalds's avatar
Linus Torvalds committed
408
	}
409
	devp = mk_kdev(disk->major,disk->first_minor);
410
	invalidate_device(devp, 1);
411
	disk->capacity = 0;
412
	disk->flags &= ~GENHD_FL_UP;
413
	unlink_gendisk(disk);
414
	disk_stat_set_all(disk, 0);
415
	disk->stamp = disk->stamp_idle = 0;
416
	devfs_remove_partitions(disk);
417
	if (disk->driverfs_dev) {
418
		sysfs_remove_link(&disk->kobj, "device");
419
		sysfs_remove_link(&disk->driverfs_dev->kobj, "block");
420 421
		put_device(disk->driverfs_dev);
	}
422
	kobject_del(&disk->kobj);
423
}
424 425 426 427 428 429 430 431 432 433 434 435 436 437

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>";
438
	char b[BDEVNAME_SIZE];
439 440
	struct dev_name *dname;
	struct list_head *tmp;
441
	int part;
442 443 444 445 446 447 448 449 450 451 452 453 454 455

	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
	 */
456
	hd = get_gendisk(dev, &part);
457
	dname->name = NULL;
458
	if (hd) {
459
		dname->name = disk_name(hd, part, dname->namebuf);
460
		module_put(hd->fops->owner);
461 462
		put_disk(hd);
	}
463
	if (!dname->name) {
464
		sprintf(dname->namebuf, "[dev %s]", __bdevname(dev, b));
465 466 467 468 469 470 471 472
		dname->name = dname->namebuf;
	}

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

	return dname->name;
}
473