xref: /haiku/src/add-ons/kernel/partitioning_systems/common/PartitionMap.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2003-2011, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ingo Weinhold, bonefish@cs.tu-berlin.de
7  */
8 
9 
10 /*!	\file PartitionMap.cpp
11 	\brief Definitions for "intel" style partitions and implementation
12 		   of related classes.
13 */
14 
15 
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 #ifndef _USER_MODE
21 #	include <util/kernel_cpp.h>
22 #	include <KernelExport.h>
23 #else
24 #	include <new>
25 #endif
26 #ifndef _BOOT_MODE
27 #	include <DiskDeviceTypes.h>
28 #else
29 #	include <boot/partitions.h>
30 #endif
31 
32 #include "PartitionMap.h"
33 
34 
35 //#define TRACE_ENABLED
36 #ifdef TRACE_ENABLED
37 #	ifdef _USER_MODE
38 #		define TRACE(x) printf x
39 #	else
40 #		define TRACE(x) dprintf x
41 #	endif
42 #else
43 #	define TRACE(x) ;
44 #endif
45 
46 using std::nothrow;
47 
48 
49 static const char* const kUnrecognizedTypeString = "Unrecognized Type ";
50 static const size_t kUnrecognizedTypeStringLength = 18;
51 
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)",
97     false },
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,
119     false },
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 system data", true },
142     { 0xfb, "VMware VMFS", false },
143     { 0xfc, "VMware VMKCORE", false },
144     { 0xfd, "Linux raid auto", false },
145     { 0, NULL, false }
146 };
147 
148 static const struct partition_type kPartitionContentTypes[] = {
149 #ifndef _USER_MODE
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 },
161 #endif
162 	{ 0, NULL }
163 };
164 
165 
166 static const char*
167 partition_type_string(uint8 type)
168 {
169 	int32 i;
170 	for (i = 0; kPartitionTypes[i].name ; i++) {
171 		if (type == kPartitionTypes[i].type)
172 			return kPartitionTypes[i].name;
173 	}
174 	return NULL;
175 }
176 
177 
178 void
179 get_partition_type_string(uint8 type, char* buffer)
180 {
181 	if (buffer) {
182 		if (const char* typeString = partition_type_string(type))
183 			strcpy(buffer, typeString);
184 		else
185 			sprintf(buffer, "%s0x%x", kUnrecognizedTypeString, type);
186 	}
187 }
188 
189 
190 static int
191 cmp_partition_offset(const void* p1, const void* p2)
192 {
193 	const Partition* partition1 = *(const Partition**)p1;
194 	const Partition* partition2 = *(const Partition**)p2;
195 
196 	if (partition1->Offset() < partition2->Offset())
197 		return -1;
198 	if (partition1->Offset() > partition2->Offset())
199 		return 1;
200 
201 	return 0;
202 }
203 
204 
205 static int
206 cmp_offset(const void* o1, const void* o2)
207 {
208 	off_t offset1 = *static_cast<const off_t*>(o1);
209 	off_t offset2 = *static_cast<const off_t*>(o2);
210 
211 	if (offset1 < offset2)
212 		return -1;
213 	if (offset1 > offset2)
214 		return 1;
215 
216 	return 0;
217 }
218 
219 
220 static bool
221 is_inside_partitions(off_t location, const Partition** partitions, int32 count)
222 {
223 	bool result = false;
224 	if (count > 0) {
225 		// binary search
226 		int32 lower = 0;
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())
232 				lower = mid + 1;
233 			else
234 				upper = mid;
235 		}
236 		const Partition* partition = partitions[lower];
237 		result = (location >= partition->Offset() &&
238 				  location < partition->Offset() + partition->Size());
239 	}
240 	return result;
241 }
242 
243 
244 //	#pragma mark - PartitionType
245 
246 
247 PartitionType::PartitionType()
248 	:
249 	fType(0),
250 	fValid(false)
251 {
252 }
253 
254 
255 /*!	\brief Sets the \a type via its ID.
256 	\param type ID of the partition type, it is in the range [0..255].
257 */
258 bool
259 PartitionType::SetType(uint8 type)
260 {
261 	fType = type;
262 	fValid = partition_type_string(type);
263 	return fValid;
264 }
265 
266 
267 /*!	\brief Sets the type via its string name.
268 	\param typeName Name of the partition type.
269 */
270 bool
271 PartitionType::SetType(const char* typeName)
272 {
273 	for (int32 i = 0; kPartitionTypes[i].name ; i++) {
274 		if (!strcmp(typeName, kPartitionTypes[i].name)) {
275 			fType = kPartitionTypes[i].type;
276 			fValid = true;
277 			return fValid;
278 		}
279 	}
280 
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) {
286 			fType = type;
287 			fValid = true;
288 			return fValid;
289 		}
290 	}
291 
292 	fValid = false;
293 	return fValid;
294 }
295 
296 
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.
299 */
300 bool
301 PartitionType::SetContentType(const char* contentType)
302 {
303 	for (int32 i = 0; kPartitionContentTypes[i].name ; i++) {
304 		if (!strcmp(contentType, kPartitionContentTypes[i].name)) {
305 			fType = kPartitionContentTypes[i].type;
306 			fValid = true;
307 			return fValid;
308 		}
309 	}
310 	fValid = false;
311 	return fValid;
312 }
313 
314 
315 /*!	\brief Finds next supported partition.
316 */
317 bool
318 PartitionType::FindNext()
319 {
320 	for (int32 i = 0; kPartitionTypes[i].name; i++) {
321 		if (fType < kPartitionTypes[i].type) {
322 			fType = kPartitionTypes[i].type;
323 			fValid = true;
324 			return true;
325 		}
326 	}
327 	fValid = false;
328 	return false;
329 }
330 
331 
332 /*!	\fn bool PartitionType::IsValid() const
333 	\brief Check whether the current type is valid.
334 */
335 
336 /*!	\fn bool PartitionType::IsEmpty() const
337 	\brief Check whether the current type describes empty type.
338 */
339 
340 /*!	\fn bool PartitionType::IsExtended() const
341 	\brief Check whether the current type describes extended partition type.
342 */
343 
344 /*!	\fn uint8 PartitionType::Type() const
345 	\brief Returns ID of the current type.
346 */
347 
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
351 	sufficient length.
352 */
353 
354 
355 //	#pragma mark - Partition
356 
357 
358 Partition::Partition()
359 	:
360 	fPartitionTableOffset(0),
361 	fOffset(0),
362 	fSize(0),
363 	fType(0),
364 	fActive(false)
365 {
366 }
367 
368 
369 Partition::Partition(const partition_descriptor* descriptor, off_t tableOffset,
370 		off_t baseOffset, uint32 blockSize)
371 	:
372 	fPartitionTableOffset(0),
373 	fOffset(0),
374 	fSize(0),
375 	fType(0),
376 	fActive(false)
377 {
378 	SetTo(descriptor, tableOffset, baseOffset, blockSize);
379 }
380 
381 
382 void
383 Partition::SetTo(const partition_descriptor* descriptor, off_t tableOffset,
384 	off_t baseOffset, uint32 blockSize)
385 {
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);
390 }
391 
392 
393 void
394 Partition::SetTo(off_t offset, off_t size, uint8 type, bool active,
395 	off_t tableOffset, uint32 blockSize)
396 {
397 	fPartitionTableOffset = tableOffset;
398 	fOffset = offset;
399 	fSize = size;
400 	fType = type;
401 	fActive = active;
402 	fBlockSize = blockSize;
403 
404 	if (fSize == 0)
405 		Unset();
406 }
407 
408 
409 void
410 Partition::Unset()
411 {
412 	fPartitionTableOffset = 0;
413 	fOffset = 0;
414 	fSize = 0;
415 	fType = 0;
416 	fActive = false;
417 }
418 
419 
420 bool
421 Partition::CheckLocation(off_t sessionSize) const
422 {
423 	if (fBlockSize == 0)
424 		return false;
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));
431 		return false;
432 	}
433 	if (fOffset % fBlockSize != 0) {
434 		TRACE(("Partition::CheckLocation() - bad offset: %lld "
435 			"(session: %lld)\n", fOffset, sessionSize));
436 		return false;
437 	}
438 	if (fSize % fBlockSize != 0) {
439 		TRACE(("Partition::CheckLocation() - bad size: %lld "
440 			"(session: %lld)\n", fSize, sessionSize));
441 		return false;
442 	}
443 	if (fPartitionTableOffset < 0 || fPartitionTableOffset >= sessionSize) {
444 		TRACE(("Partition::CheckLocation() - partition table offset outside "
445 			"session: %lld (session size: %lld)\n", fPartitionTableOffset,
446 			sessionSize));
447 		return false;
448 	}
449 	if (fOffset < 0) {
450 		TRACE(("Partition::CheckLocation() - offset before session: %lld "
451 			"(session: %lld)\n", fOffset, sessionSize));
452 		return false;
453 	}
454 	if (fOffset + fSize > sessionSize) {
455 		TRACE(("Partition::CheckLocation() - end after session: %lld "
456 			"(session: %lld)\n", fOffset + fSize, sessionSize));
457 		return false;
458 	}
459 	return true;
460 }
461 
462 
463 bool
464 Partition::FitSizeToSession(off_t sessionSize)
465 {
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;
474 		return true;
475 	}
476 
477 	return false;
478 }
479 
480 
481 //	#pragma mark - PrimaryPartition
482 
483 
484 PrimaryPartition::PrimaryPartition()
485 	:
486 	Partition(),
487 	fHead(NULL),
488 	fTail(NULL),
489 	fLogicalPartitionCount(0)
490 {
491 }
492 
493 
494 void
495 PrimaryPartition::SetTo(const partition_descriptor* descriptor,
496 	off_t tableOffset, uint32 blockSize)
497 {
498 	Unset();
499 	Partition::SetTo(descriptor, tableOffset, 0, blockSize);
500 }
501 
502 
503 void
504 PrimaryPartition::SetTo(off_t offset, off_t size, uint8 type, bool active,
505 	uint32 blockSize)
506 {
507 	Unset();
508 	Partition::SetTo(offset, size, type, active, 0, blockSize);
509 }
510 
511 
512 void
513 PrimaryPartition::Unset()
514 {
515 	while (LogicalPartition* partition = fHead) {
516 		fHead = partition->Next();
517 		delete partition;
518 	}
519 	fHead = NULL;
520 	fTail = NULL;
521 	fLogicalPartitionCount = 0;
522 	Partition::Unset();
523 }
524 
525 
526 status_t
527 PrimaryPartition::Assign(const PrimaryPartition& other)
528 {
529 	partition_descriptor descriptor;
530 	other.GetPartitionDescriptor(&descriptor);
531 	SetTo(&descriptor, 0, other.BlockSize());
532 
533 	const LogicalPartition* otherLogical = other.fHead;
534 	while (otherLogical) {
535 		off_t tableOffset = otherLogical->PartitionTableOffset();
536 		otherLogical->GetPartitionDescriptor(&descriptor);
537 
538 		LogicalPartition* logical = new(nothrow) LogicalPartition(
539 			&descriptor, tableOffset, this);
540 		if (!logical)
541 			return B_NO_MEMORY;
542 
543 		AddLogicalPartition(logical);
544 
545 		otherLogical = otherLogical->Next();
546 	}
547 
548 	return B_OK;
549 }
550 
551 
552 void
553 PrimaryPartition::GetPartitionDescriptor(partition_descriptor* descriptor) const
554 {
555 	if (IsEmpty()) {
556 		memset(descriptor, 0, sizeof(partition_descriptor));
557 	} else {
558 		descriptor->start = Offset() / BlockSize();
559 		descriptor->size = Size() / BlockSize();
560 		descriptor->type = Type();
561 		descriptor->active = Active() ? 0x80 : 0x00;
562 		descriptor->begin.SetUnused();
563 		descriptor->end.SetUnused();
564 	}
565 }
566 
567 
568 LogicalPartition*
569 PrimaryPartition::LogicalPartitionAt(int32 index) const
570 {
571 	LogicalPartition* partition = NULL;
572 	if (index >= 0 && index < fLogicalPartitionCount) {
573 		for (partition = fHead; index > 0; index--)
574 			partition = partition->Next();
575 	}
576 	return partition;
577 }
578 
579 
580 void
581 PrimaryPartition::AddLogicalPartition(LogicalPartition* partition)
582 {
583 	if (!partition)
584 		return;
585 
586 	partition->SetPrimaryPartition(this);
587 	partition->SetPrevious(fTail);
588 	if (fTail) {
589 		fTail->SetNext(partition);
590 		fTail = partition;
591 	} else
592 		fHead = fTail = partition;
593 
594 	partition->SetNext(NULL);
595 
596 	fLogicalPartitionCount++;
597 }
598 
599 
600 void
601 PrimaryPartition::RemoveLogicalPartition(LogicalPartition* partition)
602 {
603 	if (!partition || partition->GetPrimaryPartition() != this)
604 		return;
605 
606 	LogicalPartition* prev = partition->Previous();
607 	LogicalPartition* next = partition->Next();
608 
609 	if (prev)
610 		prev->SetNext(next);
611 	else
612 		fHead = next;
613 	if (next)
614 		next->SetPrevious(prev);
615 	else
616 		fTail = prev;
617 
618 	fLogicalPartitionCount--;
619 
620 	partition->SetNext(NULL);
621 	partition->SetPrevious(NULL);
622 	partition->SetPrimaryPartition(NULL);
623 }
624 
625 
626 //	#pragma mark - LogicalPartition
627 
628 
629 LogicalPartition::LogicalPartition()
630 	:
631 	Partition(),
632 	fPrimary(NULL),
633 	fNext(NULL),
634 	fPrevious(NULL)
635 {
636 }
637 
638 
639 LogicalPartition::LogicalPartition(const partition_descriptor* descriptor,
640 		off_t tableOffset, PrimaryPartition* primary)
641 	:
642 	Partition(),
643 	fPrimary(NULL),
644 	fNext(NULL),
645 	fPrevious(NULL)
646 {
647 	SetTo(descriptor, tableOffset, primary);
648 }
649 
650 
651 void
652 LogicalPartition::SetTo(const partition_descriptor* descriptor,
653 	off_t tableOffset, PrimaryPartition* primary)
654 {
655 	Unset();
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
667 		// baseOffset.
668 		off_t baseOffset = descriptor->is_extended()
669 			? primary->Offset() : tableOffset;
670 		Partition::SetTo(descriptor, tableOffset, baseOffset,
671 			primary->BlockSize());
672 		fPrimary = primary;
673 	}
674 }
675 
676 
677 void
678 LogicalPartition::SetTo(off_t offset, off_t size, uint8 type, bool active,
679 	off_t tableOffset, PrimaryPartition* primary)
680 {
681 	Unset();
682 	if (primary) {
683 		Partition::SetTo(offset, size, type, active, tableOffset,
684 			primary->BlockSize());
685 		fPrimary = primary;
686 	}
687 }
688 
689 
690 void
691 LogicalPartition::Unset()
692 {
693 	fPrimary = NULL;
694 	fNext = NULL;
695 	fPrevious = NULL;
696 	Partition::Unset();
697 }
698 
699 
700 void
701 LogicalPartition::GetPartitionDescriptor(partition_descriptor* descriptor,
702 	bool inner) const
703 {
704 	PrimaryPartition* primary = GetPrimaryPartition();
705 	if (inner) {
706 		descriptor->start = (PartitionTableOffset() - primary->Offset())
707 			/ BlockSize();
708 		descriptor->type = primary->Type();
709 	} else {
710 		descriptor->start = (Offset() - PartitionTableOffset()) / BlockSize();
711 		descriptor->type = Type();
712 	}
713 
714 	descriptor->size = Size() / BlockSize();
715 	descriptor->active = 0x00;
716 	descriptor->begin.SetUnused();
717 	descriptor->end.SetUnused();
718 }
719 
720 
721 //	#pragma mark - PartitionMap
722 
723 
724 PartitionMap::PartitionMap()
725 {
726 	for (int32 i = 0; i < 4; i++)
727 		fPrimaries[i].SetIndex(i);
728 }
729 
730 
731 PartitionMap::~PartitionMap()
732 {
733 }
734 
735 
736 void
737 PartitionMap::Unset()
738 {
739 	for (int32 i = 0; i < 4; i++)
740 		fPrimaries[i].Unset();
741 }
742 
743 
744 status_t
745 PartitionMap::Assign(const PartitionMap& other)
746 {
747 	for (int32 i = 0; i < 4; i++) {
748 		status_t error = fPrimaries[i].Assign(other.fPrimaries[i]);
749 		if (error != B_OK)
750 			return error;
751 	}
752 
753 	return B_OK;
754 }
755 
756 
757 PrimaryPartition*
758 PartitionMap::PrimaryPartitionAt(int32 index)
759 {
760 	PrimaryPartition* partition = NULL;
761 	if (index >= 0 && index < 4)
762 		partition = fPrimaries + index;
763 	return partition;
764 }
765 
766 
767 const PrimaryPartition*
768 PartitionMap::PrimaryPartitionAt(int32 index) const
769 {
770 	const PrimaryPartition* partition = NULL;
771 	if (index >= 0 && index < 4)
772 		partition = fPrimaries + index;
773 	return partition;
774 }
775 
776 
777 int32
778 PartitionMap::CountNonEmptyPrimaryPartitions() const
779 {
780 	int32 count = 0;
781 	for (int32 i = 0; i < 4; i++) {
782 		if (!fPrimaries[i].IsEmpty())
783 			count++;
784 	}
785 
786 	return count;
787 }
788 
789 
790 int32
791 PartitionMap::ExtendedPartitionIndex() const
792 {
793 	for (int32 i = 0; i < 4; i++) {
794 		if (fPrimaries[i].IsExtended())
795 			return i;
796 	}
797 
798 	return -1;
799 }
800 
801 
802 int32
803 PartitionMap::CountPartitions() const
804 {
805 	int32 count = 4;
806 	for (int32 i = 0; i < 4; i++)
807 		count += fPrimaries[i].CountLogicalPartitions();
808 	return count;
809 }
810 
811 
812 int32
813 PartitionMap::CountNonEmptyPartitions() const
814 {
815 	int32 count = 0;
816 	for (int32 i = CountPartitions() - 1; i >= 0; i--) {
817 		if (!PartitionAt(i)->IsEmpty())
818 			count++;
819 	}
820 
821 	return count;
822 }
823 
824 
825 Partition*
826 PartitionMap::PartitionAt(int32 index)
827 {
828 	Partition* partition = NULL;
829 	int32 count = CountPartitions();
830 	if (index >= 0 && index < count) {
831 		if (index < 4)
832 			partition  = fPrimaries + index;
833 		else {
834 			index -= 4;
835 			int32 primary = 0;
836 			while (index >= fPrimaries[primary].CountLogicalPartitions()) {
837 				index -= fPrimaries[primary].CountLogicalPartitions();
838 				primary++;
839 			}
840 			partition = fPrimaries[primary].LogicalPartitionAt(index);
841 		}
842 	}
843 	return partition;
844 }
845 
846 
847 const Partition*
848 PartitionMap::PartitionAt(int32 index) const
849 {
850 	return const_cast<PartitionMap*>(this)->PartitionAt(index);
851 }
852 
853 
854 bool
855 PartitionMap::Check(off_t sessionSize) const
856 {
857 	int32 partitionCount = CountPartitions();
858 
859 	// 1. check partition locations
860 	for (int32 i = 0; i < partitionCount; i++) {
861 		if (!PartitionAt(i)->CheckLocation(sessionSize))
862 			return false;
863 	}
864 
865 	// 2. check overlapping of partitions and location of partition tables
866 	bool result = true;
867 	Partition** byOffset = new(nothrow) Partition*[partitionCount];
868 	off_t* tableOffsets = new(nothrow) off_t[partitionCount - 3];
869 	if (byOffset && tableOffsets) {
870 		// fill the arrays
871 		int32 byOffsetCount = 0;
872 		int32 tableOffsetCount = 1;	// primary partition table
873 		tableOffsets[0] = 0;
874 		for (int32 i = 0; i < partitionCount; i++) {
875 			Partition* partition = (Partition*)PartitionAt(i);
876 			if (!partition->IsExtended())
877 				byOffset[byOffsetCount++] = partition;
878 
879 			// add only logical partition partition table locations
880 			if (i >= 4) {
881 				tableOffsets[tableOffsetCount++]
882 					= partition->PartitionTableOffset();
883 			}
884 		}
885 
886 		// sort the arrays
887 		qsort(byOffset, byOffsetCount, sizeof(Partition*),
888 			cmp_partition_offset);
889 		qsort(tableOffsets, tableOffsetCount, sizeof(off_t), cmp_offset);
890 
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"));
904 				} else {
905 					TRACE(("overlapping partitions! Setting partition %ld "
906 						"size to %lld\n", i - 1, previousSize));
907 					previousPartition->SetSize(previousSize);
908 				}
909 			}
910 			nextOffset = partition->Offset() + partition->Size();
911 		}
912 
913 		// check uniqueness of partition table offsets and whether they lie
914 		// outside of the non-extended partitions
915 		if (result) {
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"));
920 					result = false;
921 					break;
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"));
926 					result = false;
927 					break;
928 				}
929 			}
930 		}
931 	} else
932 		result = false;		// no memory: assume failure
933 
934 	// cleanup
935 	delete[] byOffset;
936 	delete[] tableOffsets;
937 
938 	return result;
939 }
940 
941 
942 const partition_type*
943 PartitionMap::GetNextSupportedPartitionType(uint32 index)
944 {
945 	if (index > (sizeof(kPartitionTypes) / sizeof(partition_type) - 2))
946 		return NULL;
947 
948 	return kPartitionTypes + index;
949 }
950