GPT fdisk 0.5.0

Added several features, including a restructuring of the menu system,
GPT-to-MBR conversion, and the ability to re-read the MBR to generate
a fresh GPT from the current on-disk MBR.
This commit is contained in:
srs5694
2009-09-21 20:51:47 -04:00
parent e35eb1beb6
commit 978041ca61
13 changed files with 1167 additions and 966 deletions

View File

@@ -1,3 +1,45 @@
0.5.0:
------
- Added GPT-to-MBR conversion function. It's very limited, but potentially
useful in some cases.
- Fixed bug that caused incorrect file sizes to be reported on 32-bit
Linux, thus causing problems when editing partition tables in disk images
or when loading GPT backup files.
- Fixed bug that caused bogus CRC error reports when loading backup GPT
data.
- Reorganized menus. There are now three: the main menu, the experts' menu,
and the recovery & transformation menu. The last of these has most of the
items that had been on the earlier versions' experts' menu.
- Added ability to re-load the MBR and generate a fresh GPT from it. This
is normally identical to quitting and re-running the program, but it
could be handy if, say, the GPT partitions on a hybrid configuration are
badly messed up; this will enable using the hybridized partitions as the
starting point for a new GPT setup.
- The program now generates CHS values for hybrid and GPT-to-MBR conversion
MBRs. For the moment, the assumption is the maximum number of heads and
sectors per track (255 and 63, respectively), although the bulk of the
code supports other values -- it'd just be awkward to enter the data in
the user interface.
- Fixed minor display bug that caused number of sectors on the disk to be
shown as 0 on large disks when running 32-bit binaries.
- Reverted 0.4.2's zap (destroy GPT) changes, since I don't want to wipe
out a valid MBR if the user created that MBR over an older GPT without
first properly wiping out the GPT, and the user now wants to wipe out
the GPT.
- Reformatted and edited the man page. Aside from edits related to the
preceding program changes, I've altered the markup slightly and trimmed
much of the more tutorial information from the man page to better
conform to typical terse man page style.
0.4.2: 0.4.2:
------ ------

View File

@@ -28,6 +28,6 @@ clean: #no pre-reqs
depend: $(SRCS) depend: $(SRCS)
$(DEPEND) $(SRCS) $(DEPEND) $(SRCS)
$(OBJS): $(OBJS):
# DO NOT DELETE # DO NOT DELETE

764
gdisk.8

File diff suppressed because it is too large Load Diff

364
gdisk.cc
View File

@@ -17,25 +17,27 @@
// Function prototypes.... // Function prototypes....
// int ReadPartitions(char* filename, struct GPTData* theGPT); // int ReadPartitions(char* filename, struct GPTData* theGPT);
int DoCommand(char* filename, struct GPTData* theGPT); void MainMenu(char* filename, struct GPTData* theGPT);
void ShowCommands(void); void ShowCommands(void);
void ExpertsMenu(char* filename, struct GPTData* theGPT);
void ShowExpertCommands(void); void ShowExpertCommands(void);
int ExpertsMenu(char* filename, struct GPTData* theGPT); void RecoveryMenu(char* filename, struct GPTData* theGPT);
void ShowRecoveryCommands(void);
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
GPTData theGPT; GPTData theGPT;
int doMore = 1; int doMore = 1;
char* device = NULL; char* device = NULL;
printf("GPT fdisk (gdisk) version 0.4.2\n\n"); printf("GPT fdisk (gdisk) version 0.5.0\n\n");
if (argc == 2) { // basic usage if (argc == 2) { // basic usage
if (SizesOK()) { if (SizesOK()) {
doMore = theGPT.LoadPartitions(argv[1]); doMore = theGPT.LoadPartitions(argv[1]);
while (doMore) { if (doMore) {
doMore = DoCommand(argv[1], &theGPT); MainMenu(argv[1], &theGPT);
} // while } // if (doMore)
} // if } // if (SizesOK())
} else if (argc == 3) { // usage with "-l" option } else if (argc == 3) { // usage with "-l" option
if (SizesOK()) { if (SizesOK()) {
if (strcmp(argv[1], "-l") == 0) { if (strcmp(argv[1], "-l") == 0) {
@@ -55,112 +57,242 @@ int main(int argc, char* argv[]) {
} // if/else } // if/else
} // main } // main
// Accept a command and execute it. Returns 0 if the command includes // Accept a command and execute it. Returns only when the user
// an exit condition (such as a q or w command), 1 if more commands // wants to exit (such as after a 'w' or 'q' command).
// should be processed. void MainMenu(char* filename, struct GPTData* theGPT) {
int DoCommand(char* filename, struct GPTData* theGPT) { char command, line[255], buFile[255];
char command, line[255]; int goOn = 1;
int retval = 1;
PartTypes typeHelper; PartTypes typeHelper;
uint32_t temp1, temp2; uint32_t temp1, temp2;
printf("\nCommand (m for help): "); do {
fgets(line, 255, stdin); printf("\nCommand (? for help): ");
sscanf(line, "%c", &command); fgets(line, 255, stdin);
switch (command) { sscanf(line, "%c", &command);
case 'b': case 'B': switch (command) {
theGPT->XFormDisklabel(); case 'b': case 'B':
break; printf("Enter backup filename to save: ");
case 'c': case 'C': fgets(line, 255, stdin);
if (theGPT->GetPartRange(&temp1, &temp2) > 0) sscanf(line, "%s", &buFile);
theGPT->SetName(theGPT->GetPartNum()); theGPT->SaveGPTBackup(buFile);
else break;
printf("No partitions\n"); case 'c': case 'C':
break; if (theGPT->GetPartRange(&temp1, &temp2) > 0)
case 'd': case 'D': theGPT->SetName(theGPT->GetPartNum());
theGPT->DeletePartition(); else
break; printf("No partitions\n");
case 'i': case 'I': break;
theGPT->ShowDetails(); case 'd': case 'D':
break; theGPT->DeletePartition();
case 'l': case 'L': break;
typeHelper.ShowTypes(); case 'i': case 'I':
break; theGPT->ShowDetails();
case 'n': case 'N': break;
theGPT->CreatePartition(); case 'l': case 'L':
break; typeHelper.ShowTypes();
case 'o': case 'O': break;
printf("This option deletes all partitions and creates a new " case 'n': case 'N':
"protective MBR.\nProceed? "); theGPT->CreatePartition();
if (GetYN() == 'Y') { break;
theGPT->ClearGPTData(); case 'o': case 'O':
theGPT->MakeProtectiveMBR(); printf("This option deletes all partitions and creates a new "
} // if "protective MBR.\nProceed? ");
break; if (GetYN() == 'Y') {
case 'p': case 'P': theGPT->ClearGPTData();
theGPT->DisplayGPTData(); theGPT->MakeProtectiveMBR();
break; } // if
case 'q': case 'Q': break;
retval = 0; case 'p': case 'P':
break; theGPT->DisplayGPTData();
case 's': case 'S': break;
theGPT->SortGPT(); case 'q': case 'Q':
printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n"); goOn = 0;
break; break;
case 't': case 'T': case 'r': case 'R':
theGPT->ChangePartType(); RecoveryMenu(filename, theGPT);
break; goOn = 0;
case 'v': case 'V': break;
if (theGPT->Verify() > 0) { // problems found case 's': case 'S':
printf("You may be able to correct the problems by using options on the experts\n" theGPT->SortGPT();
"menu (press 'x' at the command prompt). Good luck!\n"); printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n");
} // if break;
break; case 't': case 'T':
case 'w': case 'W': theGPT->ChangePartType();
if (theGPT->SaveGPTData() == 1) break;
retval = 0; case 'v': case 'V':
break; if (theGPT->Verify() > 0) { // problems found
case 'x': case 'X': printf("You may be able to correct the problems by using options on the experts\n"
retval = ExpertsMenu(filename, theGPT); "menu (press 'x' at the command prompt). Good luck!\n");
break; } // if
default: break;
ShowCommands(); case 'w': case 'W':
break; if (theGPT->SaveGPTData() == 1)
} // switch goOn = 0;
return (retval); break;
} // DoCommand() case 'x': case 'X':
ExpertsMenu(filename, theGPT);
goOn = 0;
break;
default:
ShowCommands();
break;
} // switch
} while (goOn);
} // MainMenu()
void ShowCommands(void) { void ShowCommands(void) {
printf("b\tconvert BSD disklabel partitions\n"); printf("b\tback up GPT data to a file\n");
printf("c\tchange a partition's name\n"); printf("c\tchange a partition's name\n");
printf("d\tdelete a partition\n"); printf("d\tdelete a partition\n");
printf("i\tshow detailed information on a partition\n"); printf("i\tshow detailed information on a partition\n");
printf("l\tlist available partition types\n"); printf("l\tlist known partition types\n");
printf("m\tprint this menu\n");
printf("n\tadd a new partition\n"); printf("n\tadd a new partition\n");
printf("o\tcreate a new empty GUID partition table (GPT)\n"); printf("o\tcreate a new empty GUID partition table (GPT)\n");
printf("p\tprint the partition table\n"); printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n"); printf("q\tquit without saving changes\n");
printf("r\trecovery and transformation options (experts only)\n");
printf("s\tsort partitions\n"); printf("s\tsort partitions\n");
printf("t\tchange a partition's type code\n"); printf("t\tchange a partition's type code\n");
printf("v\tverify disk\n"); printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n"); printf("w\twrite table to disk and exit\n");
printf("x\textra functionality (experts only)\n"); printf("x\textra functionality (experts only)\n");
printf("?\tprint this menu\n");
} // ShowCommands() } // ShowCommands()
// Accept a command and execute it. Returns 0 if the command includes // Accept a recovery & transformation menu command. Returns only when the user
// an exit condition (such as a q or w command), 1 if more commands // issues an exit command, such as 'w' or 'q'.
// should be processed. void RecoveryMenu(char* filename, struct GPTData* theGPT) {
int ExpertsMenu(char* filename, struct GPTData* theGPT) {
char command, line[255], buFile[255]; char command, line[255], buFile[255];
int retval = 1; PartTypes typeHelper;
uint32_t temp1;
int goOn = 1;
do {
printf("\nrecovery/transformation command (? for help): ");
fgets(line, 255, stdin);
sscanf(line, "%c", &command);
switch (command) {
case 'b': case 'B':
theGPT->RebuildMainHeader();
break;
case 'c': case 'C':
printf("Warning! This will probably do weird things if you've converted an MBR to\n"
"GPT form and haven't yet saved the GPT! Proceed? ");
if (GetYN() == 'Y')
theGPT->LoadSecondTableAsMain();
break;
case 'd': case 'D':
theGPT->RebuildSecondHeader();
break;
case 'e': case 'E':
printf("Warning! This will probably do weird things if you've converted an MBR to\n"
"GPT form and haven't yet saved the GPT! Proceed? ");
if (GetYN() == 'Y')
theGPT->LoadMainTable();
break;
case 'f': case 'F':
printf("Warning! This will destroy the currently defined partitions! Proceed? ");
if (GetYN() == 'Y') {
if (theGPT->LoadMBR(filename) == 1) { // successful load
theGPT->XFormPartitions();
} else {
printf("Problem loading MBR! GPT is untouched; regenerating protective MBR!\n");
theGPT->MakeProtectiveMBR();
} // if/else
} // if
break;
case 'g': case 'G':
temp1 = theGPT->XFormToMBR();
if (temp1 > 0) {
printf("Converted %d partitions. Finalize and exit? ", temp1);
if (GetYN() == 'Y') {
if (theGPT->DestroyGPT(0) > 0)
goOn = 0;
} else {
theGPT->MakeProtectiveMBR();
printf("Note: New protective MBR created.\n");
} // if/else
} // if
break;
case 'h': case 'H':
theGPT->MakeHybrid();
break;
case 'i': case 'I':
theGPT->ShowDetails();
break;
case 'l': case 'L':
printf("Enter backup filename to load: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->LoadGPTBackup(buFile);
break;
case 'm': case 'M':
MainMenu(filename, theGPT);
goOn = 0;
break;
case 'o': case 'O':
theGPT->DisplayMBRData();
break;
case 'p': case 'P':
theGPT->DisplayGPTData();
break;
case 'q': case 'Q':
goOn = 0;
break;
case 't': case 'T':
theGPT->XFormDisklabel();
break;
case 'v': case 'V':
theGPT->Verify();
break;
case 'w': case 'W':
if (theGPT->SaveGPTData() == 1) {
goOn = 0;
} // if
break;
case 'x': case 'X':
ExpertsMenu(filename, theGPT);
goOn = 0;
break;
default:
ShowRecoveryCommands();
break;
} // switch
} while (goOn);
} // RecoveryMenu()
void ShowRecoveryCommands(void) {
printf("b\tuse backup GPT header (rebuilding main)\n");
printf("c\tload backup partition table from disk (rebuilding main)\n");
printf("d\tuse main GPT header (rebuilding backup)\n");
printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tload MBR and build fresh GPT from it\n");
printf("g\tconvert GPT into MBR and exit\n");
printf("h\tmake hybrid MBR\n");
printf("i\tshow detailed information on a partition\n");
printf("l\tload partition data from a backup file\n");
printf("m\treturn to main menu\n");
printf("o\tprint protective MBR data\n");
printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n");
printf("t\ttransform BSD disklabel partition\n");
printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n");
printf("x\textra functionality (experts only)\n");
printf("?\tprint this menu\n");
} // ShowRecoveryCommands()
// Accept an experts' menu command. Returns only after the user
// selects an exit command, such as 'w' or 'q'.
void ExpertsMenu(char* filename, struct GPTData* theGPT) {
char command, line[255];
PartTypes typeHelper; PartTypes typeHelper;
uint32_t pn; uint32_t pn;
uint32_t temp1, temp2; uint32_t temp1, temp2;
int goOn = 1; int goOn = 1;
do { do {
printf("\nExpert command (m for help): "); printf("\nExpert command (? for help): ");
fgets(line, 255, stdin); fgets(line, 255, stdin);
sscanf(line, "%c", &command); sscanf(line, "%c", &command);
switch (command) { switch (command) {
@@ -170,25 +302,7 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
else else
printf("No partitions\n"); printf("No partitions\n");
break; break;
case 'b': case 'B':
theGPT->RebuildMainHeader();
break;
case 'c': case 'C': case 'c': case 'C':
printf("Warning! This will probably do weird things if you've converted an MBR to\n"
"GPT form and haven't yet saved the GPT! Proceed? ");
if (GetYN() == 'Y')
theGPT->LoadSecondTableAsMain();
break;
case 'd': case 'D':
theGPT->RebuildSecondHeader();
break;
case 'e': case 'E':
printf("Warning! This will probably do weird things if you've converted an MBR to\n"
"GPT form and haven't yet saved the GPT! Proceed? ");
if (GetYN() == 'Y')
theGPT->LoadMainTable();
break;
case 'f': case 'F':
if (theGPT->GetPartRange(&temp1, &temp2) > 0) { if (theGPT->GetPartRange(&temp1, &temp2) > 0) {
pn = theGPT->GetPartNum(); pn = theGPT->GetPartNum();
printf("Enter the partition's new unique GUID:\n"); printf("Enter the partition's new unique GUID:\n");
@@ -199,23 +313,12 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
printf("Enter the disk's unique GUID:\n"); printf("Enter the disk's unique GUID:\n");
theGPT->SetDiskGUID(GetGUID()); theGPT->SetDiskGUID(GetGUID());
break; break;
case 'h': case 'H':
theGPT->MakeHybrid();
break;
case 'i': case 'I': case 'i': case 'I':
theGPT->ShowDetails(); theGPT->ShowDetails();
break; break;
case 'k': case 'K': case 'm': case 'M':
printf("Enter backup filename to save: "); MainMenu(filename, theGPT);
fgets(line, 255, stdin); goOn = 0;
sscanf(line, "%s", &buFile);
theGPT->SaveGPTBackup(buFile);
break;
case 'l': case 'L':
printf("Enter backup filename to load: ");
fgets(line, 255, stdin);
sscanf(line, "%s", &buFile);
theGPT->LoadGPTBackup(buFile);
break; break;
case 'n': case 'N': case 'n': case 'N':
theGPT->MakeProtectiveMBR(); theGPT->MakeProtectiveMBR();
@@ -227,10 +330,10 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
theGPT->DisplayGPTData(); theGPT->DisplayGPTData();
break; break;
case 'q': case 'Q': case 'q': case 'Q':
retval = 0;
goOn = 0; goOn = 0;
break; break;
case 'r': case 'R': case 'r': case 'R':
RecoveryMenu(filename, theGPT);
goOn = 0; goOn = 0;
break; break;
case 's': case 'S': case 's': case 'S':
@@ -241,14 +344,12 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
break; break;
case 'w': case 'W': case 'w': case 'W':
if (theGPT->SaveGPTData() == 1) { if (theGPT->SaveGPTData() == 1) {
retval = 0;
goOn = 0; goOn = 0;
} // if } // if
break; break;
case 'z': case 'Z': case 'z': case 'Z':
if (theGPT->DestroyGPT() == 1) { if (theGPT->DestroyGPT() == 1) {
retval = 0; goOn = 0;
goOn = 0;
} }
break; break;
default: default:
@@ -256,29 +357,22 @@ int ExpertsMenu(char* filename, struct GPTData* theGPT) {
break; break;
} // switch } // switch
} while (goOn); } while (goOn);
return (retval);
} // ExpertsMenu() } // ExpertsMenu()
void ShowExpertCommands(void) { void ShowExpertCommands(void) {
printf("a\tset attributes\n"); printf("a\tset attributes\n");
printf("b\tuse backup GPT header (rebuilding main)\n"); printf("c\tchange partition GUID\n");
printf("c\tload backup partition table from disk (rebuilding main)\n");
printf("d\tuse main GPT header (rebuilding backup)\n");
printf("e\tload main partition table from disk (rebuilding backup)\n");
printf("f\tchange partition GUID\n");
printf("g\tchange disk GUID\n"); printf("g\tchange disk GUID\n");
printf("h\tmake hybrid MBR\n");
printf("i\tshow detailed information on a partition\n"); printf("i\tshow detailed information on a partition\n");
printf("k\tsave partition data to a backup file\n"); printf("m\treturn to main menu\n");
printf("l\tload partition data from a backup file\n");
printf("m\tprint this menu\n");
printf("n\tcreate a new protective MBR\n"); printf("n\tcreate a new protective MBR\n");
printf("o\tprint protective MBR data\n"); printf("o\tprint protective MBR data\n");
printf("p\tprint the partition table\n"); printf("p\tprint the partition table\n");
printf("q\tquit without saving changes\n"); printf("q\tquit without saving changes\n");
printf("r\treturn to main menu\n"); printf("r\trecovery and transformation options (experts only)\n");
printf("s\tresize partition table\n"); printf("s\tresize partition table\n");
printf("v\tverify disk\n"); printf("v\tverify disk\n");
printf("w\twrite table to disk and exit\n"); printf("w\twrite table to disk and exit\n");
printf("z\tDestroy GPT data structures and exit\n"); printf("z\tzap (destroy) GPT data structures and exit\n");
printf("?\tprint this menu\n");
} // ShowExpertCommands() } // ShowExpertCommands()

251
gpt.cc
View File

@@ -112,7 +112,24 @@ int GPTData::Verify(void) {
"table when you save your partitions.\n"); "table when you save your partitions.\n");
} // if } // if
// Now check that critical main and backup GPT entries match // Now check that the main and backup headers both point to themselves....
if (mainHeader.currentLBA != 1) {
problems++;
printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
"is being automatically corrected, but it may be a symptom of more serious\n"
"problems. Think carefully before saving changes with 'w' or using this disk.\n");
mainHeader.currentLBA = 1;
} // if
if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
problems++;
printf("\nProblem: The secondary header's self-pointer doesn't point to itself. This\n"
"problem is being automatically corrected, but it may be a symptom of more\n"
"serious problems. Think carefully before saving changes with 'w' or using this\n"
"disk.\n");
secondHeader.currentLBA = diskSize - UINT64_C(1);
} // if
// Now check that critical main and backup GPT entries match each other
if (mainHeader.currentLBA != secondHeader.backupLBA) { if (mainHeader.currentLBA != secondHeader.backupLBA) {
problems++; problems++;
printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n" printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
@@ -295,34 +312,38 @@ int GPTData::CheckHeaderValidity(void) {
// Note: Must be called BEFORE byte-order reversal on big-endian // Note: Must be called BEFORE byte-order reversal on big-endian
// systems! // systems!
int GPTData::CheckHeaderCRC(struct GPTHeader* header) { int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
uint32_t oldCRC, newCRC; uint32_t oldCRC, newCRC, hSize;
// Back up old header CRC 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 // computation to be valid
oldCRC = header->headerCRC; oldCRC = header->headerCRC;
if (IsLittleEndian() == 0)
ReverseBytes(&oldCRC, 4);
header->headerCRC = UINT32_C(0); header->headerCRC = UINT32_C(0);
hSize = header->headerSize;
// If big-endian system, reverse byte order
if (IsLittleEndian() == 0) {
ReverseBytes(&oldCRC, 4);
} // if
// Initialize CRC functions... // Initialize CRC functions...
chksum_crc32gentab(); chksum_crc32gentab();
// Compute CRC, restore original, and return result of comparison // Compute CRC, restore original, and return result of comparison
newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE); newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
mainHeader.headerCRC = oldCRC; header->headerCRC = oldCRC;
return (oldCRC == newCRC); return (oldCRC == newCRC);
} // GPTData::CheckHeaderCRC() } // GPTData::CheckHeaderCRC()
// Recompute all the CRCs. Must be called before saving (but after reversing // Recompute all the CRCs. Must be called before saving (but after reversing
// byte order on big-endian systems) if any changes have been made. // byte order on big-endian systems) if any changes have been made.
void GPTData::RecomputeCRCs(void) { void GPTData::RecomputeCRCs(void) {
uint32_t crc; uint32_t crc, hSize, trueNumParts;
uint32_t trueNumParts;
int littleEndian = 1; int littleEndian = 1;
// Initialize CRC functions... // Initialize CRC functions...
chksum_crc32gentab(); chksum_crc32gentab();
hSize = mainHeader.headerSize;
littleEndian = IsLittleEndian(); littleEndian = IsLittleEndian();
// Compute CRC of partition tables & store in main and secondary headers // Compute CRC of partition tables & store in main and secondary headers
@@ -342,11 +363,11 @@ void GPTData::RecomputeCRCs(void) {
secondHeader.headerCRC = 0; secondHeader.headerCRC = 0;
// Compute & store CRCs of main & secondary headers... // Compute & store CRCs of main & secondary headers...
crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE); crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
if (littleEndian == 0) if (littleEndian == 0)
ReverseBytes(&crc, 4); ReverseBytes(&crc, 4);
mainHeader.headerCRC = crc; mainHeader.headerCRC = crc;
crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE); crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
if (littleEndian == 0) if (littleEndian == 0)
ReverseBytes(&crc, 4); ReverseBytes(&crc, 4);
secondHeader.headerCRC = crc; secondHeader.headerCRC = crc;
@@ -359,7 +380,7 @@ void GPTData::RebuildMainHeader(void) {
mainHeader.signature = GPT_SIGNATURE; mainHeader.signature = GPT_SIGNATURE;
mainHeader.revision = secondHeader.revision; mainHeader.revision = secondHeader.revision;
mainHeader.headerSize = HEADER_SIZE; mainHeader.headerSize = secondHeader.headerSize;
mainHeader.headerCRC = UINT32_C(0); mainHeader.headerCRC = UINT32_C(0);
mainHeader.reserved = secondHeader.reserved; mainHeader.reserved = secondHeader.reserved;
mainHeader.currentLBA = secondHeader.backupLBA; mainHeader.currentLBA = secondHeader.backupLBA;
@@ -382,7 +403,7 @@ void GPTData::RebuildSecondHeader(void) {
secondHeader.signature = GPT_SIGNATURE; secondHeader.signature = GPT_SIGNATURE;
secondHeader.revision = mainHeader.revision; secondHeader.revision = mainHeader.revision;
secondHeader.headerSize = HEADER_SIZE; secondHeader.headerSize = mainHeader.headerSize;
secondHeader.headerCRC = UINT32_C(0); secondHeader.headerCRC = UINT32_C(0);
secondHeader.reserved = mainHeader.reserved; secondHeader.reserved = mainHeader.reserved;
secondHeader.currentLBA = mainHeader.backupLBA; secondHeader.currentLBA = mainHeader.backupLBA;
@@ -486,12 +507,6 @@ void GPTData::PartitionScan(int fd) {
printf("It will be destroyed if you continue!\n"); printf("It will be destroyed if you continue!\n");
printf("*******************************************************************\n\n\a"); printf("*******************************************************************\n\n\a");
} // if } // if
/* if (bsdFound) {
printf("\n*************************************************************************\n");
printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
"continue!\n");
printf("*************************************************************************\n\n\a");
} // if */
} // GPTData::PartitionScan() } // GPTData::PartitionScan()
// Read GPT data from a disk. // Read GPT data from a disk.
@@ -703,7 +718,7 @@ void GPTData::LoadSecondTableAsMain(void) {
// Writes GPT (and protective MBR) to disk. Returns 1 on successful // Writes GPT (and protective MBR) to disk. Returns 1 on successful
// write, 0 if there was a problem. // write, 0 if there was a problem.
int GPTData::SaveGPTData(void) { int GPTData::SaveGPTData(void) {
int allOK = 1, i; int allOK = 1;
char answer, line[256]; char answer, line[256];
int fd; int fd;
uint64_t secondTable; uint64_t secondTable;
@@ -856,6 +871,12 @@ int GPTData::SaveGPTBackup(char* filename) {
ReverseHeaderBytes(&secondHeader); ReverseHeaderBytes(&secondHeader);
} // if } // if
// Recomputing the CRCs is likely to alter them, which could be bad
// if the intent is to save a potentially bad GPT for later analysis;
// but if we don't do this, we get bogus errors when we load the
// backup. I'm favoring misses over false alarms....
RecomputeCRCs();
// Now write the protective MBR... // Now write the protective MBR...
protectiveMBR.WriteMBRData(fd); protectiveMBR.WriteMBRData(fd);
@@ -1021,8 +1042,8 @@ void GPTData::DisplayGPTData(void) {
uint64_t temp, totalFree; uint64_t temp, totalFree;
BytesToSI(diskSize * blockSize, sizeInSI); BytesToSI(diskSize * blockSize, sizeInSI);
printf("Disk %s: %lu sectors, %s\n", device, printf("Disk %s: %llu sectors, %s\n", device,
(unsigned long) diskSize, sizeInSI); (unsigned long long) diskSize, sizeInSI);
printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr)); printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts); printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
printf("First usable sector is %lu, last usable sector is %lu\n", printf("First usable sector is %lu, last usable sector is %lu\n",
@@ -1033,7 +1054,7 @@ void GPTData::DisplayGPTData(void) {
BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI)); BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
printf("\nNumber Start (sector) End (sector) Size Code Name\n"); printf("\nNumber Start (sector) End (sector) Size Code Name\n");
for (i = 0; i < mainHeader.numParts; i++) { for (i = 0; i < mainHeader.numParts; i++) {
partitions[i].ShowSummary(i, blockSize, sizeInSI); partitions[i].ShowSummary(i, blockSize);
} // for } // for
} // GPTData::DisplayGPTData() } // GPTData::DisplayGPTData()
@@ -1212,20 +1233,24 @@ void GPTData::SetAttributes(uint32_t partNum) {
// This function destroys the on-disk GPT structures. Returns 1 if the // This function destroys the on-disk GPT structures. Returns 1 if the
// user confirms destruction, 0 if the user aborts. // user confirms destruction, 0 if the user aborts.
int GPTData::DestroyGPT(void) { // If prompt == 0, don't ask user about proceeding and do NOT wipe out
// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
int GPTData::DestroyGPT(int prompt) {
int fd, i; int fd, i;
char blankSector[512], goOn; char blankSector[512], goOn = 'Y', blank = 'N';
for (i = 0; i < 512; i++) { for (i = 0; i < 512; i++) {
blankSector[i] = '\0'; blankSector[i] = '\0';
} // for } // for
if ((apmFound) || (bsdFound)) { if (((apmFound) || (bsdFound)) && prompt) {
printf("WARNING: APM or BSD disklabel structures detected! This operation could\n" printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
"damage any APM or BSD partitions on this disk!\n"); "damage any APM or BSD partitions on this disk!\n");
} // if APM or BSD } // if APM or BSD
printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device); if (prompt) {
goOn = GetYN(); printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
goOn = GetYN();
} // if
if (goOn == 'Y') { if (goOn == 'Y') {
fd = open(device, O_WRONLY); fd = open(device, O_WRONLY);
#ifdef __APPLE__ #ifdef __APPLE__
@@ -1245,18 +1270,21 @@ int GPTData::DestroyGPT(void) {
write(fd, blankSector, 512); write(fd, blankSector, 512);
lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
write(fd, blankSector, 512); // blank it out write(fd, blankSector, 512); // blank it out
printf("Blank out MBR? "); if (prompt) {
if (GetYN() == 'Y') { printf("Blank out MBR? ");
blank = GetYN();
}// if
// Note on below: Touch the MBR only if the user wants it completely
// blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
// the MBR, but this could wipe out a valid MBR that the program
// had subsequently discarded (say, if it conflicted with older GPT
// structures).
if (blank == 'Y') {
lseek64(fd, 0, SEEK_SET); lseek64(fd, 0, SEEK_SET);
write(fd, blankSector, 512); // blank it out write(fd, blankSector, 512); // blank it out
} else { // write current protective MBR, in case it's hybrid.... } else {
// find and delete 0xEE partitions in MBR printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
for (i = 0; i < 4; i++) { "with fdisk or another tool.\n");
if (protectiveMBR.GetType(i) == (uint8_t) 0xEE) {
protectiveMBR.DeletePartition(i);
} // if
} // for
protectiveMBR.WriteMBRData(fd);
} // if/else } // if/else
DiskSync(fd); DiskSync(fd);
close(fd); close(fd);
@@ -1374,8 +1402,8 @@ int GPTData::XFormPartitions(void) {
ClearGPTData(); ClearGPTData();
// Convert the smaller of the # of GPT or MBR partitions // Convert the smaller of the # of GPT or MBR partitions
if (mainHeader.numParts > (NUM_LOGICALS + 4)) if (mainHeader.numParts > (MAX_MBR_PARTS))
numToConvert = NUM_LOGICALS + 4; numToConvert = MAX_MBR_PARTS;
else else
numToConvert = mainHeader.numParts; numToConvert = mainHeader.numParts;
@@ -1467,15 +1495,102 @@ int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) {
return numDone; return numDone;
} // GPTData::XFormDisklabel(BSDData* disklabel) } // GPTData::XFormDisklabel(BSDData* disklabel)
// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
// functions. Returns 1 if operation was successful.
int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
int allOK = 1, typeCode, bootable;
uint64_t length;
char line[255];
if ((mbrPart < 0) || (mbrPart > 3)) {
printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
allOK = 0;
} // if
if (gptPart >= mainHeader.numParts) {
printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
allOK = 0;
} // if
if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
allOK = 0;
} // if
if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
(partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
printf("Caution: Partition end point past 32-bit pointer boundary;"
" some OSes may\nreact strangely.\n");
} // if partition ends past 32-bit (usually 2TiB) boundary
do {
printf("Enter an MBR hex code (default %02X): ",
typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
fgets(line, 255, stdin);
sscanf(line, "%x", &typeCode);
if (line[0] == '\n')
typeCode = partitions[gptPart].GetHexType() / 256;
} while ((typeCode <= 0) || (typeCode > 255));
printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y');
length = partitions[gptPart].GetLengthLBA();
protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
(uint32_t) length, typeCode, bootable);
} else { // partition out of range
printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
"partitions, or is\n too big; omitting it.\n", gptPart + 1);
allOK = 0;
} // if/else
return allOK;
} // GPTData::OnePartToMBR()
// Convert the GPT to MBR form. This function is necessarily limited; it
// handles at most four partitions and creates layouts that ignore CHS
// geometries. Returns the number of converted partitions; if this value
// is over 0, the calling function should call DestroyGPT() to destroy
// the GPT data, and then exit.
int GPTData::XFormToMBR(void) {
char line[255];
int i, j, numParts, numConverted = 0;
uint32_t partNums[4];
// Get the numbers of up to four partitions to add to the
// hybrid MBR....
numParts = CountParts();
printf("Counted %d partitions.\n", numParts);
// Prepare the MBR for conversion (empty it of existing partitions).
protectiveMBR.EmptyMBR(0);
protectiveMBR.SetDiskSize(diskSize);
if (numParts > 4) { // Over four partitions; engage in triage
printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
"used in the MBR, in sequence: ");
fgets(line, 255, stdin);
numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
&partNums[2], &partNums[3]);
} else { // Four or fewer partitions; convert them all
i = j = 0;
while ((j < numParts) && (i < mainHeader.numParts)) {
if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
partNums[j++] = ++i; // flag it for conversion
} else i++;
} // while
} // if/else
for (i = 0; i < numParts; i++) {
j = partNums[i] - 1;
printf("\nCreating entry for partition #%d\n", j + 1);
numConverted += OnePartToMBR(j, i);
} // for
return numConverted;
} // GPTData::XFormToMBR()
// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with // Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
// OSes that don't understand GPT. // OSes that don't understand GPT.
void GPTData::MakeHybrid(void) { void GPTData::MakeHybrid(void) {
uint32_t partNums[3]; uint32_t partNums[3];
char line[255]; char line[255];
int numParts, i, j, typeCode, bootable, mbrNum; int numParts, numConverted = 0, i, j, typeCode, mbrNum;
uint64_t length;
char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe) char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n" printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
"to use one, just hit the Enter key at the below prompt and your MBR\n" "to use one, just hit the Enter key at the below prompt and your MBR\n"
@@ -1500,38 +1615,14 @@ void GPTData::MakeHybrid(void) {
for (i = 0; i < numParts; i++) { for (i = 0; i < numParts; i++) {
j = partNums[i] - 1; j = partNums[i] - 1;
printf("\nCreating entry for partition #%d\n", j + 1); printf("\nCreating entry for partition #%d\n", j + 1);
if ((j >= 0) && (j < mainHeader.numParts)) { if (eeFirst == 'Y')
if ((partitions[j].GetLastLBA() < UINT32_MAX) && mbrNum = i + 1;
(partitions[j].GetLastLBA() > UINT64_C(0))) { else
do { mbrNum = i;
printf("Enter an MBR hex code (default %02X): ", numConverted += OnePartToMBR(j, mbrNum);
typeHelper.GUIDToID(partitions[j].GetType()) / 256);
fgets(line, 255, stdin);
sscanf(line, "%x", &typeCode);
if (line[0] == '\n')
typeCode = partitions[j].GetHexType() / 256;
} while ((typeCode <= 0) || (typeCode > 255));
printf("Set the bootable flag? ");
bootable = (GetYN() == 'Y');
length = partitions[j].GetLengthLBA();
if (eeFirst == 'Y')
mbrNum = i + 1;
else
mbrNum = i;
protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
(uint32_t) length, typeCode, bootable);
protectiveMBR.SetHybrid();
} else { // partition out of range
printf("Partition %d ends beyond the 2TiB limit of MBR partitions or does not exist;\n"
"omitting it.\n",
j + 1);
} // if/else
} else {
printf("Partition %d is out of range; omitting it.\n", j + 1);
} // if/else
} // for } // for
if (numParts > 0) { // User opted to create a hybrid MBR.... if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
// Create EFI protective partition that covers the start of the disk. // Create EFI protective partition that covers the start of the disk.
// If this location (covering the main GPT data structures) is omitted, // If this location (covering the main GPT data structures) is omitted,
// Linux won't find any partitions on the disk. Note that this is // Linux won't find any partitions on the disk. Note that this is
@@ -1544,6 +1635,7 @@ void GPTData::MakeHybrid(void) {
else else
mbrNum = numParts; mbrNum = numParts;
protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE); protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
protectiveMBR.SetHybrid();
// ... and for good measure, if there are any partition spaces left, // ... and for good measure, if there are any partition spaces left,
// optionally create another protective EFI partition to cover as much // optionally create another protective EFI partition to cover as much
@@ -1692,7 +1784,7 @@ int GPTData::ClearGPTData(void) {
// Now initialize a bunch of stuff that's static.... // Now initialize a bunch of stuff that's static....
mainHeader.signature = GPT_SIGNATURE; mainHeader.signature = GPT_SIGNATURE;
mainHeader.revision = 0x00010000; mainHeader.revision = 0x00010000;
mainHeader.headerSize = (uint32_t) HEADER_SIZE; mainHeader.headerSize = HEADER_SIZE;
mainHeader.reserved = 0; mainHeader.reserved = 0;
mainHeader.currentLBA = UINT64_C(1); mainHeader.currentLBA = UINT64_C(1);
mainHeader.partitionEntriesLBA = (uint64_t) 2; mainHeader.partitionEntriesLBA = (uint64_t) 2;
@@ -1790,6 +1882,17 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
return numFound; return numFound;
} // GPTData::GetPartRange() } // GPTData::GetPartRange()
// Returns the number of defined partitions.
uint32_t GPTData::CountParts(void) {
int i, counted = 0;
for (i = 0; i < mainHeader.numParts; i++) {
if (partitions[i].GetFirstLBA() > 0)
counted++;
} // for
return counted;
} // GPTData::CountParts()
/**************************************************** /****************************************************
* * * *
* Functions that return data about disk free space * * Functions that return data about disk free space *
@@ -2017,8 +2120,8 @@ int SizesOK(void) {
fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord)); fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
allOK = 0; allOK = 0;
} // if } // if
if (sizeof(struct EBRRecord) != 512) { if (sizeof(struct TempMBR) != 512) {
fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord)); fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
allOK = 0; allOK = 0;
} // if } // if
if (sizeof(struct GPTHeader) != 512) { if (sizeof(struct GPTHeader) != 512) {

10
gpt.h
View File

@@ -67,7 +67,6 @@ protected:
int secondPartsCrcOk; int secondPartsCrcOk;
int apmFound; // set to 1 if APM detected int apmFound; // set to 1 if APM detected
int bsdFound; // set to 1 if BSD disklabel detected in MBR int bsdFound; // set to 1 if BSD disklabel detected in MBR
// uint32_t units; // display units, in multiples of sectors
PartTypes typeHelper; PartTypes typeHelper;
public: public:
// Basic necessary functions.... // Basic necessary functions....
@@ -87,6 +86,7 @@ public:
int FindOverlaps(void); int FindOverlaps(void);
// Load or save data from/to disk // Load or save data from/to disk
int LoadMBR(char* f) {return protectiveMBR.ReadMBRData(f);}
void PartitionScan(int fd); void PartitionScan(int fd);
int LoadPartitions(char* deviceFilename); int LoadPartitions(char* deviceFilename);
int ForceLoadGPTData(int fd); int ForceLoadGPTData(int fd);
@@ -111,13 +111,15 @@ public:
void DeletePartition(void); void DeletePartition(void);
void ChangePartType(void); void ChangePartType(void);
void SetAttributes(uint32_t partNum); void SetAttributes(uint32_t partNum);
int DestroyGPT(void); // Returns 1 if user proceeds int DestroyGPT(int prompt = 1); // Returns 1 if user proceeds
// Convert to GPT from other formats (may require user interaction) // Convert between GPT and other formats (may require user interaction)
WhichToUse UseWhichPartitions(void); WhichToUse UseWhichPartitions(void);
int XFormPartitions(void); int XFormPartitions(void);
int XFormDisklabel(int OnGptPart = -1); int XFormDisklabel(int OnGptPart = -1);
int XFormDisklabel(BSDData* disklabel, int startPart); int XFormDisklabel(BSDData* disklabel, int startPart);
int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful
int XFormToMBR(void); // convert GPT to MBR, wiping GPT afterwards. Returns 1 if successful
void MakeHybrid(void); void MakeHybrid(void);
// Adjust GPT structures WITHOUT user interaction... // Adjust GPT structures WITHOUT user interaction...
@@ -139,6 +141,8 @@ public:
uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;} uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;}
uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts * uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts *
mainHeader.sizeOfPartitionEntries) / blockSize;} mainHeader.sizeOfPartitionEntries) / blockSize;}
uint32_t CountParts(void);
// Find information about free space // Find information about free space
uint64_t FindFirstAvailable(uint64_t start = 0); uint64_t FindFirstAvailable(uint64_t start = 0);

View File

@@ -9,8 +9,8 @@
// Copyright: See COPYING file that comes with this distribution // Copyright: See COPYING file that comes with this distribution
// //
// //
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed // 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. */ // under the terms of the GNU GPL version 2, as detailed in the COPYING file.
#define __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS
@@ -25,6 +25,10 @@ using namespace std;
PartTypes GPTPart::typeHelper; PartTypes GPTPart::typeHelper;
GPTPart::GPTPart(void) { GPTPart::GPTPart(void) {
int i;
for (i = 0; i < NAME_SIZE; i++)
name[i] = '\0';
} // Default constructor } // Default constructor
GPTPart::~GPTPart(void) { GPTPart::~GPTPart(void) {
@@ -128,16 +132,16 @@ void GPTPart::ReversePartBytes(void) {
} // GPTPart::ReverseBytes() } // GPTPart::ReverseBytes()
// Display summary information; does nothing if the partition is empty. // Display summary information; does nothing if the partition is empty.
void GPTPart::ShowSummary(int i, uint32_t blockSize, char* sizeInSI) { void GPTPart::ShowSummary(int partNum, uint32_t blockSize) {
int j; char sizeInSI[255];
int j = 0;
if (firstLBA != 0) { if (firstLBA != 0) {
BytesToSI(blockSize * (lastLBA - firstLBA + 1), sizeInSI); BytesToSI(blockSize * (lastLBA - firstLBA + 1), sizeInSI);
printf("%4d %14lu %14lu", i + 1, (unsigned long) firstLBA, printf("%4d %14lu %14lu", partNum + 1, (unsigned long) firstLBA,
(unsigned long) lastLBA); (unsigned long) lastLBA);
printf(" %-10s %04X ", sizeInSI, printf(" %-10s %04X ", sizeInSI,
typeHelper.GUIDToID(partitionType)); typeHelper.GUIDToID(partitionType));
j = 0;
while ((name[j] != '\0') && (j < 44)) { while ((name[j] != '\0') && (j < 44)) {
printf("%c", name[j]); printf("%c", name[j]);
j += 2; j += 2;

View File

@@ -9,8 +9,8 @@
// Copyright: See COPYING file that comes with this distribution // Copyright: See COPYING file that comes with this distribution
// //
// //
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed // 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. */ // under the terms of the GNU GPL version 2, as detailed in the COPYING file.
#ifndef __GPTPART_H #ifndef __GPTPART_H
#define __GPTPART_H #define __GPTPART_H
@@ -23,11 +23,11 @@
using namespace std; using namespace std;
/***************************************** /****************************************
* * * *
* GUIDPart class and related structures * * GPTPart class and related structures *
* * * *
*****************************************/ ****************************************/
class GPTPart { class GPTPart {
protected: protected:
@@ -73,7 +73,7 @@ class GPTPart {
// Additional functions // Additional functions
GPTPart & operator=(const GPTPart & orig); GPTPart & operator=(const GPTPart & orig);
void ShowSummary(int i, uint32_t blockSize, char* sizeInSI); // display summary information (1-line) void ShowSummary(int partNum, uint32_t blockSize); // display summary information (1-line)
void ShowDetails(uint32_t blockSize); // display detailed information (multi-line) void ShowDetails(uint32_t blockSize); // display detailed information (multi-line)
void BlankPartition(void); // empty partition of data void BlankPartition(void); // empty partition of data
int DoTheyOverlap(GPTPart* other); // returns 1 if there's overlap int DoTheyOverlap(GPTPart* other); // returns 1 if there's overlap

568
mbr.cc
View File

@@ -1,7 +1,7 @@
/* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition /* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
data. */ data. */
/* By Rod Smith, January to February, 2009 */ /* Initial coding by Rod Smith, January to February, 2009 */
/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed /* 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. */ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
@@ -35,6 +35,8 @@ MBRData::MBRData(void) {
strcpy(device, ""); strcpy(device, "");
state = invalid; state = invalid;
srand((unsigned int) time(NULL)); srand((unsigned int) time(NULL));
numHeads = MAX_HEADS;
numSecspTrack = MAX_SECSPERTRACK;
EmptyMBR(); EmptyMBR();
} // MBRData default constructor } // MBRData default constructor
@@ -43,6 +45,8 @@ MBRData::MBRData(char *filename) {
diskSize = 0; diskSize = 0;
strcpy(device, filename); strcpy(device, filename);
state = invalid; state = invalid;
numHeads = MAX_HEADS;
numSecspTrack = MAX_SECSPERTRACK;
srand((unsigned int) time(NULL)); srand((unsigned int) time(NULL));
// Try to read the specified partition table, but if it fails.... // Try to read the specified partition table, but if it fails....
@@ -55,51 +59,11 @@ MBRData::MBRData(char *filename) {
MBRData::~MBRData(void) { MBRData::~MBRData(void) {
} // MBRData destructor } // MBRData destructor
// Empty all data. Meant mainly for calling by constructors, but it's also /**********************
// used by the hybrid MBR functions in the GPTData class. * *
void MBRData::EmptyMBR(int clearBootloader) { * Disk I/O functions *
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) {
for (i = 0; i < 440; i++)
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // if
// Blank out the partitions
for (i = 0; i < 4; 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;
blockSize = SECTOR_SIZE;
diskSize = 0;
for (i = 0; i < NUM_LOGICALS; i++) {
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
} // MBRData::EmptyMBR()
// Read data from MBR. Returns 1 if read was successful (even if the // Read data from MBR. Returns 1 if read was successful (even if the
// data isn't a valid MBR), 0 if the read failed. // data isn't a valid MBR), 0 if the read failed.
@@ -120,25 +84,18 @@ int MBRData::ReadMBRData(char* deviceFilename) {
return allOK; return allOK;
} // MBRData::ReadMBRData(char* deviceFilename) } // MBRData::ReadMBRData(char* deviceFilename)
// Read data from MBR. // 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.
void MBRData::ReadMBRData(int fd, int checkBlockSize) { void MBRData::ReadMBRData(int fd, int checkBlockSize) {
int allOK = 1, i, j, maxLogicals = 0; int allOK = 1, i, j, logicalNum;
int err; int err;
TempMBR tempMBR; TempMBR tempMBR;
// Clear logical partition array // Empty existing MBR data, including the logical partitions...
for (i = 0; i < NUM_LOGICALS; i++) { EmptyMBR(0);
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
err = lseek64(fd, 0, SEEK_SET); err = lseek64(fd, 0, SEEK_SET);
err = read(fd, &tempMBR, 512); err = read(fd, &tempMBR, 512);
@@ -154,8 +111,8 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
for (j = 0; j < 3; j++) { for (j = 0; j < 3; j++) {
partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j]; partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j]; partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
} // for j... } // for j... (reading parts of CHS geometry)
} // for i... } // for i... (reading all four partitions)
MBRSignature = tempMBR.MBRSignature; MBRSignature = tempMBR.MBRSignature;
// Reverse the byte order, if necessary // Reverse the byte order, if necessary
@@ -180,21 +137,16 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
// Find block size // Find block size
if (checkBlockSize) { if (checkBlockSize) {
blockSize = GetBlockSize(fd); blockSize = GetBlockSize(fd);
// if ((blockSize = GetBlockSize(fd)) == -1) {
// blockSize = SECTOR_SIZE;
// printf("Unable to determine sector size; assuming %lu bytes!\n",
// (unsigned long) SECTOR_SIZE);
// } // if
} // if (checkBlockSize) } // if (checkBlockSize)
// Load logical partition data, if any is found.... // Load logical partition data, if any is found....
if (allOK) { if (allOK) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f) if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
|| (partitions[i].partitionType == 0x85)) { || (partitions[i].partitionType == 0x85)) {
// Found it, so call a recursive algorithm to load everything from them.... // Found it, so call a recursive algorithm to load everything from them....
maxLogicals = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), maxLogicals); logicalNum = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 4);
if ((maxLogicals < 0) || (maxLogicals > NUM_LOGICALS)) { if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
allOK = 0; allOK = 0;
fprintf(stderr, "Error reading logical partitions! List may be truncated!\n"); fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
} // if maxLogicals valid } // if maxLogicals valid
@@ -224,26 +176,70 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) {
(partitions[i].partitionType != UINT8_C(0x00))) (partitions[i].partitionType != UINT8_C(0x00)))
state = hybrid; state = hybrid;
} // for } // for
} // if hybrid } // if (hybrid detection code)
/* // Tell the user what the MBR state is...
switch (state) {
case invalid:
printf("Information: MBR appears to be empty or invalid.\n");
break;
case gpt:
printf("Information: MBR holds GPT placeholder partitions.\n");
break;
case hybrid:
printf("Information: MBR holds hybrid GPT/MBR data.\n");
break;
case mbr:
printf("Information: MBR data appears to be valid.\n");
break;
} // switch */
} // MBRData::ReadMBRData(int fd) } // MBRData::ReadMBRData(int fd)
// Write the MBR data to the default defined device. // 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:
// fd = file descriptor
// 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 MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
uint32_t diskOffset, int partNum) {
struct TempMBR ebr;
off_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 = (off_t) (extendedStart + diskOffset) * blockSize;
if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
partNum = -1;
}
if (read(fd, &ebr, 512) != 512) { // Load the data....
fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
(unsigned long) offset);
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;
fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
(unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
} // 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(fd, 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);
} // MBRData::ReadLogicalPart()
// Write the MBR data to the default defined device. Note that this writes
// ONLY the MBR itself, not the logical partition data.
int MBRData::WriteMBRData(void) { int MBRData::WriteMBRData(void) {
int allOK = 1, fd; int allOK = 1, fd;
@@ -295,12 +291,6 @@ void MBRData::WriteMBRData(int fd) {
lseek64(fd, 0, SEEK_SET); lseek64(fd, 0, SEEK_SET);
write(fd, &tempMBR, 512); write(fd, &tempMBR, 512);
/* write(fd, code, 440);
write(fd, &diskSignature, 4);
write(fd, &nulls, 2);
write(fd, partitions, 64);
write(fd, &MBRSignature, 2); */
// Reverse the byte order back, if necessary // Reverse the byte order back, if necessary
if (IsLittleEndian() == 0) { if (IsLittleEndian() == 0) {
ReverseBytes(&diskSignature, 4); ReverseBytes(&diskSignature, 4);
@@ -313,56 +303,11 @@ void MBRData::WriteMBRData(int fd) {
}// if }// if
} // MBRData::WriteMBRData(int fd) } // MBRData::WriteMBRData(int fd)
// 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 * *
// logicals[] array. Returns last index to logicals[] uses, or -1 if there was a * Functions that display data for the user *
// problem * *
int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart, ********************************************/
uint32_t diskOffset, int partNum) {
struct EBRRecord ebr;
off_t offset;
if ((partNum < NUM_LOGICALS) && (partNum >= 0)) {
offset = (off_t) (extendedStart + diskOffset) * blockSize;
if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
partNum = -1;
}
if (read(fd, &ebr, 512) != 512) { // Load the data....
fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
(unsigned long) offset);
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;
fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
(unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
} // if
// Copy over the basic data....
logicals[partNum].status = ebr.partitions[0].status;
logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
logicals[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 >= 0) &&
(partNum < (NUM_LOGICALS - 1))) {
partNum = ReadLogicalPart(fd, 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);
} // MBRData::ReadLogicalPart()
// Show the MBR data to the user.... // Show the MBR data to the user....
void MBRData::DisplayMBRData(void) { void MBRData::DisplayMBRData(void) {
@@ -373,10 +318,10 @@ void MBRData::DisplayMBRData(void) {
printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature); printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
printf("MBR partitions:\n"); printf("MBR partitions:\n");
printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n"); printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
for (i = 0; i < 4; i++) { for (i = 0; i < MAX_MBR_PARTS; i++) {
if (partitions[i].lengthLBA != 0) { if (partitions[i].lengthLBA != 0) {
if (partitions[i].status && 0x80) // it's bootable if (partitions[i].status && 0x80) // it's bootable
bootCode = '*'; bootCode = '*';
else else
bootCode = ' '; bootCode = ' ';
printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode, printf("%4d\t %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
@@ -384,31 +329,144 @@ void MBRData::DisplayMBRData(void) {
(unsigned long) partitions[i].lengthLBA, partitions[i].partitionType); (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
} // if } // if
} // for } // for
printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
// Now display logical partition data....
for (i = 0; i < NUM_LOGICALS; i++) {
if (logicals[i].lengthLBA != 0) {
printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
(unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
} // if
} // for
printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
BytesToSI(diskSize * (uint64_t) blockSize, tempStr)); BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
} // MBRData::DisplayMBRData() } // MBRData::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 MBRData::ShowState(void) {
switch (state) {
case invalid:
printf(" MBR: not present\n");
break;
case gpt:
printf(" MBR: protective\n");
break;
case hybrid:
printf(" MBR: hybrid\n");
break;
case mbr:
printf(" MBR: MBR only\n");
break;
default:
printf("\a MBR: unknown -- bug!\n");
break;
} // switch
} // MBRData::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 MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
numHeads = h;
numSecspTrack = s;
} else {
printf("Warning! Attempt to set invalid CHS geometry!\n");
} // if/else
} // MBRData::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 MBRData::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] = 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);
} // MBRData::LBAtoCHS()
/*****************************************************
* *
* 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 MBRData::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) {
for (i = 0; i < 440; i++)
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // 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;
} // MBRData::EmptyMBR()
// Create a protective MBR. Clears the boot loader area if clearBoot > 0. // Create a protective MBR. Clears the boot loader area if clearBoot > 0.
void MBRData::MakeProtectiveMBR(int clearBoot) { void MBRData::MakeProtectiveMBR(int clearBoot) {
int i;
EmptyMBR(clearBoot);
// Initialize variables // Initialize variables
nulls = 0; nulls = 0;
MBRSignature = MBR_SIGNATURE; MBRSignature = MBR_SIGNATURE;
if (clearBoot > 0) {
for (i = 0; i < 440; i++)
code[i] = (uint8_t) 0;
} // if
partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
// Write CHS data. This maxes out the use of the disk, as much as // Write CHS data. This maxes out the use of the disk, as much as
@@ -427,44 +485,43 @@ void MBRData::MakeProtectiveMBR(int clearBoot) {
partitions[0].partitionType = UINT8_C(0xEE); partitions[0].partitionType = UINT8_C(0xEE);
partitions[0].firstLBA = UINT32_C(1); partitions[0].firstLBA = UINT32_C(1);
if (diskSize < UINT32_MAX) { // If the disk is under 2TiB if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
partitions[0].lengthLBA = diskSize - 1; partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
} else { // disk is too big to represent, so fake it... } else { // disk is too big to represent, so fake it...
partitions[0].lengthLBA = UINT32_MAX; partitions[0].lengthLBA = UINT32_MAX;
} // if/else } // if/else
// Zero out three unused primary partitions...
for (i = 1; i < 4; 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
// Zero out all the logical partitions. Not necessary for data
// integrity on write, but eliminates stray entries if user wants
// to view the MBR after converting the disk
for (i = 0; i < NUM_LOGICALS; i++) {
logicals[i].status = UINT8_C(0);
logicals[i].firstSector[0] = UINT8_C(0);
logicals[i].firstSector[1] = UINT8_C(0);
logicals[i].firstSector[2] = UINT8_C(0);
logicals[i].partitionType = UINT8_C(0);
logicals[i].lastSector[0] = UINT8_C(0);
logicals[i].lastSector[1] = UINT8_C(0);
logicals[i].lastSector[2] = UINT8_C(0);
logicals[i].firstLBA = UINT32_C(0);
logicals[i].lengthLBA = UINT32_C(0);
} // for
state = gpt; state = gpt;
} // MBRData::MakeProtectiveMBR() } // MBRData::MakeProtectiveMBR()
// 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 MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
int bootable) {
if ((num >= 0) && (num < MAX_MBR_PARTS)) {
partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
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)
} // if valid partition number
} // MBRData::MakePart()
// Create a partition that fills the most available space. Returns // Create a partition that fills the most available space. Returns
// 1 if partition was created, 0 otherwise. Intended for use in // 1 if partition was created, 0 otherwise. Intended for use in
// creating hybrid MBRs. // creating hybrid MBRs.
@@ -482,11 +539,11 @@ int MBRData::MakeBiggestPart(int i, int type) {
if (firstBlock != UINT32_C(0)) { // something's free... if (firstBlock != UINT32_C(0)) { // something's free...
lastBlock = FindLastInFree(firstBlock); lastBlock = FindLastInFree(firstBlock);
segmentSize = lastBlock - firstBlock + UINT32_C(1); segmentSize = lastBlock - firstBlock + UINT32_C(1);
if (segmentSize > selectedSize) { if (segmentSize > selectedSize) {
selectedSize = segmentSize; selectedSize = segmentSize;
selectedSegment = firstBlock; selectedSegment = firstBlock;
} // if } // if
start = lastBlock + 1; start = lastBlock + 1;
} // if } // if
} while (firstBlock != 0); } while (firstBlock != 0);
if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) { if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
@@ -517,18 +574,19 @@ void MBRData::DeletePartition(int i) {
// Used to help keep GPT & hybrid MBR partitions in sync.... // Used to help keep GPT & hybrid MBR partitions in sync....
int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) { int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
uint32_t start32, length32; uint32_t start32, length32;
int i, j, deleted = 0; int i, deleted = 0;
if ((state == hybrid) && (start64 < UINT32_MAX) && (length64 < UINT32_MAX)) { if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
start32 = (uint32_t) start64; start32 = (uint32_t) start64;
length32 = (uint32_t) length64; length32 = (uint32_t) length64;
for (i = 0; i < 4; i++) { for (i = 0; i < MAX_MBR_PARTS; i++) {
if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) && if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
(partitions[i].partitionType != 0xEE)) { (partitions[i].partitionType != 0xEE)) {
DeletePartition(i); DeletePartition(i);
OptimizeEESize(); if (state == hybrid)
OptimizeEESize();
deleted = 1; deleted = 1;
} // if (match found) } // if (match found)
} // for i (partition scan) } // for i (partition scan)
} // if (hybrid & GPT partition < 2TiB) } // if (hybrid & GPT partition < 2TiB)
return deleted; return deleted;
@@ -554,74 +612,24 @@ void MBRData::OptimizeEESize(void) {
partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1; partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
} // if free space after } // if free space after
} // if partition is 0xEE } // if partition is 0xEE
if (typeFlag == 0) { // No non-hybrid partitions found
MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
} // if
} // for partition loop } // for partition loop
if (typeFlag == 0) { // No non-hybrid partitions found
MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
} // if
} // MBRData::OptimizeEESize() } // MBRData::OptimizeEESize()
// Return a pointer to a primary or logical partition, or NULL if /****************************************
// the partition is out of range.... * *
struct MBRRecord* MBRData::GetPartition(int i) { * Functions to find data on free space *
MBRRecord* thePart = NULL; * *
****************************************/
if ((i >= 0) && (i < 4)) { // primary partition
thePart = &partitions[i];
} // if
if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
thePart = &logicals[i - 4];
} // if
return thePart;
} // GetPartition()
// Displays the state, as a word, on stdout. Used for debugging & to
// tell the user about the MBR state when the program launches....
void MBRData::ShowState(void) {
switch (state) {
case invalid:
printf(" MBR: not present\n");
break;
case gpt:
printf(" MBR: protective\n");
break;
case hybrid:
printf(" MBR: hybrid\n");
break;
case mbr:
printf(" MBR: MBR only\n");
break;
default:
printf("\a MBR: unknown -- bug!\n");
break;
} // switch
} // MBRData::ShowState()
// Create a primary 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! It's
// intended as a way to create a hybrid MBR, which is a pretty funky
// setup to begin with....
void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
int bootable) {
partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
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;
} // MakePart()
// Finds the first free space on the disk from start onward; returns 0 // Finds the first free space on the disk from start onward; returns 0
// if none available.... // if none available....
uint32_t MBRData::FindFirstAvailable(uint32_t start) { uint32_t MBRData::FindFirstAvailable(uint32_t start) {
uint32_t first; uint32_t first;
uint32_t i; uint32_t i;
int firstMoved = 0; int firstMoved;
first = start; first = start;
@@ -638,7 +646,7 @@ uint32_t MBRData::FindFirstAvailable(uint32_t start) {
(first < (partitions[i].firstLBA + partitions[i].lengthLBA))) { (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
first = partitions[i].firstLBA + partitions[i].lengthLBA; first = partitions[i].firstLBA + partitions[i].lengthLBA;
firstMoved = 1; firstMoved = 1;
} // if } // if
} // for } // for
} while (firstMoved == 1); } while (firstMoved == 1);
if (first >= diskSize) if (first >= diskSize)
@@ -651,7 +659,7 @@ uint32_t MBRData::FindLastInFree(uint32_t start) {
uint32_t nearestStart; uint32_t nearestStart;
uint32_t i; uint32_t i;
if (diskSize <= UINT32_MAX) if ((diskSize <= UINT32_MAX) && (diskSize > 0))
nearestStart = diskSize - 1; nearestStart = diskSize - 1;
else else
nearestStart = UINT32_MAX - 1; nearestStart = UINT32_MAX - 1;
@@ -688,6 +696,8 @@ int MBRData::IsFree(uint32_t sector) {
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
first = partitions[i].firstLBA; 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; last = first + partitions[i].lengthLBA;
if (last > 0) last--; if (last > 0) last--;
if ((first <= sector) && (last >= sector)) if ((first <= sector) && (last >= sector))
@@ -696,6 +706,12 @@ int MBRData::IsFree(uint32_t sector) {
return isFree; return isFree;
} // MBRData::IsFree() } // MBRData::IsFree()
/******************************************************
* *
* Functions that extract data on specific partitions *
* *
******************************************************/
uint8_t MBRData::GetStatus(int i) { uint8_t MBRData::GetStatus(int i) {
MBRRecord* thePart; MBRRecord* thePart;
uint8_t retval; uint8_t retval;
@@ -729,7 +745,7 @@ uint32_t MBRData::GetFirstSector(int i) {
retval = thePart->firstLBA; retval = thePart->firstLBA;
} else } else
retval = UINT32_C(0); retval = UINT32_C(0);
return retval; return retval;
} // MBRData::GetFirstSector() } // MBRData::GetFirstSector()
uint32_t MBRData::GetLength(int i) { uint32_t MBRData::GetLength(int i) {
@@ -741,7 +757,7 @@ uint32_t MBRData::GetLength(int i) {
retval = thePart->lengthLBA; retval = thePart->lengthLBA;
} else } else
retval = UINT32_C(0); retval = UINT32_C(0);
return retval; return retval;
} // MBRData::GetLength() } // MBRData::GetLength()
// Return the MBR data as a GPT partition.... // Return the MBR data as a GPT partition....
@@ -772,7 +788,23 @@ GPTPart MBRData::AsGPT(int i) {
newPart.SetUniqueGUID(1); newPart.SetUniqueGUID(1);
newPart.SetAttributes(0); newPart.SetAttributes(0);
newPart.SetName((unsigned char*) newPart.GetNameType(tempStr)); newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
} // if } // if not extended, protective, or non-existent
} // if } // if (origPart != NULL)
return newPart; return newPart;
} // MBRData::AsGPT() } // MBRData::AsGPT()
/***********************
* *
* Protected functions *
* *
***********************/
// Return a pointer to a primary or logical partition, or NULL if
// the partition is out of range....
struct MBRRecord* MBRData::GetPartition(int i) {
MBRRecord* thePart = NULL;
if ((i >= 0) && (i < MAX_MBR_PARTS))
thePart = &partitions[i];
return thePart;
} // GetPartition()

62
mbr.h
View File

@@ -12,9 +12,12 @@
#define __MBRSTRUCTS #define __MBRSTRUCTS
#define MBR_SIGNATURE UINT16_C(0xAA55) #define MBR_SIGNATURE UINT16_C(0xAA55)
#define MAX_HEADS 255 /* numbered 0 - 254 */
#define MAX_SECSPERTRACK 63 /* numbered 1 - 63 */
#define MAX_CYLINDERS 1024 /* numbered 0 - 1023 */
// Maximum number of logical partitions supported // Maximum number of MBR partitions
#define NUM_LOGICALS 124 #define MAX_MBR_PARTS 128
using namespace std; using namespace std;
@@ -36,9 +39,10 @@ struct MBRRecord {
uint32_t lengthLBA; uint32_t lengthLBA;
}; // struct MBRRecord }; // struct MBRRecord
// Create a 512-byte data structure into which the MBR can be loaded in one // A 512-byte data structure into which the MBR can be loaded in one
// go, for the benefit of FreeBSD which seems to flake out when loading // go, for the benefit of FreeBSD which seems to flake out when loading
// from block devices in multiples other than the block size.... // from block devices in multiples other than the block size.
// Also used when loading logical partitions.
struct TempMBR { struct TempMBR {
uint8_t code[440]; uint8_t code[440];
uint32_t diskSignature; uint32_t diskSignature;
@@ -47,20 +51,6 @@ struct TempMBR {
uint16_t MBRSignature; uint16_t MBRSignature;
}; // struct TempMBR }; // struct TempMBR
// Extended Boot Record (EBR) data, used to hold one logical partition's
// data within an extended partition. Includes pointer to next record for
// in-memory linked-list access. This is similar to MBRData, but with a
// few tweaks....
struct EBRRecord {
uint8_t code[446]; // generally 0s (and we don't care if they aren't)
// First partition entry defines partition; second points to next
// entry in on-disk linked list; remaining two are unused. Note that
// addresses are relative to the extended partition, not to the disk
// as a whole.
struct MBRRecord partitions[4];
uint16_t MBRSignature;
}; // struct EBRRecord
// Possible states of the MBR // Possible states of the MBR
enum MBRValidity {invalid, gpt, hybrid, mbr}; enum MBRValidity {invalid, gpt, hybrid, mbr};
@@ -70,45 +60,55 @@ protected:
uint8_t code[440]; uint8_t code[440];
uint32_t diskSignature; uint32_t diskSignature;
uint16_t nulls; uint16_t nulls;
struct MBRRecord partitions[4]; // MAX_MBR_PARTS defaults to 128. This array holds both the primary and
// the logical partitions, to simplify data retrieval for GPT conversions.
struct MBRRecord partitions[MAX_MBR_PARTS];
uint16_t MBRSignature; uint16_t MBRSignature;
// Above are basic MBR data; now add more stuff.... // Above are basic MBR data; now add more stuff....
uint32_t blockSize; // block size (usually 512) uint32_t blockSize; // block size (usually 512)
uint64_t diskSize; // size in blocks uint64_t diskSize; // size in blocks
uint64_t numHeads; // number of heads, in CHS scheme
uint64_t numSecspTrack; // number of sectors per track, in CHS scheme
char device[256]; char device[256];
// Now an array of partitions for the logicals, in array form (easier
// than a linked list, and good enough for the GPT itself, so....)
struct MBRRecord logicals[NUM_LOGICALS];
MBRValidity state; MBRValidity state;
struct MBRRecord* GetPartition(int i); // Return primary or logical partition struct MBRRecord* GetPartition(int i); // Return primary or logical partition
public: public:
MBRData(void); MBRData(void);
MBRData(char* deviceFilename); MBRData(char* deviceFilename);
~MBRData(void); ~MBRData(void);
// Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1); // File I/O functions...
void SetDiskSize(uint64_t ds) {diskSize = ds;}
int ReadMBRData(char* deviceFilename); int ReadMBRData(char* deviceFilename);
void ReadMBRData(int fd, int checkBlockSize = 1); void ReadMBRData(int fd, int checkBlockSize = 1);
int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
int partNum);
int WriteMBRData(void); int WriteMBRData(void);
void WriteMBRData(int fd); void WriteMBRData(int fd);
// ReadLogicalPart() returns last partition # read to logicals[] array, // ReadLogicalPart() returns last partition # read to logicals[] array,
// or -1 if there was a problem.... // or -1 if there was a problem....
int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
int partNum); // Display data for user...
void DisplayMBRData(void); void DisplayMBRData(void);
void MakeProtectiveMBR(int clearBoot = 0);
MBRValidity GetValidity(void) {return state;}
void ShowValidity(void);
void ShowState(void); void ShowState(void);
// Functions that set or get disk metadata (size, CHS geometry, etc.)
void SetDiskSize(uint64_t ds) {diskSize = ds;}
MBRValidity GetValidity(void) {return state;}
void SetHybrid(void) {state = hybrid;} // Set hybrid flag
void SetCHSGeom(uint32_t h, uint32_t s);
int LBAtoCHS(uint64_t lba, uint8_t * chs); // Convert LBA to CHS
// Functions to create, delete, or change partitions
// Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1);
void MakeProtectiveMBR(int clearBoot = 0);
void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
int bootable = 0); int bootable = 0);
int MakeBiggestPart(int i, int type); // Make partition filling most space int MakeBiggestPart(int i, int type); // Make partition filling most space
void DeletePartition(int i); void DeletePartition(int i);
int DeleteByLocation(uint64_t start64, uint64_t length64); int DeleteByLocation(uint64_t start64, uint64_t length64);
void OptimizeEESize(void); void OptimizeEESize(void);
void SetHybrid(void) {state = hybrid;} // Set hybrid flag
// Functions to find information on free space.... // Functions to find information on free space....
uint32_t FindFirstAvailable(uint32_t start = 1); uint32_t FindFirstAvailable(uint32_t start = 1);

View File

@@ -179,7 +179,7 @@ PartTypes::PartTypes(void) {
"EFI System"); // EFI System (parted marks Linux boot "EFI System"); // EFI System (parted marks Linux boot
// partitions like this) // partitions like this)
AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d), AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d),
"MBR partition scheme"); // Whatever that is (from Wikipedia) "MBR partition scheme"); // Used to nest an MBR table on a GPT disk
AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74), AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74),
"BIOS boot partition"); // "BIOS boot partition"); //

View File

@@ -205,7 +205,9 @@ int GetBlockSize(int fd) {
result = SECTOR_SIZE; result = SECTOR_SIZE;
// ENOTTY = inappropriate ioctl; probably being called on a disk image // ENOTTY = inappropriate ioctl; probably being called on a disk image
// file, so don't display the warning message.... // file, so don't display the warning message....
if (errno != ENOTTY) { // 32-bit code returns EINVAL, I don't know why. I know I'm treading on
// thin ice here, but it should be OK in all but very weird cases....
if ((errno != ENOTTY) && (errno != EINVAL)) {
printf("\aError %d when determining sector size! Setting sector size to %d\n", printf("\aError %d when determining sector size! Setting sector size to %d\n",
errno, SECTOR_SIZE); errno, SECTOR_SIZE);
} // if } // if
@@ -434,8 +436,9 @@ void DiskSync(int fd) {
uint64_t disksize(int fd, int *err) { uint64_t disksize(int fd, int *err) {
long sz; // Do not delete; needed for Linux long sz; // Do not delete; needed for Linux
long long b; // Do not delete; needed for Linux long long b; // Do not delete; needed for Linux
uint64_t sectors = 0, bytes = 0; // size in sectors & bytes uint64_t sectors = 0; // size in sectors
struct stat st; off_t bytes = 0; // size in bytes
struct stat64 st;
// Note to self: I recall testing a simplified version of // Note to self: I recall testing a simplified version of
// this code, similar to what's in the __APPLE__ block, // this code, similar to what's in the __APPLE__ block,
@@ -462,17 +465,6 @@ uint64_t disksize(int fd, int *err) {
sectors = (b >> 9); sectors = (b >> 9);
} // if } // if
// if (*err) {
// sz = 0;
// if (errno != EFBIG)
// return sz;
// }
// *err = ioctl(fd, BLKGETSIZE64, &b);
// if (*err || b == 0 || b == sz)
// sectors = sz;
// else
// sectors = (b >> 9);
#endif #endif
#endif #endif
@@ -480,7 +472,7 @@ uint64_t disksize(int fd, int *err) {
// so let's assume it's a regular file (a QEMU image, dd backup, or // so let's assume it's a regular file (a QEMU image, dd backup, or
// what have you) and see what stat() gives us.... // what have you) and see what stat() gives us....
if (sectors == 0) { if (sectors == 0) {
if (fstat(fd, &st) == 0) { if (fstat64(fd, &st) == 0) {
bytes = (uint64_t) st.st_size; bytes = (uint64_t) st.st_size;
if ((bytes % UINT64_C(512)) != 0) if ((bytes % UINT64_C(512)) != 0)
fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!" fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!"

View File

@@ -4,6 +4,10 @@
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#ifndef __GPTSUPPORT
#define __GPTSUPPORT
#if defined (__FreeBSD__) || defined (__APPLE__) #if defined (__FreeBSD__) || defined (__APPLE__)
// Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64 // Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64
@@ -18,10 +22,10 @@
#include <linux/fs.h> #include <linux/fs.h>
#endif #endif
#include <string.h> #ifdef __FreeBSD__
#define fstat64 fstat
#ifndef __GPTSUPPORT #define stat64 stat
#define __GPTSUPPORT #endif
// Set this as a default // Set this as a default
#define SECTOR_SIZE UINT32_C(512) #define SECTOR_SIZE UINT32_C(512)
@@ -39,7 +43,7 @@
// Number and size of GPT entries... // Number and size of GPT entries...
#define NUM_GPT_ENTRIES 128 #define NUM_GPT_ENTRIES 128
#define GPT_SIZE 128 #define GPT_SIZE 128
#define HEADER_SIZE 92 #define HEADER_SIZE UINT32_C(92)
#define GPT_RESERVED 420 #define GPT_RESERVED 420
#define NAME_SIZE 72 #define NAME_SIZE 72