diff options
-rw-r--r-- | CHANGELOG | 47 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | Makefile.mingw | 6 | ||||
-rw-r--r-- | README.Windows | 32 | ||||
-rw-r--r-- | attributes.cc | 7 | ||||
-rw-r--r-- | attributes.h | 2 | ||||
-rw-r--r-- | bsd.cc | 12 | ||||
-rw-r--r-- | bsd.h | 5 | ||||
-rw-r--r-- | diskio-unix.cc | 15 | ||||
-rw-r--r-- | diskio-windows.cc | 10 | ||||
-rw-r--r-- | diskio.cc | 1 | ||||
-rw-r--r-- | diskio.h | 2 | ||||
-rw-r--r-- | gdisk.8 | 10 | ||||
-rw-r--r-- | gdisk.cc | 36 | ||||
-rw-r--r-- | gpt.cc | 782 | ||||
-rw-r--r-- | gpt.h | 40 | ||||
-rw-r--r-- | gptpart.cc | 39 | ||||
-rw-r--r-- | gptpart.h | 4 | ||||
-rw-r--r-- | mbr.cc | 58 | ||||
-rw-r--r-- | mbr.h | 3 | ||||
-rw-r--r-- | parttypes.h | 2 | ||||
-rw-r--r-- | sgdisk.8 | 76 | ||||
-rw-r--r-- | sgdisk.cc | 97 | ||||
-rw-r--r-- | support.cc | 12 | ||||
-rw-r--r-- | support.h | 4 |
25 files changed, 646 insertions, 660 deletions
@@ -1,6 +1,51 @@ -0.6.4 (??/??/2010): +0.6.4 (2/19/2010): ------------------- +- Added -m (--gpttombr) option to sgdisk, enabling conversion of GPT + disks to MBR format, with a limit of four partitions total, and of course + without overcoming the 2TiB limit. + +- Added -h (--hybrid) option to sgdisk, enabling creation of hybrid + MBRs. Fewer options are available in sgdisk than in gdisk, though, + in order to keep the user interface manageable. + +- Fixed off-by-one bug in specification of partition when using the + -T (--transform-bsd) option in sgdisk. + +- Changed the code to create a new MBR unique disk signature whenever a new + protective MBR is generated (when doing an MBR-to-GPT conversion, when + using the 'n' option on the experts' menu, or when using the 'o' option + on the main menu, for example). Previous versions attempted to preserve + the existing MBR disk signature in most cases, but this resulted in + values of 0x00000000 whenever an empty disk was partitioned, and often in + other cases, too. Better to risk changing this value too often than to + leave multiple disks with 0x00000000 values, I think. + +- Added transpose ('t' on experts' menu in gdisk; or -r or --transpose in + sgdisk) command to enable fine-tuning partition order without doing a + full sort. + +- Added code to clear the MBR boot loader when doing an MBR-to-GPT + conversion. (This was already done in full-disk BSD-to-GPT conversions.) + This is done because I've seen a few problem reports that make me think + some MBR boot loaders freak out and hang the system when they encounter + GPT disks, and/or they attempt to load a second-stage boot loader stored + in what is now GPT territory, causing a system hang. Since MBR boot + loaders don't work on GPT disks anyhow (even GRUB needs to be + reinstalled), this new wiping behavior shouldn't cause any problems, and + may prevent a few. + +- Fixed bug in Windows version that prevented saving backup files. + +- Fixed bug that caused second and subsequent partition numbers in + prompts in hybrid MBR conversion procedure to be displayed in + hexadecimal. + +- Fixed very obscure potential bug in hybrid MBR/GPT synchronization when + deleting partitions; code wasn't matching partition lengths correctly, + which would only affect partitions that start at the same point but have + different lengths in MBR vs. GPT. + - Fixed bug in the -E option to sgdisk; it was actually returning the last free sector, not the last free sector in the largest free block. @@ -10,8 +10,8 @@ DEPEND= makedepend $(CFLAGS) all: gdisk sgdisk -gdisk: $(LIB_OBJS) gdisk.o - $(CXX) $(LIB_OBJS) gdisk.o -L/opt/local/lib -L/usr/local/lib -luuid -o gdisk +gdisk: $(LIB_OBJS) gdisk.o gpttext.o + $(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/opt/local/lib -L/usr/local/lib -luuid -o gdisk sgdisk: $(LIB_OBJS) sgdisk.o $(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -L/usr/local/lib -luuid -lpopt -o sgdisk diff --git a/Makefile.mingw b/Makefile.mingw index 96ef8a0..f6d0ba8 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,6 +1,6 @@ -CC=/usr/bin/i586-mingw32msvc-gcc -CXX=/usr/bin/i586-mingw32msvc-g++ -STRIP=/usr/bin/i586-mingw32msvc-strip +CC=/usr/bin/i686-pc-mingw32-gcc +CXX=/usr/bin/i686-pc-mingw32-g++ +STRIP=/usr/bin/i686-pc-mingw32-strip CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -g #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g diff --git a/README.Windows b/README.Windows index 91074c4..908c40f 100644 --- a/README.Windows +++ b/README.Windows @@ -90,16 +90,28 @@ use a version for another platform, or use a different partitioning tool altogether.
I compiled gdisk.exe using MinGW (http://www.mingw.org), and in particular
-its Linux-hosted cross-compiler. I have not tested the compilability of the
-source code under more mainstream Windows compilers, or even on the
-Windows-hosted MinGW variant. MinGW was designed for porting Unix
-applications to Windows, so it's entirely possible that it will work where
-other compilers won't.
-
-Under Ubuntu Linux, the Makefile.mingw file enables compilation of the
-software. (Type "make -f Makefile.mingw" to compile the software.) If you
-try to compile using another compiler or even using MinGW under Windows or
-another Linux variety, you may need to adjust the Makefile.mingw options.
+its Linux-hosted cross-compiler. Under Ubuntu Linux, the Makefile.mingw
+file enables compilation of the software via MinGW. (Type "make -f
+Makefile.mingw" to compile the software.) If you try to compile using
+another compiler or even using MinGW under Windows or another Linux
+variety, you may need to adjust the Makefile.mingw options.
+
+I've also attempted to compile the code with OpenWatcom 1.8 and Microsoft
+Visual C++ 2008 Express. My OpenWatcom attempts failed, mostly because the
+compiler can't yet handle iostream output on standard C++ strings.
+OpenWatcom also seems to have incorrectly set the value of UINT32_MAX as if
+uint32_t values were 64-bit integers. This alone won't cause the compile to
+fail, but it would create bugs.
+
+My attemps with Visual C++ were much more successful; after tracking down
+and installing a stdint.h file (I used the one from
+http://msinttypes.googlecode.com/svn/trunk/stdint.h) and making a few other
+minor changes, the code compiled fine, and seems to run properly, although
+I've not tested it extensively. I created native projects for both
+OpenWatcom and Visual C++, ignoring the Makefile approach, but I'm not
+including the relevant project files in the source tarball, since they're
+easy enough to regenerate -- just include all the *.h files and all the
+*.cc files except diskio-unix.cc and sgdisk.cc, then build.
If you modify GPT fdisk to get it to compile under another compiler, I
welcome submission of patches.
diff --git a/attributes.cc b/attributes.cc index b0800fa..ac9f1fc 100644 --- a/attributes.cc +++ b/attributes.cc @@ -12,6 +12,7 @@ #include <stdint.h> #include <stdio.h> #include <iostream> +#include <sstream> #include "attributes.h" using namespace std; @@ -20,13 +21,13 @@ using namespace std; // data. Attributes::Attributes(void) { int i; - char temp[ATR_NAME_SIZE]; + ostringstream temp; // Most bits are undefined, so start by giving them an // appropriate name for (i = 1; i < NUM_ATR; i++) { - sprintf(temp, "Undefined bit #%d", i); - atNames[i] = temp; + temp << "Undefined bit #" << i; + atNames[i] = temp.str(); } // for // Now reset those names that are defined.... diff --git a/attributes.h b/attributes.h index 647c205..ab8cb14 100644 --- a/attributes.h +++ b/attributes.h @@ -2,7 +2,7 @@ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include <stdint.h> -#include <unistd.h> +//#include <unistd.h> #include <stdlib.h> #include <string.h> #include "support.h" @@ -10,7 +10,7 @@ #define __STDC_CONSTANT_MACROS #include <stdio.h> -#include <unistd.h> +//#include <unistd.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> @@ -37,7 +37,8 @@ BSDData::BSDData(void) { } // default constructor BSDData::~BSDData(void) { - delete[] partitions; + if (partitions != NULL) + delete[] partitions; } // destructor // Read BSD disklabel data from the specified device filename. This function @@ -64,9 +65,10 @@ int BSDData::ReadBSDData(const string & device, uint64_t startSector, uint64_t e // Load the BSD disklabel data from an already-opened disk // file, starting with the specified sector number. int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) { - uint8_t buffer[4096]; // I/O buffer - int i, foundSig = 0, bigEnd = 0, allOK = 1; + int allOK = 1; + int i, foundSig = 0, bigEnd = 0; int relative = 0; // assume absolute partition sector numbering + uint8_t buffer[4096]; // I/O buffer uint32_t realSig; uint32_t* temp32; uint16_t* temp16; @@ -158,7 +160,7 @@ int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSect // detected above, apply a correction to all partition start sectors.... if (relative) { for (i = 0; i < numParts; i++) { - partitions[i].firstLBA += startSector; + partitions[i].firstLBA += (uint32_t) startSector; } // for } // if } // if signatures OK @@ -55,6 +55,9 @@ struct BSDRecord { // the partition table }; // Full data in tweaked BSD format +// For some reason this has to be packed or MS Visual C++'s debugger complains +// about memory errors whenever a BSDData variable is destroyed. +#pragma pack (8) class BSDData { protected: // We only need a few items from the main BSD disklabel data structure.... @@ -62,7 +65,7 @@ class BSDData { uint32_t sectorSize; // # of bytes per sector uint32_t signature2; // the magic number (again) uint16_t numParts; // number of partitions in table - BSDRecord* partitions; // partition array + struct BSDRecord* partitions; // partition array // Above are basic BSD disklabel data; now add more stuff.... uint64_t labelFirstLBA; // first sector of BSD disklabel (partition or disk) diff --git a/diskio-unix.cc b/diskio-unix.cc index 00ffd99..e955739 100644 --- a/diskio-unix.cc +++ b/diskio-unix.cc @@ -97,7 +97,8 @@ int DiskIO::OpenForWrite(void) { // so the file can be re-opened without specifying the filename. void DiskIO::Close(void) { if (isOpen) - close(fd); + if (close(fd) < 0) + cerr << "Warning! Problem closing file!\n"; isOpen = 0; openForWrite = 0; } // DiskIO::Close() @@ -117,7 +118,7 @@ int DiskIO::GetBlockSize(void) { #ifdef __APPLE__ err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize); #endif -#ifdef __FreeBSD__ +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) err = ioctl(fd, DIOCGSECTORSIZE, &blockSize); #endif #ifdef __linux__ @@ -161,7 +162,7 @@ void DiskIO::DiskSync(void) { i = ioctl(fd, DKIOCSYNCHRONIZECACHE); platformFound++; #endif -#ifdef __FreeBSD__ +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) sleep(2); i = ioctl(fd, DIOCGFLUSH); cout << "Warning: The kernel may continue to use old or deleted partitions.\n" @@ -295,8 +296,6 @@ int DiskIO::Write(void* buffer, int numBytes) { // greatly since then to enable FreeBSD and MacOS support, as well as to // return correct values for disk image files. uint64_t DiskIO::DiskSize(int *err) { - long sz; // Do not delete; needed for Linux - long long b; // Do not delete; needed for Linux uint64_t sectors = 0; // size in sectors off_t bytes = 0; // size in bytes struct stat64 st; @@ -317,13 +316,15 @@ uint64_t DiskIO::DiskSize(int *err) { *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §ors); platformFound++; #endif -#ifdef __FreeBSD__ +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) *err = ioctl(fd, DIOCGMEDIASIZE, &bytes); - b = GetBlockSize(); + long long b = GetBlockSize(); sectors = bytes / b; platformFound++; #endif #ifdef __linux__ + long sz; + long long b; *err = ioctl(fd, BLKGETSIZE, &sz); if (*err) { sectors = sz = 0; diff --git a/diskio-windows.cc b/diskio-windows.cc index d5cd84b..aaf5c64 100644 --- a/diskio-windows.cc +++ b/diskio-windows.cc @@ -19,7 +19,7 @@ #include <winioctl.h> #define fstat64 fstat #define stat64 stat -//#define S_IRGRP 0 +#define S_IRGRP 0 #define S_IROTH 0 #include <stdio.h> #include <string> @@ -93,6 +93,14 @@ int DiskIO::OpenForWrite(void) { fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + // Preceding call can fail when creating backup files; if so, try + // again with different option... + if (fd == INVALID_HANDLE_VALUE) { + CloseHandle(fd); + fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + } // if if (fd == INVALID_HANDLE_VALUE) { CloseHandle(fd); isOpen = 0; @@ -25,7 +25,6 @@ #else #include <sys/ioctl.h> #endif -#include <stdio.h> #include <string> #include <stdint.h> #include <errno.h> @@ -25,7 +25,7 @@ #include <sys/ioctl.h> #endif -#if defined (__FreeBSD__) || defined (__APPLE__) +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) #define fstat64 fstat #define stat64 stat #endif @@ -1,6 +1,6 @@ .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License -.TH "GDISK" "8" "0.6.3" "Roderick W. Smith" "GPT fdisk Manual" +.TH "GDISK" "8" "0.6.4" "Roderick W. Smith" "GPT fdisk Manual" .SH "NAME" gdisk \- Interactive GUID partition table (GPT) manipulator .SH "SYNOPSIS" @@ -497,6 +497,14 @@ seem to work, and can sometimes be useful in converting MBR disks. Larger sizes also work fine. OSes may impose their own limits on the number of partitions, though. +.TP +.B t +Swap two partitions' entries in the partition table. One partition may be +empty. For instance, if partitions 1\-4 are defined, transposing 1 and 5 +results in a table with partitions numbered from 2\-5. Transposing +partitions in this way has no effect on their disk space allocation; it +only alters their order in the partition table. + .TP .B v Verify disk. This option is identical to the 'v' option in the main menu. @@ -9,24 +9,24 @@ //#include <iostream> #include <stdio.h> -#include <getopt.h> +//#include <getopt.h> #include <string.h> #include <string> #include <iostream> #include "mbr.h" -#include "gpt.h" +#include "gpttext.h" #include "support.h" // Function prototypes.... -void MainMenu(string filename, struct GPTData* theGPT); +void MainMenu(string filename, GPTDataTextUI* theGPT); void ShowCommands(void); -void ExpertsMenu(string filename, struct GPTData* theGPT); +void ExpertsMenu(string filename, GPTDataTextUI* theGPT); void ShowExpertCommands(void); -void RecoveryMenu(string filename, struct GPTData* theGPT); +void RecoveryMenu(string filename, GPTDataTextUI* theGPT); void ShowRecoveryCommands(void); int main(int argc, char* argv[]) { - GPTData theGPT; + GPTDataTextUI theGPT; int doMore = 1; char* device = NULL; @@ -72,7 +72,7 @@ int main(int argc, char* argv[]) { // Accept a command and execute it. Returns only when the user // wants to exit (such as after a 'w' or 'q' command). -void MainMenu(string filename, struct GPTData* theGPT) { +void MainMenu(string filename, GPTDataTextUI* theGPT) { char command, line[255], buFile[255]; char* junk; int goOn = 1; @@ -174,7 +174,7 @@ void ShowCommands(void) { // Accept a recovery & transformation menu command. Returns only when the user // issues an exit command, such as 'w' or 'q'. -void RecoveryMenu(string filename, struct GPTData* theGPT) { +void RecoveryMenu(string filename, GPTDataTextUI* theGPT) { char command, line[255], buFile[255]; char* junk; uint32_t temp1; @@ -221,10 +221,11 @@ void RecoveryMenu(string filename, struct GPTData* theGPT) { if (temp1 > 0) { cout << "Converted " << temp1 << " partitions. Finalize and exit? "; if (GetYN() == 'Y') { - if (theGPT->DestroyGPT(0) > 0) + if (theGPT->DestroyGPT() > 0) goOn = 0; } else { theGPT->MakeProtectiveMBR(); + theGPT->WriteProtectiveMBR(); cout << "Note: New protective MBR created.\n"; } // if/else } // if @@ -299,11 +300,10 @@ void ShowRecoveryCommands(void) { // Accept an experts' menu command. Returns only after the user // selects an exit command, such as 'w' or 'q'. -void ExpertsMenu(string filename, struct GPTData* theGPT) { +void ExpertsMenu(string filename, GPTDataTextUI* theGPT) { char command, line[255]; char* junk; - uint32_t pn; - uint32_t temp1, temp2; + uint32_t pn, temp1, temp2; int goOn = 1; GUIDData aGUID; @@ -359,10 +359,10 @@ void ExpertsMenu(string filename, struct GPTData* theGPT) { break; case 'p': case 'P': theGPT->DisplayGPTData(); - break; + break; case 'q': case 'Q': - goOn = 0; - break; + goOn = 0; + break; case 'r': case 'R': RecoveryMenu(filename, theGPT); goOn = 0; @@ -370,6 +370,9 @@ void ExpertsMenu(string filename, struct GPTData* theGPT) { case 's': case 'S': theGPT->ResizePartitionTable(); break; + case 't': case 'T': + theGPT->SwapPartitions(); + break; case 'v': case 'V': theGPT->Verify(); break; @@ -379,7 +382,7 @@ void ExpertsMenu(string filename, struct GPTData* theGPT) { } // if break; case 'z': case 'Z': - if (theGPT->DestroyGPT() == 1) { + if (theGPT->DestroyGPTwPrompt() == 1) { goOn = 0; } break; @@ -405,6 +408,7 @@ void ShowExpertCommands(void) { cout << "q\tquit without saving changes\n"; cout << "r\trecovery and transformation options (experts only)\n"; cout << "s\tresize partition table\n"; + cout << "t\ttranspose two partition table entries\n"; cout << "v\tverify disk\n"; cout << "w\twrite table to disk and exit\n"; cout << "z\tzap (destroy) GPT data structures and exit\n"; @@ -10,7 +10,6 @@ #define __STDC_CONSTANT_MACROS #include <stdio.h> -#include <unistd.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> @@ -99,7 +98,6 @@ int GPTData::Verify(void) { int problems = 0; uint32_t i, numSegments; uint64_t totalFree, largestSegment; - char siTotal[255], siLargest[255]; // First, check for CRC errors in the GPT data.... if (!mainCrcOk) { @@ -231,8 +229,6 @@ int GPTData::Verify(void) { // problems could affect the results if (problems == 0) { totalFree = FindFreeBlocks(&numSegments, &largestSegment); - strcpy(siTotal, BytesToSI(totalFree * (uint64_t) blockSize).c_str()); - strcpy(siLargest, BytesToSI(largestSegment * (uint64_t) blockSize).c_str()); cout << "No problems found. " << totalFree << " free sectors (" << BytesToSI(totalFree * (uint64_t) blockSize) << ") available in " << numSegments << "\nsegments, the largest of which is " @@ -386,10 +382,6 @@ void GPTData::RecomputeCRCs(void) { ReverseHeaderBytes(&mainHeader); ReverseHeaderBytes(&secondHeader); } // if -/* if ((littleEndian = IsLittleEndian()) == 0) { - ReverseBytes(&trueNumParts, 4); - ReverseBytes(&hSize, 4); - } // if */ // Compute CRC of partition tables & store in main and secondary headers crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE); @@ -566,10 +558,10 @@ void GPTData::PartitionScan(void) { // Read GPT data from a disk. int GPTData::LoadPartitions(const string & deviceFilename) { + BSDData bsdDisklabel; int err, allOK = 1; uint32_t i; uint64_t firstBlock, lastBlock; - BSDData bsdDisklabel; MBRValidity mbrState; // First, do a test to see if writing will be possible later.... @@ -577,7 +569,7 @@ int GPTData::LoadPartitions(const string & deviceFilename) { if ((err == 0) && (!justLooking)) { cout << "\aNOTE: Write test failed with error number " << errno << ". It will be impossible to save\nchanges to this disk's partition table!\n"; -#ifdef __FreeBSD__ +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) cout << "You may be able to enable writes by exiting this program, typing\n" << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n" << "program.\n"; @@ -604,7 +596,7 @@ int GPTData::LoadPartitions(const string & deviceFilename) { // bsdDisklabel.DisplayBSDData(); ClearGPTData(); protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1) - XFormDisklabel(&bsdDisklabel, 0); + XFormDisklabel(&bsdDisklabel); break; case use_gpt: mbrState = protectiveMBR.GetValidity(); @@ -655,12 +647,12 @@ int GPTData::ForceLoadGPTData(void) { if (mainCrcOk && (mainHeader.backupLBA < diskSize)) { allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK; } else { - if (mainHeader.backupLBA >= diskSize) + allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK; + if (mainCrcOk && (mainHeader.backupLBA >= diskSize)) cout << "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"; - allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK; } // if/else if (!allOK) state = gpt_invalid; @@ -850,7 +842,6 @@ int GPTData::CheckTable(struct GPTHeader *header) { int GPTData::SaveGPTData(int quiet) { int allOK = 1, littleEndian; char answer; -// uint64_t secondTable; uint32_t numParts; littleEndian = IsLittleEndian(); @@ -1129,6 +1120,91 @@ int GPTData::LoadGPTBackup(const string & filename) { return allOK; } // GPTData::LoadGPTBackup() +int GPTData::SaveMBR(void) { + return protectiveMBR.WriteMBRData(); +} // GPTData::SaveMBR() + +// This function destroys the on-disk GPT structures, but NOT the on-disk +// MBR. +// Returns 1 if the operation succeeds, 0 if not. +int GPTData::DestroyGPT(void) { + int i, sum, tableSize, allOK = 1; + uint8_t blankSector[512]; + uint8_t* emptyTable; + + for (i = 0; i < 512; i++) { + blankSector[i] = 0; + } // for + + if (myDisk.OpenForWrite()) { + if (!myDisk.Seek(mainHeader.currentLBA)) + allOK = 0; + if (myDisk.Write(blankSector, 512) != 512) { // blank it out + cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n"; + allOK = 0; + } // if + if (!myDisk.Seek(mainHeader.partitionEntriesLBA)) + allOK = 0; + tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; + emptyTable = new uint8_t[tableSize]; + for (i = 0; i < tableSize; i++) + emptyTable[i] = 0; + if (allOK) { + sum = myDisk.Write(emptyTable, tableSize); + if (sum != tableSize) { + cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n"; + allOK = 0; + } // if write failed + } // if + if (!myDisk.Seek(secondHeader.partitionEntriesLBA)) + allOK = 0; + if (allOK) { + sum = myDisk.Write(emptyTable, tableSize); + if (sum != tableSize) { + cerr << "Warning! GPT backup partition table not overwritten! Error is " + << errno << "\n"; + allOK = 0; + } // if wrong size written + } // if + if (!myDisk.Seek(secondHeader.currentLBA)) + allOK = 0; + if (allOK) { + if (myDisk.Write(blankSector, 512) != 512) { // blank it out + cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n"; + allOK = 0; + } // if + } // if + myDisk.DiskSync(); + myDisk.Close(); + cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n" + << "other utilities.\n"; + delete[] emptyTable; + } else { + cerr << "Problem opening " << device << " for writing! Program will now terminate.\n"; + } // if/else (fd != -1) + return (allOK); +} // GPTDataTextUI::DestroyGPT() + +// Wipe MBR data from the disk (zero it out completely) +// Returns 1 on success, 0 on failure. +int GPTData::DestroyMBR(void) { + int allOK = 1, i; + uint8_t blankSector[512]; + + for (i = 0; i < 512; i++) + blankSector[i] = 0; + + if (myDisk.OpenForWrite()) { + if (myDisk.Seek(0)) { + if (myDisk.Write(blankSector, 512) != 512) + allOK = 0; + } else allOK = 0; + } else allOK = 0; + if (!allOK) + cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n"; + return allOK; +} // GPTData::DestroyMBR(void) + // Tell user whether Apple Partition Map (APM) was discovered.... void GPTData::ShowAPMState(void) { if (apmFound) @@ -1176,20 +1252,6 @@ void GPTData::DisplayGPTData(void) { } // for } // GPTData::DisplayGPTData() -// Get partition number from user and then call ShowPartDetails(partNum) -// to show its detailed information -void GPTData::ShowDetails(void) { - int partNum; - uint32_t low, high; - - if (GetPartRange(&low, &high) > 0) { - partNum = GetPartNum(); - ShowPartDetails(partNum); - } else { - cout << "No partitions\n"; - } // if/else -} // GPTData::ShowDetails() - // Show detailed information on the specified partition void GPTData::ShowPartDetails(uint32_t partNum) { if (partitions[partNum].GetFirstLBA() != 0) { @@ -1199,220 +1261,6 @@ void GPTData::ShowPartDetails(uint32_t partNum) { } // if } // GPTData::ShowPartDetails() -/********************************************************************* - * * - * Begin functions that obtain information from the users, and often * - * do something with that information (call other functions) * - * * - *********************************************************************/ - -// Prompts user for partition number and returns the result. -uint32_t GPTData::GetPartNum(void) { - uint32_t partNum; - uint32_t low, high; - char prompt[255]; - - if (GetPartRange(&low, &high) > 0) { - sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); - partNum = GetNumber(low + 1, high + 1, low, prompt); - } else partNum = 1; - return (partNum - 1); -} // GPTData::GetPartNum() - -// What it says: Resize the partition table. (Default is 128 entries.) -void GPTData::ResizePartitionTable(void) { - int newSize; - char prompt[255]; - uint32_t curLow, curHigh; - - cout << "Current partition table size is " << mainHeader.numParts << ".\n"; - GetPartRange(&curLow, &curHigh); - curHigh++; // since GetPartRange() returns numbers starting from 0... - // There's no point in having fewer than four partitions.... - if (curHigh < 4) - curHigh = 4; - sprintf(prompt, "Enter new size (%d up, default %d): ", (int) curHigh, - (int) NUM_GPT_ENTRIES); - newSize = GetNumber(4, 65535, 128, prompt); - if (newSize < 128) { - cout << "Caution: The partition table size should officially be 16KB or larger,\n" - << "which works out to 128 entries. In practice, smaller tables seem to\n" - << "work with most OSes, but this practice is risky. I'm proceeding with\n" - << "the resize, but you may want to reconsider this action and undo it.\n\n"; - } // if - SetGPTSize(newSize); -} // GPTData::ResizePartitionTable() - -// Interactively create a partition -void GPTData::CreatePartition(void) { - uint64_t firstBlock, firstInLargest, lastBlock, sector; - uint32_t firstFreePart = 0; - char prompt[255]; - int partNum; - - // Find first free partition... - while (partitions[firstFreePart].GetFirstLBA() != 0) { - firstFreePart++; - } // while - - if (((firstBlock = FindFirstAvailable()) != 0) && - (firstFreePart < mainHeader.numParts)) { - lastBlock = FindLastAvailable(); - firstInLargest = FindFirstInLargest(); - - // Get partition number.... - do { - sprintf(prompt, "Partition number (%d-%d, default %d): ", firstFreePart + 1, - mainHeader.numParts, firstFreePart + 1); - partNum = GetNumber(firstFreePart + 1, mainHeader.numParts, - firstFreePart + 1, prompt) - 1; - if (partitions[partNum].GetFirstLBA() != 0) - cout << "partition " << partNum + 1 << " is in use.\n"; - } while (partitions[partNum].GetFirstLBA() != 0); - - // Get first block for new partition... - sprintf(prompt, "First sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ", - (unsigned long long) firstBlock, (unsigned long long) lastBlock, - (unsigned long long) firstInLargest); - do { - sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt); - } while (IsFree(sector) == 0); - Align(§or); // Align sector to correct multiple - firstBlock = sector; - - // Get last block for new partitions... - lastBlock = FindLastInFree(firstBlock); - sprintf(prompt, "Last sector (%llu-%llu, default = %llu) or {+-}size{KMGT}: ", - (unsigned long long) firstBlock, (unsigned long long) lastBlock, - (unsigned long long) lastBlock); - do { - sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt); - } while (IsFree(sector) == 0); - lastBlock = sector; - - firstFreePart = CreatePartition(partNum, firstBlock, lastBlock); - partitions[partNum].ChangeType(); - partitions[partNum].SetDefaultDescription(); - } else { - cout << "No free sectors available\n"; - } // if/else -} // GPTData::CreatePartition() - -// Interactively delete a partition (duh!) -void GPTData::DeletePartition(void) { - int partNum; - uint32_t low, high; - char prompt[255]; - - if (GetPartRange(&low, &high) > 0) { - sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); - partNum = GetNumber(low + 1, high + 1, low, prompt); - DeletePartition(partNum - 1); - } else { - cout << "No partitions\n"; - } // if/else -} // GPTData::DeletePartition() - -// Prompt user for a partition number, then change its type code -// using ChangeGPTType(struct GPTPartition*) function. -void GPTData::ChangePartType(void) { - int partNum; - uint32_t low, high; - - if (GetPartRange(&low, &high) > 0) { - partNum = GetPartNum(); - partitions[partNum].ChangeType(); - } else { - cout << "No partitions\n"; - } // if/else -} // GPTData::ChangePartType() - -// Partition attributes seem to be rarely used, but I want a way to -// adjust them for completeness.... -void GPTData::SetAttributes(uint32_t partNum) { - Attributes theAttr; - - theAttr.SetAttributes(partitions[partNum].GetAttributes()); - theAttr.DisplayAttributes(); - theAttr.ChangeAttributes(); - partitions[partNum].SetAttributes(theAttr.GetAttributes()); -} // GPTData::SetAttributes() - -// This function destroys the on-disk GPT structures. Returns 1 if the -// user confirms destruction, 0 if the user aborts. -// 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.) -// If prompt == -1, don't ask user about proceeding and DO wipe out -// MBR. -int GPTData::DestroyGPT(int prompt) { - int i, sum, tableSize; - uint8_t blankSector[512], goOn = 'Y', blank = 'N'; - uint8_t* emptyTable; - - for (i = 0; i < 512; i++) { - blankSector[i] = 0; - } // for - - if (((apmFound) || (bsdFound)) && (prompt > 0)) { - cout << "WARNING: APM or BSD disklabel structures detected! This operation could\n" - << "damage any APM or BSD partitions on this disk!\n"; - } // if APM or BSD - if (prompt > 0) { - cout << "\a\aAbout to wipe out GPT on " << device << ". Proceed? "; - goOn = GetYN(); - } // if - if (goOn == 'Y') { - if (myDisk.OpenForWrite(device)) { - myDisk.Seek(mainHeader.currentLBA); // seek to GPT header - if (myDisk.Write(blankSector, 512) != 512) { // blank it out - cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n"; - } // if - myDisk.Seek(mainHeader.partitionEntriesLBA); // seek to partition table - tableSize = mainHeader.numParts * mainHeader.sizeOfPartitionEntries; - emptyTable = new uint8_t[tableSize]; - for (i = 0; i < tableSize; i++) - emptyTable[i] = 0; - sum = myDisk.Write(emptyTable, tableSize); - if (sum != tableSize) - cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n"; - myDisk.Seek(secondHeader.partitionEntriesLBA); // seek to partition table - sum = myDisk.Write(emptyTable, tableSize); - if (sum != tableSize) - cerr << "Warning! GPT backup partition table not overwritten! Error is " << errno << "\n"; - myDisk.Seek(secondHeader.currentLBA); // seek to GPT header - if (myDisk.Write(blankSector, 512) != 512) { // blank it out - cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n"; - } // if - if (prompt > 0) { - cout << "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') || (prompt < 0)) { - myDisk.Seek(0); - if (myDisk.Write(blankSector, 512) != 512) { // blank it out - cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n"; - } // if - } else { - cout << "MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n" - << "with fdisk or another tool.\n"; - } // if/else - myDisk.DiskSync(); - myDisk.Close(); - cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n" - << "other utilities. Program will now terminate.\n"; - delete[] emptyTable; - } else { - cerr << "Problem opening " << device << " for writing! Program will now terminate.\n"; - } // if/else (fd != -1) - } // if (goOn == 'Y') - return (goOn == 'Y'); -} // GPTData::DestroyGPT() - /************************************************************************** * * * Partition table transformation functions (MBR or BSD disklabel to GPT) * @@ -1420,13 +1268,14 @@ int GPTData::DestroyGPT(int prompt) { * * **************************************************************************/ -// Examines the MBR & GPT data, and perhaps asks the user questions, to -// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt), -// or create a new set of partitions (use_new) +// Examines the MBR & GPT data to determine which set of data to use: the +// MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create +// a new set of partitions (use_new). A return value of use_abort indicates +// that this function couldn't determine what to do. Overriding functions +// in derived classes may ask users questions in such cases. WhichToUse GPTData::UseWhichPartitions(void) { WhichToUse which = use_new; MBRValidity mbrState; - int answer; mbrState = protectiveMBR.GetValidity(); @@ -1470,49 +1319,20 @@ WhichToUse GPTData::UseWhichPartitions(void) { which = use_gpt; } // if if ((state == gpt_valid) && (mbrState == mbr)) { - if (!beQuiet) { - cout << "Found valid MBR and GPT. Which do you want to use?\n"; - answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); - if (answer == 1) { - which = use_mbr; - } else if (answer == 2) { - which = use_gpt; - cout << "Using GPT and creating fresh protective MBR.\n"; - } else which = use_new; - } else which = use_abort; - } // if - - // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other - // problems) + which = use_abort; + } // if + if (state == gpt_corrupt) { - if (beQuiet) { - which = use_abort; + if (mbrState == gpt) { + cout << "\a\a****************************************************************************\n" + << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" + << "verification and recovery are STRONGLY recommended.\n" + << "****************************************************************************\n"; + which = use_gpt; } else { - if ((mbrState == mbr) || (mbrState == hybrid)) { - cout << "Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n" - << "GPT MAY permit recovery of GPT data.)\n"; - answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: "); - if (answer == 1) { - which = use_mbr; - } else if (answer == 2) { - which = use_gpt; - } else which = use_new; - } else if (mbrState == invalid) { - cout << "Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n" - << "GPT MAY permit recovery of GPT data.)\n"; - answer = GetNumber(1, 2, 1, " 1 - GPT\n 2 - Create blank GPT\n\nYour answer: "); - if (answer == 1) { - which = use_gpt; - } else which = use_new; - } else { // corrupt GPT, MBR indicates it's a GPT disk.... - cout << "\a\a****************************************************************************\n" - << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" - << "verification and recovery are STRONGLY recommended.\n" - << "****************************************************************************\n"; - which = use_gpt; - } // if/else/else - } // else (beQuiet) - } // if (corrupt GPT) + which = use_abort; + } // if/else MBR says disk is GPT + } // if GPT corrupt if (which == use_new) cout << "Creating new GPT entries.\n"; @@ -1520,13 +1340,14 @@ WhichToUse GPTData::UseWhichPartitions(void) { return which; } // UseWhichPartitions() -// Convert MBR partition table into GPT form -int GPTData::XFormPartitions(void) { +// Convert MBR partition table into GPT form. +void GPTData::XFormPartitions(void) { int i, numToConvert; uint8_t origType; // Clear out old data & prepare basics.... ClearGPTData(); + protectiveMBR.EmptyBootloader(); // Convert the smaller of the # of GPT or MBR partitions if (mainHeader.numParts > (MAX_MBR_PARTS)) @@ -1549,71 +1370,60 @@ int GPTData::XFormPartitions(void) { // Record that all original CRCs were OK so as not to raise flags // when doing a disk verification mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; - - return (1); } // GPTData::XFormPartitions() // Transforms BSD disklabel on the specified partition (numbered from 0). -// If an invalid partition number is given, the program prompts for one. -// (Default for i is -1; called without an option, it therefore prompts.) +// If an invalid partition number is given, the program does nothing. // Returns the number of new partitions created. -int GPTData::XFormDisklabel(int i) { - uint32_t low, high, partNum, startPart; - uint16_t hexCode; +int GPTData::XFormDisklabel(uint32_t partNum) { + uint32_t low, high; int goOn = 1, numDone = 0; BSDData disklabel; - if (GetPartRange(&low, &high) != 0) { - if ((i < (int) low) || (i > (int) high)) - partNum = GetPartNum(); - else - partNum = (uint32_t) i; - - // Find the partition after the last used one - startPart = high + 1; - - // Now see if the specified partition has a BSD type code.... - hexCode = partitions[partNum].GetHexType(); - if ((hexCode != 0xa500) && (hexCode != 0xa900)) { - cout << "Specified partition doesn't have a disklabel partition type " - << "code.\nContinue anyway? "; - goOn = (GetYN() == 'Y'); - } // if + if (GetPartRange(&low, &high) == 0) { + goOn = 0; + cout << "No partitions!\n"; + } // if + if (partNum > high) { + goOn = 0; + cout << "Specified partition is invalid!\n"; + } // if - // If all is OK, read the disklabel and convert it. - if (goOn) { - goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(), - partitions[partNum].GetLastLBA()); - if ((goOn) && (disklabel.IsDisklabel())) { - numDone = XFormDisklabel(&disklabel, startPart); - if (numDone == 1) - cout << "Converted " << numDone << " BSD partition.\n"; - else - cout << "Converted " << numDone << " BSD partitions.\n"; - } else { - cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n"; - } // if/else - } // if - if (numDone > 0) { // converted partitions; delete carrier - partitions[partNum].BlankPartition(); - } // if - } else { - cout << "No partitions\n"; - } // if/else + // If all is OK, read the disklabel and convert it. + if (goOn) { + goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(), + partitions[partNum].GetLastLBA()); + if ((goOn) && (disklabel.IsDisklabel())) { + numDone = XFormDisklabel(&disklabel); + if (numDone == 1) + cout << "Converted 1 BSD partition.\n"; + else + cout << "Converted " << numDone << " BSD partitions.\n"; + } else { + cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n"; + } // if/else + } // if + if (numDone > 0) { // converted partitions; delete carrier + partitions[partNum].BlankPartition(); + } // if return numDone; } // GPTData::XFormDisklable(int i) // Transform the partitions on an already-loaded BSD disklabel... -int GPTData::XFormDisklabel(BSDData* disklabel, uint32_t startPart) { - int i, numDone = 0; +int GPTData::XFormDisklabel(BSDData* disklabel) { + int i, partNum = 0, numDone = 0; - if ((disklabel->IsDisklabel()) && (startPart >= 0) && - (startPart < mainHeader.numParts)) { + if (disklabel->IsDisklabel()) { for (i = 0; i < disklabel->GetNumParts(); i++) { - partitions[i + startPart] = disklabel->AsGPT(i); - if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0)) - numDone++; + partNum = FindFirstFreePart(); + if (partNum >= 0) { + partitions[partNum] = disklabel->AsGPT(i); + if (partitions[partNum].IsUsed()) + numDone++; + } // if } // for + if (partNum == -1) + cerr << "Warning! Too many partitions to convert!\n"; } // if // Record that all original CRCs were OK so as not to raise flags @@ -1623,16 +1433,13 @@ int GPTData::XFormDisklabel(BSDData* disklabel, uint32_t startPart) { return numDone; } // GPTData::XFormDisklabel(BSDData* disklabel) -// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid() -// functions. Returns 1 if operation was successful. +// Add one GPT partition to MBR. Used by PartsToMBR() functions. Created +// partition has the active/bootable flag UNset and uses the GPT fdisk +// type code divided by 0x0100 as the MBR type code. +// Returns 1 if operation was 100% successful, 0 if there were ANY +// problems. int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) { - int allOK = 1, typeCode, bootable; - uint64_t length; - char line[255]; - char* junk; - - cout.setf(ios::uppercase); - cout.fill('0'); + int allOK = 1; if ((mbrPart < 0) || (mbrPart > 3)) { cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n"; @@ -1651,156 +1458,60 @@ int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) { if (partitions[gptPart].GetLastLBA() > UINT32_MAX) { cout << "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 { - cout << "Enter an MBR hex code (default " << hex; - cout.width(2); - cout << partitions[gptPart].GetHexType() / 0x0100 << "): "; - junk = fgets(line, 255, stdin); - if (line[0] == '\n') - typeCode = partitions[gptPart].GetHexType() / 256; - else - sscanf(line, "%x", &typeCode); - } while ((typeCode <= 0) || (typeCode > 255)); - cout << "Set the bootable flag? "; - bootable = (GetYN() == 'Y'); - length = partitions[gptPart].GetLengthLBA(); + } // if protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(), - (uint32_t) length, typeCode, bootable); + (uint32_t) partitions[gptPart].GetLengthLBA(), + partitions[gptPart].GetHexType() / 256, 0); } else { // partition out of range - cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR " - << "partitions, or is\n too big; omitting it.\n"; + if (allOK) // Display only if "else" triggered by out-of-bounds condition + cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR " + << "partitions, or is\n too big; omitting it.\n"; allOK = 0; } // if/else - cout.fill(' '); 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]; - char* junk; - int j, numParts, numConverted = 0; - uint32_t i, partNums[4]; - - // Get the numbers of up to four partitions to add to the - // hybrid MBR.... - numParts = CountParts(); - cout << "Counted " << numParts << " partitions.\n"; - - // 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 - cout << "Type from one to four GPT partition numbers, separated by spaces, to be\n" - << "used in the MBR, in sequence: "; - junk = 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 < (uint32_t) numParts; i++) { - j = partNums[i] - 1; - cout << "\nCreating entry for partition #" << j + 1 << "\n"; - numConverted += OnePartToMBR(j, i); - } // for - cout << "MBR writing returned " << protectiveMBR.WriteMBRData(&myDisk) << "\n"; - return numConverted; -} // GPTData::XFormToMBR() - -// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with -// OSes that don't understand GPT. -void GPTData::MakeHybrid(void) { - uint32_t partNums[3]; - char line[255]; - char* junk; - int numParts, numConverted = 0, i, j, typeCode, mbrNum; - char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe) - char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table - - cout << "\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" - << "partition table will be untouched.\n\n\a"; - - // Now get the numbers of up to three partitions to add to the - // hybrid MBR.... - cout << "Type from one to three GPT partition numbers, separated by spaces, to be\n" - << "added to the hybrid MBR, in sequence: "; - junk = fgets(line, 255, stdin); - numParts = sscanf(line, "%d %d %d", &partNums[0], &partNums[1], &partNums[2]); - - if (numParts > 0) { - // Blank out the protective MBR, but leave the boot loader code - // alone.... - protectiveMBR.EmptyMBR(0); +// Convert up to four partitions to MBR form and return the number done. +// Partitions are specified in an array of GPT partition numbers, +// with an associated array of partition type codes. Both must be +// at least four elements in size (longer is OK, but will be ignored). +// A partition number of MBR_EFI_GPT means to place an EFI GPT +// protective partition in that location in the table (the associated +// mbrType[] should be 0xEE), and MBR_EMPTY means not to create a +// partition in that table position. If the mbrType[] entry for a +// partition is 0, a default entry is used, based on the GPT +// partition type code. +// Returns the number of partitions converted, NOT counting EFI GPT +// protective partitions. +int GPTData::PartsToMBR(const int *gptParts, const int *mbrTypes) { + int i, numConverted = 0; + + if ((gptParts != NULL) && (mbrTypes != NULL)) { + protectiveMBR.EmptyMBR(); protectiveMBR.SetDiskSize(diskSize); - cout << "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? "; - eeFirst = GetYN(); - } // if - - for (i = 0; i < numParts; i++) { - j = partNums[i] - 1; - cout << "\nCreating entry for partition #" << j + 1 << "\n"; - if (eeFirst == 'Y') - mbrNum = i + 1; - else - mbrNum = i; - numConverted += OnePartToMBR(j, mbrNum); - } // for - - if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR.... - // Create EFI protective partition that covers the start of the disk. - // If this location (covering the main GPT data structures) is omitted, - // Linux won't find any partitions on the disk. Note that this is - // NUMBERED AFTER the hybrid partitions, contrary to what the - // gptsync utility does. This is because Windows seems to choke on - // disks with a 0xEE partition in the first slot and subsequent - // additional partitions, unless it boots from the disk. - if (eeFirst == 'Y') - mbrNum = 0; - else - mbrNum = numParts; - protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE); - protectiveMBR.SetHybrid(); - - // ... and for good measure, if there are any partition spaces left, - // optionally create another protective EFI partition to cover as much - // space as possible.... + // Do two passes, one to get "real" partitions and + // the next to create EFI GPT protective partition(s) for (i = 0; i < 4; i++) { - if (protectiveMBR.GetType(i) == 0x00) { // unused entry.... - if (fillItUp == 'M') { - cout << "\nUnused partition space(s) found. Use one to protect more partitions? "; - fillItUp = GetYN(); - typeCode = 0x00; // use this to flag a need to get type code - } // if - if (fillItUp == 'Y') { - while ((typeCode <= 0) || (typeCode > 255)) { - cout << "Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): "; - // Comment on above: Mac OS treats disks with more than one - // 0xEE MBR partition as MBR disks, not as GPT disks. - junk = fgets(line, 255, stdin); - sscanf(line, "%x", &typeCode); - if (line[0] == '\n') - typeCode = 0; - } // while - protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition - } // if (fillItUp == 'Y') - } // if unused entry - } // for (i = 0; i < 4; i++) - } // if (numParts > 0) -} // GPTData::MakeHybrid() + if (gptParts[i] >= 0) { + numConverted += OnePartToMBR((uint32_t) gptParts[i], i); + if (mbrTypes[i] != 0) + protectiveMBR.SetPartType(i, mbrTypes[i]); + } // if + } // for (regular partition pass) + for (i = 0; i < 4; i++) { + if (gptParts[i] == MBR_EFI_GPT) { + if (protectiveMBR.FindFirstAvailable() == UINT32_C(1)) { + protectiveMBR.MakePart(i, 1, protectiveMBR.FindLastInFree(1), mbrTypes[i]); + protectiveMBR.SetHybrid(); + } else { + protectiveMBR.MakeBiggestPart(i, mbrTypes[i]); + } // if/else + } // if EFI GPT partition specified + } // for (0xEE pass) + } // if arrays were passed + return numConverted; +} // GPTData::PartsToMBR() + /********************************************************************** * * @@ -1813,8 +1524,8 @@ void GPTData::MakeHybrid(void) { // necessary, copies data if it already exists. Returns 1 if all goes // well, 0 if an error is encountered. int GPTData::SetGPTSize(uint32_t numEntries) { - struct GPTPart* newParts; - struct GPTPart* trash; + GPTPart* newParts; + GPTPart* trash; uint32_t i, high, copyNum; int allOK = 1; @@ -1907,10 +1618,8 @@ int GPTData::DeletePartition(uint32_t partNum) { return retval; } // GPTData::DeletePartition(uint32_t partNum) -// Non-interactively create a partition. Note that this function is overloaded -// with another of the same name but different parameters; that one prompts -// the user for data. This one returns 1 if the operation was successful, 0 -// if a problem was discovered. +// Non-interactively create a partition. +// Returns 1 if the operation was successful, 0 if a problem was discovered. uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) { int retval = 1; // assume there'll be no problems @@ -1931,7 +1640,6 @@ uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64 // Sort the GPT entries, eliminating gaps and making for a logical // ordering. Relies on QuickSortGPT() for the bulk of the work void GPTData::SortGPT(void) { - GPTPart temp; uint32_t i, numFound, firstPart, lastPart; // First, find the last partition with data, so as not to @@ -1943,9 +1651,7 @@ void GPTData::SortGPT(void) { i = 0; while (i < lastPart) { if (partitions[i].GetFirstLBA() == 0) { - temp = partitions[i]; - partitions[i] = partitions[lastPart]; - partitions[lastPart] = temp; + SwapPartitions(i, lastPart); do { lastPart--; } while ((lastPart > 0) && (partitions[lastPart].GetFirstLBA() == 0)); @@ -1959,9 +1665,51 @@ void GPTData::SortGPT(void) { GetPartRange(&firstPart, &lastPart); // Now call the recursive quick sort routine to do the real work.... - QuickSortGPT(partitions, 0, lastPart); + QuickSortGPT(0, lastPart); } // GPTData::SortGPT() +// Recursive quick sort algorithm for GPT partitions. Note that if there +// are any empties in the specified range, they'll be sorted to the +// start, resulting in a sorted set of partitions that begins with +// partition 2, 3, or higher. +void GPTData::QuickSortGPT(int start, int finish) { + uint64_t starterValue; // starting location of median partition + int left, right; + + left = start; + right = finish; + starterValue = partitions[(start + finish) / 2].GetFirstLBA(); + do { + while (partitions[left].GetFirstLBA() < starterValue) + left++; + while (partitions[right].GetFirstLBA() > starterValue) + right--; + if (left <= right) + SwapPartitions(left++, right--); + } while (left <= right); + if (start < right) QuickSortGPT(start, right); + if (finish > left) QuickSortGPT(left, finish); +} // GPTData::QuickSortGPT() + +// Swap the contents of two partitions. +// Returns 1 if successful, 0 if either partition is out of range +// (that is, not a legal number; either or both can be empty). +// Note that if partNum1 = partNum2 and this number is in range, +// it will be considered successful. +int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) { + GPTPart temp; + int allOK = 1; + + if ((partNum1 < mainHeader.numParts) && (partNum2 < mainHeader.numParts)) { + if (partNum1 != partNum2) { + temp = partitions[partNum1]; + partitions[partNum1] = partitions[partNum2]; + partitions[partNum2] = temp; + } // if + } else allOK = 0; // partition numbers are valid + return allOK; +} // GPTData::SwapPartitions() + // Set up data structures for entirely new set of partitions on the // specified device. Returns 1 if OK, 0 if there were problems. // Note that this function does NOT clear the protectiveMBR data @@ -2151,9 +1899,9 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { for (i = 0; i < mainHeader.numParts; i++) { if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists *high = i; // since we're counting up, set the high value - // Set the low value only if it's not yet found... + // Set the low value only if it's not yet found... if (*low == (mainHeader.numParts + 1)) *low = i; - numFound++; + numFound++; } // if } // for } // if @@ -2165,12 +1913,26 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { return numFound; } // GPTData::GetPartRange() +// Returns the value of the first free partition, or -1 if none is +// unused. +int GPTData::FindFirstFreePart(void) { + int i = 0; + + if (partitions != NULL) { + while ((partitions[i].IsUsed()) && (i < (int) mainHeader.numParts)) + i++; + if (i >= (int) mainHeader.numParts) + i = -1; + } else i = -1; + return i; +} // GPTData::FindFirstFreePart() + // Returns the number of defined partitions. uint32_t GPTData::CountParts(void) { uint32_t i, counted = 0; for (i = 0; i < mainHeader.numParts; i++) { - if (partitions[i].GetFirstLBA() > 0) + if (partitions[i].IsUsed()) counted++; } // for return counted; @@ -2318,22 +2080,21 @@ int GPTData::IsFree(uint64_t sector) { if ((sector >= partitions[i].GetFirstLBA()) && (sector <= partitions[i].GetLastLBA())) { isFree = 0; - } // if + } // if } // for if ((sector < mainHeader.firstUsableLBA) || (sector > mainHeader.lastUsableLBA)) { isFree = 0; - } // if - return (isFree); + } // if + return (isFree); } // GPTData::IsFree() // Returns 1 if partNum is unused. int GPTData::IsFreePartNum(uint32_t partNum) { int retval = 1; - if ((partNum >= 0) && (partNum < mainHeader.numParts)) { - if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) || - (partitions[partNum].GetLastLBA() != UINT64_C(0))) { + if ((partNum < mainHeader.numParts) && (partitions != NULL)) { + if (partitions[partNum].IsUsed()) { retval = 0; } // if partition is in use } else retval = 0; @@ -2361,8 +2122,7 @@ void GPTData::ReverseHeaderBytes(struct GPTHeader* header) { ReverseBytes(&header->numParts, 4); ReverseBytes(&header->sizeOfPartitionEntries, 4); ReverseBytes(&header->partitionEntriesCRC, 4); - ReverseBytes(&header->reserved2, GPT_RESERVED); -// header->diskGUID.ReverseGUIDBytes(); + ReverseBytes(header->reserved2, GPT_RESERVED); } // GPTData::ReverseHeaderBytes() // IMPORTANT NOTE: This function requires non-reversed mainHeader @@ -15,7 +15,13 @@ #ifndef __GPTSTRUCTS #define __GPTSTRUCTS -#define GPTFDISK_VERSION "0.6.4-pre1" +#define GPTFDISK_VERSION "0.6.4" + +// Constants used by GPTData::PartsToMBR(). MBR_EMPTY must be the lowest- +// numbered value to refer to partition numbers. (Most will be 0 or positive, +// of course.) +#define MBR_EFI_GPT -1 +#define MBR_EMPTY -2 using namespace std; @@ -83,7 +89,7 @@ public: // Basic necessary functions.... GPTData(void); GPTData(string deviceFilename); - ~GPTData(void); + virtual ~GPTData(void); // Verify (or update) data integrity int Verify(void); @@ -98,6 +104,7 @@ public: // Load or save data from/to disk int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);} + int WriteProtectiveMBR(void) {return protectiveMBR.WriteMBRData(&myDisk);} void PartitionScan(void); int LoadPartitions(const string & deviceFilename); int ForceLoadGPTData(void); @@ -106,32 +113,24 @@ public: int SaveGPTData(int quiet = 0); int SaveGPTBackup(const string & filename); int LoadGPTBackup(const string & filename); + int SaveMBR(void); + int DestroyGPT(void); + int DestroyMBR(void); // Display data.... void ShowAPMState(void); void ShowGPTState(void); void DisplayGPTData(void); void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();} - void ShowDetails(void); void ShowPartDetails(uint32_t partNum); - // Request information from the user (& possibly do something with it) - uint32_t GetPartNum(void); - void ResizePartitionTable(void); - void CreatePartition(void); - void DeletePartition(void); - void ChangePartType(void); - void SetAttributes(uint32_t partNum); - int DestroyGPT(int prompt = 1); // Returns 1 if user proceeds - - // Convert between GPT and other formats (may require user interaction) - WhichToUse UseWhichPartitions(void); - int XFormPartitions(void); - int XFormDisklabel(int OnGptPart = -1); - int XFormDisklabel(BSDData* disklabel, uint32_t startPart); + // Convert between GPT and other formats + virtual WhichToUse UseWhichPartitions(void); + void XFormPartitions(void); + virtual int XFormDisklabel(uint32_t partNum); + int XFormDisklabel(BSDData* disklabel); 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); + int PartsToMBR(const int *gptParts, const int *mbrTypes); // Adjust GPT structures WITHOUT user interaction... int SetGPTSize(uint32_t numEntries); @@ -139,6 +138,8 @@ public: int DeletePartition(uint32_t partNum); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); void SortGPT(void); + void QuickSortGPT(int start, int finish); + int SwapPartitions(uint32_t partNum1, uint32_t partNum2); int ClearGPTData(void); void MoveSecondHeaderToEnd(); int SetName(uint32_t partNum, const string & theName = ""); @@ -150,6 +151,7 @@ public: // Return data about the GPT structures.... int GetPartRange(uint32_t* low, uint32_t* high); + int FindFirstFreePart(void); uint32_t GetNumParts(void) {return mainHeader.numParts;} uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;} uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} @@ -66,6 +66,11 @@ string GPTPart::GetDescription(void) { return theName; } // GPTPart::GetDescription() +// Return 1 if the partition is in use +int GPTPart::IsUsed(void) { + return (firstLBA != UINT64_C(0)); +} // GPTPart::IsUsed() + // Set the type code to the specified one. Also changes the partition // name *IF* the current name is the generic one for the current partition // type. @@ -139,6 +144,7 @@ void GPTPart::ShowSummary(int partNum, uint32_t blockSize) { if (firstLBA != 0) { sizeInSI = BytesToSI(blockSize * (lastLBA - firstLBA + 1)); + cout.fill(' '); cout.width(4); cout << partNum + 1 << " "; cout.width(14); @@ -256,36 +262,3 @@ void GPTPart::ChangeType(void) { SetDefaultDescription(); } // if } // GPTPart::ChangeType() - -/*********************************** - * Non-class but related functions * - ***********************************/ - -// Recursive quick sort algorithm for GPT partitions. Note that if there -// are any empties in the specified range, they'll be sorted to the -// start, resulting in a sorted set of partitions that begins with -// partition 2, 3, or higher. -void QuickSortGPT(GPTPart* partitions, int start, int finish) { - uint64_t starterValue; // starting location of median partition - int left, right; - GPTPart temp; - - left = start; - right = finish; - starterValue = partitions[(start + finish) / 2].GetFirstLBA(); - do { - while (partitions[left].GetFirstLBA() < starterValue) - left++; - while (partitions[right].GetFirstLBA() > starterValue) - right--; - if (left <= right) { - temp = partitions[left]; - partitions[left] = partitions[right]; - partitions[right] = temp; - left++; - right--; - } // if - } while (left <= right); - if (start < right) QuickSortGPT(partitions, start, right); - if (finish > left) QuickSortGPT(partitions, left, finish); -} // QuickSortGPT() @@ -59,6 +59,7 @@ class GPTPart { uint64_t GetLengthLBA(void); uint64_t GetAttributes(void) {return attributes;} string GetDescription(void); + int IsUsed(void); // Simple data assignment: void SetType(PartType t); @@ -83,7 +84,4 @@ class GPTPart { void ChangeType(void); // Change the type code }; // struct GPTPart -// A support function that doesn't quite belong in the class.... -void QuickSortGPT(GPTPart* partitions, int start, int finish); - #endif @@ -10,7 +10,7 @@ #define __STDC_CONSTANT_MACROS #include <stdio.h> -#include <unistd.h> +//#include <unistd.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> @@ -273,7 +273,7 @@ int MBRData::WriteMBRData(void) { int allOK = 1; if (myDisk != NULL) { - if (myDisk->OpenForWrite(device) != 0) { + if (myDisk->OpenForWrite() != 0) { allOK = WriteMBRData(myDisk); } else { allOK = 0; @@ -493,10 +493,7 @@ void MBRData::EmptyMBR(int clearBootloader) { // 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; + EmptyBootloader(); } // if // Blank out the partitions @@ -515,6 +512,17 @@ void MBRData::EmptyMBR(int clearBootloader) { MBRSignature = MBR_SIGNATURE; } // MBRData::EmptyMBR() +// Blank out the boot loader area. Done with the initial MBR-to-GPT +// conversion, since MBR boot loaders don't understand GPT, and so +// need to be replaced.... +void MBRData::EmptyBootloader(void) { + int i; + + for (i = 0; i < 440; i++) + code[i] = 0; + nulls = 0; +} // MBRData::EmptyBootloader + // Create a protective MBR. Clears the boot loader area if clearBoot > 0. void MBRData::MakeProtectiveMBR(int clearBoot) { @@ -523,6 +531,7 @@ void MBRData::MakeProtectiveMBR(int clearBoot) { // Initialize variables nulls = 0; MBRSignature = MBR_SIGNATURE; + diskSignature = (uint32_t) rand(); partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable @@ -561,7 +570,7 @@ void MBRData::MakeProtectiveMBR(int clearBoot) { 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].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); @@ -576,9 +585,40 @@ void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type, LBAtoCHS((uint64_t) start, partitions[num].firstSector); LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector); } // if (length > 0) + SetPartBootable(num, bootable); } // if valid partition number } // MBRData::MakePart() +// Set the partition's type code. +// Returns 1 if successful, 0 if not (invalid partition number) +int MBRData::SetPartType(int num, int type) { + int allOK = 1; + + if ((num >= 0) && (num < MAX_MBR_PARTS)) { + if (partitions[num].lengthLBA != UINT32_C(0)) { + partitions[num].partitionType = (uint8_t) type; + } else allOK = 0; + } else allOK = 0; + return allOK; +} // MBRData::SetPartType() + +// Set (or remove) the partition's bootable flag. Setting it is the +// default; pass 0 as bootable to remove the flag. +// Returns 1 if successful, 0 if not (invalid partition number) +int MBRData::SetPartBootable(int num, int bootable) { + int allOK = 1; + + if ((num >= 0) && (num < MAX_MBR_PARTS)) { + if (partitions[num].lengthLBA != UINT32_C(0)) { + if (bootable == 0) + partitions[num].status = UINT8_C(0); + else + partitions[num].status = UINT8_C(0x80); + } else allOK = 0; + } else allOK = 0; + return allOK; +} // MBRData::SetPartBootable() + // Create a partition that fills the most available space. Returns // 1 if partition was created, 0 otherwise. Intended for use in // creating hybrid MBRs. @@ -637,7 +677,7 @@ int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) { start32 = (uint32_t) start64; length32 = (uint32_t) length64; 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)) { DeletePartition(i); if (state == hybrid) @@ -717,7 +757,7 @@ uint32_t MBRData::FindLastInFree(uint32_t start) { uint32_t i; if ((diskSize <= UINT32_MAX) && (diskSize > 0)) - nearestStart = diskSize - 1; + nearestStart = (uint32_t) diskSize - 1; else nearestStart = UINT32_MAX - 1; for (i = 0; i < 4; i++) { @@ -105,9 +105,12 @@ public: // 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 EmptyBootloader(void); void MakeProtectiveMBR(int clearBoot = 0); void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, int bootable = 0); + int SetPartType(int num, int type); + int SetPartBootable(int num, int bootable = 1); int MakeBiggestPart(int i, int type); // Make partition filling most space void DeletePartition(int i); int DeleteByLocation(uint64_t start64, uint64_t length64); diff --git a/parttypes.h b/parttypes.h index 5777f9c..4a40ecd 100644 --- a/parttypes.h +++ b/parttypes.h @@ -2,7 +2,7 @@ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include <stdint.h> -#include <unistd.h> +//#include <unistd.h> #include <stdlib.h> #include <string> #include "support.h" @@ -1,6 +1,6 @@ .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License -.TH "SGDISK" "8" "0.6.3" "Roderick W. Smith" "GPT fdisk Manual" +.TH "SGDISK" "8" "0.6.4" "Roderick W. Smith" "GPT fdisk Manual" .SH "NAME" sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix .SH "SYNOPSIS" @@ -60,14 +60,15 @@ attempt to convert the MBR or disklabel into GPT form. (BSD disklabels are likely to have unusable first and/or final partitions because they overlap with the GPT data structures, though.) GPT fdisk can identify, but not use data in, Apple Partition Map (APM) disks, which are used on 680x0\- and -PowerPC\-based Macintoshes. If you specify any option that results in changes -to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes unless the -\fI\-g\fR (\fI\-\-mbrtogpt\fR) or \fI\-z\fR (\fI\-\-zap\fR) option is used. -If you use the \fI\-g\fR option, \fBsgdisk\fR replaces the MBR or disklabel -with a GPT. \fIThis action is potentially dangerous!\fR Your system may become -unbootable, and partition type codes may become corrupted if the disk uses -unrecognized type codes. Boot problems are particularly likely if you're -multi\-booting with any GPT\-unaware OS. +PowerPC\-based Macintoshes. If you specify any option that results in +changes to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes +unless the \fI\-g\fR (\fI\-\-mbrtogpt\fR), \fI\-z\fR (\fI\-\-zap\fR), or +\fI\-Z\fR (\fI\-\-zap\-all\fR) option is used. If you use the \fI\-g\fR +option, \fBsgdisk\fR replaces the MBR or disklabel with a GPT. \fIThis +action is potentially dangerous!\fR Your system may become unbootable, and +partition type codes may become corrupted if the disk uses unrecognized +type codes. Boot problems are particularly likely if you're multi\-booting +with any GPT\-unaware OS. The MBR\-to\-GPT conversion will leave at least one gap in the partition numbering if the original MBR used logical partitions. These gaps are @@ -210,6 +211,17 @@ Convert an MBR or BSD disklabel disk to a GPT disk. As a safety measure, use of this option is required on MBR or BSD disklabel disks if you intend to save your changes, in order to prevent accidentally damaging such disks. +.TP +.B \-h, \-\-hybrid +Create a hybrid MBR. This option takes from one to three partition numbers, +separated by colons, as arguments. The created hybrid MBR places an EFI GPT +(type 0xEE) partition first in the table, followed by the partition(s) you +specify. Their type codes are based on the GPT fdisk type codes divided by +0x0100, which is usually correct for Windows partitions. If the +active/bootable flag should be set, you must do so in another program, such +as \fBfdisk\fR. The \fBgdisk\fR program offers additional hybrid MBR +creation options. + .TP .B \-i, \-\-info=partnum Show detailed partition information. The summary information produced by @@ -241,6 +253,17 @@ adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel, these two\-byte codes are unique to \fBgdisk\fR and \fBsgdisk\fR. This option does not require you to specify a valid disk device filename. +.TP +.B \-m, \-\-gpttombr +Convert disk from GPT to MBR form. This option takes from one to four +partition numbers, separated by colons, as arguments. Their type codes are +based on the GPT fdisk type codes divided by 0x0100. If the active/bootable +flag should be set, you must do so in another program, such as \fBfdisk\fR. +The \fBgdisk\fR program offers additional MBR conversion options. It is not +possible to convert more than four partitions from GPT to MBR form or to +convert partitions that start above the 2TiB mark or that are larger than +2TiB. + .TP .B \-n, \-\-new=partnum:start:end Create a new partition. You enter a partition @@ -274,6 +297,15 @@ Pretend to make specified changes. In\-memory GPT data structures are altered according to other parameters, but changes are not written to disk. +.TP +.B \-r, \-\-transpose +Swap two partitions' entries in the partition table. One or both partitions +may be empty, although swapping two empty partitions is pointless. For +instance, if partitions 1\-4 are defined, transposing 1 and 5 results in a +table with partitions numbered from 2\-5. Transposing partitions in this +way has no effect on their disk space allocation; it only alters their +order in the partition table. + .TP .B \-s, \-\-sort Sort partition entries. GPT partition numbers need not match the order of @@ -317,14 +349,22 @@ specifying a device filename. .TP .B \-z, \-\-zap -Zap (destroy) the GPT data structures and exit. Use this option if you want to -repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program. -You'll be given the choice of preserving the existing MBR, in case it's a -hybrid MBR with salvageable partitions or if you've already created new MBR -partitions and want to erase the remnants of your GPT partitions. \fIIf you've -already created new MBR partitions, it's conceivable that this option will -damage the first and/or last MBR partitions!\fR Such an event is unlikely, but -could occur if your new MBR partitions overlap the old GPT data structures. +Zap (destroy) the GPT data structures and then exit. Use this option if you +want to repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware +program. This option destroys only the GPT data structures; it leaves the +MBR intact. This makes it useful for wiping out GPT data structures after a +disk has been repartitioned for MBR using a GPT-unaware utility; however, +there's a risk that it will damage boot loaders or even the start of the +first or end of the last MBR partition. If you use it on a valid GPT disk, +the MBR will be left with an inappropriate EFI GPT (0xEE) partition +definition, which you can delete using another utility. + +.TP +.B \-Z, \-\-zap\-all +Zap (destroy) the GPT and MBR data structures and then exit. This option +works much like \fI\-z\fR, but as it wipes the MBR as well as the GPT, it's +more suitable if you want to repartition a disk after using this option, +and completely unsuitable if you've already repartitioned the disk. .TP .B \-?, \-\-help @@ -353,7 +393,7 @@ Non\-GPT disk detected and no \fI\-g\fR option .B 4 An error prevented saving changes .SH "BUGS" -As of January 2010 (version 0.6.0), \fBsgdisk\fR +As of February 2010 (version 0.6.4), \fBsgdisk\fR should be considered beta software. Known bugs and limitations include: .TP @@ -25,6 +25,8 @@ using namespace std; uint64_t GetInt(char* Info, int itemNum); string GetString(char* Info, int itemNum); +int BuildMBR(GPTData* theGPT, char* argument, int isHybrid); +int CountColons(char* argument); int main(int argc, char *argv[]) { GPTData theGPT; @@ -35,8 +37,8 @@ int main(int argc, char *argv[]) { uint32_t tableSize = 128; uint64_t startSector, endSector; char *device = NULL; - char *newPartInfo = NULL, *typeCode = NULL, *partName; - char *backupFile = NULL; + char *newPartInfo = NULL, *typeCode = NULL, *partName = NULL; + char *backupFile = NULL, *twoParts = NULL, *hybrids = NULL, *mbrParts; PartType typeHelper; poptContext poptCon; @@ -50,20 +52,24 @@ int main(int argc, char *argv[]) { {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""}, {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""}, {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, + {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...]"}, {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"}, {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"}, {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""}, + {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"}, {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"}, {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""}, {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""}, + {"transpose", 'r', POPT_ARG_STRING, &twoParts, 'r', "transpose two partitions", "partnum:partnum"}, {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""}, {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:hexcode"}, {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, - {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT data structures", ""}, + {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, + {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""}, POPT_AUTOHELP { NULL, 0, 0, NULL, 0 } }; @@ -150,6 +156,11 @@ int main(int argc, char *argv[]) { saveData = 1; saveNonGPT = 1; break; + case 'h': + theGPT.JustLooking(0); + if (BuildMBR(&theGPT, hybrids, 1) == 1) + saveData = 1; + break; case 'i': theGPT.ShowPartDetails(infoPartNum - 1); break; @@ -165,6 +176,19 @@ int main(int argc, char *argv[]) { break; case 'L': break; + case 'm': + theGPT.JustLooking(0); + if (BuildMBR(&theGPT, mbrParts, 0) == 1) { + if (!pretend) { + if (theGPT.SaveMBR()) + theGPT.DestroyGPT(); + else + cerr << "Problem saving MBR!\n"; + } // if + saveNonGPT = 0; + saveData = 0; + } // if + break; case 'n': theGPT.JustLooking(0); partNum = (int) GetInt(newPartInfo, 1) - 1; @@ -190,6 +214,16 @@ int main(int argc, char *argv[]) { case 'P': pretend = 1; break; + case 'r': + theGPT.JustLooking(0); + uint64_t p1, p2; + p1 = GetInt(twoParts, 1) - 1; + p2 = GetInt(twoParts, 2) - 1; + if (theGPT.SwapPartitions((uint32_t) p1, (uint32_t) p2) == 0) { + neverSaveData = 1; + cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n"; + } else saveData = 1; + break; case 's': theGPT.JustLooking(0); theGPT.SortGPT(); @@ -217,16 +251,26 @@ int main(int argc, char *argv[]) { break; case 'T': theGPT.JustLooking(0); - theGPT.XFormDisklabel(bsdPartNum); + theGPT.XFormDisklabel(bsdPartNum - 1); saveData = 1; break; case 'v': theGPT.Verify(); break; case 'z': - if (!pretend) - theGPT.DestroyGPT(-1); + if (!pretend) { + theGPT.DestroyGPT(); + } // if + saveNonGPT = 0; + saveData = 0; + break; + case 'Z': + if (!pretend) { + theGPT.DestroyGPT(); + theGPT.DestroyMBR(); + } // if saveNonGPT = 0; + saveData = 0; break; default: cerr << "Unknown option (-" << opt << ")!\n"; @@ -287,3 +331,44 @@ string GetString(char* argument, int itemNum) { return Info.substr(startPos, endPos - startPos + 1); } // GetString() + +// Create a hybrid or regular MBR from GPT data structures +int BuildMBR(GPTData* theGPT, char* argument, int isHybrid) { + int numParts, allOK = 1, i; + int gptParts[4], mbrTypes[4]; + + for (i = 0; i < 4; i++) { + gptParts[i] = MBR_EMPTY; + mbrTypes[i] = 0; // All 0s flags to use default type + } // for + if ((theGPT != NULL) && (argument != NULL) && ((isHybrid == 0) || (isHybrid == 1))) { + numParts = CountColons(argument) + 1; + if (numParts <= (4 - isHybrid)) { + if (isHybrid) { + gptParts[0] = MBR_EFI_GPT; + mbrTypes[0] = 0xEE; + } // if + for (i = 0; i < numParts; i++) { + gptParts[i + isHybrid] = GetInt(argument, i + 1) - 1; + } // for + if (theGPT->PartsToMBR(gptParts, mbrTypes) != numParts) + allOK = 0; + } else allOK = 0; + } else allOK = 0; + if (!allOK) + cerr << "Problem creating MBR!\n"; + return allOK; +} // BuildMBR() + +// Returns the number of colons in argument string +int CountColons(char* argument) { + int num = 0, i = 0; + + if (argument != NULL) { + while (argument[i] != '\0') { + if (argument[i++] == ':') + num++; + } // while + } // if + return num; +} // CountColons() @@ -17,6 +17,7 @@ #include <sys/stat.h> #include <string> #include <iostream> +#include <sstream> #include "support.h" #include <sys/types.h> @@ -166,10 +167,9 @@ uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, const string & // form string BytesToSI(uint64_t size) { string units; - char theValue[99]; + ostringstream theValue; float sizeInSI; - theValue[0] = '\0'; sizeInSI = (float) size; units = " bytes"; if (sizeInSI > 1024.0) { @@ -192,12 +192,14 @@ string BytesToSI(uint64_t size) { sizeInSI /= 1024.0; units = " PiB"; } // if + theValue.setf(ios::fixed); if (units == " bytes") { // in bytes, so no decimal point - sprintf(theValue, "%1.0f%s", sizeInSI, units.c_str()); + theValue.precision(0); } else { - sprintf(theValue, "%1.1f%s", sizeInSI, units.c_str()); + theValue.precision(1); } // if/else - return theValue; + theValue << sizeInSI << units; + return theValue.str(); } // BlocksToSI() // Converts two consecutive characters in the input string into a @@ -2,14 +2,14 @@ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ #include <stdint.h> -#include <unistd.h> +//#include <unistd.h> #include <stdlib.h> #include <string> #ifndef __GPTSUPPORT #define __GPTSUPPORT -#if defined (__FreeBSD__) || defined (__APPLE__) +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__) // Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64 // This used to use __DARWIN_UNIX03 rather than __APPLE__, but __APPLE__ // is more general. If the code fails to work on older versions of OS X/ |