Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / partition / partitionmbr.c
blobb03a51b49373a727df4f3ae092670611f243a234
1 /*
2 Copyright © 1995-2009, The AROS Development Team. All rights reserved.
3 $Id$
5 */
7 #include <proto/exec.h>
8 #include <proto/partition.h>
10 #include <exec/memory.h>
11 #include <exec/types.h>
12 #include <libraries/partition.h>
14 #include "partition_support.h"
15 #include "partitionmbr.h"
16 #include "platform.h"
18 #ifndef DEBUG
19 #define DEBUG 1
20 #endif
21 #include "debug.h"
23 struct MBRData {
24 struct PCPartitionTable *entry;
25 UBYTE position;
28 struct FATBootSector {
29 UBYTE bs_jmp_boot[3];
30 UBYTE bs_oem_name[8];
31 UWORD bpb_bytes_per_sect;
32 UBYTE bpb_sect_per_clust;
33 UWORD bpb_rsvd_sect_count;
34 UBYTE bpb_num_fats;
35 UWORD bpb_root_entries_count;
36 UWORD bpb_total_sectors_16;
37 UBYTE bpb_media;
38 UWORD bpb_fat_size_16;
39 UWORD bpb_sect_per_track;
40 UWORD bpb_num_heads;
41 ULONG bpb_hidden_sect;
42 ULONG bpb_total_sectors_32;
44 union {
45 struct {
46 UBYTE bs_drvnum;
47 UBYTE bs_reserved1;
48 UBYTE bs_bootsig;
49 ULONG bs_volid;
50 UBYTE bs_vollab[11];
51 UBYTE bs_filsystype[8];
52 } __attribute__ ((__packed__)) fat16;
54 struct {
55 ULONG bpb_fat_size_32;
56 UWORD bpb_extflags;
57 UWORD bpb_fs_verion;
58 ULONG bpb_root_cluster;
59 UWORD bpb_fs_info;
60 UWORD bpb_back_bootsec;
61 UBYTE bpb_reserved[12];
62 UBYTE bs_drvnum;
63 UBYTE bs_reserved1;
64 UBYTE bs_bootsig;
65 ULONG bs_volid;
66 UBYTE bs_vollab[11];
67 UBYTE bs_filsystype[8];
68 } __attribute__ ((__packed__)) fat32;
69 } type;
70 UBYTE pad[420];
71 UBYTE bpb_signature[2];
72 } __attribute__ ((__packed__));
74 static LONG PartitionMBRCheckPartitionTable
76 struct Library *PartitionBase,
77 struct PartitionHandle *root
80 struct
82 union
84 struct MBR mbr;
85 struct FATBootSector bs;
86 UBYTE space[root->de.de_SizeBlock << 2];
90 blk;
91 BOOL isfat = TRUE;
92 ULONG sectorsize, clustersectors;
93 struct PCPartitionTable *pcpt;
95 if (readBlock(PartitionBase, root, 0, &blk) == 0)
97 /* Check it doesn't look like a FAT boot sector */
99 /* Valid sector size: 512, 1024, 2048, 4096 */
100 sectorsize = AROS_LE2WORD(blk.u.bs.bpb_bytes_per_sect);
101 if (
102 sectorsize != 512 && sectorsize != 1024 && sectorsize != 2048 &&
103 sectorsize != 4096
105 isfat = FALSE;
107 /* Valid bpb_sect_per_clust: 1, 2, 4, 8, 16, 32, 64, 128 */
108 clustersectors = blk.u.bs.bpb_sect_per_clust;
109 if (
110 (clustersectors & (clustersectors - 1)) != 0 ||
111 clustersectors == 0 || clustersectors > 128
113 isfat = FALSE;
115 /* Valid cluster size: 512, 1024, 2048, 4096, 8192, 16k, 32k, 64k */
116 if (clustersectors * sectorsize > 64 * 1024)
117 isfat = FALSE;
119 if (blk.u.bs.bpb_media < 0xF0)
120 isfat = FALSE;
122 /* Check status bytes of all partition slots and block signature */
123 pcpt = blk.u.mbr.pcpt;
124 if (
125 (AROS_LE2WORD(blk.u.mbr.magic) == 0xAA55) &&
126 (((pcpt[0].status & 0x0F)==0) || (pcpt[0].status & 0x80)) &&
127 (((pcpt[1].status & 0x0F)==0) || (pcpt[1].status & 0x80)) &&
128 (((pcpt[2].status & 0x0F)==0) || (pcpt[2].status & 0x80)) &&
129 (((pcpt[3].status & 0x0F)==0) || (pcpt[3].status & 0x80))
132 if (root->root || isfat)
134 return 0;
136 return 1;
139 return 0;
142 static struct PartitionHandle *PartitionMBRNewHandle
144 struct Library *PartitionBase,
145 struct PartitionHandle *root,
146 UBYTE position,
147 struct PCPartitionTable *entry
150 struct PartitionHandle *ph;
151 ULONG cylsecs;
153 if (entry->first_sector != 0)
155 ph = AllocMem(sizeof(struct PartitionHandle), MEMF_PUBLIC | MEMF_CLEAR);
156 if (ph)
158 struct MBRData *data;
160 data = AllocMem(sizeof(struct MBRData), MEMF_PUBLIC);
161 if (data)
163 cylsecs = root->de.de_BlocksPerTrack*root->de.de_Surfaces;
164 data->entry = entry;
165 data->position = position;
166 ph->root = root;
167 ph->bd = root->bd;
168 ph->data = data;
170 /* initialize DosEnvec */
172 /* Check if partition starts and ends on a cylinder boundary */
173 CopyMem(&root->de, &ph->de, sizeof(struct DosEnvec));
174 if (
175 (AROS_LE2LONG(data->entry->first_sector) % cylsecs != 0) ||
176 (AROS_LE2LONG(data->entry->count_sector) % cylsecs != 0)
179 /* Treat each track as a cylinder if possible */
180 ph->de.de_Surfaces = 1;
181 cylsecs = ph->de.de_BlocksPerTrack*ph->de.de_Surfaces;
182 if (
183 (AROS_LE2LONG(data->entry->first_sector) % cylsecs != 0) ||
184 (AROS_LE2LONG(data->entry->count_sector) % cylsecs != 0)
187 /* We can't. We could find the highest common factor of
188 first_sector and count_sector here, but currently we
189 simply use one block per cylinder */
190 ph->de.de_BlocksPerTrack = 1;
191 cylsecs = ph->de.de_BlocksPerTrack*ph->de.de_Surfaces;
194 ph->de.de_LowCyl = AROS_LE2LONG(data->entry->first_sector) / cylsecs;
195 ph->de.de_HighCyl =
196 ph->de.de_LowCyl+
197 (AROS_LE2LONG(data->entry->count_sector)/cylsecs)-1;
198 ph->de.de_TableSize = 10; // only until de_HighCyl
199 ph->ln.ln_Pri = MBR_MAX_PARTITIONS-1-position;
201 /* initialize DriveGeometry */
202 ph->dg.dg_DeviceType = DG_DIRECT_ACCESS;
203 ph->dg.dg_SectorSize = ph->de.de_SizeBlock<<2;
204 ph->dg.dg_Heads = ph->de.de_Surfaces;
205 ph->dg.dg_TrackSectors = ph->de.de_BlocksPerTrack;
206 ph->dg.dg_Cylinders = ph->de.de_HighCyl - ph->de.de_LowCyl + 1;
207 ph->dg.dg_BufMemType = ph->de.de_BufMemType;
208 return ph;
210 FreeMem(ph, sizeof(struct PartitionHandle));
213 return NULL;
216 static LONG PartitionMBROpenPartitionTable
218 struct Library *PartitionBase,
219 struct PartitionHandle *root
222 struct PartitionHandle *ph;
223 struct MBR *mbr;
224 UBYTE i;
226 mbr = AllocMem(root->de.de_SizeBlock<<2, MEMF_PUBLIC);
227 if (mbr)
229 if (readBlock(PartitionBase, root, 0, mbr) == 0)
231 NEWLIST(&root->table->list);
232 root->table->data = mbr;
233 for (i=0;i<4;i++)
235 ph = PartitionMBRNewHandle(PartitionBase, root, i, &mbr->pcpt[i]);
236 if (ph != NULL)
237 Enqueue(&root->table->list, &ph->ln);
239 return 0;
241 FreeMem(mbr, root->de.de_SizeBlock<<2);
243 return 1;
246 static void PartitionMBRFreeHandle
248 struct Library *PartitionBase,
249 struct PartitionHandle *ph
252 ClosePartitionTable(ph);
253 FreeMem(ph->data, sizeof(struct MBRData));
254 FreeMem(ph, sizeof(struct PartitionHandle));
257 static void PartitionMBRClosePartitionTable
259 struct Library *PartitionBase,
260 struct PartitionHandle *root
263 struct PartitionHandle *ph;
265 while ((ph = (struct PartitionHandle *)RemTail(&root->table->list)))
266 PartitionMBRFreeHandle(PartitionBase, ph);
267 FreeMem(root->table->data, root->de.de_SizeBlock<<2);
270 static LONG PartitionMBRWritePartitionTable
272 struct Library *PartitionBase,
273 struct PartitionHandle *root
276 #warning "FIXME: readBlock(0) and synchronize data"
278 /* root->data = mbr is up to date */
279 if (writeBlock(PartitionBase, root, 0, root->table->data))
280 return 1;
281 return 0;
284 static LONG PartitionMBRCreatePartitionTable
286 struct Library *PartitionBase,
287 struct PartitionHandle *ph
290 struct MBR *mbr;
292 mbr = AllocMem(ph->de.de_SizeBlock<<2, MEMF_PUBLIC);
293 if (mbr)
295 if (readBlock(PartitionBase, ph, 0, mbr) == 0)
297 ph->table->data = mbr;
298 fillMem((BYTE *)mbr->pcpt, sizeof(mbr->pcpt), 0);
299 mbr->magic = AROS_WORD2LE(0xAA55);
300 NEWLIST(&ph->table->list);
301 return 0;
303 FreeMem(mbr, ph->de.de_SizeBlock<<2);
305 return 1;
308 void PartitionMBRSetGeometry
310 struct PartitionHandle *root,
311 struct PCPartitionTable *entry,
312 ULONG sector,
313 ULONG count,
314 ULONG relative_sector
317 ULONG track;
318 ULONG cyl;
320 /* Store LBA start block and block count */
322 entry->first_sector = AROS_LONG2LE(sector - relative_sector);
323 entry->count_sector = AROS_LONG2LE(count);
325 /* Store CHS-address of start block. The upper two bits of the cylinder
326 number are stored in the upper two bits of the sector field */
328 track = sector/root->de.de_BlocksPerTrack;
329 cyl = track/root->de.de_Surfaces;
330 if (cyl<1024)
332 entry->start_head = track % root->de.de_Surfaces;
333 entry->start_sector =
334 ((sector % root->de.de_BlocksPerTrack) + 1)
335 | ((cyl & 0x300) >> 2);
336 entry->start_cylinder = (cyl & 0xFF);
338 else
340 entry->start_head = 0xFE;
341 entry->start_sector = 0xFF;
342 entry->start_cylinder = 0xFF;
345 /* Store CHS-address of last block */
347 sector += count - 1;
348 track = sector/root->de.de_BlocksPerTrack;
349 cyl = track/root->de.de_Surfaces;
350 if (cyl<1024)
352 entry->end_head = track % root->de.de_Surfaces;
353 entry->end_sector = ((sector % root->de.de_BlocksPerTrack) + 1)
354 | ((cyl & 0x300)>>2);
355 entry->end_cylinder = (cyl & 0xFF);
357 else
359 entry->end_head = 0xFE;
360 entry->end_sector = 0xFF;
361 entry->end_cylinder = 0xFF;
365 static void PartitionMBRSetDosEnvec
367 struct PartitionHandle *root,
368 struct PCPartitionTable *entry,
369 struct DosEnvec *de
372 ULONG sector, count;
374 sector = de->de_LowCyl * de->de_Surfaces * de->de_BlocksPerTrack;
375 count = (de->de_HighCyl - de->de_LowCyl + 1) *
376 de->de_Surfaces *
377 de->de_BlocksPerTrack;
378 PartitionMBRSetGeometry(root, entry, sector, count, 0);
381 static struct PartitionHandle *PartitionMBRAddPartition
383 struct Library *PartitionBase,
384 struct PartitionHandle *root,
385 struct TagItem *taglist
388 struct TagItem *tag;
390 tag = findTagItem(PT_DOSENVEC, taglist);
391 if (tag)
393 struct PCPartitionTable *entry;
394 struct PartitionHandle *ph;
395 struct DosEnvec *de;
396 WORD pos = -1, i;
398 de = (struct DosEnvec *)tag->ti_Data;
399 tag = findTagItem(PT_POSITION, taglist);
400 if (tag != NULL)
401 pos = tag->ti_Data;
402 else
404 // Find an unused slot
405 for (i = 0; i < MBR_MAX_PARTITIONS && pos == -1; i++)
407 entry = &((struct MBR *)root->table->data)->pcpt[i];
408 if (entry->type == 0)
409 pos = i;
413 if (pos != -1)
415 entry = &((struct MBR *)root->table->data)->pcpt[pos];
416 tag = findTagItem(PT_ACTIVE, taglist);
417 if (tag)
418 entry->status = tag->ti_Data ? 0x80 : 0;
419 else
420 entry->status = 0;
421 tag = findTagItem(PT_TYPE, taglist);
422 if (tag)
424 struct PartitionType *ptype = (struct PartitionType *)tag->ti_Data;
426 entry->type = ptype->id[0];
428 else
429 entry->type = 0;
430 PartitionMBRSetDosEnvec(root, entry, de);
431 ph = PartitionMBRNewHandle(PartitionBase, root, pos, entry);
432 if (ph != NULL)
433 Enqueue(&root->table->list, &ph->ln);
434 else
435 fillMem((BYTE *)entry, sizeof(struct PCPartitionTable), 0);
436 return ph;
439 return NULL;
442 static void PartitionMBRDeletePartition
444 struct Library *PartitionBase,
445 struct PartitionHandle *ph
448 struct MBRData *data;
450 data = (struct MBRData *)ph->data;
451 fillMem((BYTE *)data->entry, sizeof(struct PCPartitionTable), 0);
452 Remove(&ph->ln);
453 PartitionMBRFreeHandle(PartitionBase, ph);
456 static LONG PartitionMBRGetPartitionTableAttrs
458 struct Library *PartitionBase,
459 struct PartitionHandle *root,
460 struct TagItem *taglist
464 while (taglist[0].ti_Tag != TAG_DONE)
467 switch (taglist[0].ti_Tag)
469 case PTT_TYPE:
470 *((LONG *)taglist[0].ti_Data) = root->table->type;
471 break;
472 case PTT_RESERVED:
473 *((LONG *)taglist[0].ti_Data) =
474 root->de.de_BlocksPerTrack; /* One track */
475 break;
476 case PTT_MAX_PARTITIONS:
477 *((LONG *)taglist[0].ti_Data) = MBR_MAX_PARTITIONS;
479 taglist++;
481 return 0;
484 static LONG PartitionMBRSetPartitionTableAttrs
486 struct Library *PartitionBase,
487 struct PartitionHandle *root,
488 struct TagItem *taglist
492 while (taglist[0].ti_Tag != TAG_DONE)
495 switch (taglist[0].ti_Tag)
498 taglist++;
500 return 0;
503 static LONG PartitionMBRGetPartitionAttrs
505 struct Library *PartitionBase,
506 struct PartitionHandle *ph,
507 struct TagItem *taglist
511 while (taglist[0].ti_Tag != TAG_DONE)
513 struct MBRData *data = (struct MBRData *)ph->data;
515 switch (taglist[0].ti_Tag)
517 case PT_GEOMETRY:
519 struct DriveGeometry *dg = (struct DriveGeometry *)taglist[0].ti_Data;
520 CopyMem(&ph->dg, dg, sizeof(struct DriveGeometry));
522 break;
523 case PT_DOSENVEC:
524 CopyMem(&ph->de, (struct DosEnvec *)taglist[0].ti_Data, sizeof(struct DosEnvec));
525 break;
526 case PT_TYPE:
528 struct PartitionType *ptype=(struct PartitionType *)taglist[0].ti_Data;
530 ptype->id[0] = (LONG)data->entry->type;
531 ptype->id_len = 1;
533 break;
534 case PT_POSITION:
535 *((LONG *)taglist[0].ti_Data) = (LONG)data->position;
536 break;
537 case PT_ACTIVE:
538 *((LONG *)taglist[0].ti_Data) = data->entry->status & 0x80 ? 1 : 0;
539 break;
541 taglist++;
543 return 0;
546 static LONG PartitionMBRSetPartitionAttrs
548 struct Library *PartitionBase,
549 struct PartitionHandle *ph,
550 struct TagItem *taglist
554 while (taglist[0].ti_Tag != TAG_DONE)
556 struct MBRData *data = (struct MBRData *)ph->data;
558 switch (taglist[0].ti_Tag)
560 case PT_DOSENVEC:
562 struct DosEnvec *de;
563 de = (struct DosEnvec *)taglist[0].ti_Data;
564 CopyMem(de, &ph->de, sizeof(struct DosEnvec));
565 PartitionMBRSetDosEnvec(ph->root, data->entry, de);
567 break;
568 case PT_TYPE:
570 struct PartitionType *ptype=(struct PartitionType *)taglist[0].ti_Data;
572 data->entry->type = ptype->id[0];
574 break;
575 case PT_POSITION:
576 if (taglist[0].ti_Data != data->position)
578 struct PartitionHandle *node;
579 struct PCPartitionTable *entry;
581 node = (struct PartitionHandle *)ph->root->table->list.lh_Head;
582 while (node->ln.ln_Succ)
584 if (taglist[0].ti_Data == ((struct MBRData *)node->data)->position)
585 goto posbreak;
586 node = (struct PartitionHandle *)node->ln.ln_Succ;
588 data->position = taglist[0].ti_Data;
589 entry = &((struct MBR *)ph->root->table->data)->pcpt[data->position];
590 CopyMem(data->entry, entry, sizeof(struct PCPartitionTable));
591 fillMem((BYTE *)data->entry, sizeof(struct PCPartitionTable), 0);
592 data->entry = entry;
593 ph->ln.ln_Pri = MBR_MAX_PARTITIONS-1-data->position;
594 Remove(&ph->ln);
595 Enqueue(&ph->root->table->list, &ph->ln);
596 posbreak:
599 break;
600 case PT_ACTIVE:
601 if (taglist[0].ti_Data)
602 data->entry->status |= 0x80;
603 else
604 data->entry->status &= ~0x80;
605 break;
607 taglist++;
609 return 0;
612 static struct PartitionAttribute PartitionMBRPartitionTableAttrs[]=
614 {PTTA_TYPE, PLAM_READ},
615 {PTTA_RESERVED, PLAM_READ},
616 {PTTA_MAX_PARTITIONS, PLAM_READ},
617 {PTTA_DONE, 0}
620 static struct PartitionAttribute *PartitionMBRQueryPartitionTableAttrs(struct Library *PartitionBase)
622 return PartitionMBRPartitionTableAttrs;
625 static struct PartitionAttribute PartitionMBRPartitionAttrs[]=
627 {PTA_GEOMETRY, PLAM_READ},
628 {PTA_TYPE, PLAM_READ | PLAM_WRITE},
629 {PTA_POSITION, PLAM_READ | PLAM_WRITE},
630 {PTA_ACTIVE, PLAM_READ | PLAM_WRITE},
631 {PTA_DONE, 0}
634 struct PartitionAttribute *PartitionMBRQueryPartitionAttrs(struct Library *PartitionBase)
636 return PartitionMBRPartitionAttrs;
639 static ULONG PartitionMBRDestroyPartitionTable
641 struct Library *PartitionBase,
642 struct PartitionHandle *root
645 struct MBR *mbr;
647 mbr = root->table->data;
648 fillMem((BYTE *)mbr->pcpt, sizeof(mbr->pcpt), 0);
649 if (writeBlock(PartitionBase, root, 0, root->table->data))
650 return 1;
651 return 0;
654 struct PTFunctionTable PartitionMBR =
656 PHPTT_MBR,
657 "PC-MBR",
658 PartitionMBRCheckPartitionTable,
659 PartitionMBROpenPartitionTable,
660 PartitionMBRClosePartitionTable,
661 PartitionMBRWritePartitionTable,
662 PartitionMBRCreatePartitionTable,
663 PartitionMBRAddPartition,
664 PartitionMBRDeletePartition,
665 PartitionMBRGetPartitionTableAttrs,
666 PartitionMBRSetPartitionTableAttrs,
667 PartitionMBRGetPartitionAttrs,
668 PartitionMBRSetPartitionAttrs,
669 PartitionMBRQueryPartitionTableAttrs,
670 PartitionMBRQueryPartitionAttrs,
671 PartitionMBRDestroyPartitionTable