From 1e09372bca227ffbfb9dda48160826d8f63ce9bb Mon Sep 17 00:00:00 2001 From: srs5694 Date: Tue, 5 Jan 2010 00:14:19 -0500 Subject: [PATCH] Early support for larger-than-512-byte sectors and even earlier support for sgdisk program. (The latter is just proof-of-concept at this point; it doesn't do anything useful.) --- CHANGELOG | 9 +- Makefile | 3 + bsd.cc | 8 +- gdisk.cc | 5 +- gpt.cc | 99 +++++++----- gpt.h | 2 - mbr.cc | 6 +- sgdisk.cc | 464 +++++++++++++++++++++++++++++++++++++++++++++++++++++ support.cc | 88 ++++++++-- support.h | 2 + 10 files changed, 622 insertions(+), 64 deletions(-) create mode 100644 sgdisk.cc diff --git a/CHANGELOG b/CHANGELOG index bebcf1b..4280d4e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,13 @@ -0.5.3 (1/??/2009): +0.6.0 (1/??/2009): ------------------ +- Added support for disks with other than 512-byte sectors. + +- Created embryonic sgdisk program. + +0.5.3 (1/4/2009): +----------------- + - Fixed bug in display of GUIDs when compiled with some versions of GCC. - Eliminated warnings caused by additional checks in latest versions of diff --git a/Makefile b/Makefile index 7484b89..9c73a7a 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,9 @@ DEPEND= makedepend $(CFLAGS) gdisk: $(LIB_OBJS) gdisk.o $(CXX) $(LIB_OBJS) gdisk.o -o gdisk +sgdisk: $(LIB_OBJS) sgdisk.o + $(CXX) $(LIB_OBJS) sgdisk.o -o sgdisk + wipegpt: $(LIB_OBJS) wipegpt.o $(CXX) $(LIB_OBJS) wipegpt.o -o wipegpt diff --git a/bsd.cc b/bsd.cc index e03a98c..ccac953 100644 --- a/bsd.cc +++ b/bsd.cc @@ -63,7 +63,7 @@ int BSDData::ReadBSDData(char* device, uint64_t startSector, uint64_t endSector) // Load the BSD disklabel data from an already-opened disk // file, starting with the specified sector number. void BSDData::ReadBSDData(int fd, uint64_t startSector, uint64_t endSector) { - uint8_t buffer[2048]; // I/O buffer + uint8_t buffer[4096]; // I/O buffer int i, err, foundSig = 0, bigEnd = 0; int relative = 0; // assume absolute partition sector numbering uint32_t realSig; @@ -74,10 +74,10 @@ void BSDData::ReadBSDData(int fd, uint64_t startSector, uint64_t endSector) { labelFirstLBA = startSector; labelLastLBA = endSector; - // Read two sectors into memory; we'll extract data from + // Read eight sectors into memory; we'll extract data from // this buffer. (Done to work around FreeBSD limitation) - lseek64(fd, startSector * 512, SEEK_SET); - err = read(fd, buffer, 2048); + lseek64(fd, startSector * GetBlockSize(fd), SEEK_SET); + err = read(fd, buffer, 4096); // Do some strangeness to support big-endian architectures... bigEnd = (IsLittleEndian() == 0); diff --git a/gdisk.cc b/gdisk.cc index f11207e..bc238a0 100644 --- a/gdisk.cc +++ b/gdisk.cc @@ -4,7 +4,7 @@ // // by Rod Smith, project began February 2009 -/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed +/* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ //#include @@ -16,7 +16,6 @@ #include "support.h" // Function prototypes.... -// int ReadPartitions(char* filename, struct GPTData* theGPT); void MainMenu(char* filename, struct GPTData* theGPT); void ShowCommands(void); void ExpertsMenu(char* filename, struct GPTData* theGPT); @@ -29,7 +28,7 @@ int main(int argc, char* argv[]) { int doMore = 1; char* device = NULL; - printf("GPT fdisk (gdisk) version 0.5.3\n\n"); + printf("GPT fdisk (gdisk) version 0.5.4-pre1\n\n"); if (argc == 2) { // basic usage if (SizesOK()) { diff --git a/gpt.cc b/gpt.cc index 7204f2c..7352edc 100644 --- a/gpt.cc +++ b/gpt.cc @@ -49,6 +49,7 @@ GPTData::GPTData(void) { bsdFound = 0; sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default srand((unsigned int) time(NULL)); + mainHeader.numParts = 0; SetGPTSize(NUM_GPT_ENTRIES); } // GPTData default constructor @@ -68,6 +69,7 @@ GPTData::GPTData(char* filename) { bsdFound = 0; sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default srand((unsigned int) time(NULL)); + mainHeader.numParts = 0; LoadPartitions(filename); } // GPTData(char* filename) constructor @@ -601,9 +603,9 @@ int GPTData::ForceLoadGPTData(int fd) { uint32_t newCRC, sizeOfParts; // Seek to and read the main GPT header - lseek64(fd, 512, SEEK_SET); - if (read(fd, &mainHeader, 512) != 512) { // read main GPT header - fprintf(stderr, "Warning! Error %d reading secondary GPT header!", errno); + lseek64(fd, blockSize, SEEK_SET); + if (myRead(fd, (char*) &mainHeader, 512) != 512) { // read main GPT header + fprintf(stderr, "Warning! Error %d reading main GPT header!\n", errno); } // if mainCrcOk = CheckHeaderCRC(&mainHeader); if (IsLittleEndian() == 0) // big-endian system; adjust header byte order.... @@ -618,19 +620,19 @@ int GPTData::ForceLoadGPTData(int fd) { if (mainHeader.backupLBA < diskSize) { seekTo = mainHeader.backupLBA * blockSize; } else { - seekTo = (diskSize * blockSize) - UINT64_C(512); + seekTo = (diskSize * blockSize) - blockSize; printf("Warning! Disk size is smaller than the main header indicates! Loading\n" "secondary header from the last sector of the disk! You should use 'v' to\n" "verify disk integrity, and perhaps options on the experts' menu to repair\n" "the disk.\n"); } // else } else { - seekTo = (diskSize * blockSize) - UINT64_C(512); + seekTo = (diskSize * blockSize) - blockSize; } // if/else (mainCrcOk) if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { - if (read(fd, &secondHeader, 512) != 512) { // read secondary GPT header - fprintf(stderr, "Warning! Error %d reading secondary GPT header!", errno); + if (myRead(fd, (char*) &secondHeader, 512) != 512) { // read secondary GPT header + fprintf(stderr, "Warning! Error %d reading secondary GPT header!\n", errno); } // if secondCrcOk = CheckHeaderCRC(&secondHeader); if (IsLittleEndian() == 0) // big-endian system; adjust header byte order.... @@ -681,7 +683,7 @@ int GPTData::ForceLoadGPTData(int fd) { if ((lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) && (secondCrcOk)) { sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; storage = (char*) malloc(sizeOfParts); - if (read(fd, storage, sizeOfParts) != sizeOfParts) { + if (myRead(fd, storage, sizeOfParts) != sizeOfParts) { fprintf(stderr, "Warning! Error %d reading backup partition table!\n", errno); } // if newCRC = chksum_crc32((unsigned char*) storage, sizeOfParts); @@ -716,7 +718,7 @@ int GPTData::LoadMainTable(void) { // matches the stored value lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); sizeOfParts = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; - if (read(fd, partitions, sizeOfParts) != sizeOfParts) { + if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) { fprintf(stderr, "Warning! Error %d when loading the main partition table!\n", errno); } // if newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); @@ -740,7 +742,7 @@ void GPTData::LoadSecondTableAsMain(void) { if (lseek64(fd, seekTo, SEEK_SET) != (off_t) -1) { SetGPTSize(secondHeader.numParts); sizeOfParts = secondHeader.numParts * secondHeader.sizeOfPartitionEntries; - if (read(fd, partitions, sizeOfParts) != sizeOfParts) { + if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) { fprintf(stderr, "Warning! Read error %d! Misbehavior now likely!\n", errno); } // if newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts); @@ -852,15 +854,21 @@ int GPTData::SaveGPTData(void) { protectiveMBR.WriteMBRData(fd); // Now write the main GPT header... - if (allOK) - if (write(fd, &mainHeader, 512) == -1) - allOK = 0; + if (allOK) { + if (lseek64(fd, blockSize, SEEK_SET) != (off_t) -1) { + if (myWrite(fd, (char*) &mainHeader, 512) == -1) + allOK = 0; + } else allOK = 0; // if (lseek64()...) + } // if (allOK) // Now write the main partition tables... if (allOK) { - if (write(fd, partitions, GPT_SIZE * numParts) == -1) + offset = mainHeader.partitionEntriesLBA * blockSize; + if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) { + if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1) allOK = 0; - } // if + } else allOK = 0; // if (lseek64()...) + } // if (allOK) // Now seek to near the end to write the secondary GPT.... if (allOK) { @@ -872,14 +880,19 @@ int GPTData::SaveGPTData(void) { } // if // Now write the secondary partition tables.... - if (allOK) - if (write(fd, partitions, GPT_SIZE * numParts) == -1) + if (allOK) { + if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1) allOK = 0; + } // if (allOK) // Now write the secondary GPT header... - if (allOK) - if (write(fd, &secondHeader, 512) == -1) - allOK = 0; + if (allOK) { + offset = mainHeader.backupLBA * blockSize; + if (lseek64(fd, offset, SEEK_SET) != (off_t) - 1) { + if (myWrite(fd, (char*) &secondHeader, 512) == -1) + allOK = 0; + } else allOK = 0; // if (lseek64()...) + } // if (allOK) // re-read the partition table if (allOK) { @@ -944,17 +957,17 @@ int GPTData::SaveGPTBackup(char* filename) { // Now write the main GPT header... if (allOK) - if (write(fd, &mainHeader, 512) == -1) + if (myWrite(fd, (char*) &mainHeader, 512) == -1) allOK = 0; // Now write the secondary GPT header... if (allOK) - if (write(fd, &secondHeader, 512) == -1) + if (myWrite(fd, (char*) &secondHeader, 512) == -1) allOK = 0; // Now write the main partition tables... if (allOK) { - if (write(fd, partitions, GPT_SIZE * numParts) == -1) + if (myWrite(fd, (char*) partitions, GPT_SIZE * numParts) == -1) allOK = 0; } // if @@ -997,7 +1010,7 @@ int GPTData::LoadGPTBackup(char* filename) { // Load the main GPT header, check its vaility, and set the GPT // size based on the data - if (read(fd, &mainHeader, 512)) { + if (myRead(fd, (char*) &mainHeader, 512)) { fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno); } // if mainCrcOk = CheckHeaderCRC(&mainHeader); @@ -1009,7 +1022,7 @@ int GPTData::LoadGPTBackup(char* filename) { // Load the backup GPT header in much the same way as the main // GPT header.... - if (read(fd, &secondHeader, 512) != 512) { + if (myRead(fd, (char*) &secondHeader, 512) != 512) { fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno); } // if secondCrcOk = CheckHeaderCRC(&secondHeader); @@ -1045,7 +1058,7 @@ int GPTData::LoadGPTBackup(char* filename) { // Load main partition table, and record whether its CRC // matches the stored value sizeOfParts = numParts * sizeOfEntries; - if (read(fd, partitions, sizeOfParts) != sizeOfParts) { + if (myRead(fd, (char*) partitions, sizeOfParts) != sizeOfParts) { fprintf(stderr, "Warning! Read error %d; strange behavior now likely!\n", errno); } // if @@ -1109,6 +1122,7 @@ void GPTData::DisplayGPTData(void) { BytesToSI(diskSize * blockSize, sizeInSI); printf("Disk %s: %llu sectors, %s\n", device, (unsigned long long) diskSize, sizeInSI); + printf("Logical sector size: %d bytes\n", blockSize); printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr)); printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts); printf("First usable sector is %llu, last usable sector is %llu\n", @@ -1302,8 +1316,9 @@ void GPTData::SetAttributes(uint32_t partNum) { // 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, sum; + int fd, i, sum, tableSize; char blankSector[512], goOn = 'Y', blank = 'N'; + char* emptyTable; for (i = 0; i < 512; i++) { blankSector[i] = '\0'; @@ -1326,24 +1341,24 @@ int GPTData::DestroyGPT(int prompt) { } // if #endif if (fd != -1) { - lseek64(fd, mainHeader.currentLBA * 512, SEEK_SET); // seek to GPT header - if (write(fd, blankSector, 512) != 512) { // blank it out + lseek64(fd, mainHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header + if (myWrite(fd, blankSector, 512) != 512) { // blank it out fprintf(stderr, "Warning! GPT main header not overwritten! Error is %d\n", errno); } // if - lseek64(fd, mainHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table - sum = 0; - for (i = 0; i < GetBlocksInPartTable(); i++) - sum += write(fd, blankSector, 512); - if (sum != 512 * GetBlocksInPartTable()) + lseek64(fd, mainHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table + tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; + emptyTable = (char*) malloc(tableSize); + for (i = 0; i < tableSize; i++) + emptyTable[i] = (char) 0; + sum = myWrite(fd, emptyTable, tableSize); + if (sum != tableSize) fprintf(stderr, "Warning! GPT main partition table not overwritten! Error is %d\n", errno); - lseek64(fd, secondHeader.partitionEntriesLBA * 512, SEEK_SET); // seek to partition table - sum = 0; - for (i = 0; i < GetBlocksInPartTable(); i++) - sum += write(fd, blankSector, 512); - if (sum != 512 * GetBlocksInPartTable()) + lseek64(fd, secondHeader.partitionEntriesLBA * blockSize, SEEK_SET); // seek to partition table + sum = myWrite(fd, emptyTable, tableSize); + if (sum != tableSize) fprintf(stderr, "Warning! GPT backup partition table not overwritten! Error is %d\n", errno); - lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header - if (write(fd, blankSector, 512) != 512) { // blank it out + lseek64(fd, secondHeader.currentLBA * blockSize, SEEK_SET); // seek to GPT header + if (myWrite(fd, blankSector, 512) != 512) { // blank it out fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno); } // if if (prompt) { @@ -1357,7 +1372,7 @@ int GPTData::DestroyGPT(int prompt) { // structures). if (blank == 'Y') { lseek64(fd, 0, SEEK_SET); - if (write(fd, blankSector, 512) != 512) { // blank it out + if (myWrite(fd, blankSector, 512) != 512) { // blank it out fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno); } // if } else { diff --git a/gpt.h b/gpt.h index f5f862b..68baf1e 100644 --- a/gpt.h +++ b/gpt.h @@ -145,8 +145,6 @@ public: uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;} uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;} - uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts * - mainHeader.sizeOfPartitionEntries) / blockSize;} uint32_t CountParts(void); int GetAlignment(void) {return sectorAlignment;} diff --git a/mbr.cc b/mbr.cc index e06d821..efd6fd3 100644 --- a/mbr.cc +++ b/mbr.cc @@ -98,7 +98,7 @@ void MBRData::ReadMBRData(int fd, int checkBlockSize) { EmptyMBR(0); err = lseek64(fd, 0, SEEK_SET); - err = read(fd, &tempMBR, 512); + err = myRead(fd, (char*) &tempMBR, 512); for (i = 0; i < 440; i++) code[i] = tempMBR.code[i]; diskSignature = tempMBR.diskSignature; @@ -202,7 +202,7 @@ int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart, fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset); partNum = -1; } - if (read(fd, &ebr, 512) != 512) { // Load the data.... + if (myRead(fd, (char*) &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; @@ -289,7 +289,7 @@ void MBRData::WriteMBRData(int fd) { // Now write that data structure... lseek64(fd, 0, SEEK_SET); - if (write(fd, &tempMBR, 512) != 512) { + if (myWrite(fd, (char*) &tempMBR, 512) != 512) { fprintf(stderr, "Warning! Error %d when saving MBR!\n", errno); } // if diff --git a/sgdisk.cc b/sgdisk.cc new file mode 100644 index 0000000..9479afb --- /dev/null +++ b/sgdisk.cc @@ -0,0 +1,464 @@ +// sgdisk.cc +// Program modelled after Linux sfdisk, but it manipulates GPT partitions +// rather than MBR partitions. This is effectively a new user interface +// to my gdisk program. +// +// by Rod Smith, project began February 2009 + +/* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + +//#include +#include +#include +#include +#include "mbr.h" +#include "gpt.h" +#include "support.h" + +#define MAX_OPTIONS 50 + +// Function prototypes.... +/* void MainMenu(char* filename, struct GPTData* theGPT); +void ShowCommands(void); +void ExpertsMenu(char* filename, struct GPTData* theGPT); +void ShowExpertCommands(void); +void RecoveryMenu(char* filename, struct GPTData* theGPT); +void ShowRecoveryCommands(void); */ + +enum Commands { NONE, LIST, VERIFY }; + +struct Options { + Commands theCommand; + char* theArgument; +}; // struct Options + +int verbose_flag; + +static struct option long_options[] = +{ + {"verify", no_argument, NULL, 'v'}, + {"list", no_argument, NULL, 'l'}, + {0, 0, NULL, 0} +}; + +int ParseOptions(int argc, char* argv[], Options* theOptions, char** device); + +int main(int argc, char* argv[]) { + GPTData theGPT; + int doMore = 1, opt, i, numOptions = 0; + char* device = NULL; + Options theOptions[MAX_OPTIONS]; + + printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n"); + numOptions = ParseOptions(argc, argv, theOptions, &device); + + if (device != NULL) { + if (theGPT.LoadPartitions(device)) { + for (i = 0; i < numOptions; i++) { + switch (theOptions[i].theCommand) { + case LIST: + theGPT.JustLooking(); + theGPT.DisplayGPTData(); + break; + case VERIFY: + theGPT.JustLooking(); + theGPT.Verify(); + break; + case NONE: + printf("Usage: %s {-lv} device\n", argv[0]); + break; + } // switch + } // for + } // if loaded OK + } // if (device != NULL) + + return 0; +} // main + +// Parse command-line options. Returns the number of arguments retrieved +int ParseOptions(int argc, char* argv[], Options* theOptions, char** device) { + int opt, i, numOptions = 0; + int verbose_flag; + + // Use getopt() to extract commands and their arguments + /* getopt_long stores the option index here. */ + int option_index = 0; + +// c = getopt_long (argc, argv, "abc:d:f:", +// long_options, &option_index); + + while (((opt = getopt_long(argc, argv, "vl", long_options, &option_index)) != -1) + && (numOptions < MAX_OPTIONS)) { + printf("opt is %c, option_index is %d\n", opt, option_index); + switch (opt) { + case 'l': + printf("Entering list option, numOptions = %d!\n", numOptions); + theOptions[numOptions].theCommand = LIST; + theOptions[numOptions++].theArgument = NULL; + break; + case 'v': + theOptions[numOptions].theCommand = VERIFY; + theOptions[numOptions++].theArgument = NULL; + break; + default: + printf("Default switch; opt is %c\n", opt); + break; +// abort(); + } // switch + } // while + + // Find non-option arguments. If the user types a legal command, there + // will be only one of these: The device filename.... + opt = 0; + printf("Searching for device filename; optind is %d\n", optind); + for (i = optind; i < argc; i++) { + *device = argv[i]; + printf("Setting device to %s\n", argv[i]); + opt++; + } // for + if (opt > 1) { + fprintf(stderr, "Warning! Found stray unrecognized arguments! Program may misbehave!\n"); + } // if + + return numOptions; +} // ParseOptions() + +/* // Accept a command and execute it. Returns only when the user +// wants to exit (such as after a 'w' or 'q' command). +void MainMenu(char* filename, struct GPTData* theGPT) { + char command, line[255], buFile[255]; + char* junk; + int goOn = 1; + PartTypes typeHelper; + uint32_t temp1, temp2; + + do { + printf("\nCommand (? for help): "); + junk = fgets(line, 255, stdin); + sscanf(line, "%c", &command); + switch (command) { + case 'b': case 'B': + printf("Enter backup filename to save: "); + junk = fgets(line, 255, stdin); + sscanf(line, "%s", (char*) &buFile); + theGPT->SaveGPTBackup(buFile); + break; + case 'c': case 'C': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) + theGPT->SetName(theGPT->GetPartNum()); + else + printf("No partitions\n"); + break; + case 'd': case 'D': + theGPT->DeletePartition(); + break; + case 'i': case 'I': + theGPT->ShowDetails(); + break; + case 'l': case 'L': + typeHelper.ShowTypes(); + break; + case 'n': case 'N': + theGPT->CreatePartition(); + break; + case 'o': case 'O': + printf("This option deletes all partitions and creates a new " + "protective MBR.\nProceed? "); + if (GetYN() == 'Y') { + theGPT->ClearGPTData(); + theGPT->MakeProtectiveMBR(); + } // if + break; + case 'p': case 'P': + theGPT->DisplayGPTData(); + break; + case 'q': case 'Q': + goOn = 0; + break; + case 'r': case 'R': + RecoveryMenu(filename, theGPT); + goOn = 0; + break; + case 's': case 'S': + theGPT->SortGPT(); + printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n"); + break; + case 't': case 'T': + theGPT->ChangePartType(); + break; + case 'v': case 'V': + if (theGPT->Verify() > 0) { // problems found + printf("You may be able to correct the problems by using options on the experts\n" + "menu (press 'x' at the command prompt). Good luck!\n"); + } // if + break; + case 'w': case 'W': + if (theGPT->SaveGPTData() == 1) + goOn = 0; + break; + case 'x': case 'X': + ExpertsMenu(filename, theGPT); + goOn = 0; + break; + default: + ShowCommands(); + break; + } // switch + } while (goOn); +} // MainMenu() + +void ShowCommands(void) { + printf("b\tback up GPT data to a file\n"); + printf("c\tchange a partition's name\n"); + printf("d\tdelete a partition\n"); + printf("i\tshow detailed information on a partition\n"); + printf("l\tlist known partition types\n"); + printf("n\tadd a new partition\n"); + printf("o\tcreate a new empty GUID partition table (GPT)\n"); + printf("p\tprint the partition table\n"); + printf("q\tquit without saving changes\n"); + printf("r\trecovery and transformation options (experts only)\n"); + printf("s\tsort partitions\n"); + printf("t\tchange a partition's type code\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"); +} // ShowCommands() + +// Accept a recovery & transformation menu command. Returns only when the user +// issues an exit command, such as 'w' or 'q'. +void RecoveryMenu(char* filename, struct GPTData* theGPT) { + char command, line[255], buFile[255]; + char* junk; + PartTypes typeHelper; + uint32_t temp1; + int goOn = 1; + + do { + printf("\nrecovery/transformation command (? for help): "); + junk = 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: "); + junk = fgets(line, 255, stdin); + sscanf(line, "%s", (char*) &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]; + char* junk; + PartTypes typeHelper; + uint32_t pn; + uint32_t temp1, temp2; + int goOn = 1; + + do { + printf("\nExpert command (? for help): "); + junk = fgets(line, 255, stdin); + sscanf(line, "%c", &command); + switch (command) { + case 'a': case 'A': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) + theGPT->SetAttributes(theGPT->GetPartNum()); + else + printf("No partitions\n"); + break; + case 'c': case 'C': + if (theGPT->GetPartRange(&temp1, &temp2) > 0) { + pn = theGPT->GetPartNum(); + printf("Enter the partition's new unique GUID:\n"); + theGPT->SetPartitionGUID(pn, GetGUID()); + } else printf("No partitions\n"); + break; + case 'd': case 'D': + printf("The number of logical sectors per physical sector is %d.\n", + theGPT->GetAlignment()); + break; + case 'e': case 'E': + printf("Relocating backup data structures to the end of the disk\n"); + theGPT->MoveSecondHeaderToEnd(); + break; + case 'g': case 'G': + printf("Enter the disk's unique GUID:\n"); + theGPT->SetDiskGUID(GetGUID()); + break; + case 'i': case 'I': + theGPT->ShowDetails(); + break; + case 'l': case 'L': + temp1 = GetNumber(1, 128, 8, "Enter the number of logical sectors in a physical sector on the\ndisk (1-128, default = 8): "); + theGPT->SetAlignment(temp1); + break; + case 'm': case 'M': + MainMenu(filename, theGPT); + goOn = 0; + break; + case 'n': case 'N': + theGPT->MakeProtectiveMBR(); + break; + case 'o': case 'O': + theGPT->DisplayMBRData(); + break; + case 'p': case 'P': + theGPT->DisplayGPTData(); + break; + case 'q': case 'Q': + goOn = 0; + break; + case 'r': case 'R': + RecoveryMenu(filename, theGPT); + goOn = 0; + break; + case 's': case 'S': + theGPT->ResizePartitionTable(); + break; + case 'v': case 'V': + theGPT->Verify(); + break; + case 'w': case 'W': + if (theGPT->SaveGPTData() == 1) { + goOn = 0; + } // if + break; + case 'z': case 'Z': + if (theGPT->DestroyGPT() == 1) { + goOn = 0; + } + break; + default: + ShowExpertCommands(); + break; + } // switch + } while (goOn); +} // ExpertsMenu() + +void ShowExpertCommands(void) { + printf("a\tset attributes\n"); + printf("c\tchange partition GUID\n"); + printf("d\tdisplay the number of logical sectors per physical sector\n"); + printf("e\trelocate backup data structures to the end of the disk\n"); + printf("g\tchange disk GUID\n"); + printf("i\tshow detailed information on a partition\n"); + printf("b\tset the number of logical sectors per physical sector\n"); + printf("m\treturn to main menu\n"); + printf("n\tcreate a new protective MBR\n"); + printf("o\tprint protective MBR data\n"); + printf("p\tprint the partition table\n"); + printf("q\tquit without saving changes\n"); + printf("r\trecovery and transformation options (experts only)\n"); + printf("s\tresize partition table\n"); + printf("v\tverify disk\n"); + printf("w\twrite table to disk and exit\n"); + printf("z\tzap (destroy) GPT data structures and exit\n"); + printf("?\tprint this menu\n"); +} // ShowExpertCommands() +*/ diff --git a/support.cc b/support.cc index 9763dbf..8ab9a2e 100644 --- a/support.cc +++ b/support.cc @@ -223,10 +223,10 @@ int GetBlockSize(int fd) { } // if } // if - if (result != 512) { +/* if (result != 512) { printf("\aWARNING! Sector size is not 512 bytes! This program is likely to "); printf("misbehave!\nProceed at your own risk!\n\n"); - } // if + } // if */ return (result); } // GetBlockSize() @@ -244,9 +244,12 @@ int FindAlignment(int fd) { err = -1; #endif - if (err < 0) { - result = 8; - } else { + if (err < 0) { // ioctl didn't work; have to guess.... + if (GetBlockSize(fd) == 512) + result = 8; // play it safe; align for 4096-byte sectors + else + result = 1; // unusual sector size; assume it's the real physical size + } else { // ioctl worked; compute alignment result = physicalSectorSize / GetBlockSize(fd); } // if/else return result; @@ -501,7 +504,9 @@ uint64_t disksize(int fd, int *err) { else sectors = (b >> 9); } // if - + // Unintuitively, the above returns values in 512-byte blocks, no + // matter what the underlying device's block size. Correct for this.... + sectors /= (GetBlockSize(fd) / 512); #endif #endif @@ -517,7 +522,72 @@ uint64_t disksize(int fd, int *err) { sectors = bytes / UINT64_C(512); } // if } // if -// sectors = 25000000; -// printf("Returning bogus sector size: %d\n", sectors); return sectors; -} +} // disksize() + +// A variant on the standard read() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read +int myRead(int fd, char* buffer, int numBytes) { + int blockSize = 512, i, numBlocks, retval; + char* tempSpace; + + // Compute required space and allocate memory + blockSize = GetBlockSize(fd); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Read the data into temporary space, then copy it to buffer + retval = read(fd, tempSpace, numBlocks * blockSize); + for (i = 0; i < numBytes; i++) { + buffer[i] = tempSpace[i]; + } // for + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + return retval; +} // myRead() + +// A variant on the standard write() function. Done to work around +// limitations in FreeBSD concerning the matching of the sector +// size with the number of bytes read +int myWrite(int fd, char* buffer, int numBytes) { + int blockSize = 512, i, numBlocks, retval; + char* tempSpace; + + // Compute required space and allocate memory + blockSize = GetBlockSize(fd); + if (numBytes <= blockSize) { + numBlocks = 1; + tempSpace = (char*) malloc(blockSize); + } else { + numBlocks = numBytes / blockSize; + if ((numBytes % blockSize) != 0) numBlocks++; + tempSpace = (char*) malloc(numBlocks * blockSize); + } // if/else + + // Copy the data to my own buffer, then write it + for (i = 0; i < numBytes; i++) { + tempSpace[i] = buffer[i]; + } // for + for (i = numBytes; i < numBlocks * blockSize; i++) { + tempSpace[i] = 0; + } // for + retval = write(fd, tempSpace, numBlocks * blockSize); + + // Adjust the return value, if necessary.... + if (((numBlocks * blockSize) != numBytes) && (retval > 0)) + retval = numBytes; + + free(tempSpace); + return retval; +} // myRead() diff --git a/support.h b/support.h index e414df0..b261b76 100644 --- a/support.h +++ b/support.h @@ -68,6 +68,8 @@ int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-en void ReverseBytes(void* theValue, int numBytes); // Reverses byte-order of theValue uint64_t PowerOf2(int value); int OpenForWrite(char* deviceFilename); +int myRead(int fd, char* buffer, int numBytes); +int myWrite(int fd, char* buffer, int numBytes); void DiskSync(int fd); // resync disk caches to use new partitions uint64_t disksize(int fd, int* err);