diff options
author | srs5694 <srs5694@users.sourceforge.net> | 2011-03-01 22:03:26 -0500 |
---|---|---|
committer | srs5694 <srs5694@users.sourceforge.net> | 2011-03-01 22:03:26 -0500 |
commit | f2efa7defc5db19ede49ac4a7dc298eaf47c8ac0 (patch) | |
tree | 6ba5c0461e32ca58600920ca73d3387994e91c5d /basicmbr.cc | |
parent | df9d363d341a0ffdd05250fd4ffb842f59815690 (diff) | |
download | sgdisk-f2efa7defc5db19ede49ac4a7dc298eaf47c8ac0.tar.gz |
New files
Diffstat (limited to 'basicmbr.cc')
-rw-r--r-- | basicmbr.cc | 976 |
1 files changed, 976 insertions, 0 deletions
diff --git a/basicmbr.cc b/basicmbr.cc new file mode 100644 index 0000000..6a8d103 --- /dev/null +++ b/basicmbr.cc @@ -0,0 +1,976 @@ +/* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition + data. */ + +/* Initial coding by Rod Smith, January to February, 2009 */ + +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> +#include <iostream> +#include "mbr.h" +#include "partnotes.h" +#include "support.h" + +using namespace std; + +/**************************************** + * * + * MBRData class and related structures * + * * + ****************************************/ + +BasicMBRData::BasicMBRData(void) { + blockSize = SECTOR_SIZE; + diskSize = 0; + device = ""; + state = invalid; + srand((unsigned int) time(NULL)); + numHeads = MAX_HEADS; + numSecspTrack = MAX_SECSPERTRACK; + myDisk = NULL; + canDeleteMyDisk = 0; + EmptyMBR(); +} // BasicMBRData default constructor + +BasicMBRData::BasicMBRData(string filename) { + blockSize = SECTOR_SIZE; + diskSize = 0; + device = filename; + state = invalid; + numHeads = MAX_HEADS; + numSecspTrack = MAX_SECSPERTRACK; + myDisk = NULL; + canDeleteMyDisk = 0; + + srand((unsigned int) time(NULL)); + // Try to read the specified partition table, but if it fails.... + if (!ReadMBRData(filename)) { + EmptyMBR(); + device = ""; + } // if +} // BasicMBRData(string filename) constructor + +// Free space used by myDisk only if that's OK -- sometimes it will be +// copied from an outside source, in which case that source should handle +// it! +BasicMBRData::~BasicMBRData(void) { + if (canDeleteMyDisk) + delete myDisk; +} // BasicMBRData destructor + +// Assignment operator -- copy entire set of MBR data. +BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) { + int i; + + for (i = 0; i < 440; i++) + code[i] = orig.code[i]; + diskSignature = orig.diskSignature; + nulls = orig.nulls; + MBRSignature = orig.MBRSignature; + blockSize = orig.blockSize; + diskSize = orig.diskSize; + numHeads = orig.numHeads; + numSecspTrack = orig.numSecspTrack; + canDeleteMyDisk = orig.canDeleteMyDisk; + device = orig.device; + state = orig.state; + + myDisk = new DiskIO; + myDisk->OpenForRead(orig.myDisk->GetName()); + + for (i = 0; i < MAX_MBR_PARTS; i++) { + partitions[i] = orig.partitions[i]; + } // for + return *this; +} // BasicMBRData::operator=() + +/********************** + * * + * Disk I/O functions * + * * + **********************/ + +// Read data from MBR. Returns 1 if read was successful (even if the +// data isn't a valid MBR), 0 if the read failed. +int BasicMBRData::ReadMBRData(const string & deviceFilename) { + int allOK = 1; + + if (myDisk == NULL) { + myDisk = new DiskIO; + canDeleteMyDisk = 1; + } // if + if (myDisk->OpenForRead(deviceFilename)) { + allOK = ReadMBRData(myDisk); + } else { + allOK = 0; + } // if + + if (allOK) + device = deviceFilename; + + return allOK; +} // BasicMBRData::ReadMBRData(const string & deviceFilename) + +// Read data from MBR. If checkBlockSize == 1 (the default), the block +// size is checked; otherwise it's set to the default (512 bytes). +// Note that any extended partition(s) present will be explicitly stored +// in the partitions[] array, along with their contained partitions; the +// extended container partition(s) should be ignored by other functions. +int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) { + int allOK = 1, i, j, logicalNum; + int err = 1; + TempMBR tempMBR; + + if ((myDisk != NULL) && (canDeleteMyDisk)) { + delete myDisk; + canDeleteMyDisk = 0; + } // if + + myDisk = theDisk; + + // Empty existing MBR data, including the logical partitions... + EmptyMBR(0); + + if (myDisk->Seek(0)) + if (myDisk->Read(&tempMBR, 512)) + err = 0; + if (err) { + cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n"; + } else { + for (i = 0; i < 440; i++) + code[i] = tempMBR.code[i]; + diskSignature = tempMBR.diskSignature; + nulls = tempMBR.nulls; + for (i = 0; i < 4; i++) { + partitions[i].status = tempMBR.partitions[i].status; + partitions[i].partitionType = tempMBR.partitions[i].partitionType; + partitions[i].firstLBA = tempMBR.partitions[i].firstLBA; + partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA; + for (j = 0; j < 3; j++) { + partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j]; + partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j]; + } // for j... (reading parts of CHS geometry) + } // for i... (reading all four partitions) + MBRSignature = tempMBR.MBRSignature; + + // Reverse the byte order, if necessary + if (IsLittleEndian() == 0) { + ReverseBytes(&diskSignature, 4); + ReverseBytes(&nulls, 2); + ReverseBytes(&MBRSignature, 2); + for (i = 0; i < 4; i++) { + ReverseBytes(&partitions[i].firstLBA, 4); + ReverseBytes(&partitions[i].lengthLBA, 4); + } // for + } // if + + if (MBRSignature != MBR_SIGNATURE) { + allOK = 0; + state = invalid; + } // if + + // Find disk size + diskSize = myDisk->DiskSize(&err); + + // Find block size + if (checkBlockSize) { + blockSize = myDisk->GetBlockSize(); + } // if (checkBlockSize) + + // Load logical partition data, if any is found.... + if (allOK) { + for (i = 0; i < 4; i++) { + if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f) + || (partitions[i].partitionType == 0x85)) { + // Found it, so call a recursive algorithm to load everything from them.... + logicalNum = ReadLogicalPart(partitions[i].firstLBA, UINT32_C(0), 4); + if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) { + allOK = 0; + cerr << "Error reading logical partitions! List may be truncated!\n"; + } // if maxLogicals valid + } // if primary partition is extended + } // for primary partition loop + if (allOK) { // Loaded logicals OK + state = mbr; + } else { + state = invalid; + } // if + } // if + + // Check to see if it's in GPT format.... + if (allOK) { + for (i = 0; i < 4; i++) { + if (partitions[i].partitionType == UINT8_C(0xEE)) { + state = gpt; + } // if + } // for + } // if + + // If there's an EFI GPT partition, look for other partition types, + // to flag as hybrid + if (state == gpt) { + for (i = 0 ; i < 4; i++) { + if ((partitions[i].partitionType != UINT8_C(0xEE)) && + (partitions[i].partitionType != UINT8_C(0x00))) + state = hybrid; + } // for + } // if (hybrid detection code) + } // no initial error + return allOK; +} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) + +// This is a recursive function to read all the logical partitions, following the +// logical partition linked list from the disk and storing the basic data in the +// partitions[] array. Returns last index to partitions[] used, or -1 if there was +// a problem. +// Parameters: +// extendedStart = LBA of the start of the extended partition +// diskOffset = LBA offset WITHIN the extended partition of the one to be read +// partNum = location in partitions[] array to store retrieved data +int BasicMBRData::ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset, int partNum) { + struct TempMBR ebr; + uint64_t offset; + + // Check for a valid partition number. Note that partitions MAY be read into + // the area normally used by primary partitions, although the only calling + // function as of GPT fdisk version 0.5.0 doesn't do so. + if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) { + offset = (uint64_t) (extendedStart + diskOffset); + if (myDisk->Seek(offset) == 0) { // seek to EBR record + cerr << "Unable to seek to " << offset << "! Aborting!\n"; + partNum = -1; + } + if (myDisk->Read(&ebr, 512) != 512) { // Load the data.... + cerr << "Error seeking to or reading logical partition data from " << offset + << "!\nAborting!\n"; + partNum = -1; + } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data.... + ReverseBytes(&ebr.MBRSignature, 2); + ReverseBytes(&ebr.partitions[0].firstLBA, 4); + ReverseBytes(&ebr.partitions[0].lengthLBA, 4); + ReverseBytes(&ebr.partitions[1].firstLBA, 4); + ReverseBytes(&ebr.partitions[1].lengthLBA, 4); + } // if/else/if + + if (ebr.MBRSignature != MBR_SIGNATURE) { + partNum = -1; + cerr << "MBR signature in logical partition invalid; read 0x"; + cerr.fill('0'); + cerr.width(4); + cerr.setf(ios::uppercase); + cerr << hex << ebr.MBRSignature << ", but should be 0x"; + cerr.width(4); + cerr << MBR_SIGNATURE << dec << "\n"; + cerr.fill(' '); + } // if + + // Copy over the basic data.... + partitions[partNum].status = ebr.partitions[0].status; + partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart; + partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA; + partitions[partNum].partitionType = ebr.partitions[0].partitionType; + + // Find the next partition (if there is one) and recurse.... + if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) && + (partNum < (MAX_MBR_PARTS - 1))) { + partNum = ReadLogicalPart(extendedStart, ebr.partitions[1].firstLBA, + partNum + 1); + } else { + partNum++; + } // if another partition + } // Not enough space for all the logicals (or previous error encountered) + return (partNum); +} // BasicMBRData::ReadLogicalPart() + +// Write the MBR data to the default defined device. This writes both the +// MBR itself and any defined logical partitions, provided there's an +// MBR extended partition. +int BasicMBRData::WriteMBRData(void) { + int allOK = 1; + + if (myDisk != NULL) { + if (myDisk->OpenForWrite() != 0) { + allOK = WriteMBRData(myDisk); + } else { + allOK = 0; + } // if/else + myDisk->Close(); + } else allOK = 0; + return allOK; +} // BasicMBRData::WriteMBRData(void) + +// Save the MBR data to a file. This writes both the +// MBR itself and any defined logical partitions, provided there's an +// MBR extended partition. +int BasicMBRData::WriteMBRData(DiskIO *theDisk) { + int i, j, partNum, allOK, moreLogicals = 0; + uint32_t extFirstLBA = 0; + uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range + TempMBR tempMBR; + + // First write the main MBR data structure.... + for (i = 0; i < 440; i++) + tempMBR.code[i] = code[i]; + tempMBR.diskSignature = diskSignature; + tempMBR.nulls = nulls; + tempMBR.MBRSignature = MBRSignature; + for (i = 0; i < 4; i++) { + tempMBR.partitions[i].status = partitions[i].status; + tempMBR.partitions[i].partitionType = partitions[i].partitionType; + tempMBR.partitions[i].firstLBA = partitions[i].firstLBA; + tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA; + for (j = 0; j < 3; j++) { + tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j]; + tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j]; + } // for j... + if (partitions[i].partitionType == 0x0f) { + extFirstLBA = partitions[i].firstLBA; + moreLogicals = 1; + } // if + } // for i... + allOK = WriteMBRData(tempMBR, theDisk, 0); + + // Set up tempMBR with some constant data for logical partitions... + tempMBR.diskSignature = 0; + for (i = 2; i < 4; i++) { + tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0; + tempMBR.partitions[i].partitionType = 0x00; + for (j = 0; j < 3; j++) { + tempMBR.partitions[i].firstSector[j] = 0; + tempMBR.partitions[i].lastSector[j] = 0; + } // for j + } // for i + + partNum = 4; + writeEbrTo = (uint64_t) extFirstLBA; + // If extended partition is present, write logicals... + while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) { + tempMBR.partitions[0] = partitions[partNum]; + tempMBR.partitions[0].firstLBA = 1; // partition starts on sector after EBR + // tempMBR.partitions[1] points to next EBR or terminates EBR linked list... + if ((partNum < MAX_MBR_PARTS - 1) && (partitions[partNum + 1].firstLBA > 0)) { + tempMBR.partitions[1].partitionType = 0x0f; + tempMBR.partitions[1].firstLBA = partitions[partNum + 1].firstLBA - 1; + tempMBR.partitions[1].lengthLBA = partitions[partNum + 1].lengthLBA + 1; + LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA, + (uint8_t *) &tempMBR.partitions[1].firstSector); + LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA, + (uint8_t *) &tempMBR.partitions[1].lastSector); + } else { + tempMBR.partitions[1].partitionType = 0x00; + tempMBR.partitions[1].firstLBA = 0; + tempMBR.partitions[1].lengthLBA = 0; + moreLogicals = 0; + } // if/else + allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo); + partNum++; + writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA; + } // while + return allOK; +} // BasicMBRData::WriteMBRData(DiskIO *theDisk) + +int BasicMBRData::WriteMBRData(const string & deviceFilename) { + device = deviceFilename; + return WriteMBRData(); +} // BasicMBRData::WriteMBRData(const string & deviceFilename) + +// Write a single MBR record to the specified sector. Used by the like-named +// function to write both the MBR and multiple EBR (for logical partition) +// records. +// Returns 1 on success, 0 on failure +int BasicMBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) { + int i, allOK; + + // Reverse the byte order, if necessary + if (IsLittleEndian() == 0) { + ReverseBytes(&mbr.diskSignature, 4); + ReverseBytes(&mbr.nulls, 2); + ReverseBytes(&mbr.MBRSignature, 2); + for (i = 0; i < 4; i++) { + ReverseBytes(&mbr.partitions[i].firstLBA, 4); + ReverseBytes(&mbr.partitions[i].lengthLBA, 4); + } // for + } // if + + // Now write the data structure... + allOK = theDisk->OpenForWrite(); + if (allOK && theDisk->Seek(sector)) { + if (theDisk->Write(&mbr, 512) != 512) { + allOK = 0; + cerr << "Error " << errno << " when saving MBR!\n"; + } // if + } else { + allOK = 0; + cerr << "Error " << errno << " when seeking to MBR to write it!\n"; + } // if/else + theDisk->Close(); + + // Reverse the byte order back, if necessary + if (IsLittleEndian() == 0) { + ReverseBytes(&mbr.diskSignature, 4); + ReverseBytes(&mbr.nulls, 2); + ReverseBytes(&mbr.MBRSignature, 2); + for (i = 0; i < 4; i++) { + ReverseBytes(&mbr.partitions[i].firstLBA, 4); + ReverseBytes(&mbr.partitions[i].lengthLBA, 4); + } // for + }// if + return allOK; +} // BasicMBRData::WriteMBRData(uint64_t sector) + +/******************************************** + * * + * Functions that display data for the user * + * * + ********************************************/ + +// Show the MBR data to the user, up to the specified maximum number +// of partitions.... +void BasicMBRData::DisplayMBRData(int maxParts) { + int i; + char bootCode; + + if (maxParts > MAX_MBR_PARTS) + maxParts = MAX_MBR_PARTS; + cout << "MBR disk identifier: 0x"; + cout.width(8); + cout.fill('0'); + cout.setf(ios::uppercase); + cout << hex << diskSignature << dec << "\n"; + cout << "MBR partitions:\n"; + cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n"; + for (i = 0; i < maxParts; i++) { + if (partitions[i].lengthLBA != 0) { + if (partitions[i].status && 0x80) // it's bootable + bootCode = '*'; + else + bootCode = ' '; + cout.fill(' '); + cout.width(4); + cout << i + 1 << "\t " << bootCode << "\t"; + cout.width(13); + cout << partitions[i].firstLBA << "\t"; + cout.width(15); + cout << partitions[i].lengthLBA << " \t0x"; + cout.width(2); + cout.fill('0'); + cout << hex << (int) partitions[i].partitionType << dec << "\n"; + } // if + cout.fill(' '); + } // for + cout << "\nDisk size is " << diskSize << " sectors (" + << BytesToSI(diskSize, blockSize) << ")\n"; +} // BasicMBRData::DisplayMBRData() + +// Displays the state, as a word, on stdout. Used for debugging & to +// tell the user about the MBR state when the program launches.... +void BasicMBRData::ShowState(void) { + switch (state) { + case invalid: + cout << " MBR: not present\n"; + break; + case gpt: + cout << " MBR: protective\n"; + break; + case hybrid: + cout << " MBR: hybrid\n"; + break; + case mbr: + cout << " MBR: MBR only\n"; + break; + default: + cout << "\a MBR: unknown -- bug!\n"; + break; + } // switch +} // BasicMBRData::ShowState() + +/********************************************************************* + * * + * Functions that set or get disk metadata (CHS geometry, disk size, * + * etc.) * + * * + *********************************************************************/ + +// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function. +// Note that this only sets the heads and sectors; the number of +// cylinders is determined by these values and the disk size. +void BasicMBRData::SetCHSGeom(uint32_t h, uint32_t s) { + if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) { + numHeads = h; + numSecspTrack = s; + } else { + cout << "Warning! Attempt to set invalid CHS geometry!\n"; + } // if/else +} // BasicMBRData::SetCHSGeom() + +// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion +// was within the range that can be expressed by CHS (including 0, for an +// empty partition), 0 if the value is outside that range, and -1 if chs is +// invalid. +int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) { + uint64_t cylinder, head, sector; // all numbered from 0 + uint64_t remainder; + int retval = 1; + int done = 0; + + if (chs != NULL) { + // Special case: In case of 0 LBA value, zero out CHS values.... + if (lba == 0) { + chs[0] = chs[1] = chs[2] = UINT8_C(0); + done = 1; + } // if + // If LBA value is too large for CHS, max out CHS values.... + if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) { + chs[0] = 254; + chs[1] = chs[2] = 255; + done = 1; + retval = 0; + } // if + // If neither of the above applies, compute CHS values.... + if (!done) { + cylinder = lba / (uint64_t) (numHeads * numSecspTrack); + remainder = lba - (cylinder * numHeads * numSecspTrack); + head = remainder / numSecspTrack; + remainder -= head * numSecspTrack; + sector = remainder; + if (head < numHeads) + chs[0] = (uint8_t) head; + else + retval = 0; + if (sector < numSecspTrack) { + chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64); + chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF)); + } else { + retval = 0; + } // if/else + } // if value is expressible and non-0 + } else { // Invalid (NULL) chs pointer + retval = -1; + } // if CHS pointer valid + return (retval); +} // BasicMBRData::LBAtoCHS() + +// Look for problems -- overlapping partitions, etc. +int BasicMBRData::Verify(void) { + int i, j, theyOverlap, numProbs = 0, numEE = 0; + uint32_t firstLBA1, firstLBA2, lastLBA1, lastLBA2; + + for (i = 0; i < MAX_MBR_PARTS; i++) { + for (j = i + 1; j < MAX_MBR_PARTS; j++) { + theyOverlap = 0; + firstLBA1 = partitions[i].firstLBA; + firstLBA2 = partitions[j].firstLBA; + if ((firstLBA1 != 0) && (firstLBA2 != 0)) { + lastLBA1 = partitions[i].firstLBA + partitions[i].lengthLBA - 1; + lastLBA2 = partitions[j].firstLBA + partitions[j].lengthLBA - 1; + if ((firstLBA1 < lastLBA2) && (lastLBA1 >= firstLBA2)) + theyOverlap = 1; + if ((firstLBA2 < lastLBA1) && (lastLBA2 >= firstLBA1)) + theyOverlap = 1; + } // if + if (theyOverlap) { + numProbs++; + cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1 + << " overlap!\n"; + } // if + } // for (j...) + if (partitions[i].partitionType == 0xEE) { + numEE++; + if (partitions[i].firstLBA != 1) + cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause problems\n" + << "in some OSes.\n"; + } // if + } // for (i...) + if (numEE > 1) + cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n" + << "in some OSes.\n"; + + return numProbs; +} // BasicMBRData::Verify() + +/***************************************************** + * * + * Functions to create, delete, or change partitions * + * * + *****************************************************/ + +// Empty all data. Meant mainly for calling by constructors, but it's also +// used by the hybrid MBR functions in the GPTData class. +void BasicMBRData::EmptyMBR(int clearBootloader) { + int i; + + // Zero out the boot loader section, the disk signature, and the + // 2-byte nulls area only if requested to do so. (This is the + // default.) + if (clearBootloader == 1) { + EmptyBootloader(); + } // if + + // Blank out the partitions + for (i = 0; i < MAX_MBR_PARTS; i++) { + partitions[i].status = UINT8_C(0); + partitions[i].firstSector[0] = UINT8_C(0); + partitions[i].firstSector[1] = UINT8_C(0); + partitions[i].firstSector[2] = UINT8_C(0); + partitions[i].partitionType = UINT8_C(0); + partitions[i].lastSector[0] = UINT8_C(0); + partitions[i].lastSector[1] = UINT8_C(0); + partitions[i].lastSector[2] = UINT8_C(0); + partitions[i].firstLBA = UINT32_C(0); + partitions[i].lengthLBA = UINT32_C(0); + } // for + MBRSignature = MBR_SIGNATURE; +} // BasicMBRData::EmptyMBR() + +// Blank out the boot loader area. Done with the initial MBR-to-GPT +// conversion, since MBR boot loaders don't understand GPT, and so +// need to be replaced.... +void BasicMBRData::EmptyBootloader(void) { + int i; + + for (i = 0; i < 440; i++) + code[i] = 0; + nulls = 0; +} // BasicMBRData::EmptyBootloader + +// Create a partition of the specified number, starting LBA, and +// length. This function does *NO* error checking, so it's possible +// to seriously screw up a partition table using this function! +// Note: This function should NOT be used to create the 0xEE partition +// in a conventional GPT configuration, since that partition has +// specific size requirements that this function won't handle. It may +// be used for creating the 0xEE partition(s) in a hybrid MBR, though, +// since those toss the rulebook away anyhow.... +void BasicMBRData::MakePart(int num, uint32_t start, uint32_t length, int type, int bootable) { + if ((num >= 0) && (num < MAX_MBR_PARTS)) { + partitions[num].firstSector[0] = UINT8_C(0); + partitions[num].firstSector[1] = UINT8_C(0); + partitions[num].firstSector[2] = UINT8_C(0); + partitions[num].partitionType = (uint8_t) type; + partitions[num].lastSector[0] = UINT8_C(0); + partitions[num].lastSector[1] = UINT8_C(0); + partitions[num].lastSector[2] = UINT8_C(0); + partitions[num].firstLBA = start; + partitions[num].lengthLBA = length; + // If this is a "real" partition, set its CHS geometry + if (length > 0) { + LBAtoCHS((uint64_t) start, partitions[num].firstSector); + LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector); + } // if (length > 0) + SetPartBootable(num, bootable); + } // if valid partition number +} // BasicMBRData::MakePart() + +// Set the partition's type code. +// Returns 1 if successful, 0 if not (invalid partition number) +int BasicMBRData::SetPartType(int num, int type) { + int allOK = 1; + + if ((num >= 0) && (num < MAX_MBR_PARTS)) { + if (partitions[num].lengthLBA != UINT32_C(0)) { + partitions[num].partitionType = (uint8_t) type; + } else allOK = 0; + } else allOK = 0; + return allOK; +} // BasicMBRData::SetPartType() + +// Set (or remove) the partition's bootable flag. Setting it is the +// default; pass 0 as bootable to remove the flag. +// Returns 1 if successful, 0 if not (invalid partition number) +int BasicMBRData::SetPartBootable(int num, int bootable) { + int allOK = 1; + + if ((num >= 0) && (num < MAX_MBR_PARTS)) { + if (partitions[num].lengthLBA != UINT32_C(0)) { + if (bootable == 0) + partitions[num].status = UINT8_C(0); + else + partitions[num].status = UINT8_C(0x80); + } else allOK = 0; + } else allOK = 0; + return allOK; +} // BasicMBRData::SetPartBootable() + +// Create a partition that fills the most available space. Returns +// 1 if partition was created, 0 otherwise. Intended for use in +// creating hybrid MBRs. +int BasicMBRData::MakeBiggestPart(int i, int type) { + uint32_t start = UINT32_C(1); // starting point for each search + uint32_t firstBlock; // first block in a segment + uint32_t lastBlock; // last block in a segment + uint32_t segmentSize; // size of segment in blocks + uint32_t selectedSegment = UINT32_C(0); // location of largest segment + uint32_t selectedSize = UINT32_C(0); // size of largest segment in blocks + int found = 0; + + do { + firstBlock = FindFirstAvailable(start); + if (firstBlock != UINT32_C(0)) { // something's free... + lastBlock = FindLastInFree(firstBlock); + segmentSize = lastBlock - firstBlock + UINT32_C(1); + if (segmentSize > selectedSize) { + selectedSize = segmentSize; + selectedSegment = firstBlock; + } // if + start = lastBlock + 1; + } // if + } while (firstBlock != 0); + if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) { + found = 1; + MakePart(i, selectedSegment, selectedSize, type, 0); + } else { + found = 0; + } // if/else + return found; +} // BasicMBRData::MakeBiggestPart(int i) + +// Delete partition #i +void BasicMBRData::DeletePartition(int i) { + int j; + + partitions[i].firstLBA = UINT32_C(0); + partitions[i].lengthLBA = UINT32_C(0); + partitions[i].status = UINT8_C(0); + partitions[i].partitionType = UINT8_C(0); + for (j = 0; j < 3; j++) { + partitions[i].firstSector[j] = UINT8_C(0); + partitions[i].lastSector[j] = UINT8_C(0); + } // for j (CHS data blanking) +} // BasicMBRData::DeletePartition() + +// Recomputes the CHS values for the specified partition and adjusts the value. +// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE) +// protective partitions, but this is required by some buggy BIOSes, so I'm +// providing a function to do this deliberately at the user's command. +// This function does nothing if the partition's length is 0. +void BasicMBRData::RecomputeCHS(int partNum) { + uint64_t firstLBA, lengthLBA; + + firstLBA = (uint64_t) partitions[partNum].firstLBA; + lengthLBA = (uint64_t) partitions[partNum].lengthLBA; + + if (lengthLBA > 0) { + LBAtoCHS(firstLBA, partitions[partNum].firstSector); + LBAtoCHS(firstLBA + lengthLBA - 1, partitions[partNum].lastSector); + } // if +} // BasicMBRData::RecomputeCHS() + +// Creates an MBR extended partition holding logical partitions that +// correspond to the list of GPT partitions in theList. The extended +// partition is placed in position #4 (counting from 1) in the MBR. +// The logical partition data are copied to the partitions[] array in +// positions 4 and up (counting from 0). Neither the MBR nor the EBR +// entries are written to disk; that is left for the WriteMBRData() +// function. +// Returns number of converted partitions +int BasicMBRData::CreateLogicals(PartNotes * notes) { + uint64_t extEndLBA = 0, extStartLBA = UINT64_MAX; + int i = 4, numLogicals = 0; + struct PartInfo aPart; + + // Find bounds of the extended partition.... + notes->Rewind(); + while (notes->GetNextInfo(&aPart) >= 0) { + if (aPart.type == LOGICAL) { + if (extStartLBA > aPart.firstLBA) + extStartLBA = aPart.firstLBA; + if (extEndLBA < aPart.lastLBA) + extEndLBA = aPart.lastLBA; + numLogicals++; + } // if + } // while + extStartLBA--; + + if ((extStartLBA < UINT32_MAX) && ((extEndLBA - extStartLBA + 1) < UINT32_MAX)) { + notes->Rewind(); + i = 4; + while ((notes->GetNextInfo(&aPart) >= 0) && (i < MAX_MBR_PARTS)) { + if (aPart.type == LOGICAL) { + partitions[i].partitionType = aPart.hexCode; + partitions[i].firstLBA = (uint32_t) (aPart.firstLBA - extStartLBA); + partitions[i].lengthLBA = (uint32_t) (aPart.lastLBA - aPart.firstLBA + 1); + LBAtoCHS(UINT64_C(1), (uint8_t *) &partitions[i].firstSector); + LBAtoCHS(partitions[i].lengthLBA, (uint8_t *) &partitions[i].lastSector); + partitions[i].status = aPart.active * 0x80; + i++; + } // if + } // while + MakePart(3, (uint32_t) extStartLBA, (uint32_t) (extEndLBA - extStartLBA + 1), 0x0f, 0); + } else { + if (numLogicals > 0) { + cerr << "Unable to create logical partitions; they exceed the 2 TiB limit!\n"; +// cout << "extStartLBA = " << extStartLBA << ", extEndLBA = " << extEndLBA << "\n"; + } + } // if/else + return (i - 4); +} // BasicMBRData::CreateLogicals() + +/**************************************** + * * + * Functions to find data on free space * + * * + ****************************************/ + +// Finds the first free space on the disk from start onward; returns 0 +// if none available.... +uint32_t BasicMBRData::FindFirstAvailable(uint32_t start) { + uint32_t first; + uint32_t i; + int firstMoved; + + first = start; + + // ...now search through all partitions; if first is within an + // existing partition, move it to the next sector after that + // partition and repeat. If first was moved, set firstMoved + // flag; repeat until firstMoved is not set, so as to catch + // cases where partitions are out of sequential order.... + do { + firstMoved = 0; + for (i = 0; i < 4; i++) { + // Check if it's in the existing partition + if ((first >= partitions[i].firstLBA) && + (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) { + first = partitions[i].firstLBA + partitions[i].lengthLBA; + firstMoved = 1; + } // if + } // for + } while (firstMoved == 1); + if (first >= diskSize) + first = 0; + return (first); +} // BasicMBRData::FindFirstAvailable() + +// Finds the last free sector on the disk from start forward. +uint32_t BasicMBRData::FindLastInFree(uint32_t start) { + uint32_t nearestStart; + uint32_t i; + + if ((diskSize <= UINT32_MAX) && (diskSize > 0)) + nearestStart = (uint32_t) diskSize - 1; + else + nearestStart = UINT32_MAX - 1; + for (i = 0; i < 4; i++) { + if ((nearestStart > partitions[i].firstLBA) && + (partitions[i].firstLBA > start)) { + nearestStart = partitions[i].firstLBA - 1; + } // if + } // for + return (nearestStart); +} // BasicMBRData::FindLastInFree() + +// Finds the first free sector on the disk from start backward. +uint32_t BasicMBRData::FindFirstInFree(uint32_t start) { + uint32_t bestLastLBA, thisLastLBA; + int i; + + bestLastLBA = 1; + for (i = 0; i < 4; i++) { + thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA; + if (thisLastLBA > 0) + thisLastLBA--; + if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) + bestLastLBA = thisLastLBA + 1; + } // for + return (bestLastLBA); +} // BasicMBRData::FindFirstInFree() + +// Returns 1 if the specified sector is unallocated, 0 if it's +// allocated. +int BasicMBRData::IsFree(uint32_t sector) { + int i, isFree = 1; + uint32_t first, last; + + for (i = 0; i < 4; i++) { + first = partitions[i].firstLBA; + // Note: Weird two-line thing to avoid subtracting 1 from a 0 value + // for an unsigned int.... + last = first + partitions[i].lengthLBA; + if (last > 0) + last--; + if ((first <= sector) && (last >= sector)) + isFree = 0; + } // for + return isFree; +} // BasicMBRData::IsFree() + +/****************************************************** + * * + * Functions that extract data on specific partitions * + * * + ******************************************************/ + +uint8_t BasicMBRData::GetStatus(int i) { + MBRRecord* thePart; + uint8_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) + retval = thePart->status; + else + retval = UINT8_C(0); + return retval; +} // BasicMBRData::GetStatus() + +uint8_t BasicMBRData::GetType(int i) { + MBRRecord* thePart; + uint8_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) + retval = thePart->partitionType; + else + retval = UINT8_C(0); + return retval; +} // BasicMBRData::GetType() + +uint32_t BasicMBRData::GetFirstSector(int i) { + MBRRecord* thePart; + uint32_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) { + retval = thePart->firstLBA; + } else + retval = UINT32_C(0); + return retval; +} // BasicMBRData::GetFirstSector() + +uint32_t BasicMBRData::GetLength(int i) { + MBRRecord* thePart; + uint32_t retval; + + thePart = GetPartition(i); + if (thePart != NULL) { + retval = thePart->lengthLBA; + } else + retval = UINT32_C(0); + return retval; +} // BasicMBRData::GetLength() + +/*********************** + * * + * Protected functions * + * * + ***********************/ + +// Return a pointer to a primary or logical partition, or NULL if +// the partition is out of range.... +struct MBRRecord* BasicMBRData::GetPartition(int i) { + MBRRecord* thePart = NULL; + + if ((i >= 0) && (i < MAX_MBR_PARTS)) + thePart = &partitions[i]; + return thePart; +} // GetPartition() |