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