vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / partitioning_systems / intel / PartitionMapParser.cpp
blob98b640ccb0056b0c99d66cb84438695afe468b2a
1 /*
2 * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold, bonefish@cs.tu-berlin.de
7 */
10 #ifndef _USER_MODE
11 # include <KernelExport.h>
12 #endif
14 #include <errno.h>
15 #include <stdio.h>
16 #include <unistd.h>
17 #include <string.h>
19 #include <new>
21 #include "PartitionMap.h"
22 #include "PartitionMapParser.h"
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
36 #ifdef _USER_MODE
37 # define ERROR(x) printf x
38 #else
39 # define ERROR(x) dprintf x
40 #endif
42 using std::nothrow;
44 // Maximal number of logical partitions per extended partition we allow.
45 static const int32 kMaxLogicalPartitionCount = 128;
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' };
52 // constructor
53 PartitionMapParser::PartitionMapParser(int deviceFD, off_t sessionOffset,
54 off_t sessionSize, uint32 blockSize)
56 fDeviceFD(deviceFD),
57 fBlockSize(blockSize),
58 fSessionOffset(sessionOffset),
59 fSessionSize(sessionSize),
60 fPartitionTable(NULL),
61 fMap(NULL)
66 // destructor
67 PartitionMapParser::~PartitionMapParser()
72 // Parse
73 status_t
74 PartitionMapParser::Parse(const uint8* block, PartitionMap* map)
76 if (map == NULL)
77 return B_BAD_VALUE;
79 status_t error;
80 bool hadToReFitSize = false;
82 fMap = map;
83 fMap->Unset();
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);
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"));
104 fBlockSize = 512;
105 fMap->Unset();
106 error = _ParsePrimary(&table, hadToReFitSize);
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);
121 if (error == B_OK && !fMap->Check(fSessionSize))
122 error = B_BAD_DATA;
124 fMap = NULL;
126 return error;
130 // _ParsePrimary
131 status_t
132 PartitionMapParser::_ParsePrimary(const partition_table* table,
133 bool& hadToReFitSize)
135 if (table == NULL)
136 return B_BAD_VALUE;
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;
145 hadToReFitSize = false;
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);
153 // work-around potential BIOS/OS problems
154 hadToReFitSize |= partition->FitSizeToSession(fSessionSize);
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();
164 // allocate a partition_table buffer
165 fPartitionTable = new(nothrow) partition_table;
166 if (fPartitionTable == NULL)
167 return B_NO_MEMORY;
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());
177 // cleanup
178 delete fPartitionTable;
179 fPartitionTable = NULL;
181 return error;
185 // _ParseExtended
186 status_t
187 PartitionMapParser::_ParseExtended(PrimaryPartition* primary, off_t offset)
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;
199 // read the partition table
200 if (error == B_OK)
201 error = _ReadPartitionTable(offset);
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;
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;
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;
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"));
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"));
256 if (partition == NULL)
257 break;
259 // work-around potential BIOS/OS problems
260 partition->FitSizeToSession(fSessionSize);
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()));
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;
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;
289 return error;
293 // _ReadPartitionTable
294 status_t
295 PartitionMapParser::_ReadPartitionTable(off_t offset, partition_table* table)
297 int32 toRead = sizeof(partition_table);
299 // check the offset
300 if (offset < 0 || offset + toRead > fSessionSize) {
301 TRACE(("intel: _ReadPartitionTable(): bad offset: %Ld\n", offset));
302 return B_BAD_VALUE;
305 if (table == NULL)
306 table = fPartitionTable;
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;
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;
328 if (memcmp(gptSignature, kGPTSignature, kGPTSignatureSize) == 0) {
329 ERROR(("intel: Found GPT signature, ignoring.\n"));
330 return B_BAD_DATA;
333 return B_OK;