1 /* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition 2 data. */ 3 4 /* Initial coding by Rod Smith, January to February, 2009 */ 5 6 /* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed 7 under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ 8 9 #define __STDC_LIMIT_MACROS 10 #ifndef __STDC_CONSTANT_MACROS 11 #define __STDC_CONSTANT_MACROS 12 #endif 13 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <stdint.h> 17 #include <fcntl.h> 18 #include <string.h> 19 #include <time.h> 20 #include <sys/stat.h> 21 #include <errno.h> 22 #include <iostream> 23 #include <algorithm> 24 #include "mbr.h" 25 #include "support.h" 26 27 using namespace std; 28 29 /**************************************** 30 * * 31 * MBRData class and related structures * 32 * * 33 ****************************************/ 34 35 BasicMBRData::BasicMBRData(void) { 36 blockSize = SECTOR_SIZE; 37 diskSize = 0; 38 device = ""; 39 state = invalid; 40 numHeads = MAX_HEADS; 41 numSecspTrack = MAX_SECSPERTRACK; 42 myDisk = NULL; 43 canDeleteMyDisk = 0; 44 // memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t)); 45 EmptyMBR(); 46 } // BasicMBRData default constructor 47 48 BasicMBRData::BasicMBRData(string filename) { 49 blockSize = SECTOR_SIZE; 50 diskSize = 0; 51 device = filename; 52 state = invalid; 53 numHeads = MAX_HEADS; 54 numSecspTrack = MAX_SECSPERTRACK; 55 myDisk = NULL; 56 canDeleteMyDisk = 0; 57 // memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t)); 58 59 // Try to read the specified partition table, but if it fails.... 60 if (!ReadMBRData(filename)) { 61 EmptyMBR(); 62 device = ""; 63 } // if 64 } // BasicMBRData(string filename) constructor 65 66 // Free space used by myDisk only if that's OK -- sometimes it will be 67 // copied from an outside source, in which case that source should handle 68 // it! 69 BasicMBRData::~BasicMBRData(void) { 70 if (canDeleteMyDisk) 71 delete myDisk; 72 } // BasicMBRData destructor 73 74 // Assignment operator -- copy entire set of MBR data. 75 BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) { 76 int i; 77 78 memcpy(code, orig.code, 440); 79 diskSignature = orig.diskSignature; 80 nulls = orig.nulls; 81 MBRSignature = orig.MBRSignature; 82 blockSize = orig.blockSize; 83 diskSize = orig.diskSize; 84 numHeads = orig.numHeads; 85 numSecspTrack = orig.numSecspTrack; 86 canDeleteMyDisk = orig.canDeleteMyDisk; 87 device = orig.device; 88 state = orig.state; 89 90 myDisk = new DiskIO; 91 if (myDisk == NULL) { 92 cerr << "Unable to allocate memory in BasicMBRData::operator=()! Terminating!\n"; 93 exit(1); 94 } // if 95 if (orig.myDisk != NULL) 96 myDisk->OpenForRead(orig.myDisk->GetName()); 97 98 for (i = 0; i < MAX_MBR_PARTS; i++) { 99 partitions[i] = orig.partitions[i]; 100 } // for 101 return *this; 102 } // BasicMBRData::operator=() 103 104 /********************** 105 * * 106 * Disk I/O functions * 107 * * 108 **********************/ 109 110 // Read data from MBR. Returns 1 if read was successful (even if the 111 // data isn't a valid MBR), 0 if the read failed. 112 int BasicMBRData::ReadMBRData(const string & deviceFilename) { 113 int allOK = 1; 114 115 if (myDisk == NULL) { 116 myDisk = new DiskIO; 117 if (myDisk == NULL) { 118 cerr << "Unable to allocate memory in BasicMBRData::ReadMBRData()! Terminating!\n"; 119 exit(1); 120 } // if 121 canDeleteMyDisk = 1; 122 } // if 123 if (myDisk->OpenForRead(deviceFilename)) { 124 allOK = ReadMBRData(myDisk); 125 } else { 126 allOK = 0; 127 } // if 128 129 if (allOK) 130 device = deviceFilename; 131 132 return allOK; 133 } // BasicMBRData::ReadMBRData(const string & deviceFilename) 134 135 // Read data from MBR. If checkBlockSize == 1 (the default), the block 136 // size is checked; otherwise it's set to the default (512 bytes). 137 // Note that any extended partition(s) present will be omitted from 138 // in the partitions[] array; these partitions must be re-created when 139 // the partition table is saved in MBR format. 140 int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) { 141 int allOK = 1, i, logicalNum = 3; 142 int err = 1; 143 TempMBR tempMBR; 144 145 if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) { 146 delete myDisk; 147 canDeleteMyDisk = 0; 148 } // if 149 150 myDisk = theDisk; 151 152 // Empty existing MBR data, including the logical partitions... 153 EmptyMBR(0); 154 155 if (myDisk->Seek(0)) 156 if (myDisk->Read(&tempMBR, 512)) 157 err = 0; 158 if (err) { 159 cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n"; 160 } else { 161 for (i = 0; i < 440; i++) 162 code[i] = tempMBR.code[i]; 163 diskSignature = tempMBR.diskSignature; 164 nulls = tempMBR.nulls; 165 for (i = 0; i < 4; i++) { 166 partitions[i] = tempMBR.partitions[i]; 167 if (partitions[i].GetLengthLBA() > 0) 168 partitions[i].SetInclusion(PRIMARY); 169 } // for i... (reading all four partitions) 170 MBRSignature = tempMBR.MBRSignature; 171 ReadCHSGeom(); 172 173 // Reverse the byte order, if necessary 174 if (IsLittleEndian() == 0) { 175 ReverseBytes(&diskSignature, 4); 176 ReverseBytes(&nulls, 2); 177 ReverseBytes(&MBRSignature, 2); 178 for (i = 0; i < 4; i++) { 179 partitions[i].ReverseByteOrder(); 180 } // for 181 } // if 182 183 if (MBRSignature != MBR_SIGNATURE) { 184 allOK = 0; 185 state = invalid; 186 } // if 187 188 // Find disk size 189 diskSize = myDisk->DiskSize(&err); 190 191 // Find block size 192 if (checkBlockSize) { 193 blockSize = myDisk->GetBlockSize(); 194 } // if (checkBlockSize) 195 196 // Load logical partition data, if any is found.... 197 if (allOK) { 198 for (i = 0; i < 4; i++) { 199 if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f) 200 || (partitions[i].GetType() == 0x85)) { 201 // Found it, so call a function to load everything from them.... 202 logicalNum = ReadLogicalParts(partitions[i].GetStartLBA(), abs(logicalNum) + 1); 203 if (logicalNum < 0) { 204 cerr << "Error reading logical partitions! List may be truncated!\n"; 205 } // if maxLogicals valid 206 DeletePartition(i); 207 } // if primary partition is extended 208 } // for primary partition loop 209 if (allOK) { // Loaded logicals OK 210 state = mbr; 211 } else { 212 state = invalid; 213 } // if 214 } // if 215 216 // Check to see if it's in GPT format.... 217 if (allOK) { 218 for (i = 0; i < 4; i++) { 219 if (partitions[i].GetType() == UINT8_C(0xEE)) { 220 state = gpt; 221 } // if 222 } // for 223 } // if 224 225 // If there's an EFI GPT partition, look for other partition types, 226 // to flag as hybrid 227 if (state == gpt) { 228 for (i = 0 ; i < 4; i++) { 229 if ((partitions[i].GetType() != UINT8_C(0xEE)) && 230 (partitions[i].GetType() != UINT8_C(0x00))) 231 state = hybrid; 232 if (logicalNum != 3) 233 cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n" 234 << "EXTREMELY dangerous configuration!\n\a"; 235 } // for 236 } // if (hybrid detection code) 237 } // no initial error 238 return allOK; 239 } // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) 240 241 // This is a function to read all the logical partitions, following the 242 // logical partition linked list from the disk and storing the basic data in the 243 // partitions[] array. Returns last index to partitions[] used, or -1 times the 244 // that index if there was a problem. (Some problems can leave valid logical 245 // partition data.) 246 // Parameters: 247 // extendedStart = LBA of the start of the extended partition 248 // partNum = number of first partition in extended partition (normally 4). 249 int BasicMBRData::ReadLogicalParts(uint64_t extendedStart, int partNum) { 250 struct TempMBR ebr; 251 int i, another = 1, allOK = 1; 252 uint8_t ebrType; 253 uint64_t offset; 254 uint64_t EbrLocations[MAX_MBR_PARTS]; 255 256 offset = extendedStart; 257 memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint64_t)); 258 while (another && (partNum < MAX_MBR_PARTS) && (partNum >= 0) && (allOK > 0)) { 259 for (i = 0; i < MAX_MBR_PARTS; i++) { 260 if (EbrLocations[i] == offset) { // already read this one; infinite logical partition loop! 261 cerr << "Logical partition infinite loop detected! This is being corrected.\n"; 262 allOK = -1; 263 partNum -= 1; 264 } // if 265 } // for 266 EbrLocations[partNum] = offset; 267 if (myDisk->Seek(offset) == 0) { // seek to EBR record 268 cerr << "Unable to seek to " << offset << "! Aborting!\n"; 269 allOK = -1; 270 } 271 if (myDisk->Read(&ebr, 512) != 512) { // Load the data.... 272 cerr << "Error seeking to or reading logical partition data from " << offset 273 << "!\nSome logical partitions may be missing!\n"; 274 allOK = -1; 275 } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data.... 276 ReverseBytes(&ebr.MBRSignature, 2); 277 ReverseBytes(&ebr.partitions[0].firstLBA, 4); 278 ReverseBytes(&ebr.partitions[0].lengthLBA, 4); 279 ReverseBytes(&ebr.partitions[1].firstLBA, 4); 280 ReverseBytes(&ebr.partitions[1].lengthLBA, 4); 281 } // if/else/if 282 283 if (ebr.MBRSignature != MBR_SIGNATURE) { 284 allOK = -1; 285 cerr << "EBR signature for logical partition invalid; read 0x"; 286 cerr.fill('0'); 287 cerr.width(4); 288 cerr.setf(ios::uppercase); 289 cerr << hex << ebr.MBRSignature << ", but should be 0x"; 290 cerr.width(4); 291 cerr << MBR_SIGNATURE << dec << "\n"; 292 cerr.fill(' '); 293 } // if 294 295 if ((partNum >= 0) && (partNum < MAX_MBR_PARTS) && (allOK > 0)) { 296 // Sometimes an EBR points directly to another EBR, rather than defining 297 // a logical partition and then pointing to another EBR. Thus, we skip 298 // the logical partition when this is the case.... 299 ebrType = ebr.partitions[0].partitionType; 300 if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) { 301 cout << "EBR describes a logical partition!\n"; 302 offset = extendedStart + ebr.partitions[0].firstLBA; 303 } else { 304 // Copy over the basic data.... 305 partitions[partNum] = ebr.partitions[0]; 306 // Adjust the start LBA, since it's encoded strangely.... 307 partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + offset); 308 partitions[partNum].SetInclusion(LOGICAL); 309 310 // Find the next partition (if there is one) 311 if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum < (MAX_MBR_PARTS - 1))) { 312 offset = extendedStart + ebr.partitions[1].firstLBA; 313 partNum++; 314 } else { 315 another = 0; 316 } // if another partition 317 } // if/else 318 } // if 319 } // while() 320 return (partNum * allOK); 321 } // BasicMBRData::ReadLogicalPart() 322 323 // Write the MBR data to the default defined device. This writes both the 324 // MBR itself and any defined logical partitions, provided there's an 325 // MBR extended partition. 326 int BasicMBRData::WriteMBRData(void) { 327 int allOK = 1; 328 329 if (myDisk != NULL) { 330 if (myDisk->OpenForWrite() != 0) { 331 allOK = WriteMBRData(myDisk); 332 cout << "Done writing data!\n"; 333 } else { 334 allOK = 0; 335 } // if/else 336 myDisk->Close(); 337 } else allOK = 0; 338 return allOK; 339 } // BasicMBRData::WriteMBRData(void) 340 341 // Save the MBR data to a file. This writes both the 342 // MBR itself and any defined logical partitions. 343 int BasicMBRData::WriteMBRData(DiskIO *theDisk) { 344 int i, j, partNum, next, allOK = 1, moreLogicals = 0; 345 uint64_t extFirstLBA = 0; 346 uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range 347 TempMBR tempMBR; 348 349 allOK = CreateExtended(); 350 if (allOK) { 351 // First write the main MBR data structure.... 352 memcpy(tempMBR.code, code, 440); 353 tempMBR.diskSignature = diskSignature; 354 tempMBR.nulls = nulls; 355 tempMBR.MBRSignature = MBRSignature; 356 for (i = 0; i < 4; i++) { 357 partitions[i].StoreInStruct(&tempMBR.partitions[i]); 358 if (partitions[i].GetType() == 0x0f) { 359 extFirstLBA = partitions[i].GetStartLBA(); 360 moreLogicals = 1; 361 } // if 362 } // for i... 363 } // if 364 allOK = allOK && WriteMBRData(tempMBR, theDisk, 0); 365 366 // Set up tempMBR with some constant data for logical partitions... 367 tempMBR.diskSignature = 0; 368 for (i = 2; i < 4; i++) { 369 tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0; 370 tempMBR.partitions[i].partitionType = 0x00; 371 for (j = 0; j < 3; j++) { 372 tempMBR.partitions[i].firstSector[j] = 0; 373 tempMBR.partitions[i].lastSector[j] = 0; 374 } // for j 375 } // for i 376 377 partNum = FindNextInUse(4); 378 writeEbrTo = (uint64_t) extFirstLBA; 379 // Write logicals... 380 while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS) && (partNum >= 0)) { 381 partitions[partNum].StoreInStruct(&tempMBR.partitions[0]); 382 tempMBR.partitions[0].firstLBA = 1; 383 // tempMBR.partitions[1] points to next EBR or terminates EBR linked list... 384 next = FindNextInUse(partNum + 1); 385 if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) { 386 tempMBR.partitions[1].partitionType = 0x0f; 387 tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1); 388 tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 1); 389 LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA, 390 (uint8_t *) &tempMBR.partitions[1].firstSector); 391 LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA, 392 (uint8_t *) &tempMBR.partitions[1].lastSector); 393 } else { 394 tempMBR.partitions[1].partitionType = 0x00; 395 tempMBR.partitions[1].firstLBA = 0; 396 tempMBR.partitions[1].lengthLBA = 0; 397 moreLogicals = 0; 398 } // if/else 399 allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo); 400 writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA; 401 partNum = next; 402 } // while 403 DeleteExtendedParts(); 404 return allOK; 405 } // BasicMBRData::WriteMBRData(DiskIO *theDisk) 406 407 int BasicMBRData::WriteMBRData(const string & deviceFilename) { 408 device = deviceFilename; 409 return WriteMBRData(); 410 } // BasicMBRData::WriteMBRData(const string & deviceFilename) 411 412 // Write a single MBR record to the specified sector. Used by the like-named 413 // function to write both the MBR and multiple EBR (for logical partition) 414 // records. 415 // Returns 1 on success, 0 on failure 416 int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) { 417 int i, allOK; 418 419 // Reverse the byte order, if necessary 420 if (IsLittleEndian() == 0) { 421 ReverseBytes(&mbr.diskSignature, 4); 422 ReverseBytes(&mbr.nulls, 2); 423 ReverseBytes(&mbr.MBRSignature, 2); 424 for (i = 0; i < 4; i++) { 425 ReverseBytes(&mbr.partitions[i].firstLBA, 4); 426 ReverseBytes(&mbr.partitions[i].lengthLBA, 4); 427 } // for 428 } // if 429 430 // Now write the data structure... 431 allOK = theDisk->OpenForWrite(); 432 if (allOK && theDisk->Seek(sector)) { 433 if (theDisk->Write(&mbr, 512) != 512) { 434 allOK = 0; 435 cerr << "Error " << errno << " when saving MBR!\n"; 436 } // if 437 } else { 438 allOK = 0; 439 cerr << "Error " << errno << " when seeking to MBR to write it!\n"; 440 } // if/else 441 theDisk->Close(); 442 443 // Reverse the byte order back, if necessary 444 if (IsLittleEndian() == 0) { 445 ReverseBytes(&mbr.diskSignature, 4); 446 ReverseBytes(&mbr.nulls, 2); 447 ReverseBytes(&mbr.MBRSignature, 2); 448 for (i = 0; i < 4; i++) { 449 ReverseBytes(&mbr.partitions[i].firstLBA, 4); 450 ReverseBytes(&mbr.partitions[i].lengthLBA, 4); 451 } // for 452 }// if 453 return allOK; 454 } // BasicMBRData::WriteMBRData(uint64_t sector) 455 456 // Set a new disk device; used in copying one disk's partition 457 // table to another disk. 458 void BasicMBRData::SetDisk(DiskIO *theDisk) { 459 int err; 460 461 myDisk = theDisk; 462 diskSize = theDisk->DiskSize(&err); 463 canDeleteMyDisk = 0; 464 ReadCHSGeom(); 465 } // BasicMBRData::SetDisk() 466 467 /******************************************** 468 * * 469 * Functions that display data for the user * 470 * * 471 ********************************************/ 472 473 // Show the MBR data to the user, up to the specified maximum number 474 // of partitions.... 475 void BasicMBRData::DisplayMBRData(void) { 476 int i; 477 478 cout << "\nDisk size is " << diskSize << " sectors (" 479 << BytesToIeee(diskSize, blockSize) << ")\n"; 480 cout << "MBR disk identifier: 0x"; 481 cout.width(8); 482 cout.fill('0'); 483 cout.setf(ios::uppercase); 484 cout << hex << diskSignature << dec << "\n"; 485 cout << "MBR partitions:\n\n"; 486 if ((state == gpt) || (state == hybrid)) { 487 cout << "Number Boot Start Sector End Sector Status Code\n"; 488 } else { 489 cout << " Can Be Can Be\n"; 490 cout << "Number Boot Start Sector End Sector Status Logical Primary Code\n"; 491 UpdateCanBeLogical(); 492 } // 493 for (i = 0; i < MAX_MBR_PARTS; i++) { 494 if (partitions[i].GetLengthLBA() != 0) { 495 cout.fill(' '); 496 cout.width(4); 497 cout << i + 1 << " "; 498 partitions[i].ShowData((state == gpt) || (state == hybrid)); 499 } // if 500 cout.fill(' '); 501 } // for 502 } // BasicMBRData::DisplayMBRData() 503 504 // Displays the state, as a word, on stdout. Used for debugging & to 505 // tell the user about the MBR state when the program launches.... 506 void BasicMBRData::ShowState(void) { 507 switch (state) { 508 case invalid: 509 cout << " MBR: not present\n"; 510 break; 511 case gpt: 512 cout << " MBR: protective\n"; 513 break; 514 case hybrid: 515 cout << " MBR: hybrid\n"; 516 break; 517 case mbr: 518 cout << " MBR: MBR only\n"; 519 break; 520 default: 521 cout << "\a MBR: unknown -- bug!\n"; 522 break; 523 } // switch 524 } // BasicMBRData::ShowState() 525 526 /************************ 527 * * 528 * GPT Checks and fixes * 529 * * 530 ************************/ 531 532 // Perform a very rudimentary check for GPT data on the disk; searches for 533 // the GPT signature in the main and backup metadata areas. 534 // Returns 0 if GPT data not found, 1 if main data only is found, 2 if 535 // backup only is found, 3 if both main and backup data are found, and 536 // -1 if a disk error occurred. 537 int BasicMBRData::CheckForGPT(void) { 538 int retval = 0, err; 539 char signature1[9], signature2[9]; 540 541 if (myDisk != NULL) { 542 if (myDisk->OpenForRead() != 0) { 543 if (myDisk->Seek(1)) { 544 myDisk->Read(signature1, 8); 545 signature1[8] = '\0'; 546 } else retval = -1; 547 if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) { 548 myDisk->Read(signature2, 8); 549 signature2[8] = '\0'; 550 } else retval = -1; 551 if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0)) 552 retval += 1; 553 if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0)) 554 retval += 2; 555 } else { 556 retval = -1; 557 } // if/else 558 myDisk->Close(); 559 } else retval = -1; 560 return retval; 561 } // BasicMBRData::CheckForGPT() 562 563 // Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk, 564 // but only if GPT data are verified on the disk, and only for the sector(s) 565 // with GPT signatures. 566 // Returns 1 if operation completes successfully, 0 if not (returns 1 if 567 // no GPT data are found on the disk). 568 int BasicMBRData::BlankGPTData(void) { 569 int allOK = 1, err; 570 uint8_t blank[512]; 571 572 memset(blank, 0, 512); 573 switch (CheckForGPT()) { 574 case -1: 575 allOK = 0; 576 break; 577 case 0: 578 break; 579 case 1: 580 if ((myDisk != NULL) && (myDisk->OpenForWrite())) { 581 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512))) 582 allOK = 0; 583 myDisk->Close(); 584 } else allOK = 0; 585 break; 586 case 2: 587 if ((myDisk != NULL) && (myDisk->OpenForWrite())) { 588 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) && 589 (myDisk->Write(blank, 512) == 512))) 590 allOK = 0; 591 myDisk->Close(); 592 } else allOK = 0; 593 break; 594 case 3: 595 if ((myDisk != NULL) && (myDisk->OpenForWrite())) { 596 if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512))) 597 allOK = 0; 598 if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) && 599 (myDisk->Write(blank, 512) == 512))) 600 allOK = 0; 601 myDisk->Close(); 602 } else allOK = 0; 603 break; 604 default: 605 break; 606 } // switch() 607 return allOK; 608 } // BasicMBRData::BlankGPTData 609 610 /********************************************************************* 611 * * 612 * Functions that set or get disk metadata (CHS geometry, disk size, * 613 * etc.) * 614 * * 615 *********************************************************************/ 616 617 // Read the CHS geometry using OS calls, or if that fails, set to 618 // the most common value for big disks (255 heads, 63 sectors per 619 // track, & however many cylinders that computes to). 620 void BasicMBRData::ReadCHSGeom(void) { 621 int err; 622 623 numHeads = myDisk->GetNumHeads(); 624 numSecspTrack = myDisk->GetNumSecsPerTrack(); 625 diskSize = myDisk->DiskSize(&err); 626 blockSize = myDisk->GetBlockSize(); 627 partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize); 628 } // BasicMBRData::ReadCHSGeom() 629 630 // Find the low and high used partition numbers (numbered from 0). 631 // Return value is the number of partitions found. Note that the 632 // *low and *high values are both set to 0 when no partitions 633 // are found, as well as when a single partition in the first 634 // position exists. Thus, the return value is the only way to 635 // tell when no partitions exist. 636 int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) { 637 uint32_t i; 638 int numFound = 0; 639 640 *low = MAX_MBR_PARTS + 1; // code for "not found" 641 *high = 0; 642 for (i = 0; i < MAX_MBR_PARTS; i++) { 643 if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists 644 *high = i; // since we're counting up, set the high value 645 // Set the low value only if it's not yet found... 646 if (*low == (MAX_MBR_PARTS + 1)) 647 *low = i; 648 numFound++; 649 } // if 650 } // for 651 652 // Above will leave *low pointing to its "not found" value if no partitions 653 // are defined, so reset to 0 if this is the case.... 654 if (*low == (MAX_MBR_PARTS + 1)) 655 *low = 0; 656 return numFound; 657 } // GPTData::GetPartRange() 658 659 // Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion 660 // was within the range that can be expressed by CHS (including 0, for an 661 // empty partition), 0 if the value is outside that range, and -1 if chs is 662 // invalid. 663 int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) { 664 uint64_t cylinder, head, sector; // all numbered from 0 665 uint64_t remainder; 666 int retval = 1; 667 int done = 0; 668 669 if (chs != NULL) { 670 // Special case: In case of 0 LBA value, zero out CHS values.... 671 if (lba == 0) { 672 chs[0] = chs[1] = chs[2] = UINT8_C(0); 673 done = 1; 674 } // if 675 // If LBA value is too large for CHS, max out CHS values.... 676 if ((!done) && (lba >= ((uint64_t) numHeads * numSecspTrack * MAX_CYLINDERS))) { 677 chs[0] = 254; 678 chs[1] = chs[2] = 255; 679 done = 1; 680 retval = 0; 681 } // if 682 // If neither of the above applies, compute CHS values.... 683 if (!done) { 684 cylinder = lba / (uint64_t) (numHeads * numSecspTrack); 685 remainder = lba - (cylinder * numHeads * numSecspTrack); 686 head = remainder / numSecspTrack; 687 remainder -= head * numSecspTrack; 688 sector = remainder; 689 if (head < numHeads) 690 chs[0] = (uint8_t) head; 691 else 692 retval = 0; 693 if (sector < numSecspTrack) { 694 chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64); 695 chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF)); 696 } else { 697 retval = 0; 698 } // if/else 699 } // if value is expressible and non-0 700 } else { // Invalid (NULL) chs pointer 701 retval = -1; 702 } // if CHS pointer valid 703 return (retval); 704 } // BasicMBRData::LBAtoCHS() 705 706 // Look for overlapping partitions. Also looks for a couple of non-error 707 // conditions that the user should be told about. 708 // Returns the number of problems found 709 int BasicMBRData::FindOverlaps(void) { 710 int i, j, numProbs = 0, numEE = 0, ProtectiveOnOne = 0; 711 712 for (i = 0; i < MAX_MBR_PARTS; i++) { 713 for (j = i + 1; j < MAX_MBR_PARTS; j++) { 714 if ((partitions[i].GetInclusion() != NONE) && (partitions[j].GetInclusion() != NONE) && 715 (partitions[i].DoTheyOverlap(partitions[j]))) { 716 numProbs++; 717 cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1 718 << " overlap!\n"; 719 } // if 720 } // for (j...) 721 if (partitions[i].GetType() == 0xEE) { 722 numEE++; 723 if (partitions[i].GetStartLBA() == 1) 724 ProtectiveOnOne = 1; 725 } // if 726 } // for (i...) 727 728 if (numEE > 1) 729 cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n" 730 << "in some OSes.\n"; 731 if (!ProtectiveOnOne && (numEE > 0)) 732 cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause " 733 << "problems\nin some OSes.\n"; 734 735 return numProbs; 736 } // BasicMBRData::FindOverlaps() 737 738 // Returns the number of primary partitions, including the extended partition 739 // required to hold any logical partitions found. 740 int BasicMBRData::NumPrimaries(void) { 741 int i, numPrimaries = 0, logicalsFound = 0; 742 743 for (i = 0; i < MAX_MBR_PARTS; i++) { 744 if (partitions[i].GetLengthLBA() > 0) { 745 if (partitions[i].GetInclusion() == PRIMARY) 746 numPrimaries++; 747 if (partitions[i].GetInclusion() == LOGICAL) 748 logicalsFound = 1; 749 } // if 750 } // for 751 return (numPrimaries + logicalsFound); 752 } // BasicMBRData::NumPrimaries() 753 754 // Returns the number of logical partitions. 755 int BasicMBRData::NumLogicals(void) { 756 int i, numLogicals = 0; 757 758 for (i = 0; i < MAX_MBR_PARTS; i++) { 759 if (partitions[i].GetInclusion() == LOGICAL) 760 numLogicals++; 761 } // for 762 return numLogicals; 763 } // BasicMBRData::NumLogicals() 764 765 // Returns the number of partitions (primaries plus logicals), NOT including 766 // the extended partition required to house the logicals. 767 int BasicMBRData::CountParts(void) { 768 int i, num = 0; 769 770 for (i = 0; i < MAX_MBR_PARTS; i++) { 771 if ((partitions[i].GetInclusion() == LOGICAL) || 772 (partitions[i].GetInclusion() == PRIMARY)) 773 num++; 774 } // for 775 return num; 776 } // BasicMBRData::CountParts() 777 778 // Updates the canBeLogical and canBePrimary flags for all the partitions. 779 void BasicMBRData::UpdateCanBeLogical(void) { 780 int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR; 781 uint64_t firstLogical, lastLogical, lStart, pStart; 782 783 numPrimaries = NumPrimaries(); 784 numLogicals = NumLogicals(); 785 firstLogical = FirstLogicalLBA() - 1; 786 lastLogical = LastLogicalLBA(); 787 for (i = 0; i < MAX_MBR_PARTS; i++) { 788 usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR); 789 if (usedAsEBR) { 790 partitions[i].SetCanBeLogical(0); 791 partitions[i].SetCanBePrimary(0); 792 } else if (partitions[i].GetLengthLBA() > 0) { 793 // First determine if it can be logical.... 794 sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1); 795 lStart = partitions[i].GetStartLBA(); // start of potential logical part. 796 if ((lastLogical > 0) && 797 ((sectorBefore == EBR) || (sectorBefore == NONE))) { 798 // Assume it can be logical, then search for primaries that make it 799 // not work and, if found, flag appropriately. 800 partitions[i].SetCanBeLogical(1); 801 for (j = 0; j < MAX_MBR_PARTS; j++) { 802 if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) { 803 pStart = partitions[j].GetStartLBA(); 804 if (((pStart < lStart) && (firstLogical < pStart)) || 805 ((pStart > lStart) && (firstLogical > pStart))) { 806 partitions[i].SetCanBeLogical(0); 807 } // if/else 808 } // if 809 } // for 810 } else { 811 if ((sectorBefore != EBR) && (sectorBefore != NONE)) 812 partitions[i].SetCanBeLogical(0); 813 else 814 partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already 815 } // if/else 816 // Now determine if it can be primary. Start by assuming it can be... 817 partitions[i].SetCanBePrimary(1); 818 if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) { 819 partitions[i].SetCanBePrimary(0); 820 if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) && 821 (numPrimaries == 4)) 822 partitions[i].SetCanBePrimary(1); 823 } // if 824 if ((partitions[i].GetStartLBA() > (firstLogical + 1)) && 825 (partitions[i].GetLastLBA() < lastLogical)) 826 partitions[i].SetCanBePrimary(0); 827 } // else if 828 } // for 829 } // BasicMBRData::UpdateCanBeLogical() 830 831 // Returns the first sector occupied by any logical partition. Note that 832 // this does NOT include the logical partition's EBR! Returns UINT32_MAX 833 // if there are no logical partitions defined. 834 uint64_t BasicMBRData::FirstLogicalLBA(void) { 835 int i; 836 uint64_t firstFound = UINT32_MAX; 837 838 for (i = 0; i < MAX_MBR_PARTS; i++) { 839 if ((partitions[i].GetInclusion() == LOGICAL) && 840 (partitions[i].GetStartLBA() < firstFound)) { 841 firstFound = partitions[i].GetStartLBA(); 842 } // if 843 } // for 844 return firstFound; 845 } // BasicMBRData::FirstLogicalLBA() 846 847 // Returns the last sector occupied by any logical partition, or 0 if 848 // there are no logical partitions defined. 849 uint64_t BasicMBRData::LastLogicalLBA(void) { 850 int i; 851 uint64_t lastFound = 0; 852 853 for (i = 0; i < MAX_MBR_PARTS; i++) { 854 if ((partitions[i].GetInclusion() == LOGICAL) && 855 (partitions[i].GetLastLBA() > lastFound)) 856 lastFound = partitions[i].GetLastLBA(); 857 } // for 858 return lastFound; 859 } // BasicMBRData::LastLogicalLBA() 860 861 // Returns 1 if logical partitions are contiguous (have no primaries 862 // in their midst), or 0 if one or more primaries exist between 863 // logicals. 864 int BasicMBRData::AreLogicalsContiguous(void) { 865 int allOK = 1, i = 0; 866 uint64_t firstLogical, lastLogical; 867 868 firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR 869 lastLogical = LastLogicalLBA(); 870 if (lastLogical > 0) { 871 do { 872 if ((partitions[i].GetInclusion() == PRIMARY) && 873 (partitions[i].GetStartLBA() >= firstLogical) && 874 (partitions[i].GetStartLBA() <= lastLogical)) { 875 allOK = 0; 876 } // if 877 i++; 878 } while ((i < MAX_MBR_PARTS) && allOK); 879 } // if 880 return allOK; 881 } // BasicMBRData::AreLogicalsContiguous() 882 883 // Returns 1 if all partitions fit on the disk, given its size; 0 if any 884 // partition is too big. 885 int BasicMBRData::DoTheyFit(void) { 886 int i, allOK = 1; 887 888 for (i = 0; i < MAX_MBR_PARTS; i++) { 889 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) { 890 allOK = 0; 891 } // if 892 } // for 893 return allOK; 894 } // BasicMBRData::DoTheyFit(void) 895 896 // Returns 1 if there's at least one free sector immediately preceding 897 // all partitions flagged as logical; 0 if any logical partition lacks 898 // this space. 899 int BasicMBRData::SpaceBeforeAllLogicals(void) { 900 int i = 0, allOK = 1; 901 902 do { 903 if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) { 904 allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR); 905 } // if 906 i++; 907 } while (allOK && (i < MAX_MBR_PARTS)); 908 return allOK; 909 } // BasicMBRData::SpaceBeforeAllLogicals() 910 911 // Returns 1 if the partitions describe a legal layout -- all logicals 912 // are contiguous and have at least one preceding empty sector, 913 // the number of primaries is under 4 (or under 3 if there are any 914 // logicals), there are no overlapping partitions, etc. 915 // Does NOT assume that primaries are numbered 1-4; uses the 916 // IsItPrimary() function of the MBRPart class to determine 917 // primary status. Also does NOT consider partition order; there 918 // can be gaps and it will still be considered legal. 919 int BasicMBRData::IsLegal(void) { 920 int allOK = 1; 921 922 allOK = (FindOverlaps() == 0); 923 allOK = (allOK && (NumPrimaries() <= 4)); 924 allOK = (allOK && AreLogicalsContiguous()); 925 allOK = (allOK && DoTheyFit()); 926 allOK = (allOK && SpaceBeforeAllLogicals()); 927 return allOK; 928 } // BasicMBRData::IsLegal() 929 930 // Returns 1 if the 0xEE partition in the protective/hybrid MBR is marked as 931 // active/bootable. 932 int BasicMBRData::IsEEActive(void) { 933 int i, IsActive = 0; 934 935 for (i = 0; i < MAX_MBR_PARTS; i++) { 936 if ((partitions[i].GetStatus() & 0x80) && (partitions[i].GetType() == 0xEE)) 937 IsActive = 1; 938 } 939 return IsActive; 940 } // BasicMBRData::IsEEActive() 941 942 // Finds the next in-use partition, starting with start (will return start 943 // if it's in use). Returns -1 if no subsequent partition is in use. 944 int BasicMBRData::FindNextInUse(int start) { 945 if (start >= MAX_MBR_PARTS) 946 start = -1; 947 while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE)) 948 start++; 949 if ((start < 0) || (start >= MAX_MBR_PARTS)) 950 start = -1; 951 return start; 952 } // BasicMBRData::FindFirstLogical(); 953 954 /***************************************************** 955 * * 956 * Functions to create, delete, or change partitions * 957 * * 958 *****************************************************/ 959 960 // Empty all data. Meant mainly for calling by constructors, but it's also 961 // used by the hybrid MBR functions in the GPTData class. 962 void BasicMBRData::EmptyMBR(int clearBootloader) { 963 int i; 964 965 // Zero out the boot loader section, the disk signature, and the 966 // 2-byte nulls area only if requested to do so. (This is the 967 // default.) 968 if (clearBootloader == 1) { 969 EmptyBootloader(); 970 } // if 971 972 // Blank out the partitions 973 for (i = 0; i < MAX_MBR_PARTS; i++) { 974 partitions[i].Empty(); 975 } // for 976 MBRSignature = MBR_SIGNATURE; 977 state = mbr; 978 } // BasicMBRData::EmptyMBR() 979 980 // Blank out the boot loader area. Done with the initial MBR-to-GPT 981 // conversion, since MBR boot loaders don't understand GPT, and so 982 // need to be replaced.... 983 void BasicMBRData::EmptyBootloader(void) { 984 int i; 985 986 for (i = 0; i < 440; i++) 987 code[i] = 0; 988 nulls = 0; 989 } // BasicMBRData::EmptyBootloader 990 991 // Create a partition of the specified number based on the passed 992 // partition. This function does *NO* error checking, so it's possible 993 // to seriously screw up a partition table using this function! 994 // Note: This function should NOT be used to create the 0xEE partition 995 // in a conventional GPT configuration, since that partition has 996 // specific size requirements that this function won't handle. It may 997 // be used for creating the 0xEE partition(s) in a hybrid MBR, though, 998 // since those toss the rulebook away anyhow.... 999 void BasicMBRData::AddPart(int num, const MBRPart& newPart) { 1000 partitions[num] = newPart; 1001 } // BasicMBRData::AddPart() 1002 1003 // Create a partition of the specified number, starting LBA, and 1004 // length. This function does almost no error checking, so it's possible 1005 // to seriously screw up a partition table using this function! 1006 // Note: This function should NOT be used to create the 0xEE partition 1007 // in a conventional GPT configuration, since that partition has 1008 // specific size requirements that this function won't handle. It may 1009 // be used for creating the 0xEE partition(s) in a hybrid MBR, though, 1010 // since those toss the rulebook away anyhow.... 1011 void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) { 1012 if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) { 1013 partitions[num].Empty(); 1014 partitions[num].SetType(type); 1015 partitions[num].SetLocation(start, length); 1016 if (num < 4) 1017 partitions[num].SetInclusion(PRIMARY); 1018 else 1019 partitions[num].SetInclusion(LOGICAL); 1020 SetPartBootable(num, bootable); 1021 } // if valid partition number & size 1022 } // BasicMBRData::MakePart() 1023 1024 // Set the partition's type code. 1025 // Returns 1 if successful, 0 if not (invalid partition number) 1026 int BasicMBRData::SetPartType(int num, int type) { 1027 int allOK = 1; 1028 1029 if ((num >= 0) && (num < MAX_MBR_PARTS)) { 1030 if (partitions[num].GetLengthLBA() != UINT32_C(0)) { 1031 allOK = partitions[num].SetType(type); 1032 } else allOK = 0; 1033 } else allOK = 0; 1034 return allOK; 1035 } // BasicMBRData::SetPartType() 1036 1037 // Set (or remove) the partition's bootable flag. Setting it is the 1038 // default; pass 0 as bootable to remove the flag. 1039 // Returns 1 if successful, 0 if not (invalid partition number) 1040 int BasicMBRData::SetPartBootable(int num, int bootable) { 1041 int allOK = 1; 1042 1043 if ((num >= 0) && (num < MAX_MBR_PARTS)) { 1044 if (partitions[num].GetLengthLBA() != UINT32_C(0)) { 1045 if (bootable == 0) 1046 partitions[num].SetStatus(UINT8_C(0x00)); 1047 else 1048 partitions[num].SetStatus(UINT8_C(0x80)); 1049 } else allOK = 0; 1050 } else allOK = 0; 1051 return allOK; 1052 } // BasicMBRData::SetPartBootable() 1053 1054 // Create a partition that fills the most available space. Returns 1055 // 1 if partition was created, 0 otherwise. Intended for use in 1056 // creating hybrid MBRs. 1057 int BasicMBRData::MakeBiggestPart(int i, int type) { 1058 uint64_t start = UINT64_C(1); // starting point for each search 1059 uint64_t firstBlock; // first block in a segment 1060 uint64_t lastBlock; // last block in a segment 1061 uint64_t segmentSize; // size of segment in blocks 1062 uint64_t selectedSegment = UINT64_C(0); // location of largest segment 1063 uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks 1064 int found = 0; 1065 string anything; 1066 1067 do { 1068 firstBlock = FindFirstAvailable(start); 1069 if (firstBlock > UINT64_C(0)) { // something's free... 1070 lastBlock = FindLastInFree(firstBlock); 1071 segmentSize = lastBlock - firstBlock + UINT64_C(1); 1072 if (segmentSize > selectedSize) { 1073 selectedSize = segmentSize; 1074 selectedSegment = firstBlock; 1075 } // if 1076 start = lastBlock + 1; 1077 } // if 1078 } while (firstBlock != 0); 1079 if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) { 1080 found = 1; 1081 MakePart(i, selectedSegment, selectedSize, type, 0); 1082 } else { 1083 found = 0; 1084 } // if/else 1085 return found; 1086 } // BasicMBRData::MakeBiggestPart(int i) 1087 1088 // Delete partition #i 1089 void BasicMBRData::DeletePartition(int i) { 1090 partitions[i].Empty(); 1091 } // BasicMBRData::DeletePartition() 1092 1093 // Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity 1094 // checks to ensure the table remains legal. 1095 // Returns 1 on success, 0 on failure. 1096 int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) { 1097 int allOK = 1, origValue; 1098 1099 if (IsLegal()) { 1100 if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) { 1101 origValue = partitions[num].GetInclusion(); 1102 partitions[num].SetInclusion(inclStatus); 1103 if (!IsLegal()) { 1104 partitions[num].SetInclusion(origValue); 1105 cerr << "Specified change is not legal! Aborting change!\n"; 1106 } // if 1107 } else { 1108 cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n"; 1109 } // if/else 1110 } else { 1111 cerr << "Partition table is not currently in a valid state. Aborting change!\n"; 1112 allOK = 0; 1113 } // if/else 1114 return allOK; 1115 } // BasicMBRData::SetInclusionwChecks() 1116 1117 // Recomputes the CHS values for the specified partition and adjusts the value. 1118 // Note that this will create a technically incorrect CHS value for EFI GPT (0xEE) 1119 // protective partitions, but this is required by some buggy BIOSes, so I'm 1120 // providing a function to do this deliberately at the user's command. 1121 // This function does nothing if the partition's length is 0. 1122 void BasicMBRData::RecomputeCHS(int partNum) { 1123 partitions[partNum].RecomputeCHS(); 1124 } // BasicMBRData::RecomputeCHS() 1125 1126 // Sorts the partitions starting with partition #start. This function 1127 // does NOT pay attention to primary/logical assignment, which is 1128 // critical when writing the partitions. 1129 void BasicMBRData::SortMBR(int start) { 1130 if ((start < MAX_MBR_PARTS) && (start >= 0)) 1131 sort(partitions + start, partitions + MAX_MBR_PARTS); 1132 } // BasicMBRData::SortMBR() 1133 1134 // Delete any partitions that are too big to fit on the disk 1135 // or that are too big for MBR (32-bit limits). 1136 // This deletes the partitions by setting values to 0, not just 1137 // by setting them as being omitted. 1138 // Returns the number of partitions deleted in this way. 1139 int BasicMBRData::DeleteOversizedParts() { 1140 int num = 0, i; 1141 1142 for (i = 0; i < MAX_MBR_PARTS; i++) { 1143 if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) || 1144 (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) { 1145 cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = " 1146 << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n"; 1147 partitions[i].Empty(); 1148 num++; 1149 } // if 1150 } // for 1151 return num; 1152 } // BasicMBRData::DeleteOversizedParts() 1153 1154 // Search for and delete extended partitions. 1155 // Returns the number of partitions deleted. 1156 int BasicMBRData::DeleteExtendedParts() { 1157 int i, numDeleted = 0; 1158 uint8_t type; 1159 1160 for (i = 0; i < MAX_MBR_PARTS; i++) { 1161 type = partitions[i].GetType(); 1162 if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) && 1163 (partitions[i].GetLengthLBA() > 0)) { 1164 partitions[i].Empty(); 1165 numDeleted++; 1166 } // if 1167 } // for 1168 return numDeleted; 1169 } // BasicMBRData::DeleteExtendedParts() 1170 1171 // Finds any overlapping partitions and omits the smaller of the two. 1172 void BasicMBRData::OmitOverlaps() { 1173 int i, j; 1174 1175 for (i = 0; i < MAX_MBR_PARTS; i++) { 1176 for (j = i + 1; j < MAX_MBR_PARTS; j++) { 1177 if ((partitions[i].GetInclusion() != NONE) && 1178 partitions[i].DoTheyOverlap(partitions[j])) { 1179 if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA()) 1180 partitions[i].SetInclusion(NONE); 1181 else 1182 partitions[j].SetInclusion(NONE); 1183 } // if 1184 } // for (j...) 1185 } // for (i...) 1186 } // BasicMBRData::OmitOverlaps() 1187 1188 // Convert as many partitions into logicals as possible, except for 1189 // the first partition, if possible. 1190 void BasicMBRData::MaximizeLogicals() { 1191 int earliestPart = 0, earliestPartWas = NONE, i; 1192 1193 for (i = MAX_MBR_PARTS - 1; i >= 0; i--) { 1194 UpdateCanBeLogical(); 1195 earliestPart = i; 1196 if (partitions[i].CanBeLogical()) { 1197 partitions[i].SetInclusion(LOGICAL); 1198 } else if (partitions[i].CanBePrimary()) { 1199 partitions[i].SetInclusion(PRIMARY); 1200 } else { 1201 partitions[i].SetInclusion(NONE); 1202 } // if/elseif/else 1203 } // for 1204 // If we have spare primaries, convert back the earliest partition to 1205 // its original state.... 1206 if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL)) 1207 partitions[earliestPart].SetInclusion(earliestPartWas); 1208 } // BasicMBRData::MaximizeLogicals() 1209 1210 // Add primaries up to the maximum allowed, from the omitted category. 1211 void BasicMBRData::MaximizePrimaries() { 1212 int num, i = 0; 1213 1214 num = NumPrimaries(); 1215 while ((num < 4) && (i < MAX_MBR_PARTS)) { 1216 if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) { 1217 partitions[i].SetInclusion(PRIMARY); 1218 num++; 1219 UpdateCanBeLogical(); 1220 } // if 1221 i++; 1222 } // while 1223 } // BasicMBRData::MaximizePrimaries() 1224 1225 // Remove primary partitions in excess of 4, starting with the later ones, 1226 // in terms of the array location.... 1227 void BasicMBRData::TrimPrimaries(void) { 1228 int numToDelete, i = MAX_MBR_PARTS - 1; 1229 1230 numToDelete = NumPrimaries() - 4; 1231 while ((numToDelete > 0) && (i >= 0)) { 1232 if (partitions[i].GetInclusion() == PRIMARY) { 1233 partitions[i].SetInclusion(NONE); 1234 numToDelete--; 1235 } // if 1236 i--; 1237 } // while (numToDelete > 0) 1238 } // BasicMBRData::TrimPrimaries() 1239 1240 // Locates primary partitions located between logical partitions and 1241 // either converts the primaries into logicals (if possible) or omits 1242 // them. 1243 void BasicMBRData::MakeLogicalsContiguous(void) { 1244 uint64_t firstLogicalLBA, lastLogicalLBA; 1245 int i; 1246 1247 firstLogicalLBA = FirstLogicalLBA(); 1248 lastLogicalLBA = LastLogicalLBA(); 1249 for (i = 0; i < MAX_MBR_PARTS; i++) { 1250 if ((partitions[i].GetInclusion() == PRIMARY) && 1251 (partitions[i].GetStartLBA() >= firstLogicalLBA) && 1252 (partitions[i].GetLastLBA() <= lastLogicalLBA)) { 1253 if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE) 1254 partitions[i].SetInclusion(LOGICAL); 1255 else 1256 partitions[i].SetInclusion(NONE); 1257 } // if 1258 } // for 1259 } // BasicMBRData::MakeLogicalsContiguous() 1260 1261 // If MBR data aren't legal, adjust primary/logical assignments and, 1262 // if necessary, drop partitions, to make the data legal. 1263 void BasicMBRData::MakeItLegal(void) { 1264 if (!IsLegal()) { 1265 DeleteOversizedParts(); 1266 MaximizeLogicals(); 1267 MaximizePrimaries(); 1268 if (!AreLogicalsContiguous()) 1269 MakeLogicalsContiguous(); 1270 if (NumPrimaries() > 4) 1271 TrimPrimaries(); 1272 OmitOverlaps(); 1273 } // if 1274 } // BasicMBRData::MakeItLegal() 1275 1276 // Removes logical partitions and deactivated partitions from first four 1277 // entries (primary space). 1278 // Returns the number of partitions moved. 1279 int BasicMBRData::RemoveLogicalsFromFirstFour(void) { 1280 int i, j = 4, numMoved = 0, swapped = 0; 1281 MBRPart temp; 1282 1283 for (i = 0; i < 4; i++) { 1284 if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) { 1285 j = 4; 1286 swapped = 0; 1287 do { 1288 if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) { 1289 temp = partitions[j]; 1290 partitions[j] = partitions[i]; 1291 partitions[i] = temp; 1292 swapped = 1; 1293 numMoved++; 1294 } // if 1295 j++; 1296 } while ((j < MAX_MBR_PARTS) && !swapped); 1297 if (j >= MAX_MBR_PARTS) 1298 cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n"; 1299 } // if 1300 } // for i... 1301 return numMoved; 1302 } // BasicMBRData::RemoveLogicalsFromFirstFour() 1303 1304 // Move all primaries into the first four partition spaces 1305 // Returns the number of partitions moved. 1306 int BasicMBRData::MovePrimariesToFirstFour(void) { 1307 int i, j = 0, numMoved = 0, swapped = 0; 1308 MBRPart temp; 1309 1310 for (i = 4; i < MAX_MBR_PARTS; i++) { 1311 if (partitions[i].GetInclusion() == PRIMARY) { 1312 j = 0; 1313 swapped = 0; 1314 do { 1315 if (partitions[j].GetInclusion() != PRIMARY) { 1316 temp = partitions[j]; 1317 partitions[j] = partitions[i]; 1318 partitions[i] = temp; 1319 swapped = 1; 1320 numMoved++; 1321 } // if 1322 j++; 1323 } while ((j < 4) && !swapped); 1324 } // if 1325 } // for 1326 return numMoved; 1327 } // BasicMBRData::MovePrimariesToFirstFour() 1328 1329 // Create an extended partition, if necessary, to hold the logical partitions. 1330 // This function also sorts the primaries into the first four positions of 1331 // the table. 1332 // Returns 1 on success, 0 on failure. 1333 int BasicMBRData::CreateExtended(void) { 1334 int allOK = 1, i = 0, swapped = 0; 1335 MBRPart temp; 1336 1337 if (IsLegal()) { 1338 // Move logicals out of primary space... 1339 RemoveLogicalsFromFirstFour(); 1340 // Move primaries out of logical space... 1341 MovePrimariesToFirstFour(); 1342 1343 // Create the extended partition 1344 if (NumLogicals() > 0) { 1345 SortMBR(4); // sort starting from 4 -- that is, logicals only 1346 temp.Empty(); 1347 temp.SetStartLBA(FirstLogicalLBA() - 1); 1348 temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2); 1349 temp.SetType(0x0f, 1); 1350 temp.SetInclusion(PRIMARY); 1351 do { 1352 if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) { 1353 partitions[i] = temp; 1354 swapped = 1; 1355 } // if 1356 i++; 1357 } while ((i < 4) && !swapped); 1358 if (!swapped) { 1359 cerr << "Could not create extended partition; no room in primary table!\n"; 1360 allOK = 0; 1361 } // if 1362 } // if (NumLogicals() > 0) 1363 } else allOK = 0; 1364 // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found 1365 // along with an extended partition 1366 for (i = 0; i < MAX_MBR_PARTS; i++) 1367 if (swapped && partitions[i].GetType() == 0xEE) 1368 allOK = 0; 1369 return allOK; 1370 } // BasicMBRData::CreateExtended() 1371 1372 /**************************************** 1373 * * 1374 * Functions to find data on free space * 1375 * * 1376 ****************************************/ 1377 1378 // Finds the first free space on the disk from start onward; returns 0 1379 // if none available.... 1380 uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) { 1381 uint64_t first; 1382 uint64_t i; 1383 int firstMoved; 1384 1385 if ((start >= (UINT32_MAX - 1)) || (start >= (diskSize - 1))) 1386 return 0; 1387 1388 first = start; 1389 1390 // ...now search through all partitions; if first is within an 1391 // existing partition, move it to the next sector after that 1392 // partition and repeat. If first was moved, set firstMoved 1393 // flag; repeat until firstMoved is not set, so as to catch 1394 // cases where partitions are out of sequential order.... 1395 do { 1396 firstMoved = 0; 1397 for (i = 0; i < 4; i++) { 1398 // Check if it's in the existing partition 1399 if ((first >= partitions[i].GetStartLBA()) && 1400 (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) { 1401 first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA(); 1402 firstMoved = 1; 1403 } // if 1404 } // for 1405 } while (firstMoved == 1); 1406 if ((first >= diskSize) || (first > UINT32_MAX)) 1407 first = 0; 1408 return (first); 1409 } // BasicMBRData::FindFirstAvailable() 1410 1411 // Finds the last free sector on the disk from start forward. 1412 uint64_t BasicMBRData::FindLastInFree(uint64_t start) { 1413 uint64_t nearestStart; 1414 uint64_t i; 1415 1416 if ((diskSize <= UINT32_MAX) && (diskSize > 0)) 1417 nearestStart = diskSize - 1; 1418 else 1419 nearestStart = UINT32_MAX - 1; 1420 1421 for (i = 0; i < 4; i++) { 1422 if ((nearestStart > partitions[i].GetStartLBA()) && 1423 (partitions[i].GetStartLBA() > start)) { 1424 nearestStart = partitions[i].GetStartLBA() - 1; 1425 } // if 1426 } // for 1427 return (nearestStart); 1428 } // BasicMBRData::FindLastInFree() 1429 1430 // Finds the first free sector on the disk from start backward. 1431 uint64_t BasicMBRData::FindFirstInFree(uint64_t start) { 1432 uint64_t bestLastLBA, thisLastLBA; 1433 int i; 1434 1435 bestLastLBA = 1; 1436 for (i = 0; i < 4; i++) { 1437 thisLastLBA = partitions[i].GetLastLBA() + 1; 1438 if (thisLastLBA > 0) 1439 thisLastLBA--; 1440 if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) 1441 bestLastLBA = thisLastLBA + 1; 1442 } // for 1443 return (bestLastLBA); 1444 } // BasicMBRData::FindFirstInFree() 1445 1446 // Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID. 1447 // Note: If the sector immediately before a logical partition is in use by 1448 // another partition, this function returns PRIMARY or LOGICAL for that 1449 // sector, rather than EBR. 1450 int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) { 1451 int i = 0, usedAs = NONE; 1452 1453 do { 1454 if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector)) 1455 usedAs = partitions[i].GetInclusion(); 1456 if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL)) 1457 usedAs = EBR; 1458 if (sector == 0) 1459 usedAs = EBR; 1460 if (sector >= diskSize) 1461 usedAs = INVALID; 1462 i++; 1463 } while ((i < topPartNum) && ((usedAs == NONE) || (usedAs == EBR))); 1464 return usedAs; 1465 } // BasicMBRData::SectorUsedAs() 1466 1467 /****************************************************** 1468 * * 1469 * Functions that extract data on specific partitions * 1470 * * 1471 ******************************************************/ 1472 1473 uint8_t BasicMBRData::GetStatus(int i) { 1474 MBRPart* thePart; 1475 uint8_t retval; 1476 1477 thePart = GetPartition(i); 1478 if (thePart != NULL) 1479 retval = thePart->GetStatus(); 1480 else 1481 retval = UINT8_C(0); 1482 return retval; 1483 } // BasicMBRData::GetStatus() 1484 1485 uint8_t BasicMBRData::GetType(int i) { 1486 MBRPart* thePart; 1487 uint8_t retval; 1488 1489 thePart = GetPartition(i); 1490 if (thePart != NULL) 1491 retval = thePart->GetType(); 1492 else 1493 retval = UINT8_C(0); 1494 return retval; 1495 } // BasicMBRData::GetType() 1496 1497 uint64_t BasicMBRData::GetFirstSector(int i) { 1498 MBRPart* thePart; 1499 uint64_t retval; 1500 1501 thePart = GetPartition(i); 1502 if (thePart != NULL) { 1503 retval = thePart->GetStartLBA(); 1504 } else 1505 retval = UINT32_C(0); 1506 return retval; 1507 } // BasicMBRData::GetFirstSector() 1508 1509 uint64_t BasicMBRData::GetLength(int i) { 1510 MBRPart* thePart; 1511 uint64_t retval; 1512 1513 thePart = GetPartition(i); 1514 if (thePart != NULL) { 1515 retval = thePart->GetLengthLBA(); 1516 } else 1517 retval = UINT64_C(0); 1518 return retval; 1519 } // BasicMBRData::GetLength() 1520 1521 /*********************** 1522 * * 1523 * Protected functions * 1524 * * 1525 ***********************/ 1526 1527 // Return a pointer to a primary or logical partition, or NULL if 1528 // the partition is out of range.... 1529 MBRPart* BasicMBRData::GetPartition(int i) { 1530 MBRPart* thePart = NULL; 1531 1532 if ((i >= 0) && (i < MAX_MBR_PARTS)) 1533 thePart = &partitions[i]; 1534 return thePart; 1535 } // GetPartition() 1536 1537 /******************************************* 1538 * * 1539 * Functions that involve user interaction * 1540 * * 1541 *******************************************/ 1542 1543 // Present the MBR operations menu. Note that the 'w' option does not 1544 // immediately write data; that's handled by the calling function. 1545 // Returns the number of partitions defined on exit, or -1 if the 1546 // user selected the 'q' option. (Thus, the caller should save data 1547 // if the return value is >0, or possibly >=0 depending on intentions.) 1548 int BasicMBRData::DoMenu(const string& prompt) { 1549 int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0; 1550 unsigned int hexCode; 1551 string tempStr; 1552 1553 do { 1554 cout << prompt; 1555 switch (ReadString()[0]) { 1556 case '\0': 1557 goOn = cin.good(); 1558 break; 1559 case 'a': case 'A': 1560 num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1; 1561 if (partitions[num].GetInclusion() != NONE) 1562 partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80); 1563 break; 1564 case 'c': case 'C': 1565 for (num = 0; num < MAX_MBR_PARTS; num++) 1566 RecomputeCHS(num); 1567 break; 1568 case 'l': case 'L': 1569 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1; 1570 SetInclusionwChecks(num, LOGICAL); 1571 break; 1572 case 'o': case 'O': 1573 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1; 1574 SetInclusionwChecks(num, NONE); 1575 break; 1576 case 'p': case 'P': 1577 if (!haveShownInfo) { 1578 cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical " 1579 << "status,\n** unlike in most MBR partitioning tools!\n\a"; 1580 cout << "\n** Extended partitions are not displayed, but will be generated " 1581 << "as required.\n"; 1582 haveShownInfo = 1; 1583 } // if 1584 DisplayMBRData(); 1585 break; 1586 case 'q': case 'Q': 1587 cout << "This will abandon your changes. Are you sure? "; 1588 if (GetYN() == 'Y') { 1589 goOn = 0; 1590 quitting = 1; 1591 } // if 1592 break; 1593 case 'r': case 'R': 1594 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1; 1595 SetInclusionwChecks(num, PRIMARY); 1596 break; 1597 case 's': case 'S': 1598 SortMBR(); 1599 break; 1600 case 't': case 'T': 1601 num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1; 1602 hexCode = 0x00; 1603 if (partitions[num].GetLengthLBA() > 0) { 1604 while ((hexCode <= 0) || (hexCode > 255)) { 1605 cout << "Enter an MBR hex code: "; 1606 tempStr = ReadString(); 1607 if (IsHex(tempStr)) 1608 sscanf(tempStr.c_str(), "%x", &hexCode); 1609 } // while 1610 partitions[num].SetType(hexCode); 1611 } // if 1612 break; 1613 case 'w': case 'W': 1614 goOn = 0; 1615 break; 1616 default: 1617 ShowCommands(); 1618 break; 1619 } // switch 1620 } while (goOn); 1621 if (quitting) 1622 retval = -1; 1623 else 1624 retval = CountParts(); 1625 return (retval); 1626 } // BasicMBRData::DoMenu() 1627 1628 void BasicMBRData::ShowCommands(void) { 1629 cout << "a\ttoggle the active/boot flag\n"; 1630 cout << "c\trecompute all CHS values\n"; 1631 cout << "l\tset partition as logical\n"; 1632 cout << "o\tomit partition\n"; 1633 cout << "p\tprint the MBR partition table\n"; 1634 cout << "q\tquit without saving changes\n"; 1635 cout << "r\tset partition as primary\n"; 1636 cout << "s\tsort MBR partitions\n"; 1637 cout << "t\tchange partition type code\n"; 1638 cout << "w\twrite the MBR partition table to disk and exit\n"; 1639 } // BasicMBRData::ShowCommands() 1640