/*
MBRPart class, part of GPT fdisk program family.
Copyright (C) 2011 Roderick W. Smith
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define __STDC_LIMIT_MACROS
#ifndef __STDC_CONSTANT_MACROS
#define __STDC_CONSTANT_MACROS
#endif
#include <stddef.h>
#include <stdint.h>
#include <iostream>
#include "support.h"
#include "mbrpart.h"
using namespace std;
uint32_t MBRPart::numHeads = MAX_HEADS;
uint32_t MBRPart::numSecspTrack = MAX_SECSPERTRACK;
uint64_t MBRPart::diskSize = 0;
uint32_t MBRPart::blockSize = 512;
int MBRPart::numInstances = 0;
MBRPart::MBRPart() {
int i;
status = 0;
for (i = 0; i < 3; i++) {
firstSector[i] = 0;
lastSector[i] = 0;
} // for
partitionType = 0x00;
firstLBA = 0;
lengthLBA = 0;
includeAs = NONE;
canBePrimary = 0;
canBeLogical = 0;
if (numInstances == 0) {
numHeads = MAX_HEADS;
numSecspTrack = MAX_SECSPERTRACK;
diskSize = 0;
blockSize = 512;
} // if
numInstances++;
}
MBRPart::MBRPart(const MBRPart& orig) {
numInstances++;
operator=(orig);
}
MBRPart::~MBRPart() {
numInstances--;
}
MBRPart& MBRPart::operator=(const MBRPart& orig) {
int i;
status = orig.status;
for (i = 0; i < 3; i++) {
firstSector[i] = orig.firstSector[i];
lastSector[i] = orig.lastSector[i];
} // for
partitionType = orig.partitionType;
firstLBA = orig.firstLBA;
lengthLBA = orig.lengthLBA;
includeAs = orig.includeAs;
canBePrimary = orig.canBePrimary;
canBeLogical = orig.canBeLogical;
return *this;
} // MBRPart::operator=(const MBRPart& orig)
// Set partition data from packed MBRRecord structure.
MBRPart& MBRPart::operator=(const struct MBRRecord& orig) {
int i;
status = orig.status;
for (i = 0; i < 3; i++) {
firstSector[i] = orig.firstSector[i];
lastSector[i] = orig.lastSector[i];
} // for
partitionType = orig.partitionType;
firstLBA = orig.firstLBA;
lengthLBA = orig.lengthLBA;
if (lengthLBA > 0)
includeAs = PRIMARY;
else
includeAs = NONE;
return *this;
} // MBRPart::operator=(const struct MBRRecord& orig)
// Compare the values, and return a bool result.
// Because this is intended for sorting and a lengthLBA value of 0 denotes
// a partition that's not in use and so that should be sorted upwards,
// we return the opposite of the usual arithmetic result when either
// lengthLBA value is 0.
bool MBRPart::operator<(const MBRPart &other) const {
if (lengthLBA && other.lengthLBA)
return (firstLBA < other.firstLBA);
else
return (other.firstLBA < firstLBA);
} // operator<()
/**************************************************
* *
* Set information on partitions or disks without *
* interacting with the user.... *
* *
**************************************************/
void MBRPart::SetGeometry(uint32_t heads, uint32_t sectors, uint64_t ds, uint32_t bs) {
numHeads = heads;
numSecspTrack = sectors;
diskSize = ds;
blockSize = bs;
} // MBRPart::SetGeometry
// Empty the partition (zero out all values).
void MBRPart::Empty(void) {
status = UINT8_C(0);
firstSector[0] = UINT8_C(0);
firstSector[1] = UINT8_C(0);
firstSector[2] = UINT8_C(0);
partitionType = UINT8_C(0);
lastSector[0] = UINT8_C(0);
lastSector[1] = UINT8_C(0);
lastSector[2] = UINT8_C(0);
firstLBA = UINT32_C(0);
lengthLBA = UINT32_C(0);
includeAs = NONE;
} // MBRPart::Empty()
// Sets the type code, but silently refuses to change it to an extended type
// code.
// Returns 1 on success, 0 on failure (extended type code)
int MBRPart::SetType(uint8_t typeCode, int isExtended) {
int allOK = 0;
if ((isExtended == 1) || ((typeCode != 0x05) && (typeCode != 0x0f) && (typeCode != 0x85))) {
partitionType = typeCode;
allOK = 1;
} // if
return allOK;
} // MBRPart::SetType()
void MBRPart::SetStartLBA(uint64_t start) {
if (start > UINT32_MAX)
cerr << "Partition start out of range! Continuing, but problems now likely!\n";
firstLBA = (uint32_t) start;
RecomputeCHS();
} // MBRPart::SetStartLBA()
void MBRPart::SetLengthLBA(uint64_t length) {
if (length > UINT32_MAX)
cerr << "Partition length out of range! Continuing, but problems now likely!\n";
lengthLBA = (uint32_t) length;
RecomputeCHS();
} // MBRPart::SetLengthLBA()
// Set the start point and length of the partition. This function takes LBA
// values, sets them directly, and sets the CHS values based on the LBA
// values and the current geometry settings.
void MBRPart::SetLocation(uint64_t start, uint64_t length) {
int validCHS;
if ((start > UINT32_MAX) || (length > UINT32_MAX)) {
cerr << "Partition values out of range in MBRPart::SetLocation()!\n"
<< "Continuing, but strange problems are now likely!\n";
} // if
firstLBA = (uint32_t) start;
lengthLBA = (uint32_t) length;
validCHS = RecomputeCHS();
// If this is a complete 0xEE protective MBR partition, max out its
// CHS last sector value, as per the GPT spec. (Set to 0xffffff,
// although the maximum legal MBR value is 0xfeffff, which is
// actually what GNU Parted and Apple's Disk Utility use, in
// violation of the GPT spec.)
if ((partitionType == 0xEE) && (!validCHS) && (firstLBA == 1) &&
((lengthLBA == diskSize - 1) || (lengthLBA == UINT32_MAX))) {
lastSector[0] = lastSector[1] = lastSector[2] = 0xFF;
} // if
} // MBRPart::SetLocation()
// Store the MBR data in the packed structure used for disk I/O...
void MBRPart::StoreInStruct(MBRRecord* theStruct) {
int i;
theStruct->firstLBA = firstLBA;
theStruct->lengthLBA = lengthLBA;
theStruct->partitionType = partitionType;
theStruct->status = status;
for (i = 0; i < 3; i++) {
theStruct->firstSector[i] = firstSector[i];
theStruct->lastSector[i] = lastSector[i];
} // for
} // MBRPart::StoreInStruct()
/**********************************************
* *
* Get information on partitions or disks.... *
* *
**********************************************/
// Returns the last LBA value. Note that this can theoretically be a 33-bit
// value, so we return a 64-bit value. If lengthLBA == 0, returns 0, even if
// firstLBA is non-0.
uint64_t MBRPart::GetLastLBA(void) const {
if (lengthLBA > 0)
return (uint64_t) firstLBA + (uint64_t) lengthLBA - UINT64_C(1);
else
return 0;
} // MBRPart::GetLastLBA()
// Returns 1 if other overlaps with the current partition, 0 if they don't
// overlap
int MBRPart::DoTheyOverlap (const MBRPart& other) {
return lengthLBA && other.lengthLBA &&
(firstLBA <= other.GetLastLBA()) != (GetLastLBA() < other.firstLBA);
} // MBRPart::DoTheyOverlap()
/*************************************************
* *
* Adjust information on partitions or disks.... *
* *
*************************************************/
// Recompute the CHS values for the start and end points.
// Returns 1 if both computed values are within the range
// that can be expressed by that CHS, 0 otherwise.
int MBRPart::RecomputeCHS(void) {
int retval = 1;
if (lengthLBA > 0) {
retval = LBAtoCHS(firstLBA, firstSector);
retval *= LBAtoCHS(firstLBA + lengthLBA - 1, lastSector);
} // if
return retval;
} // MBRPart::RecomputeCHS()
// Converts 32-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 MBRPart::LBAtoCHS(uint32_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 / (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 & UINT32_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);
} // MBRPart::LBAtoCHS()
// Reverses the byte order, but only if we're on a big-endian platform.
// Note that most data come in 8-bit structures, so don't need reversing;
// only the LBA data needs to be reversed....
void MBRPart::ReverseByteOrder(void) {
if (IsLittleEndian() == 0) {
ReverseBytes(&firstLBA, 4);
ReverseBytes(&lengthLBA, 4);
} // if
} // MBRPart::ReverseByteOrder()
/**************************
* *
* User I/O functions.... *
* *
**************************/
// Show MBR data. Should update canBeLogical flags before calling.
// If isGpt == 1, omits the "can be logical" and "can be primary" columns.
void MBRPart::ShowData(int isGpt) {
char bootCode = ' ';
if (status & 0x80) // it's bootable
bootCode = '*';
cout.fill(' ');
cout << bootCode << " ";
cout.width(13);
cout << firstLBA;
cout.width(13);
cout << GetLastLBA() << " ";
switch (includeAs) {
case PRIMARY:
cout << "primary";
break;
case LOGICAL:
cout << "logical";
break;
case NONE:
cout << "omitted";
break;
default:
cout << "error ";
break;
} // switch
cout.width(7);
if (!isGpt) {
if (canBeLogical)
cout << " Y ";
else
cout << " ";
if (canBePrimary)
cout << " Y ";
else
cout << " ";
} // if
cout << "0x";
cout.width(2);
cout.fill('0');
cout << hex << (int) partitionType << dec << "\n";
} // MBRPart::ShowData()