You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
362 lines
11 KiB
362 lines
11 KiB
/*
|
|
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()
|