summaryrefslogtreecommitdiff
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
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....
-rw-r--r--CHANGELOG16
-rw-r--r--README21
-rw-r--r--gdisk.862
-rw-r--r--gpt.cc196
-rw-r--r--gpt.h5
-rw-r--r--mbr.cc59
-rw-r--r--support.cc34
-rw-r--r--support.h2
8 files changed, 316 insertions, 79 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 53d3c5c..ea47e39 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,19 @@
+0.3.5:
+------
+
+- Tweaked the disk type identification code to warn users to re-sync their
+ hybrid MBRs when one is detected.
+
+- Tweaked MBR-reading code to ignore 0xEE (EFI GPT) partitions. This will
+ only have an effect on a poorly partitioned MBR disk that contains an
+ inappropriate EFI GPT partition, or when attempting to recover a
+ corrupted disk by using the hybrid MBR for data recovery.
+
+- Added big-endian (PowerPC, etc.) support!
+
+- Added code to identify and warn of the presence of an Apple Partition
+ Map (APM) on the disk.
+
0.3.4:
------
diff --git a/README b/README
index 8cb8cd4..a212e8f 100644
--- a/README
+++ b/README
@@ -38,7 +38,7 @@ Installing
To compile gdisk, you must have appropriate development tools installed,
most notably the GNU Compiler Collection (GCC) and its g++ compiler for
-C++. uncompress the package and type "make" at the command prompt in the
+C++. Uncompress the package and type "make" at the command prompt in the
resulting directory. The result should be a program file called gdisk. You
can use this in place or copy the file to a suitable directory, such as
/usr/local/sbin. You can copy the man page (gdisk.8) to /usr/local/man/man8
@@ -55,20 +55,15 @@ extensive changes to a handful of 80-160 GiB hard disks. I believe all
data-corruption bugs to be squashed, but I know full well that the odds of
my missing something are high. This is particularly true for large drives;
I have no way of testing the software with > 2TiB drives, which will test
-the 64-bit sector pointer support.
-
-The MBR-to-GPT feature seems to work well for data drives, but it's largely
-untested on boot drives. One attempt with Windows failed miserably, but I
-believe that was because of Windows' inherent limitations with respect to
-GPT. (The partitions themselves were intact.)
+the 64-bit sector pointer support. I've received user reports of success
+with >2TiB drives, though.
My main development platform is a system running the 64-bit version of
-Ubuntu. I've also tested on 64-bit OpenSuSE and 32-bit Fedora 10. Problems
-relating to 64-bit integers on the 32-bit Linux have been common during
-development and may crop up in the future. The Mac OS X support is new,
-and has at least one bug/limitation: It seems to be impossible to write
-a new partition table if any partitions from the disk are currently
-mounted.
+Ubuntu 8.04. I've also tested on 64-bit OpenSuSE, 32-bit Fedora 10, 32-bit
+Ubuntu 6.10, 64-bit Gentoo, 32-bit PowerPC Linux, and 32-bit Intel-based
+Mac OS X. Problems relating to 64-bit integers on the 32-bit Linux have
+been common during development and may crop up in the future. The Mac OS
+X and big-endian (PowerPC) support is new.
Redistribution
--------------
diff --git a/gdisk.8 b/gdisk.8
index 22be012..1207f87 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -27,7 +27,7 @@ partitions; no redundancy or error correction capabilities; and 32-bit data
structures that, in conjunction with the common 512-byte sector size,
impose a hard 2 TiB limit on the size of partitions and disks. This final
drawback makes MBR partitions unsuitable for use on large hardware RAID
-arrays. Individual disk sizes are expected to reach the 2 TiB limit in
+arrays. Individual disk sizes are expected to surpass the 2 TiB limit in
2009, so MBR will become an unsuitable partitioning system even for
individual hard disks in the near future.
@@ -94,7 +94,10 @@ program operates mainly on the GPT headers and partition tables; however,
it can and will generate a fresh protective MBR, when required. (Any boot
loader code in the protective MBR will not be disturbed.) If you've created
an unusual protective MBR, such as a hybrid MBR created by
-.IR "gptsync",
+.IR "gptsync"
+or
+.BR "gdisk"'s
+own hybrid MBR creation feature,
this should not be disturbed by most ordinary actions. Some advanced data
recovery options require you to understand the distinctions between the
main and backup data, as well as between the GPT headers and the partition
@@ -102,7 +105,7 @@ tables.
The
.B "gdisk"
-program employs a user interface similar to that of
+program employs a user interface similar to that of Linux's
.BR "fdisk",
but
.B "gdisk"
@@ -170,13 +173,6 @@ will note that
.B "gdisk"
lacks the options and limitations associated with CHS geometries.
-For best results, you should always use an OS-specific partition table
-program. For example, you should make Mac OS X partitions with the Mac OS
-X Disk Utility
-program and Linux partitions with the Linux
-.B "gdisk"
-or GNU Parted program.
-
Upon start,
.B gdisk
attempts to identify the partition type in use on the specified disk. If it
@@ -215,8 +211,8 @@ and in whatever sizes are desired.
.TP
.B *
-Boot disks for EFI-based systems require an
-.IR "EFI System Partition" (
+Boot disks for EFI-based systems require an "EFI System
+Partition" (
.B "gdisk"
internal code 0xEF00) formatted as FAT-32. The recommended size of this
partition is 100 MiB. Boot-related files are stored here. (Note that GNU
@@ -224,6 +220,16 @@ Parted identifies such partitions as having the "boot flag" set.)
.TP
.B *
+Some boot loaders for BIOS-based systems make use of a "BIOS Boot
+Partition" (
+.B "gdisk"
+internal code 0xEF02), in which the secondary boot loader is stored,
+possibly without the benefit of a filesystem. This partition can
+typically be quite small (a few tens of kilobytes), but you should
+consult your boot loader documentation for details.
+
+.TP
+.B *
If Windows is to boot from a GPT disk, a partition of type "Microsoft
Reserved" (
.B "gdisk"
@@ -327,8 +333,8 @@ use the 'i' command.
.B q
Quit from the program
.IR "without saving data".
-Use it if you just wanted to view information or if you make a mistake and
-want to back out of all your changes.
+Use this option if you just wanted to view information or if you make a
+mistake and want to back out of all your changes.
.TP
.B s
@@ -477,14 +483,16 @@ the main menu.
.TP
.B r
Return to the main menu. You can go back to the main menu with this option.
+
.TP
.B s
-Resize partition table. The partition table may be resized with this
-option. The default size is 128 entries. Officially, sizes of less than
-16KB (128 entries, given the normal entry size) are unsupported by the GPT
-specification; however, in practice they seem to work, and can sometimes be
-useful in converting MBR disks. Larger sizes also work fine. Linux imposes
-its own limits on the number of partitions, though.
+Resize partition table. The default partition table size is 128 entries.
+Officially, sizes of less than 16KB (128 entries, given the normal entry
+size) are unsupported by the GPT specification; however, in practice they
+seem to work, and can sometimes be useful in converting MBR disks. Larger
+sizes also work fine. OSes may impose their own limits on the number of
+partitions, though.
+
.TP
.B v
Verify disk. This option is identical to the 'v' option in the main menu.
@@ -510,23 +518,17 @@ entering data. When only one option is possible,
usually bypasses the prompt entirely.
.SH BUGS
-As of August of 2009 (version 0.3.3),
+As of August of 2009 (version 0.3.5),
.B gdisk
should be considered early beta software. Known bugs and
limitations include:
.TP
.B *
-The program runs correctly only on little-endian (Intel and similar) CPUs.
-It should fail gracefully on PowerPC and other big-endian CPUs, but this
-hasn't been tested.
-
-.TP
-.B *
The program compiles correctly only on Linux and Mac OS X. Both 64-bit
(x86-64) and 32-bit (x86) versions for Linux have been tested, the former
more thoroughly than the latter. The Mac OS X support was added with
-version 0.3.1 and has not been thoroughly tested.
+version 0.3.1 and has not been as thoroughly tested.
.TP
.B *
@@ -593,6 +595,10 @@ favor of MBR may be your only options in this case.
.PP
+The support for big-endian CPUs (PowerPC, for example) is new, as of version
+0.3.5. I advise using caution on that platform, particularly with the more
+obscure features of the program.
+
.SH AUTHORS
Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
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()
diff --git a/gpt.h b/gpt.h
index 4c2904e..d5057f6 100644
--- a/gpt.h
+++ b/gpt.h
@@ -12,6 +12,9 @@
#define __GPTSTRUCTS
#define GPT_SIGNATURE UINT64_C(0x5452415020494645)
+// Signatures for Apple (APM) disks, multiplied by 0x100000000
+#define APM_SIGNATURE1 UINT64_C(0x00004D5000000000)
+#define APM_SIGNATURE2 UINT64_C(0x0000535400000000)
/* Number and size of GPT entries... */
#define NUM_GPT_ENTRIES 128
@@ -132,6 +135,8 @@ public:
int SaveGPTBackup(char* filename);
int LoadGPTBackup(char* filename);
int DestroyGPT(void); // Returns 1 if user proceeds
+ void ReverseHeaderBytes(struct GPTHeader* header); // for endianness
+ void ReversePartitionBytes(); // for endianness
// Return data about the GPT structures....
uint32_t GetNumParts(void) {return mainHeader.numParts;}
diff --git a/mbr.cc b/mbr.cc
index e09d6b6..d907b56 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -141,6 +141,18 @@ void MBRData::ReadMBRData(int fd) {
read(fd, &nulls, 2);
read(fd, partitions, 64);
read(fd, &MBRSignature, 2);
+
+ // Reverse the byte order, if necessary
+ if (IsLittleEndian() == 0) {
+ ReverseBytes((char*) &diskSignature, 4);
+ ReverseBytes((char*) &nulls, 2);
+ ReverseBytes((char*) &MBRSignature, 2);
+ for (i = 0; i < 4; i++) {
+ ReverseBytes((char*) &partitions[i].firstLBA, 4);
+ ReverseBytes((char*) &partitions[i].lengthLBA, 4);
+ } // for
+ } // if
+
if (MBRSignature != MBR_SIGNATURE) {
allOK = 0;
state = invalid;
@@ -179,9 +191,19 @@ void MBRData::ReadMBRData(int fd) {
for (i = 0; i < 4; i++) {
if (partitions[i].partitionType == UINT8_C(0xEE)) {
state = gpt;
- } /* if */
- } /* for */
- } /* if */
+ } // 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
/* // Tell the user what the MBR state is...
switch (state) {
@@ -216,11 +238,35 @@ int MBRData::WriteMBRData(void) {
// Save the MBR data to a file. Note that this function writes ONLY the
// MBR data, not the logical partitions (if any are defined).
void MBRData::WriteMBRData(int fd) {
+ int i;
+
+ // Reverse the byte order, if necessary
+ if (IsLittleEndian() == 0) {
+ ReverseBytes((char*) &diskSignature, 4);
+ ReverseBytes((char*) &nulls, 2);
+ ReverseBytes((char*) &MBRSignature, 2);
+ for (i = 0; i < 4; i++) {
+ ReverseBytes((char*) &partitions[i].firstLBA, 4);
+ ReverseBytes((char*) &partitions[i].lengthLBA, 4);
+ } // for
+ } // if
+
write(fd, code, 440);
write(fd, &diskSignature, 4);
write(fd, &nulls, 2);
write(fd, partitions, 64);
write(fd, &MBRSignature, 2);
+
+ // Reverse the byte order, if necessary
+ if (IsLittleEndian() == 0) {
+ ReverseBytes((char*) &diskSignature, 4);
+ ReverseBytes((char*) &nulls, 2);
+ ReverseBytes((char*) &MBRSignature, 2);
+ for (i = 0; i < 4; i++) {
+ ReverseBytes((char*) &partitions[i].firstLBA, 4);
+ ReverseBytes((char*) &partitions[i].lengthLBA, 4);
+ } // for
+ }// if
} // MBRData::WriteMBRData(int fd)
// This is a recursive function to read all the logical partitions, following the
@@ -240,7 +286,12 @@ int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
(unsigned long) offset);
allOK = 0;
- }
+ } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
+ ReverseBytes((char*) &ebr.MBRSignature, 2);
+ ReverseBytes((char*) &ebr.partitions[0].firstLBA, 4);
+ ReverseBytes((char*) &ebr.partitions[0].lengthLBA, 4);
+ } // if/else/if
+
if (ebr.MBRSignature != MBR_SIGNATURE) {
allOK = 0;
printf("MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
diff --git a/support.cc b/support.cc
index 8ee5f33..1cef7d6 100644
--- a/support.cc
+++ b/support.cc
@@ -130,7 +130,6 @@ uint64_t GetLastSector(uint64_t low, uint64_t high, char prompt[]) {
return ((uint64_t) response);
} // GetLastSector()
-// Return a plain-text name for a partition type.
// Takes a size in bytes (in size) and converts this to a size in
// SI units (KiB, MiB, GiB, TiB, or PiB), returned in C++ string
// form
@@ -182,8 +181,8 @@ int GetBlockSize(int fd) {
#endif
if (result != 512) {
- printf("\aWARNING! Sector size is not 512 bytes! This program is likely to");
- printf("misbehave! Proceed at your own risk!\n\n");
+ printf("\aWARNING! Sector size is not 512 bytes! This program is likely to ");
+ printf("misbehave!\nProceed at your own risk!\n\n");
} // if
if (err == -1)
@@ -191,6 +190,7 @@ int GetBlockSize(int fd) {
return (result);
} // GetBlockSize()
+// Return a plain-text name for a partition type.
// Convert a GUID to a string representation, suitable for display
// to humans....
char* GUIDToStr(struct GUIDData theGUID, char* theString) {
@@ -303,6 +303,34 @@ GUIDData GetGUID(void) {
return theGUID;
} // GetGUID()
+// Return 1 if the CPU architecture is little endian, 0 if it's big endian....
+int IsLittleEndian(void) {
+ int littleE = 1; // assume little-endian (Intel-style)
+ union {
+ uint32_t num;
+ unsigned char uc[sizeof(uint32_t)];
+ } endian;
+
+ endian.num = 1;
+ if (endian.uc[0] != (unsigned char) 1) {
+ littleE = 0;
+ } // if
+ return (littleE);
+} // IsLittleEndian()
+
+// Reverse the byte order of theValue; numBytes is number of bytes
+void ReverseBytes(char* theValue, int numBytes) {
+ char* tempValue;
+ int i;
+
+ tempValue = (char*) malloc(numBytes);
+ for (i = 0; i < numBytes; i++)
+ tempValue[i] = theValue[i];
+ for (i = 0; i < numBytes; i++)
+ theValue[i] = tempValue[numBytes - i - 1];
+ free(tempValue);
+} // ReverseBytes()
+
// Compute (2 ^ value). Given the return type, value must be 63 or less.
// Used in some bit-fiddling functions
uint64_t PowerOf2(int value) {
diff --git a/support.h b/support.h
index e1e7203..d0b3e57 100644
--- a/support.h
+++ b/support.h
@@ -38,6 +38,8 @@ char* BytesToSI(uint64_t size, char theValue[]);
int GetBlockSize(int fd);
char* GUIDToStr(struct GUIDData theGUID, char* theString);
GUIDData GetGUID(void);
+int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-endian
+void ReverseBytes(char* theValue, int numBytes); // Reverses byte-order of theValue
uint64_t PowerOf2(int value);
uint64_t disksize(int fd, int* err);