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