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