xref: /haiku/src/add-ons/kernel/partitioning_systems/intel/PartitionMapParser.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 
71 	fMap = map;
72 	fMap->Unset();
73 
74 	if (block) {
75 		const partition_table* table = (const partition_table*)block;
76 		error = _ParsePrimary(table);
77 	} else {
78 		partition_table table;
79 		error = _ReadPartitionTable(0, &table);
80 		if (error == B_OK)
81 			error = _ParsePrimary(&table);
82 	}
83 
84 	if (error == B_OK && !fMap->Check(fSessionSize))
85 		error = B_BAD_DATA;
86 
87 	fMap = NULL;
88 
89 	return error;
90 }
91 
92 
93 // _ParsePrimary
94 status_t
95 PartitionMapParser::_ParsePrimary(const partition_table* table)
96 {
97 	if (table == NULL)
98 		return B_BAD_VALUE;
99 
100 	// check the signature
101 	if (table->signature != kPartitionTableSectorSignature) {
102 		TRACE(("intel: _ParsePrimary(): invalid PartitionTable signature: %lx\n",
103 			(uint32)table->signature));
104 		return B_BAD_DATA;
105 	}
106 
107 	// examine the table
108 	for (int32 i = 0; i < 4; i++) {
109 		const partition_descriptor* descriptor = &table->table[i];
110 		PrimaryPartition* partition = fMap->PrimaryPartitionAt(i);
111 		partition->SetTo(descriptor, 0, fBlockSize);
112 
113 #ifdef _BOOT_MODE
114 		// work-around potential BIOS problems
115 		partition->AdjustSize(fSessionSize);
116 #endif
117 		// ignore, if location is bad
118 		if (!partition->CheckLocation(fSessionSize)) {
119 			TRACE(("intel: _ParsePrimary(): partition %ld: bad location, "
120 				"ignoring\n", i));
121 			partition->Unset();
122 		}
123 	}
124 
125 	// allocate a partition_table buffer
126 	fPartitionTable = new(nothrow) partition_table;
127 	if (fPartitionTable == NULL)
128 		return B_NO_MEMORY;
129 
130 	// parse extended partitions
131 	status_t error = B_OK;
132 	for (int32 i = 0; error == B_OK && i < 4; i++) {
133 		PrimaryPartition* primary = fMap->PrimaryPartitionAt(i);
134 		if (primary->IsExtended())
135 			error = _ParseExtended(primary, primary->Offset());
136 	}
137 
138 	// cleanup
139 	delete fPartitionTable;
140 	fPartitionTable = NULL;
141 
142 	return error;
143 }
144 
145 
146 // _ParseExtended
147 status_t
148 PartitionMapParser::_ParseExtended(PrimaryPartition* primary, off_t offset)
149 {
150 	status_t error = B_OK;
151 	int32 partitionCount = 0;
152 	while (error == B_OK) {
153 		// check for cycles
154 		if (++partitionCount > kMaxLogicalPartitionCount) {
155 			TRACE(("intel: _ParseExtended(): Maximal number of logical "
156 				   "partitions for extended partition reached. Cycle?\n"));
157 			error = B_BAD_DATA;
158 		}
159 
160 		// read the partition table
161 		if (error == B_OK)
162 			error = _ReadPartitionTable(offset);
163 
164 		// check the signature
165 		if (error == B_OK
166 			&& fPartitionTable->signature != kPartitionTableSectorSignature) {
167 			TRACE(("intel: _ParseExtended(): invalid partition table signature: "
168 				"%lx\n", (uint32)fPartitionTable->signature));
169 			error = B_BAD_DATA;
170 		}
171 
172 		// ignore the partition table, if any error occured till now
173 		if (error != B_OK) {
174 			TRACE(("intel: _ParseExtended(): ignoring this partition table\n"));
175 			error = B_OK;
176 			break;
177 		}
178 
179 		// Examine the table, there is exactly one extended and one
180 		// non-extended logical partition. All four table entries are
181 		// examined though. If there is no inner extended partition,
182 		// the end of the linked list is reached.
183 		// The first partition table describing both an "inner extended" parition
184 		// and a "data" partition (non extended and not empty) is the start
185 		// sector of the primary extended partition. The next partition table in
186 		// the linked list is the start sector of the inner extended partition
187 		// described in this partition table.
188 		LogicalPartition extended;
189 		LogicalPartition nonExtended;
190 		for (int32 i = 0; error == B_OK && i < 4; i++) {
191 			const partition_descriptor* descriptor = &fPartitionTable->table[i];
192 			if (descriptor->is_empty())
193 				continue;
194 
195 			LogicalPartition* partition = NULL;
196 			if (descriptor->is_extended()) {
197 				if (extended.IsEmpty()) {
198 					extended.SetTo(descriptor, offset, primary);
199 					partition = &extended;
200 				} else {
201 					// only one extended partition allowed
202 					error = B_BAD_DATA;
203 					TRACE(("intel: _ParseExtended(): "
204 						   "only one extended partition allowed\n"));
205 				}
206 			} else {
207 				if (nonExtended.IsEmpty()) {
208 					nonExtended.SetTo(descriptor, offset, primary);
209 					partition = &nonExtended;
210 				} else {
211 					// only one non-extended partition allowed
212 					error = B_BAD_DATA;
213 					TRACE(("intel: _ParseExtended(): only one "
214 						   "non-extended partition allowed\n"));
215 				}
216 			}
217 			if (partition == NULL)
218 				break;
219 #ifdef _BOOT_MODE
220 			// work-around potential BIOS problems
221 			partition->AdjustSize(fSessionSize);
222 #endif
223 			// check the partition's location
224 			if (!partition->CheckLocation(fSessionSize)) {
225 				error = B_BAD_DATA;
226 				TRACE(("intel: _ParseExtended(): Invalid partition "
227 					"location: pts: %lld, offset: %lld, size: %lld\n",
228 					partition->PartitionTableOffset(), partition->Offset(),
229 					partition->Size()));
230 			}
231 		}
232 
233 		// add non-extended partition to list
234 		if (error == B_OK && !nonExtended.IsEmpty()) {
235 			LogicalPartition* partition
236 				= new(nothrow) LogicalPartition(nonExtended);
237 			if (partition)
238 				primary->AddLogicalPartition(partition);
239 			else
240 				error = B_NO_MEMORY;
241 		}
242 
243 		// prepare to parse next extended/non-extended partition pair
244 		if (error == B_OK && !extended.IsEmpty())
245 			offset = extended.Offset();
246 		else
247 			break;
248 	}
249 
250 	return error;
251 }
252 
253 
254 // _ReadPartitionTable
255 status_t
256 PartitionMapParser::_ReadPartitionTable(off_t offset, partition_table* table)
257 {
258 	int32 toRead = sizeof(partition_table);
259 
260 	// check the offset
261 	if (offset < 0 || offset + toRead > fSessionSize) {
262 		TRACE(("intel: _ReadPartitionTable(): bad offset: %Ld\n", offset));
263 		return B_BAD_VALUE;
264 	}
265 
266 	if (table == NULL)
267 		table = fPartitionTable;
268 
269 	status_t error = B_OK;
270 
271 	// read
272 	if (read_pos(fDeviceFD, fSessionOffset + offset, table, toRead) != toRead) {
273 #ifndef _BOOT_MODE
274 		error = errno;
275 		if (error == B_OK)
276 			error = B_IO_ERROR;
277 #else
278 		error = B_IO_ERROR;
279 #endif
280 		TRACE(("intel: _ReadPartitionTable(): reading the partition table "
281 			"failed: %lx\n", error));
282 	}
283 	return error;
284 }
285 
286