* added 0.99 linux version
[mascara-docs.git] / i386 / linux / linux-2.3.21 / fs / partitions / msdos.c
blobc05d6cf309f7856f6405febfbf401dccf6a38cc9
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>
29 #include <linux/ide.h> /* IDE xlate */
31 #include <asm/system.h>
33 #include "check.h"
34 #include "msdos.h"
36 static int current_minor;
39 * Many architectures don't like unaligned accesses, which is
40 * frequently the case with the nr_sects and start_sect partition
41 * table entries.
43 #include <asm/unaligned.h>
45 #define SYS_IND(p) (get_unaligned(&p->sys_ind))
46 #define NR_SECTS(p) ({ __typeof__(p->nr_sects) __a = \
47 get_unaligned(&p->nr_sects); \
48 le32_to_cpu(__a); \
51 #define START_SECT(p) ({ __typeof__(p->start_sect) __a = \
52 get_unaligned(&p->start_sect); \
53 le32_to_cpu(__a); \
56 static inline int is_extended_partition(struct partition *p)
58 return (SYS_IND(p) == DOS_EXTENDED_PARTITION ||
59 SYS_IND(p) == WIN98_EXTENDED_PARTITION ||
60 SYS_IND(p) == LINUX_EXTENDED_PARTITION);
64 * Create devices for each logical partition in an extended partition.
65 * The logical partitions form a linked list, with each entry being
66 * a partition table with two entries. The first entry
67 * is the real data partition (with a start relative to the partition
68 * table start). The second is a pointer to the next logical partition
69 * (with a start relative to the entire extended partition).
70 * We do not create a Linux partition for the partition tables, but
71 * only for the actual data partitions.
74 static void extended_partition(struct gendisk *hd, kdev_t dev)
76 struct buffer_head *bh;
77 struct partition *p;
78 unsigned long first_sector, first_size, this_sector, this_size;
79 int mask = (1 << hd->minor_shift) - 1;
80 int sector_size = get_hardsect_size(dev) / 512;
81 int loopct = 0; /* number of links followed
82 without finding a data partition */
83 int i;
85 first_sector = hd->part[MINOR(dev)].start_sect;
86 first_size = hd->part[MINOR(dev)].nr_sects;
87 this_sector = first_sector;
89 while (1) {
90 if (++loopct > 100)
91 return;
92 if ((current_minor & mask) == 0)
93 return;
94 if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
95 return;
97 if ((*(__u16 *) (bh->b_data+510)) != cpu_to_le16(MSDOS_LABEL_MAGIC))
98 goto done;
100 p = (struct partition *) (0x1BE + bh->b_data);
102 this_size = hd->part[MINOR(dev)].nr_sects;
105 * Usually, the first entry is the real data partition,
106 * the 2nd entry is the next extended partition, or empty,
107 * and the 3rd and 4th entries are unused.
108 * However, DRDOS sometimes has the extended partition as
109 * the first entry (when the data partition is empty),
110 * and OS/2 seems to use all four entries.
114 * First process the data partition(s)
116 for (i=0; i<4; i++, p++) {
117 if (!NR_SECTS(p) || is_extended_partition(p))
118 continue;
120 /* Check the 3rd and 4th entries -
121 these sometimes contain random garbage */
122 if (i >= 2
123 && START_SECT(p) + NR_SECTS(p) > this_size
124 && (this_sector + START_SECT(p) < first_sector ||
125 this_sector + START_SECT(p) + NR_SECTS(p) >
126 first_sector + first_size))
127 continue;
129 add_gd_partition(hd, current_minor,
130 this_sector+START_SECT(p)*sector_size,
131 NR_SECTS(p)*sector_size);
132 current_minor++;
133 loopct = 0;
134 if ((current_minor & mask) == 0)
135 goto done;
138 * Next, process the (first) extended partition, if present.
139 * (So far, there seems to be no reason to make
140 * extended_partition() recursive and allow a tree
141 * of extended partitions.)
142 * It should be a link to the next logical partition.
143 * Create a minor for this just long enough to get the next
144 * partition table. The minor will be reused for the next
145 * data partition.
147 p -= 4;
148 for (i=0; i<4; i++, p++)
149 if(NR_SECTS(p) && is_extended_partition(p))
150 break;
151 if (i == 4)
152 goto done; /* nothing left to do */
154 hd->part[current_minor].nr_sects = NR_SECTS(p) * sector_size; /* JSt */
155 hd->part[current_minor].start_sect = first_sector + START_SECT(p) * sector_size;
156 this_sector = first_sector + START_SECT(p) * sector_size;
157 dev = MKDEV(hd->major, current_minor);
159 /* Use bforget(), as we have changed the disk geometry */
160 bforget(bh);
162 done:
163 bforget(bh);
166 #ifdef CONFIG_SOLARIS_X86_PARTITION
167 static void
168 solaris_x86_partition(struct gendisk *hd, kdev_t dev, long offset) {
170 struct buffer_head *bh;
171 struct solaris_x86_vtoc *v;
172 struct solaris_x86_slice *s;
173 int i;
175 if(!(bh = bread(dev, 0, get_ptable_blocksize(dev))))
176 return;
177 v = (struct solaris_x86_vtoc *)(bh->b_data + 512);
178 if(v->v_sanity != SOLARIS_X86_VTOC_SANE) {
179 brelse(bh);
180 return;
182 printk(" <solaris:");
183 if(v->v_version != 1) {
184 printk(" cannot handle version %ld vtoc>", v->v_version);
185 brelse(bh);
186 return;
188 for(i=0; i<SOLARIS_X86_NUMSLICE; i++) {
189 s = &v->v_slice[i];
191 if (s->s_size == 0)
192 continue;
193 printk(" [s%d]", i);
194 /* solaris partitions are relative to current MS-DOS
195 * one but add_gd_partition starts relative to sector
196 * zero of the disk. Therefore, must add the offset
197 * of the current partition */
198 add_gd_partition(hd, current_minor, s->s_start+offset, s->s_size);
199 current_minor++;
201 brelse(bh);
202 printk(" >");
204 #endif
206 #ifdef CONFIG_BSD_DISKLABEL
207 static void check_and_add_bsd_partition(struct gendisk *hd,
208 struct bsd_partition *bsd_p, kdev_t dev)
210 struct hd_struct *lin_p;
211 /* check relative position of partitions. */
212 for (lin_p = hd->part + 1 + MINOR(dev);
213 lin_p - hd->part - MINOR(dev) < current_minor; lin_p++) {
214 /* no relationship -> try again */
215 if (lin_p->start_sect + lin_p->nr_sects <= bsd_p->p_offset
216 || lin_p->start_sect >= bsd_p->p_offset + bsd_p->p_size)
217 continue;
218 /* equal -> no need to add */
219 if (lin_p->start_sect == bsd_p->p_offset &&
220 lin_p->nr_sects == bsd_p->p_size)
221 return;
222 /* bsd living within dos partition */
223 if (lin_p->start_sect <= bsd_p->p_offset && lin_p->start_sect
224 + lin_p->nr_sects >= bsd_p->p_offset + bsd_p->p_size) {
225 #ifdef DEBUG_BSD_DISKLABEL
226 printk("w: %d %ld+%ld,%d+%d",
227 lin_p - hd->part,
228 lin_p->start_sect, lin_p->nr_sects,
229 bsd_p->p_offset, bsd_p->p_size);
230 #endif
231 break;
233 /* ouch: bsd and linux overlap. Don't even try for that partition */
234 #ifdef DEBUG_BSD_DISKLABEL
235 printk("???: %d %ld+%ld,%d+%d",
236 lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects,
237 bsd_p->p_offset, bsd_p->p_size);
238 #endif
239 printk("???");
240 return;
241 } /* if the bsd partition is not currently known to linux, we end
242 * up here
244 add_gd_partition(hd, current_minor, bsd_p->p_offset, bsd_p->p_size);
245 current_minor++;
248 * Create devices for BSD partitions listed in a disklabel, under a
249 * dos-like partition. See extended_partition() for more information.
251 static void bsd_disklabel_partition(struct gendisk *hd, kdev_t dev,
252 int max_partitions)
254 struct buffer_head *bh;
255 struct bsd_disklabel *l;
256 struct bsd_partition *p;
257 int mask = (1 << hd->minor_shift) - 1;
259 if (!(bh = bread(dev,0,get_ptable_blocksize(dev))))
260 return;
261 l = (struct bsd_disklabel *) (bh->b_data+512);
262 if (l->d_magic != BSD_DISKMAGIC) {
263 brelse(bh);
264 return;
267 if (l->d_npartitions < max_partitions)
268 max_partitions = l->d_npartitions;
269 for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
270 if ((current_minor & mask) >= (4 + hd->max_p))
271 break;
273 if (p->p_fstype != BSD_FS_UNUSED)
274 check_and_add_bsd_partition(hd, p, dev);
277 /* Use bforget(), as we have changed the disk setup */
278 bforget(bh);
281 #endif
283 #ifdef CONFIG_UNIXWARE_DISKLABEL
285 * Create devices for Unixware partitions listed in a disklabel, under a
286 * dos-like partition. See extended_partition() for more information.
288 static void unixware_partition(struct gendisk *hd, kdev_t dev)
290 struct buffer_head *bh;
291 struct unixware_disklabel *l;
292 struct unixware_slice *p;
293 int mask = (1 << hd->minor_shift) - 1;
295 if (!(bh = bread(dev, 14, get_ptable_blocksize(dev))))
296 return;
297 l = (struct unixware_disklabel *) (bh->b_data+512);
298 if (le32_to_cpu(l->d_magic) != UNIXWARE_DISKMAGIC ||
299 le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) {
300 brelse(bh);
301 return;
303 printk(" <unixware:");
304 p = &l->vtoc.v_slice[1];
305 /* I omit the 0th slice as it is the same as whole disk. */
306 while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) {
307 if ((current_minor & mask) == 0)
308 break;
310 if (p->s_label != UNIXWARE_FS_UNUSED) {
311 add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
312 current_minor++;
314 p++;
316 /* Use bforget, as we have changed the disk setup */
317 bforget(bh);
318 printk(" >");
320 #endif
322 int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector, int first_part_minor)
324 int i, minor = current_minor = first_part_minor;
325 struct buffer_head *bh;
326 struct partition *p;
327 unsigned char *data;
328 int mask = (1 << hd->minor_shift) - 1;
329 int sector_size = get_hardsect_size(dev) / 512;
330 #ifdef CONFIG_BSD_DISKLABEL
331 /* no bsd disklabel as a default */
332 kdev_t bsd_kdev = 0;
333 int bsd_maxpart = BSD_MAXPARTITIONS;
334 #endif
335 #ifdef CONFIG_BLK_DEV_IDE
336 int tested_for_xlate = 0;
338 read_mbr:
339 #endif
340 if (!(bh = bread(dev,0,get_ptable_blocksize(dev)))) {
341 printk(" unable to read partition table\n");
342 return -1;
344 data = bh->b_data;
345 #ifdef CONFIG_BLK_DEV_IDE
346 check_table:
347 #endif
348 /* Use bforget(), because we have potentially changed the disk geometry */
349 if (*(unsigned short *) (0x1fe + data) != cpu_to_le16(MSDOS_LABEL_MAGIC)) {
350 bforget(bh);
351 return 0;
353 p = (struct partition *) (0x1be + data);
355 #ifdef CONFIG_BLK_DEV_IDE
356 if (!tested_for_xlate++) { /* Do this only once per disk */
358 * Look for various forms of IDE disk geometry translation
360 unsigned int sig = le16_to_cpu(*(unsigned short *)(data + 2));
361 int heads = 0;
363 * The i386 partition handling programs very often
364 * make partitions end on cylinder boundaries.
365 * There is no need to do so, and Linux fdisk doesnt always
366 * do this, and Windows NT on Alpha doesnt do this either,
367 * but still, this helps to guess #heads.
369 for (i = 0; i < 4; i++) {
370 struct partition *q = &p[i];
371 if (NR_SECTS(q)) {
372 if ((q->sector & 63) == 1 &&
373 (q->end_sector & 63) == 63)
374 heads = q->end_head + 1;
375 break;
378 if (SYS_IND(p) == EZD_PARTITION) {
380 * Accesses to sector 0 must go to sector 1 instead.
382 if (ide_xlate_1024(dev, -1, heads, " [EZD]")) {
383 data += 512;
384 goto check_table;
386 } else if (SYS_IND(p) == DM6_PARTITION) {
389 * Everything on the disk is offset by 63 sectors,
390 * including a "new" MBR with its own partition table.
392 if (ide_xlate_1024(dev, 1, heads, " [DM6:DDO]")) {
393 bforget(bh);
394 goto read_mbr; /* start over with new MBR */
396 } else if (sig <= 0x1ae &&
397 data[sig] == 0xAA && data[sig+1] == 0x55 &&
398 (data[sig+2] & 1)) {
399 /* DM6 signature in MBR, courtesy of OnTrack */
400 (void) ide_xlate_1024 (dev, 0, heads, " [DM6:MBR]");
401 } else if (SYS_IND(p) == DM6_AUX1PARTITION ||
402 SYS_IND(p) == DM6_AUX3PARTITION) {
404 * DM6 on other than the first (boot) drive
406 (void) ide_xlate_1024(dev, 0, heads, " [DM6:AUX]");
407 } else {
408 (void) ide_xlate_1024(dev, 2, heads, " [PTBL]");
411 #endif /* CONFIG_BLK_DEV_IDE */
413 current_minor += 4; /* first "extra" minor (for extended partitions) */
414 for (i=1 ; i<=4 ; minor++,i++,p++) {
415 if (!NR_SECTS(p))
416 continue;
417 add_gd_partition(hd, minor, first_sector+START_SECT(p)*sector_size, NR_SECTS(p)*sector_size);
418 if (is_extended_partition(p)) {
419 printk(" <");
421 * If we are rereading the partition table, we need
422 * to set the size of the partition so that we will
423 * be able to bread the block containing the extended
424 * partition info.
426 hd->sizes[minor] = hd->part[minor].nr_sects
427 >> (BLOCK_SIZE_BITS - 9);
428 extended_partition(hd, MKDEV(hd->major, minor));
429 printk(" >");
430 /* prevent someone doing mkfs or mkswap on an
431 extended partition, but leave room for LILO */
432 if (hd->part[minor].nr_sects > 2)
433 hd->part[minor].nr_sects = 2;
435 #ifdef CONFIG_BSD_DISKLABEL
436 /* tag first disklabel for late recognition */
437 if (SYS_IND(p) == BSD_PARTITION || SYS_IND(p) == NETBSD_PARTITION) {
438 printk("!");
439 if (!bsd_kdev)
440 bsd_kdev = MKDEV(hd->major, minor);
441 } else if (SYS_IND(p) == OPENBSD_PARTITION) {
442 printk("!");
443 if (!bsd_kdev) {
444 bsd_kdev = MKDEV(hd->major, minor);
445 bsd_maxpart = OPENBSD_MAXPARTITIONS;
448 #endif
449 #ifdef CONFIG_UNIXWARE_DISKLABEL
450 if (SYS_IND(p) == UNIXWARE_PARTITION)
451 unixware_partition(hd, MKDEV(hd->major, minor));
452 #endif
453 #ifdef CONFIG_SOLARIS_X86_PARTITION
455 /* james@bpgc.com: Solaris has a nasty indicator: 0x82
456 * which also means linux swap. For that reason, all
457 * of the prints are done inside the
458 * solaris_x86_partition routine */
460 if(SYS_IND(p) == SOLARIS_X86_PARTITION) {
461 solaris_x86_partition(hd, MKDEV(hd->major, minor),
462 first_sector+START_SECT(p));
464 #endif
466 #ifdef CONFIG_BSD_DISKLABEL
467 if (bsd_kdev) {
468 printk(" <");
469 bsd_disklabel_partition(hd, bsd_kdev, bsd_maxpart);
470 printk(" >");
472 #endif
474 * Check for old-style Disk Manager partition table
476 if (*(unsigned short *) (data+0xfc) == cpu_to_le16(MSDOS_LABEL_MAGIC)) {
477 p = (struct partition *) (0x1be + data);
478 for (i = 4 ; i < 16 ; i++, current_minor++) {
479 p--;
480 if ((current_minor & mask) == 0)
481 break;
482 if (!(START_SECT(p) && NR_SECTS(p)))
483 continue;
484 add_gd_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
487 printk("\n");
488 bforget(bh);
489 return 1;