summaryrefslogtreecommitdiff
path: root/gpt.cc
diff options
context:
space:
mode:
authorsrs5694 <srs5694@users.sourceforge.net>2009-08-26 00:48:01 -0400
committersrs5694 <srs5694@users.sourceforge.net>2009-08-26 00:48:01 -0400
commit2a9f5da3c3c4ccccd291462bda9d2aefcd485ff8 (patch)
treee328d746064b4d8c19d82644322a60847b0d3c53 /gpt.cc
parente19ba095c0c78fbd76a823ed300fada07ad258a9 (diff)
downloadsgdisk-2a9f5da3c3c4ccccd291462bda9d2aefcd485ff8.tar.gz
Added support for big-endian architectures.
New support seems OK so far for me, but I want to test it a bit more before making an official 0.3.5 release....
Diffstat (limited to 'gpt.cc')
-rw-r--r--gpt.cc196
1 files changed, 165 insertions, 31 deletions
diff --git a/gpt.cc b/gpt.cc
index 4fdc5fc..19246fe 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -182,13 +182,11 @@ int GPTData::LoadPartitions(char* deviceFilename) {
switch (UseWhichPartitions()) {
case use_mbr:
-// printf("In LoadPartitions(), using MBR\n");
XFormPartitions(&protectiveMBR);
break;
case use_gpt:
break;
case use_new:
-// printf("In LoadPartitions(), making new\n");
ClearGPTData();
protectiveMBR.MakeProtectiveMBR();
break;
@@ -230,6 +228,8 @@ int GPTData::ForceLoadGPTData(int fd) {
lseek64(fd, 512, SEEK_SET);
read(fd, &mainHeader, 512); // read main GPT header
mainCrcOk = CheckHeaderCRC(&mainHeader);
+ if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
+ ReverseHeaderBytes(&mainHeader);
// Load backup header, check its CRC, and store the results of
// the check for future reference
@@ -237,6 +237,8 @@ int GPTData::ForceLoadGPTData(int fd) {
if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) {
read(fd, &secondHeader, 512); // read secondary GPT header
secondCrcOk = CheckHeaderCRC(&secondHeader);
+ if (IsLittleEndian() == 0) // big-endian system; adjust header byte order....
+ ReverseHeaderBytes(&secondHeader);
} else {
allOK = 0;
state = gpt_invalid;
@@ -319,6 +321,8 @@ int GPTData::LoadMainTable(void) {
read(fd, partitions, sizeOfParts);
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
+ if (IsLittleEndian() == 0)
+ ReversePartitionBytes();
retval = 1;
} // if
return retval;
@@ -334,7 +338,7 @@ WhichToUse GPTData::UseWhichPartitions(void) {
mbrState = protectiveMBR.GetValidity();
- if ((state == gpt_invalid) && (mbrState == mbr)) {
+ if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
printf("\n\a***************************************************************\n"
"Found invalid GPT and valid MBR; converting MBR to GPT format.\n"
"THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n"
@@ -342,10 +346,16 @@ WhichToUse GPTData::UseWhichPartitions(void) {
"***************************************************************\n\n");
which = use_mbr;
} // if
+
if ((state == gpt_valid) && (mbrState == gpt)) {
printf("Found valid GPT with protective MBR; using GPT.\n");
which = use_gpt;
} // if
+ if ((state == gpt_valid) && (mbrState == hybrid)) {
+ printf("Found valid GPT with hybrid MBR; using GPT.\n");
+ printf("\aIf you change GPT partitions' sizes, you may need to re-create the hybrid MBR!\n");
+ which = use_gpt;
+ } // if
if ((state == gpt_valid) && (mbrState == invalid)) {
printf("\aFound valid GPT with corrupt MBR; using GPT and will create new\nprotective MBR on save.\n");
which = use_gpt;
@@ -366,7 +376,7 @@ WhichToUse GPTData::UseWhichPartitions(void) {
// Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
// problems)
if (state == gpt_corrupt) {
- if (mbrState == mbr) {
+ if ((mbrState == mbr) || (mbrState == hybrid)) {
printf("Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
"GPT MAY permit recovery of GPT data.)\n");
answer = GetNumber(1, 3, 2, (char*) " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
@@ -383,13 +393,13 @@ WhichToUse GPTData::UseWhichPartitions(void) {
if (answer == 1) {
which = use_gpt;
} else which = use_new;
- } else {
+ } else { // corrupt GPT, MBR indicates it's a GPT disk....
printf("\a\a****************************************************************************\n"
"Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
"verification and recovery are STRONGLY recommended.\n"
"****************************************************************************\n");
- } // if
- } // if
+ } // if/else/else
+ } // if (corrupt GPT)
if (which == use_new)
printf("Creating new GPT entries.\n");
@@ -717,14 +727,11 @@ int GPTData::XFormPartitions(MBRData* origParts) {
else
numToConvert = mainHeader.numParts;
-// printf("In XFormPartitions(), numToConvert = %d\n", numToConvert);
-
for (i = 0; i < numToConvert; i++) {
origType = origParts->GetType(i);
-// printf("Converting partition of type 0x%02X\n", (int) origType);
- // don't convert extended partitions or null (non-existent) partitions
- if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00)) {
+ // don't convert extended, hybrid protective, or null (non-existent) partitions
+ if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x00) && (origType != 0xEE)) {
partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i);
partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t)
origParts->GetLength(i) - 1;
@@ -1034,16 +1041,29 @@ int GPTData::CheckHeaderValidity(void) {
(unsigned long) mainHeader.revision, UINT32_C(0x00010000));
} // if/else/if
+ // If MBR bad, check for an Apple disk signature
+ if ((protectiveMBR.GetValidity() == invalid) &&
+ (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
+ (mainHeader.signature << 32) == APM_SIGNATURE2)) {
+ printf("\n*******************************************************************\n");
+ printf("This disk appears to contain an Apple-format (APM) partition table!\n");
+ printf("*******************************************************************\n\n\a");
+ } // if
+
return valid;
} // GPTData::CheckHeaderValidity()
// Check the header CRC to see if it's OK...
+// Note: Must be called BEFORE byte-order reversal on big-endian
+// systems!
int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
uint32_t oldCRC, newCRC;
- // Back up old header and then blank it, since it must be 0 for
+ // Back up old header CRC and then blank it, since it must be 0 for
// computation to be valid
oldCRC = header->headerCRC;
+ if (IsLittleEndian() == 0)
+ ReverseBytes((char*) &oldCRC, 4);
header->headerCRC = UINT32_C(0);
// Initialize CRC functions...
@@ -1055,18 +1075,29 @@ int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
return (oldCRC == newCRC);
} // GPTData::CheckHeaderCRC()
-// Recompute all the CRCs. Must be called before saving if any changes
-// have been made.
+// Recompute all the CRCs. Must be called before saving (but after reversing
+// byte order on big-endian systems) if any changes have been made.
void GPTData::RecomputeCRCs(void) {
uint32_t crc;
+ uint32_t trueNumParts, crcTemp;
+ int littleEndian = 1;
// Initialize CRC functions...
chksum_crc32gentab();
+ littleEndian = IsLittleEndian();
+
// Compute CRC of partition tables & store in main and secondary headers
- crc = chksum_crc32((unsigned char*) partitions, mainHeader.numParts * GPT_SIZE);
+ trueNumParts = mainHeader.numParts;
+ if (littleEndian == 0)
+ ReverseBytes((char*) &trueNumParts, 4); // unreverse this key piece of data....
+ crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
mainHeader.partitionEntriesCRC = crc;
secondHeader.partitionEntriesCRC = crc;
+ if (littleEndian == 0) {
+ ReverseBytes((char*) &mainHeader.partitionEntriesCRC, 4);
+ ReverseBytes((char*) &secondHeader.partitionEntriesCRC, 4);
+ } // if
// Zero out GPT tables' own CRCs (required for correct computation)
mainHeader.headerCRC = 0;
@@ -1074,8 +1105,12 @@ void GPTData::RecomputeCRCs(void) {
// Compute & store CRCs of main & secondary headers...
crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
+ if (littleEndian == 0)
+ ReverseBytes((char*) &crc, 4);
mainHeader.headerCRC = crc;
crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
+ if (littleEndian == 0)
+ ReverseBytes((char*) &crc, 4);
secondHeader.headerCRC = crc;
} // GPTData::RecomputeCRCs()
@@ -1271,6 +1306,8 @@ void GPTData::LoadSecondTableAsMain(void) {
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
mainPartsCrcOk = secondPartsCrcOk;
+ if (IsLittleEndian() == 0)
+ ReversePartitionBytes();
if (!secondPartsCrcOk) {
printf("Error! After loading backup partitions, the CRC still doesn't check out!\n");
} // if
@@ -1424,6 +1461,7 @@ int GPTData::SaveGPTData(void) {
char answer, line[256];
int fd;
uint64_t secondTable;
+ uint32_t numParts;
off_t offset;
if (strlen(device) == 0) {
@@ -1463,6 +1501,17 @@ int GPTData::SaveGPTData(void) {
} // for j...
} // for i...
+ // Pull out some data that's needed before doing byte-order reversal on
+ // big-endian systems....
+ numParts = mainHeader.numParts;
+ secondTable = secondHeader.partitionEntriesLBA;
+ if (IsLittleEndian() == 0) {
+ // Reverse partition bytes first, since that function requires non-reversed
+ // data from the main header....
+ ReversePartitionBytes();
+ ReverseHeaderBytes(&mainHeader);
+ ReverseHeaderBytes(&secondHeader);
+ } // if
RecomputeCRCs();
if (allOK) {
@@ -1499,13 +1548,12 @@ int GPTData::SaveGPTData(void) {
// Now write the main partition tables...
if (allOK) {
- if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ if (write(fd, partitions, GPT_SIZE * numParts) == -1)
allOK = 0;
} // if
// Now seek to near the end to write the secondary GPT....
if (allOK) {
- secondTable = secondHeader.partitionEntriesLBA;
offset = (off_t) secondTable * (off_t) (blockSize);
if (lseek64(fd, offset, SEEK_SET) == (off_t) - 1) {
allOK = 0;
@@ -1515,7 +1563,7 @@ int GPTData::SaveGPTData(void) {
// Now write the secondary partition tables....
if (allOK)
- if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ if (write(fd, partitions, GPT_SIZE * numParts) == -1)
allOK = 0;
// Now write the secondary GPT header...
@@ -1558,6 +1606,14 @@ int GPTData::SaveGPTData(void) {
printf("Aborting write of new partition table.\n");
} // if
+ if (IsLittleEndian() == 0) {
+ // Reverse (normalize) header bytes first, since ReversePartitionBytes()
+ // requires non-reversed data in mainHeader...
+ ReverseHeaderBytes(&mainHeader);
+ ReverseHeaderBytes(&secondHeader);
+ ReversePartitionBytes();
+ } // if
+
return (allOK);
} // GPTData::SaveGPTData()
@@ -1568,10 +1624,19 @@ int GPTData::SaveGPTData(void) {
// table; it discards the backup partition table, since it should be
// identical to the main partition table on healthy disks.
int GPTData::SaveGPTBackup(char* filename) {
- int fd, allOK = 1;;
+ int fd, allOK = 1;
+ uint32_t numParts;
if ((fd = open(filename, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)) != -1) {
- // First, write the protective MBR...
+ // Reverse the byte order, if necessary....
+ numParts = mainHeader.numParts;
+ if (IsLittleEndian() == 0) {
+ ReversePartitionBytes();
+ ReverseHeaderBytes(&mainHeader);
+ ReverseHeaderBytes(&secondHeader);
+ } // if
+
+ // Now write the protective MBR...
protectiveMBR.WriteMBRData(fd);
// Now write the main GPT header...
@@ -1586,7 +1651,7 @@ int GPTData::SaveGPTBackup(char* filename) {
// Now write the main partition tables...
if (allOK) {
- if (write(fd, partitions, GPT_SIZE * mainHeader.numParts) == -1)
+ if (write(fd, partitions, GPT_SIZE * numParts) == -1)
allOK = 0;
} // if
@@ -1597,6 +1662,13 @@ int GPTData::SaveGPTBackup(char* filename) {
printf("It may not be useable!\n");
} // if/else
close(fd);
+
+ // Now reverse the byte-order reversal, if necessary....
+ if (IsLittleEndian() == 0) {
+ ReverseHeaderBytes(&mainHeader);
+ ReverseHeaderBytes(&secondHeader);
+ ReversePartitionBytes();
+ } // if
} else {
fprintf(stderr, "Unable to open file %s for writing! Aborting!\n", filename);
allOK = 0;
@@ -1611,8 +1683,12 @@ int GPTData::SaveGPTBackup(char* filename) {
int GPTData::LoadGPTBackup(char* filename) {
int fd, allOK = 1, val;
uint32_t numParts, sizeOfEntries, sizeOfParts, newCRC;
+ int littleEndian = 1;
if ((fd = open(filename, O_RDONLY)) != -1) {
+ if (IsLittleEndian() == 0)
+ littleEndian = 0;
+
// Let the MBRData class load the saved MBR...
protectiveMBR.ReadMBRData(fd);
@@ -1621,11 +1697,21 @@ int GPTData::LoadGPTBackup(char* filename) {
read(fd, &mainHeader, 512);
mainCrcOk = CheckHeaderCRC(&mainHeader);
+ // Reverse byte order, if necessary
+ if (littleEndian == 0) {
+ ReverseHeaderBytes(&mainHeader);
+ } // if
+
// Load the backup GPT header in much the same way as the main
// GPT header....
read(fd, &secondHeader, 512);
secondCrcOk = CheckHeaderCRC(&secondHeader);
+ // Reverse byte order, if necessary
+ if (littleEndian == 0) {
+ ReverseHeaderBytes(&secondHeader);
+ } // if
+
// Return valid headers code: 0 = both headers bad; 1 = main header
// good, backup bad; 2 = backup header good, main header bad;
// 3 = both headers good. Note these codes refer to valid GPT
@@ -1660,6 +1746,11 @@ int GPTData::LoadGPTBackup(char* filename) {
newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
mainPartsCrcOk = (newCRC == mainHeader.partitionEntriesCRC);
secondPartsCrcOk = (newCRC == secondHeader.partitionEntriesCRC);
+ // Reverse byte order, if necessary
+ if (littleEndian == 0) {
+ ReversePartitionBytes();
+ } // if
+
} else {
allOK = 0;
} // if/else
@@ -1722,16 +1813,60 @@ int GPTData::DestroyGPT(void) {
return (goOn == 'Y');
} // GPTData::DestroyGPT()
+void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
+ ReverseBytes((char*) &header->signature, 8);
+ ReverseBytes((char*) &header->revision, 4);
+ ReverseBytes((char*) &header->headerSize, 4);
+ ReverseBytes((char*) &header->headerCRC, 4);
+ ReverseBytes((char*) &header->reserved, 4);
+ ReverseBytes((char*) &header->currentLBA, 8);
+ ReverseBytes((char*) &header->backupLBA, 8);
+ ReverseBytes((char*) &header->firstUsableLBA, 8);
+ ReverseBytes((char*) &header->lastUsableLBA, 8);
+ ReverseBytes((char*) &header->partitionEntriesLBA, 8);
+ ReverseBytes((char*) &header->numParts, 4);
+ ReverseBytes((char*) &header->sizeOfPartitionEntries, 4);
+ ReverseBytes((char*) &header->partitionEntriesCRC, 4);
+ ReverseBytes((char*) header->reserved2, GPT_RESERVED);
+ ReverseBytes((char*) &header->diskGUID.data1, 8);
+ ReverseBytes((char*) &header->diskGUID.data2, 8);
+} // GPTData::ReverseHeaderBytes()
+
+// IMPORTANT NOTE: This function requires non-reversed mainHeader
+// structure!
+void GPTData::ReversePartitionBytes() {
+ uint32_t i;
+
+ // Check GPT signature on big-endian systems; this will mismatch
+ // if the function is called out of order. Unfortunately, it'll also
+ // mismatch if there's data corruption.
+ if ((mainHeader.signature != GPT_SIGNATURE) && (IsLittleEndian() == 0)) {
+ printf("GPT signature mismatch in GPTData::ReversePartitionBytes(). This indicates\n"
+ "data corruption or a misplaced call to this function.\n");
+ } // if signature mismatch....
+ for (i = 0; i < mainHeader.numParts; i++) {
+ ReverseBytes((char*) &partitions[i].partitionType.data1, 8);
+ ReverseBytes((char*) &partitions[i].partitionType.data2, 8);
+ ReverseBytes((char*) &partitions[i].uniqueGUID.data1, 8);
+ ReverseBytes((char*) &partitions[i].uniqueGUID.data2, 8);
+ ReverseBytes((char*) &partitions[i].firstLBA, 8);
+ ReverseBytes((char*) &partitions[i].lastLBA, 8);
+ ReverseBytes((char*) &partitions[i].attributes, 8);
+ } // for
+} // GPTData::ReversePartitionBytes()
+
+/******************************************
+ * *
+ * Additional non-class support functions *
+ * *
+ ******************************************/
+
// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
// never fail these tests, but the struct types may fail depending on compile options.
// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
// sizes.
int SizesOK(void) {
int allOK = 1;
- union {
- uint32_t num;
- unsigned char uc[sizeof(uint32_t)];
- } endian;
if (sizeof(uint8_t) != 1) {
fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
@@ -1762,11 +1897,10 @@ int SizesOK(void) {
allOK = 0;
} // if
// Determine endianness; set allOK = 0 if running on big-endian hardware
- endian.num = 1;
- if (endian.uc[0] != (unsigned char) 1) {
- fprintf(stderr, "Running on big-endian hardware, but this program only works on little-endian\n"
- "systems; aborting!\n");
- allOK = 0;
+ if (IsLittleEndian() == 0) {
+ fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly"
+ " tested!\nBeware!\n");
+ // allOK = 0;
} // if
return (allOK);
} // SizesOK()