blob: 6a8d103ecb7c1d9ad260fee1e1dc545eef060dca [file] [log] [blame]
/* 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()