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