2 * Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold, bonefish@cs.tu-berlin.de
10 /*! \file PartitionMap.cpp
11 \brief Definitions for "intel" style partitions and implementation
21 # include <util/kernel_cpp.h>
22 # include <KernelExport.h>
27 # include <DiskDeviceTypes.h>
29 # include <boot/partitions.h>
32 #include "PartitionMap.h"
35 //#define TRACE_ENABLED
38 # define TRACE(x) printf x
40 # define TRACE(x) dprintf x
49 static const char* const kUnrecognizedTypeString
= "Unrecognized Type ";
50 static const size_t kUnrecognizedTypeStringLength
= 18;
52 static const struct partition_type kPartitionTypes
[] = {
53 // Can be created (in display order)
54 { 0x00, "empty", true },
55 { 0x0f, INTEL_EXTENDED_PARTITION_NAME
, true },
56 { 0x0c, "FAT 32-bit, LBA-mapped", true },
57 { 0x82, "Linux swap", true },
58 { 0x83, "Linux native", true },
59 { 0xa5, "FreeBSD", true },
60 { 0xa6, "OpenBSD", true },
61 { 0xa9, "NetBSD", true },
62 { 0xa8, "MacOS X", true },
63 { 0xab, "MacOS X boot", true },
64 { 0xaf, "MacOS X HFS/HFS+", true },
65 { 0x4d, "QNX 4", true },
66 { 0xb3, "QNX 6", true },
67 { 0xeb, BFS_NAME
, true },
68 // Known file system types
69 { 0x01, "FAT 12-bit", false},
70 { 0x02, "Xenix root", false },
71 { 0x03, "Xenix user", false },
72 { 0x04, "FAT 16-bit (dos 3.0)", false },
73 { 0x05, INTEL_EXTENDED_PARTITION_NAME
, false },
74 { 0x06, "FAT 16-bit (dos 3.31)", false },
75 { 0x07, "Windows NT, OS/2 IFS, Advanced Unix", false },
76 { 0x08, "AIX", false },
77 { 0x09, "AIX bootable", false },
78 { 0x0a, "OS/2 Boot Manager", false },
79 { 0x0b, "FAT 32-bit", false },
80 { 0x0e, "FAT 16-bit, LBA-mapped", false },
81 { 0x10, "OPUS", false },
82 { 0x11, "Hidden FAT 12-bit", false },
83 { 0x12, "Compaq diagnostic", false },
84 { 0x14, "Hidden FAT 16-bit", false },
85 { 0x16, "Hidden FAT 16-bit", false },
86 { 0x17, "Hidden HPFS/NTFS", false },
87 { 0x18, "AST SmartSleep", false },
88 { 0x1b, "Hidden W95 FAT 32-bit", false },
89 { 0x1c, "Hidden W95 FAT 32-bit", false },
90 { 0x1e, "Hidden W95 FAT 16-bit", false },
91 { 0x24, "NEC DOS", false },
92 { 0x39, "Plan 9", false },
93 { 0x3c, "PartitionMagic", false },
94 { 0x40, "Venix 80286", false },
95 { 0x41, "PPC PReP Boot", false },
96 { 0x42, "Windows 2000 marker (proprietary extended)",
98 { 0x4e, "QNX 4 2nd part", false },
99 { 0x4f, "QNX 4 3rd part", false },
100 { 0x50, "OnTrack DM", false },
101 { 0x51, "OnTrack DM6 Aux", false },
102 { 0x52, "CP/M", false },
103 { 0x53, "OnTrack DM6 Aux", false },
104 { 0x54, "OnTrack DM6", false },
105 { 0x55, "EZ-Drive", false },
106 { 0x56, "Golden Bow", false },
107 { 0x5c, "Priam Edisk", false },
108 { 0x61, "SpeedStor", false },
109 { 0x63, "GNU HURD", false },
110 { 0x64, "Novell Netware", false },
111 { 0x65, "Novell Netware", false },
112 { 0x70, "DiskSecure Mult", false },
113 { 0x75, "PC/IX", false },
114 { 0x78, "XOSL boot loader", false },
115 { 0x80, "Old Minix", false },
116 { 0x81, "Minix", false },
117 { 0x84, "OS/2 hidden", false },
118 { 0x85, /*"Linux extendend partition"*/INTEL_EXTENDED_PARTITION_NAME
,
120 { 0x86, "NTFS volume set", false },
121 { 0x87, "NTFS volume set", true },
122 { 0x88, "Linux plaintext", false },
123 { 0x8e, "Linux LVM", false },
124 { 0x93, "Amoeba", false },
125 { 0x94, "Amoeba BBT", false },
126 { 0x9f, "BSD/OS", false },
127 { 0xa0, "IBM Hibernation", false },
128 { 0xa7, "NextSTEP", false },
129 { 0xb1, "QNX 6", false},
130 { 0xb2, "QNX 6", false},
131 { 0xb7, "BSDI fs", false },
132 { 0xb8, "BSDI swap", false },
133 { 0xbe, "Solaris 8 boot", false },
134 { 0xbf, "Solaris 10", false },
135 { 0xc1, "DR-DOS FAT", false },
136 { 0xc4, "DR-DOS FAT", false },
137 { 0xc6, "DR-DOS FAT", false },
138 { 0xc7, "Syrinx", false },
139 { 0xe4, "SpeedStor", false },
140 { 0xee, "GPT", false },
141 { 0xef, "EFI", false },
142 { 0xfb, "VMware VMFS", false },
143 { 0xfc, "VMware VMKCORE", false },
144 { 0xfd, "Linux raid auto", false },
148 static const struct partition_type kPartitionContentTypes
[] = {
150 { 0x01, kPartitionTypeFAT12
},
151 { 0x07, kPartitionTypeEXFAT
},
152 { 0x0c, kPartitionTypeFAT32
},
153 { 0x0f, kPartitionTypeIntelExtended
},
154 { 0x83, kPartitionTypeBTRFS
},
155 { 0x83, kPartitionTypeEXT2
},
156 { 0x83, kPartitionTypeEXT3
},
157 { 0x83, kPartitionTypeReiser
},
158 { 0xaf, kPartitionTypeHFS
},
159 { 0xaf, kPartitionTypeHFSPlus
},
160 { 0xeb, kPartitionTypeBFS
},
167 partition_type_string(uint8 type
)
170 for (i
= 0; kPartitionTypes
[i
].name
; i
++) {
171 if (type
== kPartitionTypes
[i
].type
)
172 return kPartitionTypes
[i
].name
;
179 get_partition_type_string(uint8 type
, char* buffer
)
182 if (const char* typeString
= partition_type_string(type
))
183 strcpy(buffer
, typeString
);
185 sprintf(buffer
, "%s0x%x", kUnrecognizedTypeString
, type
);
191 cmp_partition_offset(const void* p1
, const void* p2
)
193 const Partition
* partition1
= *(const Partition
**)p1
;
194 const Partition
* partition2
= *(const Partition
**)p2
;
196 if (partition1
->Offset() < partition2
->Offset())
198 if (partition1
->Offset() > partition2
->Offset())
206 cmp_offset(const void* o1
, const void* o2
)
208 off_t offset1
= *static_cast<const off_t
*>(o1
);
209 off_t offset2
= *static_cast<const off_t
*>(o2
);
211 if (offset1
< offset2
)
213 if (offset1
> offset2
)
221 is_inside_partitions(off_t location
, const Partition
** partitions
, int32 count
)
227 int32 upper
= count
- 1;
228 while (lower
< upper
) {
229 int32 mid
= (lower
+ upper
) / 2;
230 const Partition
* midPartition
= partitions
[mid
];
231 if (location
>= midPartition
->Offset() + midPartition
->Size())
236 const Partition
* partition
= partitions
[lower
];
237 result
= (location
>= partition
->Offset() &&
238 location
< partition
->Offset() + partition
->Size());
244 // #pragma mark - PartitionType
247 PartitionType::PartitionType()
255 /*! \brief Sets the \a type via its ID.
256 \param type ID of the partition type, it is in the range [0..255].
259 PartitionType::SetType(uint8 type
)
262 fValid
= partition_type_string(type
);
267 /*! \brief Sets the type via its string name.
268 \param typeName Name of the partition type.
271 PartitionType::SetType(const char* typeName
)
273 for (int32 i
= 0; kPartitionTypes
[i
].name
; i
++) {
274 if (!strcmp(typeName
, kPartitionTypes
[i
].name
)) {
275 fType
= kPartitionTypes
[i
].type
;
281 // If this is an unrecognized type, parse the type number.
282 if (strncmp(typeName
, kUnrecognizedTypeString
,
283 kUnrecognizedTypeStringLength
) == 0) {
284 long type
= strtol(typeName
+ kUnrecognizedTypeStringLength
, NULL
, 0);
285 if (type
!= 0 && type
<= 255) {
297 /*! \brief Converts content type to the partition type that fits best.
298 \param content_type Name of the content type, it is standardized by system.
301 PartitionType::SetContentType(const char* contentType
)
303 for (int32 i
= 0; kPartitionContentTypes
[i
].name
; i
++) {
304 if (!strcmp(contentType
, kPartitionContentTypes
[i
].name
)) {
305 fType
= kPartitionContentTypes
[i
].type
;
315 /*! \brief Finds next supported partition.
318 PartitionType::FindNext()
320 for (int32 i
= 0; kPartitionTypes
[i
].name
; i
++) {
321 if (fType
< kPartitionTypes
[i
].type
) {
322 fType
= kPartitionTypes
[i
].type
;
332 /*! \fn bool PartitionType::IsValid() const
333 \brief Check whether the current type is valid.
336 /*! \fn bool PartitionType::IsEmpty() const
337 \brief Check whether the current type describes empty type.
340 /*! \fn bool PartitionType::IsExtended() const
341 \brief Check whether the current type describes extended partition type.
344 /*! \fn uint8 PartitionType::Type() const
345 \brief Returns ID of the current type.
348 /*! \fn void PartitionType::GetTypeString(char *buffer) const
349 \brief Returns string name of the current type.
350 \param buffer Buffer where the name is stored, has to be allocated with
355 // #pragma mark - Partition
358 Partition::Partition()
360 fPartitionTableOffset(0),
369 Partition::Partition(const partition_descriptor
* descriptor
, off_t tableOffset
,
370 off_t baseOffset
, uint32 blockSize
)
372 fPartitionTableOffset(0),
378 SetTo(descriptor
, tableOffset
, baseOffset
, blockSize
);
383 Partition::SetTo(const partition_descriptor
* descriptor
, off_t tableOffset
,
384 off_t baseOffset
, uint32 blockSize
)
386 TRACE(("Partition::SetTo(): active: %x\n", descriptor
->active
));
387 SetTo(baseOffset
+ (off_t
)descriptor
->start
* blockSize
,
388 (off_t
)descriptor
->size
* blockSize
, descriptor
->type
,
389 descriptor
->active
, tableOffset
, blockSize
);
394 Partition::SetTo(off_t offset
, off_t size
, uint8 type
, bool active
,
395 off_t tableOffset
, uint32 blockSize
)
397 fPartitionTableOffset
= tableOffset
;
402 fBlockSize
= blockSize
;
412 fPartitionTableOffset
= 0;
421 Partition::CheckLocation(off_t sessionSize
) const
425 // offsets and size must be block aligned, partition table and partition must
426 // lie within the session
427 if (fPartitionTableOffset
% fBlockSize
!= 0) {
428 TRACE(("Partition::CheckLocation() - bad partition table offset: %lld "
429 "(session: %lld), (fBlockSize: %ld)\n", fPartitionTableOffset
,
430 sessionSize
, fBlockSize
));
433 if (fOffset
% fBlockSize
!= 0) {
434 TRACE(("Partition::CheckLocation() - bad offset: %lld "
435 "(session: %lld)\n", fOffset
, sessionSize
));
438 if (fSize
% fBlockSize
!= 0) {
439 TRACE(("Partition::CheckLocation() - bad size: %lld "
440 "(session: %lld)\n", fSize
, sessionSize
));
443 if (fPartitionTableOffset
< 0 || fPartitionTableOffset
>= sessionSize
) {
444 TRACE(("Partition::CheckLocation() - partition table offset outside "
445 "session: %lld (session size: %lld)\n", fPartitionTableOffset
,
450 TRACE(("Partition::CheckLocation() - offset before session: %lld "
451 "(session: %lld)\n", fOffset
, sessionSize
));
454 if (fOffset
+ fSize
> sessionSize
) {
455 TRACE(("Partition::CheckLocation() - end after session: %lld "
456 "(session: %lld)\n", fOffset
+ fSize
, sessionSize
));
464 Partition::FitSizeToSession(off_t sessionSize
)
466 // To work around buggy (or older) BIOS, we shrink the partition size to
467 // always fit into its session - this should improve detection of boot
468 // partitions (see bug #238 for more information).
469 // Also, the drive size is obviously reported differently sometimes; this
470 // should let us read problematic drives - let the file system figure out
471 // if something is wrong.
472 if (sessionSize
< fOffset
+ fSize
&& sessionSize
> fOffset
) {
473 fSize
= sessionSize
- fOffset
;
481 // #pragma mark - PrimaryPartition
484 PrimaryPartition::PrimaryPartition()
489 fLogicalPartitionCount(0)
495 PrimaryPartition::SetTo(const partition_descriptor
* descriptor
,
496 off_t tableOffset
, uint32 blockSize
)
499 Partition::SetTo(descriptor
, tableOffset
, 0, blockSize
);
504 PrimaryPartition::SetTo(off_t offset
, off_t size
, uint8 type
, bool active
,
508 Partition::SetTo(offset
, size
, type
, active
, 0, blockSize
);
513 PrimaryPartition::Unset()
515 while (LogicalPartition
* partition
= fHead
) {
516 fHead
= partition
->Next();
521 fLogicalPartitionCount
= 0;
527 PrimaryPartition::Assign(const PrimaryPartition
& other
)
529 partition_descriptor descriptor
;
530 other
.GetPartitionDescriptor(&descriptor
);
531 SetTo(&descriptor
, 0, other
.BlockSize());
533 const LogicalPartition
* otherLogical
= other
.fHead
;
534 while (otherLogical
) {
535 off_t tableOffset
= otherLogical
->PartitionTableOffset();
536 otherLogical
->GetPartitionDescriptor(&descriptor
);
538 LogicalPartition
* logical
= new(nothrow
) LogicalPartition(
539 &descriptor
, tableOffset
, this);
543 AddLogicalPartition(logical
);
545 otherLogical
= otherLogical
->Next();
553 PrimaryPartition::GetPartitionDescriptor(partition_descriptor
* descriptor
) const
556 memset(descriptor
, 0, sizeof(partition_descriptor
));
558 descriptor
->start
= Offset() / BlockSize();
559 descriptor
->size
= Size() / BlockSize();
560 descriptor
->type
= Type();
561 descriptor
->active
= Active() ? 0x80 : 0x00;
562 descriptor
->begin
.Unset();
563 descriptor
->end
.Unset();
569 PrimaryPartition::LogicalPartitionAt(int32 index
) const
571 LogicalPartition
* partition
= NULL
;
572 if (index
>= 0 && index
< fLogicalPartitionCount
) {
573 for (partition
= fHead
; index
> 0; index
--)
574 partition
= partition
->Next();
581 PrimaryPartition::AddLogicalPartition(LogicalPartition
* partition
)
586 partition
->SetPrimaryPartition(this);
587 partition
->SetPrevious(fTail
);
589 fTail
->SetNext(partition
);
592 fHead
= fTail
= partition
;
594 partition
->SetNext(NULL
);
596 fLogicalPartitionCount
++;
601 PrimaryPartition::RemoveLogicalPartition(LogicalPartition
* partition
)
603 if (!partition
|| partition
->GetPrimaryPartition() != this)
606 LogicalPartition
* prev
= partition
->Previous();
607 LogicalPartition
* next
= partition
->Next();
614 next
->SetPrevious(prev
);
618 fLogicalPartitionCount
--;
620 partition
->SetNext(NULL
);
621 partition
->SetPrevious(NULL
);
622 partition
->SetPrimaryPartition(NULL
);
626 // #pragma mark - LogicalPartition
629 LogicalPartition::LogicalPartition()
639 LogicalPartition::LogicalPartition(const partition_descriptor
* descriptor
,
640 off_t tableOffset
, PrimaryPartition
* primary
)
647 SetTo(descriptor
, tableOffset
, primary
);
652 LogicalPartition::SetTo(const partition_descriptor
* descriptor
,
653 off_t tableOffset
, PrimaryPartition
* primary
)
656 if (descriptor
&& primary
) {
657 // There are two types of LogicalPartitions. There are so called
658 // "inner extended" partitions and the "real" logical partitions
659 // which contain data. The "inner extended" partitions don't contain
660 // data and are only used to point to the next partition table in the
661 // linked list of logical partitions. For "inner extended" partitions,
662 // the baseOffset is in relation to the (first sector of the)
663 // "primary extended" partition, in another words, all inner extended
664 // partitions use the same base offset for reference.
665 // The data containing, real logical partitions use the offset of the
666 // partition table that contains their partition descriptor as their
668 off_t baseOffset
= descriptor
->is_extended()
669 ? primary
->Offset() : tableOffset
;
670 Partition::SetTo(descriptor
, tableOffset
, baseOffset
,
671 primary
->BlockSize());
678 LogicalPartition::SetTo(off_t offset
, off_t size
, uint8 type
, bool active
,
679 off_t tableOffset
, PrimaryPartition
* primary
)
683 Partition::SetTo(offset
, size
, type
, active
, tableOffset
,
684 primary
->BlockSize());
691 LogicalPartition::Unset()
701 LogicalPartition::GetPartitionDescriptor(partition_descriptor
* descriptor
,
704 PrimaryPartition
* primary
= GetPrimaryPartition();
706 descriptor
->start
= (PartitionTableOffset() - primary
->Offset())
708 descriptor
->type
= primary
->Type();
710 descriptor
->start
= (Offset() - PartitionTableOffset()) / BlockSize();
711 descriptor
->type
= Type();
714 descriptor
->size
= Size() / BlockSize();
715 descriptor
->active
= 0x00;
716 descriptor
->begin
.Unset();
717 descriptor
->end
.Unset();
721 // #pragma mark - PartitionMap
724 PartitionMap::PartitionMap()
726 for (int32 i
= 0; i
< 4; i
++)
727 fPrimaries
[i
].SetIndex(i
);
731 PartitionMap::~PartitionMap()
737 PartitionMap::Unset()
739 for (int32 i
= 0; i
< 4; i
++)
740 fPrimaries
[i
].Unset();
745 PartitionMap::Assign(const PartitionMap
& other
)
747 for (int32 i
= 0; i
< 4; i
++) {
748 status_t error
= fPrimaries
[i
].Assign(other
.fPrimaries
[i
]);
758 PartitionMap::PrimaryPartitionAt(int32 index
)
760 PrimaryPartition
* partition
= NULL
;
761 if (index
>= 0 && index
< 4)
762 partition
= fPrimaries
+ index
;
767 const PrimaryPartition
*
768 PartitionMap::PrimaryPartitionAt(int32 index
) const
770 const PrimaryPartition
* partition
= NULL
;
771 if (index
>= 0 && index
< 4)
772 partition
= fPrimaries
+ index
;
778 PartitionMap::CountNonEmptyPrimaryPartitions() const
781 for (int32 i
= 0; i
< 4; i
++) {
782 if (!fPrimaries
[i
].IsEmpty())
791 PartitionMap::ExtendedPartitionIndex() const
793 for (int32 i
= 0; i
< 4; i
++) {
794 if (fPrimaries
[i
].IsExtended())
803 PartitionMap::CountPartitions() const
806 for (int32 i
= 0; i
< 4; i
++)
807 count
+= fPrimaries
[i
].CountLogicalPartitions();
813 PartitionMap::CountNonEmptyPartitions() const
816 for (int32 i
= CountPartitions() - 1; i
>= 0; i
--) {
817 if (!PartitionAt(i
)->IsEmpty())
826 PartitionMap::PartitionAt(int32 index
)
828 Partition
* partition
= NULL
;
829 int32 count
= CountPartitions();
830 if (index
>= 0 && index
< count
) {
832 partition
= fPrimaries
+ index
;
836 while (index
>= fPrimaries
[primary
].CountLogicalPartitions()) {
837 index
-= fPrimaries
[primary
].CountLogicalPartitions();
840 partition
= fPrimaries
[primary
].LogicalPartitionAt(index
);
848 PartitionMap::PartitionAt(int32 index
) const
850 return const_cast<PartitionMap
*>(this)->PartitionAt(index
);
855 PartitionMap::Check(off_t sessionSize
) const
857 int32 partitionCount
= CountPartitions();
859 // 1. check partition locations
860 for (int32 i
= 0; i
< partitionCount
; i
++) {
861 if (!PartitionAt(i
)->CheckLocation(sessionSize
))
865 // 2. check overlapping of partitions and location of partition tables
867 Partition
** byOffset
= new(nothrow
) Partition
*[partitionCount
];
868 off_t
* tableOffsets
= new(nothrow
) off_t
[partitionCount
- 3];
869 if (byOffset
&& tableOffsets
) {
871 int32 byOffsetCount
= 0;
872 int32 tableOffsetCount
= 1; // primary partition table
874 for (int32 i
= 0; i
< partitionCount
; i
++) {
875 Partition
* partition
= (Partition
*)PartitionAt(i
);
876 if (!partition
->IsExtended())
877 byOffset
[byOffsetCount
++] = partition
;
879 // add only logical partition partition table locations
881 tableOffsets
[tableOffsetCount
++]
882 = partition
->PartitionTableOffset();
887 qsort(byOffset
, byOffsetCount
, sizeof(Partition
*),
888 cmp_partition_offset
);
889 qsort(tableOffsets
, tableOffsetCount
, sizeof(off_t
), cmp_offset
);
891 // check for overlappings
892 off_t nextOffset
= 0;
893 for (int32 i
= 0; i
< byOffsetCount
; i
++) {
894 Partition
* partition
= byOffset
[i
];
895 if (partition
->Offset() < nextOffset
&& i
> 0) {
896 Partition
* previousPartition
= byOffset
[i
- 1];
897 off_t previousSize
= previousPartition
->Size()
898 - (nextOffset
- partition
->Offset());
899 TRACE(("intel: PartitionMap::Check(): "));
900 if (previousSize
== 0) {
901 previousPartition
->Unset();
902 TRACE(("partition offset hides previous partition."
903 " Removing previous partition from disk layout.\n"));
905 TRACE(("overlapping partitions! Setting partition %ld "
906 "size to %lld\n", i
- 1, previousSize
));
907 previousPartition
->SetSize(previousSize
);
910 nextOffset
= partition
->Offset() + partition
->Size();
913 // check uniqueness of partition table offsets and whether they lie
914 // outside of the non-extended partitions
916 for (int32 i
= 0; i
< tableOffsetCount
; i
++) {
917 if (i
> 0 && tableOffsets
[i
] == tableOffsets
[i
- 1]) {
918 TRACE(("intel: PartitionMap::Check(): same partition table "
919 "for different extended partitions!\n"));
922 } else if (is_inside_partitions(tableOffsets
[i
],
923 (const Partition
**)byOffset
, byOffsetCount
)) {
924 TRACE(("intel: PartitionMap::Check(): a partition table "
925 "lies inside a non-extended partition!\n"));
932 result
= false; // no memory: assume failure
936 delete[] tableOffsets
;
942 const partition_type
*
943 PartitionMap::GetNextSupportedPartitionType(uint32 index
)
945 if (index
> (sizeof(kPartitionTypes
) / sizeof(partition_type
) - 2))
948 return kPartitionTypes
+ index
;