1/*
2 * Copyright 2003-2009, 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#ifndef _USER_MODE
11#	include <KernelExport.h>
12#endif
13
14#include <errno.h>
15#include <stdio.h>
16#include <unistd.h>
17#include <string.h>
18
19#include <new>
20
21#include "PartitionMap.h"
22#include "PartitionMapParser.h"
23
24
25//#define TRACE_ENABLED
26#ifdef TRACE_ENABLED
27#	ifdef _USER_MODE
28#		define TRACE(x) printf x
29#	else
30#		define TRACE(x) dprintf x
31#	endif
32#else
33#	define TRACE(x) ;
34#endif
35
36#ifdef _USER_MODE
37#	define ERROR(x) printf x
38#else
39#	define ERROR(x) dprintf x
40#endif
41
42using std::nothrow;
43
44// Maximal number of logical partitions per extended partition we allow.
45static const int32 kMaxLogicalPartitionCount = 128;
46
47// Constants used to verify if a disk uses a GPT
48static const int32 kGPTSignatureSize = 8;
49static const char kGPTSignature[8] = { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' };
50
51
52// constructor
53PartitionMapParser::PartitionMapParser(int deviceFD, off_t sessionOffset,
54		off_t sessionSize, uint32 blockSize)
55	:
56	fDeviceFD(deviceFD),
57	fBlockSize(blockSize),
58	fSessionOffset(sessionOffset),
59	fSessionSize(sessionSize),
60	fPartitionTable(NULL),
61	fMap(NULL)
62{
63}
64
65
66// destructor
67PartitionMapParser::~PartitionMapParser()
68{
69}
70
71
72// Parse
73status_t
74PartitionMapParser::Parse(const uint8* block, PartitionMap* map)
75{
76	if (map == NULL)
77		return B_BAD_VALUE;
78
79	status_t error;
80	bool hadToReFitSize = false;
81
82	fMap = map;
83	fMap->Unset();
84
85	if (block) {
86		const partition_table* table = (const partition_table*)block;
87		error = _ParsePrimary(table, hadToReFitSize);
88	} else {
89		partition_table table;
90		error = _ReadPartitionTable(0, &table);
91		if (error == B_OK) {
92			error = _ParsePrimary(&table, hadToReFitSize);
93
94			if (fBlockSize != 512 && (hadToReFitSize
95					|| !fMap->Check(fSessionSize))) {
96				// This might be a fixed 512 byte MBR on a non-512 medium.
97				// We do that for the anyboot images for example. so retry
98				// with a fixed 512 block size and see if we get better
99				// results
100				int32 previousPartitionCount = fMap->CountNonEmptyPartitions();
101				uint32 previousBlockSize = fBlockSize;
102				TRACE(("intel: Parse(): trying with a fixed 512 block size\n"));
103
104				fBlockSize = 512;
105				fMap->Unset();
106				error = _ParsePrimary(&table, hadToReFitSize);
107
108				if (fMap->CountNonEmptyPartitions() < previousPartitionCount
109					|| error != B_OK || hadToReFitSize
110					|| !fMap->Check(fSessionSize)) {
111					// That didn't improve anything, let's revert.
112					TRACE(("intel: Parse(): try failed, reverting\n"));
113					fBlockSize = previousBlockSize;
114					fMap->Unset();
115					error = _ParsePrimary(&table, hadToReFitSize);
116				}
117			}
118		}
119	}
120
121	if (error == B_OK && !fMap->Check(fSessionSize))
122		error = B_BAD_DATA;
123
124	fMap = NULL;
125
126	return error;
127}
128
129
130// _ParsePrimary
131status_t
132PartitionMapParser::_ParsePrimary(const partition_table* table,
133	bool& hadToReFitSize)
134{
135	if (table == NULL)
136		return B_BAD_VALUE;
137
138	// check the signature
139	if (table->signature != kPartitionTableSectorSignature) {
140		TRACE(("intel: _ParsePrimary(): invalid PartitionTable signature: %lx\n",
141			(uint32)table->signature));
142		return B_BAD_DATA;
143	}
144
145	hadToReFitSize = false;
146
147	// examine the table
148	for (int32 i = 0; i < 4; i++) {
149		const partition_descriptor* descriptor = &table->table[i];
150		PrimaryPartition* partition = fMap->PrimaryPartitionAt(i);
151		partition->SetTo(descriptor, 0, fBlockSize);
152
153		// work-around potential BIOS/OS problems
154		hadToReFitSize |= partition->FitSizeToSession(fSessionSize);
155
156		// ignore, if location is bad
157		if (!partition->CheckLocation(fSessionSize)) {
158			TRACE(("intel: _ParsePrimary(): partition %ld: bad location, "
159				"ignoring\n", i));
160			partition->Unset();
161		}
162	}
163
164	// allocate a partition_table buffer
165	fPartitionTable = new(nothrow) partition_table;
166	if (fPartitionTable == NULL)
167		return B_NO_MEMORY;
168
169	// parse extended partitions
170	status_t error = B_OK;
171	for (int32 i = 0; error == B_OK && i < 4; i++) {
172		PrimaryPartition* primary = fMap->PrimaryPartitionAt(i);
173		if (primary->IsExtended())
174			error = _ParseExtended(primary, primary->Offset());
175	}
176
177	// cleanup
178	delete fPartitionTable;
179	fPartitionTable = NULL;
180
181	return error;
182}
183
184
185// _ParseExtended
186status_t
187PartitionMapParser::_ParseExtended(PrimaryPartition* primary, off_t offset)
188{
189	status_t error = B_OK;
190	int32 partitionCount = 0;
191	while (error == B_OK) {
192		// check for cycles
193		if (++partitionCount > kMaxLogicalPartitionCount) {
194			TRACE(("intel: _ParseExtended(): Maximal number of logical "
195				   "partitions for extended partition reached. Cycle?\n"));
196			error = B_BAD_DATA;
197		}
198
199		// read the partition table
200		if (error == B_OK)
201			error = _ReadPartitionTable(offset);
202
203		// check the signature
204		if (error == B_OK
205			&& fPartitionTable->signature != kPartitionTableSectorSignature) {
206			TRACE(("intel: _ParseExtended(): invalid partition table signature: "
207				"%lx\n", (uint32)fPartitionTable->signature));
208			error = B_BAD_DATA;
209		}
210
211		// ignore the partition table, if any error occured till now
212		if (error != B_OK) {
213			TRACE(("intel: _ParseExtended(): ignoring this partition table\n"));
214			error = B_OK;
215			break;
216		}
217
218		// Examine the table, there is exactly one extended and one
219		// non-extended logical partition. All four table entries are
220		// examined though. If there is no inner extended partition,
221		// the end of the linked list is reached.
222		// The first partition table describing both an "inner extended" parition
223		// and a "data" partition (non extended and not empty) is the start
224		// sector of the primary extended partition. The next partition table in
225		// the linked list is the start sector of the inner extended partition
226		// described in this partition table.
227		LogicalPartition extended;
228		LogicalPartition nonExtended;
229		for (int32 i = 0; error == B_OK && i < 4; i++) {
230			const partition_descriptor* descriptor = &fPartitionTable->table[i];
231			if (descriptor->is_empty())
232				continue;
233
234			LogicalPartition* partition = NULL;
235			if (descriptor->is_extended()) {
236				if (extended.IsEmpty()) {
237					extended.SetTo(descriptor, offset, primary);
238					partition = &extended;
239				} else {
240					// only one extended partition allowed
241					error = B_BAD_DATA;
242					TRACE(("intel: _ParseExtended(): "
243						   "only one extended partition allowed\n"));
244				}
245			} else {
246				if (nonExtended.IsEmpty()) {
247					nonExtended.SetTo(descriptor, offset, primary);
248					partition = &nonExtended;
249				} else {
250					// only one non-extended partition allowed
251					error = B_BAD_DATA;
252					TRACE(("intel: _ParseExtended(): only one "
253						   "non-extended partition allowed\n"));
254				}
255			}
256			if (partition == NULL)
257				break;
258
259			// work-around potential BIOS/OS problems
260			partition->FitSizeToSession(fSessionSize);
261
262			// check the partition's location
263			if (!partition->CheckLocation(fSessionSize)) {
264				error = B_BAD_DATA;
265				TRACE(("intel: _ParseExtended(): Invalid partition "
266					"location: pts: %lld, offset: %lld, size: %lld\n",
267					partition->PartitionTableOffset(), partition->Offset(),
268					partition->Size()));
269			}
270		}
271
272		// add non-extended partition to list
273		if (error == B_OK && !nonExtended.IsEmpty()) {
274			LogicalPartition* partition
275				= new(nothrow) LogicalPartition(nonExtended);
276			if (partition)
277				primary->AddLogicalPartition(partition);
278			else
279				error = B_NO_MEMORY;
280		}
281
282		// prepare to parse next extended/non-extended partition pair
283		if (error == B_OK && !extended.IsEmpty())
284			offset = extended.Offset();
285		else
286			break;
287	}
288
289	return error;
290}
291
292
293// _ReadPartitionTable
294status_t
295PartitionMapParser::_ReadPartitionTable(off_t offset, partition_table* table)
296{
297	int32 toRead = sizeof(partition_table);
298
299	// check the offset
300	if (offset < 0 || offset + toRead > fSessionSize) {
301		TRACE(("intel: _ReadPartitionTable(): bad offset: %lld\n", offset));
302		return B_BAD_VALUE;
303	}
304
305	if (table == NULL)
306		table = fPartitionTable;
307
308	// Read the partition table from the device into the table structure
309	ssize_t bytesRead = read_pos(fDeviceFD, fSessionOffset + offset,
310		table, toRead);
311	if (bytesRead != (ssize_t)toRead) {
312		TRACE(("intel: _ReadPartitionTable(): reading the partition "
313			"table failed: %lx\n", errno));
314		return bytesRead < 0 ? errno : B_IO_ERROR;
315	}
316
317	// check for GPT signature "EFI PART"
318	// located in the 8bytes following the mbr
319	toRead = kGPTSignatureSize;
320	char gptSignature[8];
321	bytesRead = read_pos(fDeviceFD, fSessionOffset + offset
322		+ sizeof(partition_table), &gptSignature, toRead);
323	if (bytesRead != (ssize_t)toRead) {
324		TRACE(("intel: _ReadPartitionTable(): checking for GPT "
325			"signature failed: %lx\n", errno));
326		return bytesRead < 0 ? errno : B_IO_ERROR;
327	}
328	if (memcmp(gptSignature, kGPTSignature, kGPTSignatureSize) == 0) {
329		ERROR(("intel: Found GPT signature, ignoring.\n"));
330		return B_BAD_DATA;
331	}
332
333	return B_OK;
334}
335
336