xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/PartitionMapParser.cpp (revision 7eab6b486ebadb54ca3c306601f4b04dd92359fa)
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 
42 using std::nothrow;
43 
44 // Maximal number of logical partitions per extended partition we allow.
45 static const int32 kMaxLogicalPartitionCount = 128;
46 
47 // Constants used to verify if a disk uses a GPT
48 static const int32 kGPTSignatureSize = 8;
49 static const char kGPTSignature[8] = { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' };
50 
51 
52 // constructor
53 PartitionMapParser::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
67 PartitionMapParser::~PartitionMapParser()
68 {
69 }
70 
71 
72 // Parse
73 status_t
74 PartitionMapParser::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
131 status_t
132 PartitionMapParser::_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
186 status_t
187 PartitionMapParser::_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
294 status_t
295 PartitionMapParser::_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