File: /usr/src/linux/fs/partitions/msdos.c
1 /*
2 * fs/partitions/msdos.c
3 *
4 * Code extracted from drivers/block/genhd.c
5 * Copyright (C) 1991-1998 Linus Torvalds
6 *
7 * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
8 * in the early extended-partition checks and added DM partitions
9 *
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).
14 *
15 * More flexible handling of extended partitions - aeb, 950831
16 *
17 * Check partition table on IDE disks for common CHS translations
18 *
19 * Re-organised Feb 1998 Russell King
20 */
21
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>
29
30 #ifdef CONFIG_BLK_DEV_IDE
31 #include <linux/ide.h> /* IDE xlate */
32 #endif /* CONFIG_BLK_DEV_IDE */
33
34 #include <asm/system.h>
35
36 #include "check.h"
37 #include "msdos.h"
38
39 #if CONFIG_BLK_DEV_MD
40 extern void md_autodetect_dev(kdev_t dev);
41 #endif
42
43 static int current_minor;
44
45 /*
46 * Many architectures don't like unaligned accesses, which is
47 * frequently the case with the nr_sects and start_sect partition
48 * table entries.
49 */
50 #include <asm/unaligned.h>
51
52 #define SYS_IND(p) (get_unaligned(&p->sys_ind))
53 #define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
54 get_unaligned(&p->nr_sects); \
55 le32_to_cpu(__a); \
56 })
57
58 #define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
59 get_unaligned(&p->start_sect); \
60 le32_to_cpu(__a); \
61 })
62
63 static inline int is_extended_partition(struct partition *p)
64 {
65 return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
66 SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
67 SYS_IND(p) == LINUX_EXTENDED_PARTITION);
68 }
69
70 /*
71 * partition_name() formats the short partition name into the supplied
72 * buffer, and returns a pointer to that buffer.
73 * Used by several partition types which makes conditional inclusion messy,
74 * use __attribute__ ((unused)) instead.
75 */
76 static char __attribute__ ((unused))
77 *partition_name (struct gendisk *hd, int minor, char *buf)
78 {
79 #ifdef CONFIG_DEVFS_FS
80 sprintf(buf, "p%d", (minor & ((1 << hd->minor_shift) - 1)));
81 return buf;
82 #else
83 return disk_name(hd, minor, buf);
84 #endif
85 }
86
87 #define MSDOS_LABEL_MAGIC1 0x55
88 #define MSDOS_LABEL_MAGIC2 0xAA
89
90 static inline int
91 msdos_magic_present(unsigned char *p) {
92 return (p[0] == MSDOS_LABEL_MAGIC1 && p[1] == MSDOS_LABEL_MAGIC2);
93 }
94
95 /*
96 * Create devices for each logical partition in an extended partition.
97 * The logical partitions form a linked list, with each entry being
98 * a partition table with two entries. The first entry
99 * is the real data partition (with a start relative to the partition
100 * table start). The second is a pointer to the next logical partition
101 * (with a start relative to the entire extended partition).
102 * We do not create a Linux partition for the partition tables, but
103 * only for the actual data partitions.
104 */
105
106 static void extended_partition(struct gendisk *hd, kdev_t dev)
107 {
108 struct buffer_head *bh;
109 struct partition *p;
110 unsigned long first_sector, first_size, this_sector, this_size;
111 int mask = (1 << hd->minor_shift) - 1;
112 int sector_size = get_hardsect_size(dev) / 512;
113 int loopct = 0; /* number of links followed
114 without finding a data partition */
115 int i;
116
117 first_sector = hd->part[MINOR(dev)].start_sect;
118 first_size = hd->part[MINOR(dev)].nr_sects;
119 this_sector = first_sector;
120
121 while (1) {
122 if (++loopct > 100)
123 return;
124 if ((current_minor & mask) == 0)
125 return;
126 if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
127 return;
128
129 if (!msdos_magic_present(bh->b_data + 510))
130 goto done;
131
132 p = (struct partition *) (bh->b_data + 0x1be);
133
134 this_size = hd->part[MINOR(dev)].nr_sects;
135
136 /*
137 * Usually, the first entry is the real data partition,
138 * the 2nd entry is the next extended partition, or empty,
139 * and the 3rd and 4th entries are unused.
140 * However, DRDOS sometimes has the extended partition as
141 * the first entry (when the data partition is empty),
142 * and OS/2 seems to use all four entries.
143 */
144
145 /*
146 * First process the data partition(s)
147 */
148 for (i=0; i<4; i++, p++) {
149 if (!NR_SECTS(p) || is_extended_partition(p))
150 continue;
151
152 /* Check the 3rd and 4th entries -
153 these sometimes contain random garbage */
154 if (i >= 2
155 && START_SECT(p) + NR_SECTS(p) > this_size
156 && (this_sector + START_SECT(p) < first_sector ||
157 this_sector + START_SECT(p) + NR_SECTS(p) >
158 first_sector + first_size))
159 continue;
160
161 add_gd_partition(hd, current_minor,
162 this_sector+START_SECT(p)*sector_size,
163 NR_SECTS(p)*sector_size);
164 #if CONFIG_BLK_DEV_MD
165 if (SYS_IND(p) == LINUX_RAID_PARTITION) {
166 md_autodetect_dev(MKDEV(hd->major,current_minor));
167 }
168 #endif
169
170 current_minor++;
171 loopct = 0;
172 if ((current_minor & mask) == 0)
173 goto done;
174 }
175 /*
176 * Next, process the (first) extended partition, if present.
177 * (So far, there seems to be no reason to make
178 * extended_partition() recursive and allow a tree
179 * of extended partitions.)
180 * It should be a link to the next logical partition.
181 * Create a minor for this just long enough to get the next
182 * partition table. The minor will be reused for the next
183 * data partition.
184 */
185 p -= 4;
186 for (i=0; i<4; i++, p++)
187 if(NR_SECTS(p) && is_extended_partition(p))
188 break;
189 if (i == 4)
190 goto done; /* nothing left to do */
191
192 hd->part[current_minor].nr_sects = NR_SECTS(p) * sector_size; /* JSt */
193 hd->part[current_minor].start_sect = first_sector + START_SECT(p) * sector_size;
194 this_sector = first_sector + START_SECT(p) * sector_size;
195 dev = MKDEV(hd->major, current_minor);
196
197 /* Use bforget(), as we have changed the disk geometry */
198 bforget(bh);
199 }
200 done:
201 bforget(bh);
202 }
203
204 static inline struct buffer_head *
205 get_partition_table_block(struct gendisk *hd, int minor, int blocknr) {
206 kdev_t dev = MKDEV(hd->major, minor);
207 return bread(dev, blocknr, get_ptable_blocksize(dev));
208 }
209
210 #ifdef CONFIG_SOLARIS_X86_PARTITION
211
212 /* james@bpgc.com: Solaris has a nasty indicator: 0x82 which also
213 indicates linux swap. Be careful before believing this is Solaris. */
214
215 static void
216 solaris_x86_partition(struct gendisk *hd, int minor) {
217 long offset = hd->part[minor].start_sect;
218
219 struct buffer_head *bh;
220 struct solaris_x86_vtoc *v;
221 struct solaris_x86_slice *s;
222 int mask = (1 << hd->minor_shift) - 1;
223 int i;
224 char buf[40];
225
226 if(!(bh = get_partition_table_block(hd, minor, 0)))
227 return;
228 v = (struct solaris_x86_vtoc *)(bh->b_data + 512);
229 if(le32_to_cpu(v->v_sanity) != SOLARIS_X86_VTOC_SANE) {
230 brelse(bh);
231 return;
232 }
233 printk(" %s: <solaris:", partition_name(hd, minor, buf));
234 if(le32_to_cpu(v->v_version) != 1) {
235 printk(" cannot handle version %d vtoc>\n",
236 le32_to_cpu(v->v_version));
237 brelse(bh);
238 return;
239 }
240 for(i=0; i<SOLARIS_X86_NUMSLICE; i++) {
241 if ((current_minor & mask) == 0)
242 break;
243 s = &v->v_slice[i];
244
245 if (s->s_size == 0)
246 continue;
247 printk(" [s%d]", i);
248 /* solaris partitions are relative to current MS-DOS
249 * one but add_gd_partition starts relative to sector
250 * zero of the disk. Therefore, must add the offset
251 * of the current partition */
252 add_gd_partition(hd, current_minor, le32_to_cpu(s->s_start)+offset,
253 le32_to_cpu(s->s_size));
254 current_minor++;
255 }
256 brelse(bh);
257 printk(" >\n");
258 }
259 #endif
260
261 #ifdef CONFIG_BSD_DISKLABEL
262 static void
263 check_and_add_bsd_partition(struct gendisk *hd,
264 struct bsd_partition *bsd_p, int minor) {
265 struct hd_struct *lin_p;
266 /* check relative position of partitions. */
267 for (lin_p = hd->part + 1 + minor;
268 lin_p - hd->part - minor < current_minor; lin_p++) {
269 /* no relationship -> try again */
270 if (lin_p->start_sect + lin_p->nr_sects <= le32_to_cpu(bsd_p->p_offset) ||
271 lin_p->start_sect >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size))
272 continue;
273 /* equal -> no need to add */
274 if (lin_p->start_sect == le32_to_cpu(bsd_p->p_offset) &&
275 lin_p->nr_sects == le32_to_cpu(bsd_p->p_size))
276 return;
277 /* bsd living within dos partition */
278 if (lin_p->start_sect <= le32_to_cpu(bsd_p->p_offset) && lin_p->start_sect
279 + lin_p->nr_sects >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size)) {
280 #ifdef DEBUG_BSD_DISKLABEL
281 printk("w: %d %ld+%ld,%d+%d",
282 lin_p - hd->part,
283 lin_p->start_sect, lin_p->nr_sects,
284 le32_to_cpu(bsd_p->p_offset),
285 le32_to_cpu(bsd_p->p_size));
286 #endif
287 break;
288 }
289 /* ouch: bsd and linux overlap. Don't even try for that partition */
290 #ifdef DEBUG_BSD_DISKLABEL
291 printk("???: %d %ld+%ld,%d+%d",
292 lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects,
293 le32_to_cpu(bsd_p->p_offset), le32_to_cpu(bsd_p->p_size));
294 #endif
295 printk("???");
296 return;
297 } /* if the bsd partition is not currently known to linux, we end
298 * up here
299 */
300 add_gd_partition(hd, current_minor, le32_to_cpu(bsd_p->p_offset),
301 le32_to_cpu(bsd_p->p_size));
302 current_minor++;
303 }
304
305 /*
306 * Create devices for BSD partitions listed in a disklabel, under a
307 * dos-like partition. See extended_partition() for more information.
308 */
309 static void bsd_disklabel_partition(struct gendisk *hd, int minor, int type) {
310 struct buffer_head *bh;
311 struct bsd_disklabel *l;
312 struct bsd_partition *p;
313 int max_partitions;
314 int mask = (1 << hd->minor_shift) - 1;
315 char buf[40];
316
317 if (!(bh = get_partition_table_block(hd, minor, 0)))
318 return;
319 l = (struct bsd_disklabel *) (bh->b_data+512);
320 if (le32_to_cpu(l->d_magic) != BSD_DISKMAGIC) {
321 brelse(bh);
322 return;
323 }
324 printk(" %s:", partition_name(hd, minor, buf));
325 printk((type == OPENBSD_PARTITION) ? " <openbsd:" :
326 (type == NETBSD_PARTITION) ? " <netbsd:" : " <bsd:");
327
328 max_partitions = ((type == OPENBSD_PARTITION) ? OPENBSD_MAXPARTITIONS
329 : BSD_MAXPARTITIONS);
330 if (le16_to_cpu(l->d_npartitions) < max_partitions)
331 max_partitions = le16_to_cpu(l->d_npartitions);
332 for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
333 if ((current_minor & mask) == 0)
334 break;
335
336 if (p->p_fstype != BSD_FS_UNUSED)
337 check_and_add_bsd_partition(hd, p, minor);
338 }
339
340 /* Use bforget(), as we have changed the disk setup */
341 bforget(bh);
342
343 printk(" >\n");
344 }
345 #endif
346
347 #ifdef CONFIG_UNIXWARE_DISKLABEL
348 /*
349 * Create devices for Unixware partitions listed in a disklabel, under a
350 * dos-like partition. See extended_partition() for more information.
351 */
352 static void unixware_partition(struct gendisk *hd, int minor) {
353 struct buffer_head *bh;
354 struct unixware_disklabel *l;
355 struct unixware_slice *p;
356 int mask = (1 << hd->minor_shift) - 1;
357 char buf[40];
358
359 if (!(bh = get_partition_table_block(hd, minor, 14)))
360 return;
361 l = (struct unixware_disklabel *) (bh->b_data+512);
362 if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC ||
363 le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) {
364 brelse(bh);
365 return;
366 }
367 printk(" %s: <unixware:", partition_name(hd, minor, buf));
368 p = &l->vtoc.v_slice[1];
369 /* I omit the 0th slice as it is the same as whole disk. */
370 while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
371 if ((current_minor & mask) == 0)
372 break;
373
374 if (p->s_label != UNIXWARE_FS_UNUSED) {
375 add_gd_partition(hd, current_minor, START_SECT(p),
376 NR_SECTS(p));
377 current_minor++;
378 }
379 p++;
380 }
381 /* Use bforget, as we have changed the disk setup */
382 bforget(bh);
383 printk(" >\n");
384 }
385 #endif
386
387 #ifdef CONFIG_MINIX_SUBPARTITION
388 /*
389 * Minix 2.0.0/2.0.2 subpartition support.
390 * Anand Krishnamurthy <anandk@wiproge.med.ge.com>
391 * Rajeev V. Pillai <rajeevvp@yahoo.com>
392 */
393 static void minix_partition(struct gendisk *hd, int minor)
394 {
395 struct buffer_head *bh;
396 struct partition *p;
397 int mask = (1 << hd->minor_shift) - 1;
398 int i;
399 char buf[40];
400
401 if (!(bh = get_partition_table_block(hd, minor, 0)))
402 return;
403 bh->b_state = 0;
404
405 p = (struct partition *)(bh->b_data + 0x1be);
406
407 /* The first sector of a Minix partition can have either
408 * a secondary MBR describing its subpartitions, or
409 * the normal boot sector. */
410 if (msdos_magic_present (bh->b_data + 510) &&
411 SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */
412
413 printk(" %s: <minix:", partition_name(hd, minor, buf));
414 for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) {
415 if ((current_minor & mask) == 0)
416 break;
417 /* add each partition in use */
418 if (SYS_IND(p) == MINIX_PARTITION) {
419 add_gd_partition(hd, current_minor,
420 START_SECT(p), NR_SECTS(p));
421 current_minor++;
422 }
423 }
424 printk(" >\n");
425 }
426 brelse(bh);
427 }
428 #endif /* CONFIG_MINIX_SUBPARTITION */
429
430 int msdos_partition(struct gendisk *hd, kdev_t dev,
431 unsigned long first_sector, int first_part_minor) {
432 int i, minor = current_minor = first_part_minor;
433 struct buffer_head *bh;
434 struct partition *p;
435 unsigned char *data;
436 int mask = (1 << hd->minor_shift) - 1;
437 int sector_size = get_hardsect_size(dev) / 512;
438 #ifdef CONFIG_BLK_DEV_IDE
439 int tested_for_xlate = 0;
440
441 read_mbr:
442 #endif /* CONFIG_BLK_DEV_IDE */
443 if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
444 if (warn_no_part) printk(" unable to read partition table\n");
445 return -1;
446 }
447 data = bh->b_data;
448 #ifdef CONFIG_BLK_DEV_IDE
449 check_table:
450 #endif /* CONFIG_BLK_DEV_IDE */
451 /* Use bforget(), because we may have changed the disk geometry */
452 if (!msdos_magic_present(data + 510)) {
453 bforget(bh);
454 return 0;
455 }
456 p = (struct partition *) (data + 0x1be);
457
458 #ifdef CONFIG_BLK_DEV_IDE
459 if (!tested_for_xlate++) { /* Do this only once per disk */
460 /*
461 * Look for various forms of IDE disk geometry translation
462 */
463 unsigned int sig = le16_to_cpu(*(unsigned short *)(data + 2));
464 int heads = 0;
465 /*
466 * The i386 partition handling programs very often
467 * make partitions end on cylinder boundaries.
468 * There is no need to do so, and Linux fdisk doesnt always
469 * do this, and Windows NT on Alpha doesnt do this either,
470 * but still, this helps to guess #heads.
471 */
472 for (i = 0; i < 4; i++) {
473 struct partition *q = &p[i];
474 if (NR_SECTS(q)) {
475 if ((q->sector & 63) == 1 &&
476 (q->end_sector & 63) == 63)
477 heads = q->end_head + 1;
478 break;
479 }
480 }
481 if (SYS_IND(p) == EZD_PARTITION) {
482 /*
483 * Accesses to sector 0 must go to sector 1 instead.
484 */
485 if (ide_xlate_1024(dev, -1, heads, " [EZD]")) {
486 data += 512;
487 goto check_table;
488 }
489 } else if (SYS_IND(p) == DM6_PARTITION) {
490
491 /*
492 * Everything on the disk is offset by 63 sectors,
493 * including a "new" MBR with its own partition table.
494 */
495 if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]")) {
496 bforget(bh);
497 goto read_mbr; /* start over with new MBR */
498 }
499 } else if (sig <= 0x1ae &&
500 data[sig] == 0xAA && data[sig+1] == 0x55 &&
501 (data[sig+2] & 1)) {
502 /* DM6 signature in MBR, courtesy of OnTrack */
503 (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]");
504 } else if (SYS_IND(p) == DM6_AUX1PARTITION ||
505 SYS_IND(p) == DM6_AUX3PARTITION) {
506 /*
507 * DM6 on other than the first (boot) drive
508 */
509 (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]");
510 } else {
511 (void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
512 }
513 }
514 #endif /* CONFIG_BLK_DEV_IDE */
515
516 /* Look for partitions in two passes:
517 First find the primary partitions, and the DOS-type extended partitions.
518 On the second pass look inside *BSD and Unixware and Solaris partitions. */
519
520 current_minor += 4; /* first "extra" minor (for extended partitions) */
521 for (i=1 ; i<=4 ; minor++,i++,p++) {
522 if (!NR_SECTS(p))
523 continue;
524 add_gd_partition(hd, minor, first_sector+START_SECT(p)*sector_size,
525 NR_SECTS(p)*sector_size);
526 #if CONFIG_BLK_DEV_MD
527 if (SYS_IND(p) == LINUX_RAID_PARTITION) {
528 md_autodetect_dev(MKDEV(hd->major,minor));
529 }
530 #endif
531 if (is_extended_partition(p)) {
532 printk(" <");
533 /*
534 * If we are rereading the partition table, we need
535 * to set the size of the partition so that we will
536 * be able to bread the block containing the extended
537 * partition info.
538 */
539 hd->sizes[minor] = hd->part[minor].nr_sects
540 >> (BLOCK_SIZE_BITS - 9);
541 extended_partition(hd, MKDEV(hd->major, minor));
542 printk(" >");
543 /* prevent someone doing mkfs or mkswap on an
544 extended partition, but leave room for LILO */
545 if (hd->part[minor].nr_sects > 2)
546 hd->part[minor].nr_sects = 2;
547 }
548 }
549
550 /*
551 * Check for old-style Disk Manager partition table
552 */
553 if (msdos_magic_present(data + 0xfc)) {
554 p = (struct partition *) (0x1be + data);
555 for (i = 4 ; i < 16 ; i++, current_minor++) {
556 p--;
557 if ((current_minor & mask) == 0)
558 break;
559 if (!(START_SECT(p) && NR_SECTS(p)))
560 continue;
561 add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
562 }
563 }
564 printk("\n");
565
566 /* second pass - output for each on a separate line */
567 minor -= 4;
568 p = (struct partition *) (0x1be + data);
569 for (i=1 ; i<=4 ; minor++,i++,p++) {
570 if (!NR_SECTS(p))
571 continue;
572 #ifdef CONFIG_BSD_DISKLABEL
573 if (SYS_IND(p) == BSD_PARTITION ||
574 SYS_IND(p) == NETBSD_PARTITION ||
575 SYS_IND(p) == OPENBSD_PARTITION)
576 bsd_disklabel_partition(hd, minor, SYS_IND(p));
577 #endif
578 #ifdef CONFIG_MINIX_SUBPARTITION
579 if (SYS_IND(p) == MINIX_PARTITION) {
580 minix_partition(hd, minor);
581 }
582 #endif
583 #ifdef CONFIG_UNIXWARE_DISKLABEL
584 if (SYS_IND(p) == UNIXWARE_PARTITION)
585 unixware_partition(hd, minor);
586 #endif
587 #ifdef CONFIG_SOLARIS_X86_PARTITION
588 if(SYS_IND(p) == SOLARIS_X86_PARTITION)
589 solaris_x86_partition(hd, minor);
590 #endif
591 }
592
593 bforget(bh);
594 return 1;
595 }
596