Import 2.4.0-test2pre7
[davej-history.git] / fs / partitions / msdos.c
blob29e455045406cc055cd4ca26d8e50dd7203df754
1 /*
2 * fs/partitions/msdos.c
4 * Code extracted from drivers/block/genhd.c
5 * Copyright (C) 1991-1998 Linus Torvalds
7 * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
8 * in the early extended-partition checks and added DM partitions
10 * Support for DiskManager v6.0x added by Mark Lord,
11 * with information provided by OnTrack. This now works for linux fdisk
12 * and LILO, as well as loadlin and bootln. Note that disks other than
13 * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
15 * More flexible handling of extended partitions - aeb, 950831
17 * Check partition table on IDE disks for common CHS translations
19 * Re-organised Feb 1998 Russell King
22 #include <linux/config.h>
23 #include <linux/fs.h>
24 #include <linux/genhd.h>
25 #include <linux/kernel.h>
26 #include <linux/major.h>
27 #include <linux/string.h>
28 #include <linux/blk.h>
30 #ifdef CONFIG_BLK_DEV_IDE
31 #include <linux/ide.h> /* IDE xlate */
32 #endif /* CONFIG_BLK_DEV_IDE */
34 #include <asm/system.h>
36 #include "check.h"
37 #include "msdos.h"
39 static int current_minor;
42 * Many architectures don't like unaligned accesses, which is
43 * frequently the case with the nr_sects and start_sect partition
44 * table entries.
46 #include <asm/unaligned.h>
48 #define SYS_IND(p) (get_unaligned(&p->sys_ind))
49 #define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
50 get_unaligned(&p->nr_sects); \
51 le32_to_cpu(__a); \
54 #define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
55 get_unaligned(&p->start_sect); \
56 le32_to_cpu(__a); \
59 static inline int is_extended_partition(struct partition *p)
61 return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
62 SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
63 SYS_IND(p) == LINUX_EXTENDED_PARTITION);
67 * Create devices for each logical partition in an extended partition.
68 * The logical partitions form a linked list, with each entry being
69 * a partition table with two entries. The first entry
70 * is the real data partition (with a start relative to the partition
71 * table start). The second is a pointer to the next logical partition
72 * (with a start relative to the entire extended partition).
73 * We do not create a Linux partition for the partition tables, but
74 * only for the actual data partitions.
77 static void extended_partition(struct gendisk *hd, kdev_t dev)
79 struct buffer_head *bh;
80 struct partition *p;
81 unsigned long first_sector, first_size, this_sector, this_size;
82 int mask = (1 << hd->minor_shift) - 1;
83 int sector_size = get_hardsect_size(dev) / 512;
84 int loopct = 0; /* number of links followed
85 without finding a data partition */
86 int i;
88 first_sector = hd->part[MINOR(dev)].start_sect;
89 first_size = hd->part[MINOR(dev)].nr_sects;
90 this_sector = first_sector;
92 while (1) {
93 if (++loopct > 100)
94 return;
95 if ((current_minor & mask) == 0)
96 return;
97 if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
98 return;
100 if ((*(__u16 *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC))
101 goto done;
103 p = (struct partition *) (0x1BE + bh->b_data);
105 this_size = hd->part[MINOR(dev)].nr_sects;
108 * Usually, the first entry is the real data partition,
109 * the 2nd entry is the next extended partition, or empty,
110 * and the 3rd and 4th entries are unused.
111 * However, DRDOS sometimes has the extended partition as
112 * the first entry (when the data partition is empty),
113 * and OS/2 seems to use all four entries.
117 * First process the data partition(s)
119 for (i=0; i<4; i++, p++) {
120 if (!NR_SECTS(p) || is_extended_partition(p))
121 continue;
123 /* Check the 3rd and 4th entries -
124 these sometimes contain random garbage */
125 if (i >= 2
126 && START_SECT(p) + NR_SECTS(p) > this_size
127 && (this_sector + START_SECT(p) < first_sector ||
128 this_sector + START_SECT(p) + NR_SECTS(p) >
129 first_sector + first_size))
130 continue;
132 add_gd_partition(hd, current_minor,
133 this_sector+START_SECT(p)*sector_size,
134 NR_SECTS(p)*sector_size);
135 current_minor++;
136 loopct = 0;
137 if ((current_minor & mask) == 0)
138 goto done;
141 * Next, process the (first) extended partition, if present.
142 * (So far, there seems to be no reason to make
143 * extended_partition() recursive and allow a tree
144 * of extended partitions.)
145 * It should be a link to the next logical partition.
146 * Create a minor for this just long enough to get the next
147 * partition table. The minor will be reused for the next
148 * data partition.
150 p -= 4;
151 for (i=0; i<4; i++, p++)
152 if(NR_SECTS(p) && is_extended_partition(p))
153 break;
154 if (i == 4)
155 goto done; /* nothing left to do */
157 hd->part[current_minor].nr_sects = NR_SECTS(p) * sector_size; /* JSt */
158 hd->part[current_minor].start_sect = first_sector + START_SECT(p) * sector_size;
159 this_sector = first_sector + START_SECT(p) * sector_size;
160 dev = MKDEV(hd->major, current_minor);
162 /* Use bforget(), as we have changed the disk geometry */
163 bforget(bh);
165 done:
166 bforget(bh);
169 static inline struct buffer_head *
170 get_partition_table_block(struct gendisk *hd, int minor, int blocknr) {
171 kdev_t dev = MKDEV(hd->major, minor);
172 return bread(dev, blocknr, get_ptable_blocksize(dev));
175 #ifdef CONFIG_SOLARIS_X86_PARTITION
177 /* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
178 indicates linux swap. Be careful before believing this is Solaris. */
180 static void
181 solaris_x86_partition(struct gendisk *hd, int minor) {
182 long offset = hd->part[minor].start_sect;
184 struct buffer_head *bh;
185 struct solaris_x86_vtoc *v;
186 struct solaris_x86_slice *s;
187 int i;
188 char buf[40];
190 if(!(bh = get_partition_table_block(hd, minor, 0)))
191 return;
192 v = (struct solaris_x86_vtoc *)(bh->b_data + 512);
193 if(v->v_sanity != SOLARIS_X86_VTOC_SANE) {
194 brelse(bh);
195 return;
197 printk(" %s: <solaris:", disk_name(hd, minor, buf));
198 if(v->v_version != 1) {
199 printk(" cannot handle version %ld vtoc>\n", v->v_version);
200 brelse(bh);
201 return;
203 for(i=0; i<SOLARIS_X86_NUMSLICE; i++) {
204 s = &v->v_slice[i];
206 if (s->s_size == 0)
207 continue;
208 printk(" [s%d]", i);
209 /* solaris partitions are relative to current MS-DOS
210 * one but add_gd_partition starts relative to sector
211 * zero of the disk. Therefore, must add the offset
212 * of the current partition */
213 add_gd_partition(hd, current_minor, s->s_start+offset, s->s_size);
214 current_minor++;
216 brelse(bh);
217 printk(" >\n");
219 #endif
221 #ifdef CONFIG_BSD_DISKLABEL
222 static void
223 check_and_add_bsd_partition(struct gendisk *hd,
224 struct bsd_partition *bsd_p, int minor) {
225 struct hd_struct *lin_p;
226 /* check relative position of partitions. */
227 for (lin_p = hd->part + 1 + minor;
228 lin_p - hd->part - minor < current_minor; lin_p++) {
229 /* no relationship -> try again */
230 if (lin_p->start_sect + lin_p->nr_sects <= bsd_p->p_offset ||
231 lin_p->start_sect >= bsd_p->p_offset + bsd_p->p_size)
232 continue;
233 /* equal -> no need to add */
234 if (lin_p->start_sect == bsd_p->p_offset &&
235 lin_p->nr_sects == bsd_p->p_size)
236 return;
237 /* bsd living within dos partition */
238 if (lin_p->start_sect <= bsd_p->p_offset && lin_p->start_sect
239 + lin_p->nr_sects >= bsd_p->p_offset + bsd_p->p_size) {
240 #ifdef DEBUG_BSD_DISKLABEL
241 printk("w: %d %ld+%ld,%d+%d",
242 lin_p - hd->part,
243 lin_p->start_sect, lin_p->nr_sects,
244 bsd_p->p_offset, bsd_p->p_size);
245 #endif
246 break;
248 /* ouch: bsd and linux overlap. Don't even try for that partition */
249 #ifdef DEBUG_BSD_DISKLABEL
250 printk("???: %d %ld+%ld,%d+%d",
251 lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects,
252 bsd_p->p_offset, bsd_p->p_size);
253 #endif
254 printk("???");
255 return;
256 } /* if the bsd partition is not currently known to linux, we end
257 * up here
259 add_gd_partition(hd, current_minor, bsd_p->p_offset, bsd_p->p_size);
260 current_minor++;
264 * Create devices for BSD partitions listed in a disklabel, under a
265 * dos-like partition. See extended_partition() for more information.
267 static void bsd_disklabel_partition(struct gendisk *hd, int minor, int type) {
268 struct buffer_head *bh;
269 struct bsd_disklabel *l;
270 struct bsd_partition *p;
271 int max_partitions;
272 int mask = (1 << hd->minor_shift) - 1;
273 char buf[40];
275 if (!(bh = get_partition_table_block(hd, minor, 0)))
276 return;
277 l = (struct bsd_disklabel *) (bh->b_data+512);
278 if (l->d_magic != BSD_DISKMAGIC) {
279 brelse(bh);
280 return;
282 printk(" %s:", disk_name(hd, minor, buf));
283 printk((type == OPENBSD_PARTITION) ? " <openbsd:" :
284 (type == NETBSD_PARTITION) ? " <netbsd:" : " <bsd:");
286 max_partitions = ((type == OPENBSD_PARTITION) ? OPENBSD_MAXPARTITIONS
287 : BSD_MAXPARTITIONS);
288 if (l->d_npartitions < max_partitions)
289 max_partitions = l->d_npartitions;
290 for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
291 if ((current_minor & mask) >= (4 + hd->max_p))
292 break;
294 if (p->p_fstype != BSD_FS_UNUSED)
295 check_and_add_bsd_partition(hd, p, minor);
298 /* Use bforget(), as we have changed the disk setup */
299 bforget(bh);
301 printk(" >\n");
303 #endif
305 #ifdef CONFIG_UNIXWARE_DISKLABEL
307 * Create devices for Unixware partitions listed in a disklabel, under a
308 * dos-like partition. See extended_partition() for more information.
310 static void unixware_partition(struct gendisk *hd, int minor) {
311 struct buffer_head *bh;
312 struct unixware_disklabel *l;
313 struct unixware_slice *p;
314 int mask = (1 << hd->minor_shift) - 1;
315 char buf[40];
317 if (!(bh = get_partition_table_block(hd, minor, 14)))
318 return;
319 l = (struct unixware_disklabel *) (bh->b_data+512);
320 if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC ||
321 le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) {
322 brelse(bh);
323 return;
325 printk(" %s: <unixware:", disk_name(hd, minor, buf));
326 p = &l->vtoc.v_slice[1];
327 /* I omit the 0th slice as it is the same as whole disk. */
328 while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
329 if ((current_minor & mask) == 0)
330 break;
332 if (p->s_label != UNIXWARE_FS_UNUSED) {
333 add_gd_partition(hd, current_minor, START_SECT(p),
334 NR_SECTS(p));
335 current_minor++;
337 p++;
339 /* Use bforget, as we have changed the disk setup */
340 bforget(bh);
341 printk(" >\n");
343 #endif
345 int msdos_partition(struct gendisk *hd, kdev_t dev,
346 unsigned long first_sector, int first_part_minor) {
347 int i, minor = current_minor = first_part_minor;
348 struct buffer_head *bh;
349 struct partition *p;
350 unsigned char *data;
351 int mask = (1 << hd->minor_shift) - 1;
352 int sector_size = get_hardsect_size(dev) / 512;
353 #ifdef CONFIG_BLK_DEV_IDE
354 int tested_for_xlate = 0;
356 read_mbr:
357 #endif /* CONFIG_BLK_DEV_IDE */
358 if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
359 if (warn_no_part) printk(" unable to read partition table\n");
360 return -1;
362 data = bh->b_data;
363 #ifdef CONFIG_BLK_DEV_IDE
364 check_table:
365 #endif /* CONFIG_BLK_DEV_IDE */
366 /* Use bforget(), because we may have changed the disk geometry */
367 if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) {
368 bforget(bh);
369 return 0;
371 p = (struct partition *) (0x1be + data);
373 #ifdef CONFIG_BLK_DEV_IDE
374 if (!tested_for_xlate++) { /* Do this only once per disk */
376 * Look for various forms of IDE disk geometry translation
378 unsigned int sig = le16_to_cpu(*(unsigned short *)(data + 2));
379 int heads = 0;
381 * The i386 partition handling programs very often
382 * make partitions end on cylinder boundaries.
383 * There is no need to do so, and Linux fdisk doesnt always
384 * do this, and Windows NT on Alpha doesnt do this either,
385 * but still, this helps to guess #heads.
387 for (i = 0; i < 4; i++) {
388 struct partition *q = &p[i];
389 if (NR_SECTS(q)) {
390 if ((q->sector & 63) == 1 &&
391 (q->end_sector & 63) == 63)
392 heads = q->end_head + 1;
393 break;
396 if (SYS_IND(p) == EZD_PARTITION) {
398 * Accesses to sector 0 must go to sector 1 instead.
400 if (ide_xlate_1024(dev, -1, heads, " [EZD]")) {
401 data += 512;
402 goto check_table;
404 } else if (SYS_IND(p) == DM6_PARTITION) {
407 * Everything on the disk is offset by 63 sectors,
408 * including a "new" MBR with its own partition table.
410 if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]")) {
411 bforget(bh);
412 goto read_mbr; /* start over with new MBR */
414 } else if (sig <= 0x1ae &&
415 data[sig] == 0xAA && data[sig+1] == 0x55 &&
416 (data[sig+2] & 1)) {
417 /* DM6 signature in MBR, courtesy of OnTrack */
418 (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]");
419 } else if (SYS_IND(p) == DM6_AUX1PARTITION ||
420 SYS_IND(p) == DM6_AUX3PARTITION) {
422 * DM6 on other than the first (boot) drive
424 (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]");
425 } else {
426 (void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
429 #endif /* CONFIG_BLK_DEV_IDE */
431 /* Look for partitions in two passes:
432 First find the primary partitions, and the DOS-type extended partitions.
433 On the second pass look inside *BSD and Unixware and Solaris partitions. */
435 current_minor += 4; /* first "extra" minor (for extended partitions) */
436 for (i=1 ; i<=4 ; minor++,i++,p++) {
437 if (!NR_SECTS(p))
438 continue;
439 add_gd_partition(hd, minor, first_sector+START_SECT(p)*sector_size,
440 NR_SECTS(p)*sector_size);
441 #if CONFIG_BLK_DEV_MD && CONFIG_AUTODETECT_RAID
442 if (SYS_IND(p) == LINUX_RAID_PARTITION) {
443 md_autodetect_dev(MKDEV(hd->major,minor));
445 #endif
446 if (is_extended_partition(p)) {
447 printk(" <");
449 * If we are rereading the partition table, we need
450 * to set the size of the partition so that we will
451 * be able to bread the block containing the extended
452 * partition info.
454 hd->sizes[minor] = hd->part[minor].nr_sects
455 >> (BLOCK_SIZE_BITS - 9);
456 extended_partition(hd, MKDEV(hd->major, minor));
457 printk(" >");
458 /* prevent someone doing mkfs or mkswap on an
459 extended partition, but leave room for LILO */
460 if (hd->part[minor].nr_sects > 2)
461 hd->part[minor].nr_sects = 2;
466 * Check for old-style Disk Manager partition table
468 if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) {
469 p = (struct partition *) (0x1be + data);
470 for (i = 4 ; i < 16 ; i++, current_minor++) {
471 p--;
472 if ((current_minor & mask) == 0)
473 break;
474 if (!(START_SECT(p) && NR_SECTS(p)))
475 continue;
476 add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
479 printk("\n");
481 /* second pass - output for each on a separate line */
482 minor -= 4;
483 p = (struct partition *) (0x1be + data);
484 for (i=1 ; i<=4 ; minor++,i++,p++) {
485 if (!NR_SECTS(p))
486 continue;
487 #ifdef CONFIG_BSD_DISKLABEL
488 if (SYS_IND(p) == BSD_PARTITION ||
489 SYS_IND(p) == NETBSD_PARTITION ||
490 SYS_IND(p) == OPENBSD_PARTITION)
491 bsd_disklabel_partition(hd, minor, SYS_IND(p));
492 #endif
493 #ifdef CONFIG_UNIXWARE_DISKLABEL
494 if (SYS_IND(p) == UNIXWARE_PARTITION)
495 unixware_partition(hd, minor);
496 #endif
497 #ifdef CONFIG_SOLARIS_X86_PARTITION
498 if(SYS_IND(p) == SOLARIS_X86_PARTITION)
499 solaris_x86_partition(hd, minor);
500 #endif
503 bforget(bh);
504 return 1;