summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG47
-rw-r--r--Makefile4
-rw-r--r--Makefile.mingw6
-rw-r--r--README.Windows32
-rw-r--r--attributes.cc7
-rw-r--r--attributes.h2
-rw-r--r--bsd.cc12
-rw-r--r--bsd.h5
-rw-r--r--diskio-unix.cc15
-rw-r--r--diskio-windows.cc10
-rw-r--r--diskio.cc1
-rw-r--r--diskio.h2
-rw-r--r--gdisk.810
-rw-r--r--gdisk.cc36
-rw-r--r--gpt.cc782
-rw-r--r--gpt.h40
-rw-r--r--gptpart.cc39
-rw-r--r--gptpart.h4
-rw-r--r--mbr.cc58
-rw-r--r--mbr.h3
-rw-r--r--parttypes.h2
-rw-r--r--sgdisk.876
-rw-r--r--sgdisk.cc97
-rw-r--r--support.cc12
-rw-r--r--support.h4
25 files changed, 646 insertions, 660 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 70098ce..5ad500a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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.
diff --git a/Makefile b/Makefile
index 8e14b07..6554ef3 100644
--- a/Makefile
+++ b/Makefile
@@ -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"
diff --git a/bsd.cc b/bsd.cc
index 5077555..c3ecadf 100644
--- a/bsd.cc
+++ b/bsd.cc
@@ -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
diff --git a/bsd.h b/bsd.h
index 16f3d2c..ffbe5cc 100644
--- a/bsd.h
+++ b/bsd.h
@@ -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, &sectors);
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;
diff --git a/diskio.cc b/diskio.cc
index 1bb0178..bb5bea7 100644
--- a/diskio.cc
+++ b/diskio.cc
@@ -25,7 +25,6 @@
#else
#include <sys/ioctl.h>
#endif
-#include <stdio.h>
#include <string>
#include <stdint.h>
#include <errno.h>
diff --git a/diskio.h b/diskio.h
index 630d1b4..75512c1 100644
--- a/diskio.h
+++ b/diskio.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
diff --git a/gdisk.8 b/gdisk.8
index 3a683c7..c83c0da 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -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.
diff --git a/gdisk.cc b/gdisk.cc
index b81a802..7840415 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -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";
diff --git a/gpt.cc b/gpt.cc
index a2e216f..5ce5b7f 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -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(&sector); // 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
diff --git a/gpt.h b/gpt.h
index 2478648..81fd16c 100644
--- a/gpt.h
+++ b/gpt.h
@@ -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;}
diff --git a/gptpart.cc b/gptpart.cc
index 283eb3d..d82c0f6 100644
--- a/gptpart.cc
+++ b/gptpart.cc
@@ -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()
diff --git a/gptpart.h b/gptpart.h
index e6467c1..7ae1260 100644
--- a/gptpart.h
+++ b/gptpart.h
@@ -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
diff --git a/mbr.cc b/mbr.cc
index 2417d77..3f9b522 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -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++) {
diff --git a/mbr.h b/mbr.h
index 35cd107..01cabcb 100644
--- a/mbr.h
+++ b/mbr.h
@@ -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"
diff --git a/sgdisk.8 b/sgdisk.8
index 5a554cd..28a2c98 100644
--- a/sgdisk.8
+++ b/sgdisk.8
@@ -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
diff --git a/sgdisk.cc b/sgdisk.cc
index 9b5ae92..b4da1e0 100644
--- a/sgdisk.cc
+++ b/sgdisk.cc
@@ -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()
diff --git a/support.cc b/support.cc
index f179c2f..168262e 100644
--- a/support.cc
+++ b/support.cc
@@ -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
diff --git a/support.h b/support.h
index f17b14c..de9b798 100644
--- a/support.h
+++ b/support.h
@@ -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/