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