Version 0.6.4

This commit is contained in:
srs5694
2010-02-19 17:19:55 -05:00
parent fad064250b
commit 08bb0da079
25 changed files with 642 additions and 656 deletions

View File

@@ -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 - 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. last free sector, not the last free sector in the largest free block.

View File

@@ -10,8 +10,8 @@ DEPEND= makedepend $(CFLAGS)
all: gdisk sgdisk all: gdisk sgdisk
gdisk: $(LIB_OBJS) gdisk.o gdisk: $(LIB_OBJS) gdisk.o gpttext.o
$(CXX) $(LIB_OBJS) gdisk.o -L/opt/local/lib -L/usr/local/lib -luuid -o gdisk $(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/opt/local/lib -L/usr/local/lib -luuid -o gdisk
sgdisk: $(LIB_OBJS) sgdisk.o sgdisk: $(LIB_OBJS) sgdisk.o
$(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -L/usr/local/lib -luuid -lpopt -o sgdisk $(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -L/usr/local/lib -luuid -lpopt -o sgdisk

View File

@@ -1,6 +1,6 @@
CC=/usr/bin/i586-mingw32msvc-gcc CC=/usr/bin/i686-pc-mingw32-gcc
CXX=/usr/bin/i586-mingw32msvc-g++ CXX=/usr/bin/i686-pc-mingw32-g++
STRIP=/usr/bin/i586-mingw32msvc-strip STRIP=/usr/bin/i686-pc-mingw32-strip
CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g 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 -g
#CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g

View File

@@ -90,16 +90,28 @@ use a version for another platform, or use a different partitioning tool
altogether. altogether.
I compiled gdisk.exe using MinGW (http://www.mingw.org), and in particular 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 its Linux-hosted cross-compiler. Under Ubuntu Linux, the Makefile.mingw
source code under more mainstream Windows compilers, or even on the file enables compilation of the software via MinGW. (Type "make -f
Windows-hosted MinGW variant. MinGW was designed for porting Unix Makefile.mingw" to compile the software.) If you try to compile using
applications to Windows, so it's entirely possible that it will work where another compiler or even using MinGW under Windows or another Linux
other compilers won't. variety, you may need to adjust the Makefile.mingw options.
Under Ubuntu Linux, the Makefile.mingw file enables compilation of the I've also attempted to compile the code with OpenWatcom 1.8 and Microsoft
software. (Type "make -f Makefile.mingw" to compile the software.) If you Visual C++ 2008 Express. My OpenWatcom attempts failed, mostly because the
try to compile using another compiler or even using MinGW under Windows or compiler can't yet handle iostream output on standard C++ strings.
another Linux variety, you may need to adjust the Makefile.mingw options. 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 If you modify GPT fdisk to get it to compile under another compiler, I
welcome submission of patches. welcome submission of patches.

View File

@@ -12,6 +12,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <iostream> #include <iostream>
#include <sstream>
#include "attributes.h" #include "attributes.h"
using namespace std; using namespace std;
@@ -20,13 +21,13 @@ using namespace std;
// data. // data.
Attributes::Attributes(void) { Attributes::Attributes(void) {
int i; int i;
char temp[ATR_NAME_SIZE]; ostringstream temp;
// Most bits are undefined, so start by giving them an // Most bits are undefined, so start by giving them an
// appropriate name // appropriate name
for (i = 1; i < NUM_ATR; i++) { for (i = 1; i < NUM_ATR; i++) {
sprintf(temp, "Undefined bit #%d", i); temp << "Undefined bit #" << i;
atNames[i] = temp; atNames[i] = temp.str();
} // for } // for
// Now reset those names that are defined.... // Now reset those names that are defined....

View File

@@ -2,7 +2,7 @@
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
#include <stdint.h> #include <stdint.h>
#include <unistd.h> //#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "support.h" #include "support.h"

10
bsd.cc
View File

@@ -10,7 +10,7 @@
#define __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS
#include <stdio.h> #include <stdio.h>
#include <unistd.h> //#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <fcntl.h> #include <fcntl.h>
@@ -37,6 +37,7 @@ BSDData::BSDData(void) {
} // default constructor } // default constructor
BSDData::~BSDData(void) { BSDData::~BSDData(void) {
if (partitions != NULL)
delete[] partitions; delete[] partitions;
} // destructor } // destructor
@@ -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 // Load the BSD disklabel data from an already-opened disk
// file, starting with the specified sector number. // file, starting with the specified sector number.
int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) { int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) {
uint8_t buffer[4096]; // I/O buffer int allOK = 1;
int i, foundSig = 0, bigEnd = 0, allOK = 1; int i, foundSig = 0, bigEnd = 0;
int relative = 0; // assume absolute partition sector numbering int relative = 0; // assume absolute partition sector numbering
uint8_t buffer[4096]; // I/O buffer
uint32_t realSig; uint32_t realSig;
uint32_t* temp32; uint32_t* temp32;
uint16_t* temp16; 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.... // detected above, apply a correction to all partition start sectors....
if (relative) { if (relative) {
for (i = 0; i < numParts; i++) { for (i = 0; i < numParts; i++) {
partitions[i].firstLBA += startSector; partitions[i].firstLBA += (uint32_t) startSector;
} // for } // for
} // if } // if
} // if signatures OK } // if signatures OK

5
bsd.h
View File

@@ -55,6 +55,9 @@ struct BSDRecord { // the partition table
}; };
// Full data in tweaked BSD format // 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 { class BSDData {
protected: protected:
// We only need a few items from the main BSD disklabel data structure.... // 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 sectorSize; // # of bytes per sector
uint32_t signature2; // the magic number (again) uint32_t signature2; // the magic number (again)
uint16_t numParts; // number of partitions in table 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.... // Above are basic BSD disklabel data; now add more stuff....
uint64_t labelFirstLBA; // first sector of BSD disklabel (partition or disk) uint64_t labelFirstLBA; // first sector of BSD disklabel (partition or disk)

View File

@@ -97,7 +97,8 @@ int DiskIO::OpenForWrite(void) {
// so the file can be re-opened without specifying the filename. // so the file can be re-opened without specifying the filename.
void DiskIO::Close(void) { void DiskIO::Close(void) {
if (isOpen) if (isOpen)
close(fd); if (close(fd) < 0)
cerr << "Warning! Problem closing file!\n";
isOpen = 0; isOpen = 0;
openForWrite = 0; openForWrite = 0;
} // DiskIO::Close() } // DiskIO::Close()
@@ -117,7 +118,7 @@ int DiskIO::GetBlockSize(void) {
#ifdef __APPLE__ #ifdef __APPLE__
err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize); err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize);
#endif #endif
#ifdef __FreeBSD__ #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
err = ioctl(fd, DIOCGSECTORSIZE, &blockSize); err = ioctl(fd, DIOCGSECTORSIZE, &blockSize);
#endif #endif
#ifdef __linux__ #ifdef __linux__
@@ -161,7 +162,7 @@ void DiskIO::DiskSync(void) {
i = ioctl(fd, DKIOCSYNCHRONIZECACHE); i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
platformFound++; platformFound++;
#endif #endif
#ifdef __FreeBSD__ #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
sleep(2); sleep(2);
i = ioctl(fd, DIOCGFLUSH); i = ioctl(fd, DIOCGFLUSH);
cout << "Warning: The kernel may continue to use old or deleted partitions.\n" 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 // greatly since then to enable FreeBSD and MacOS support, as well as to
// return correct values for disk image files. // return correct values for disk image files.
uint64_t DiskIO::DiskSize(int *err) { 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 uint64_t sectors = 0; // size in sectors
off_t bytes = 0; // size in bytes off_t bytes = 0; // size in bytes
struct stat64 st; struct stat64 st;
@@ -317,13 +316,15 @@ uint64_t DiskIO::DiskSize(int *err) {
*err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors); *err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors);
platformFound++; platformFound++;
#endif #endif
#ifdef __FreeBSD__ #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
*err = ioctl(fd, DIOCGMEDIASIZE, &bytes); *err = ioctl(fd, DIOCGMEDIASIZE, &bytes);
b = GetBlockSize(); long long b = GetBlockSize();
sectors = bytes / b; sectors = bytes / b;
platformFound++; platformFound++;
#endif #endif
#ifdef __linux__ #ifdef __linux__
long sz;
long long b;
*err = ioctl(fd, BLKGETSIZE, &sz); *err = ioctl(fd, BLKGETSIZE, &sz);
if (*err) { if (*err) {
sectors = sz = 0; sectors = sz = 0;

View File

@@ -19,7 +19,7 @@
#include <winioctl.h> #include <winioctl.h>
#define fstat64 fstat #define fstat64 fstat
#define stat64 stat #define stat64 stat
//#define S_IRGRP 0 #define S_IRGRP 0
#define S_IROTH 0 #define S_IROTH 0
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
@@ -93,6 +93,14 @@ int DiskIO::OpenForWrite(void) {
fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE, fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL); 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) { if (fd == INVALID_HANDLE_VALUE) {
CloseHandle(fd); CloseHandle(fd);
isOpen = 0; isOpen = 0;

View File

@@ -25,7 +25,6 @@
#else #else
#include <sys/ioctl.h> #include <sys/ioctl.h>
#endif #endif
#include <stdio.h>
#include <string> #include <string>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>

View File

@@ -25,7 +25,7 @@
#include <sys/ioctl.h> #include <sys/ioctl.h>
#endif #endif
#if defined (__FreeBSD__) || defined (__APPLE__) #if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)
#define fstat64 fstat #define fstat64 fstat
#define stat64 stat #define stat64 stat
#endif #endif

10
gdisk.8
View File

@@ -1,6 +1,6 @@
.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
.\" May be distributed under the GNU General Public License .\" 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" .SH "NAME"
gdisk \- Interactive GUID partition table (GPT) manipulator gdisk \- Interactive GUID partition table (GPT) manipulator
.SH "SYNOPSIS" .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 sizes also work fine. OSes may impose their own limits on the number of
partitions, though. 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 .TP
.B v .B v
Verify disk. This option is identical to the 'v' option in the main menu. Verify disk. This option is identical to the 'v' option in the main menu.

View File

@@ -9,24 +9,24 @@
//#include <iostream> //#include <iostream>
#include <stdio.h> #include <stdio.h>
#include <getopt.h> //#include <getopt.h>
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include "mbr.h" #include "mbr.h"
#include "gpt.h" #include "gpttext.h"
#include "support.h" #include "support.h"
// Function prototypes.... // Function prototypes....
void MainMenu(string filename, struct GPTData* theGPT); void MainMenu(string filename, GPTDataTextUI* theGPT);
void ShowCommands(void); void ShowCommands(void);
void ExpertsMenu(string filename, struct GPTData* theGPT); void ExpertsMenu(string filename, GPTDataTextUI* theGPT);
void ShowExpertCommands(void); void ShowExpertCommands(void);
void RecoveryMenu(string filename, struct GPTData* theGPT); void RecoveryMenu(string filename, GPTDataTextUI* theGPT);
void ShowRecoveryCommands(void); void ShowRecoveryCommands(void);
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
GPTData theGPT; GPTDataTextUI theGPT;
int doMore = 1; int doMore = 1;
char* device = NULL; char* device = NULL;
@@ -72,7 +72,7 @@ int main(int argc, char* argv[]) {
// Accept a command and execute it. Returns only when the user // Accept a command and execute it. Returns only when the user
// wants to exit (such as after a 'w' or 'q' command). // 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 command, line[255], buFile[255];
char* junk; char* junk;
int goOn = 1; int goOn = 1;
@@ -174,7 +174,7 @@ void ShowCommands(void) {
// Accept a recovery & transformation menu command. Returns only when the user // Accept a recovery & transformation menu command. Returns only when the user
// issues an exit command, such as 'w' or 'q'. // 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 command, line[255], buFile[255];
char* junk; char* junk;
uint32_t temp1; uint32_t temp1;
@@ -221,10 +221,11 @@ void RecoveryMenu(string filename, struct GPTData* theGPT) {
if (temp1 > 0) { if (temp1 > 0) {
cout << "Converted " << temp1 << " partitions. Finalize and exit? "; cout << "Converted " << temp1 << " partitions. Finalize and exit? ";
if (GetYN() == 'Y') { if (GetYN() == 'Y') {
if (theGPT->DestroyGPT(0) > 0) if (theGPT->DestroyGPT() > 0)
goOn = 0; goOn = 0;
} else { } else {
theGPT->MakeProtectiveMBR(); theGPT->MakeProtectiveMBR();
theGPT->WriteProtectiveMBR();
cout << "Note: New protective MBR created.\n"; cout << "Note: New protective MBR created.\n";
} // if/else } // if/else
} // if } // if
@@ -299,11 +300,10 @@ void ShowRecoveryCommands(void) {
// Accept an experts' menu command. Returns only after the user // Accept an experts' menu command. Returns only after the user
// selects an exit command, such as 'w' or 'q'. // 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 command, line[255];
char* junk; char* junk;
uint32_t pn; uint32_t pn, temp1, temp2;
uint32_t temp1, temp2;
int goOn = 1; int goOn = 1;
GUIDData aGUID; GUIDData aGUID;
@@ -370,6 +370,9 @@ void ExpertsMenu(string filename, struct GPTData* theGPT) {
case 's': case 'S': case 's': case 'S':
theGPT->ResizePartitionTable(); theGPT->ResizePartitionTable();
break; break;
case 't': case 'T':
theGPT->SwapPartitions();
break;
case 'v': case 'V': case 'v': case 'V':
theGPT->Verify(); theGPT->Verify();
break; break;
@@ -379,7 +382,7 @@ void ExpertsMenu(string filename, struct GPTData* theGPT) {
} // if } // if
break; break;
case 'z': case 'Z': case 'z': case 'Z':
if (theGPT->DestroyGPT() == 1) { if (theGPT->DestroyGPTwPrompt() == 1) {
goOn = 0; goOn = 0;
} }
break; break;
@@ -405,6 +408,7 @@ void ShowExpertCommands(void) {
cout << "q\tquit without saving changes\n"; cout << "q\tquit without saving changes\n";
cout << "r\trecovery and transformation options (experts only)\n"; cout << "r\trecovery and transformation options (experts only)\n";
cout << "s\tresize partition table\n"; cout << "s\tresize partition table\n";
cout << "t\ttranspose two partition table entries\n";
cout << "v\tverify disk\n"; cout << "v\tverify disk\n";
cout << "w\twrite table to disk and exit\n"; cout << "w\twrite table to disk and exit\n";
cout << "z\tzap (destroy) GPT data structures and exit\n"; cout << "z\tzap (destroy) GPT data structures and exit\n";

718
gpt.cc
View File

@@ -10,7 +10,6 @@
#define __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <fcntl.h> #include <fcntl.h>
@@ -99,7 +98,6 @@ int GPTData::Verify(void) {
int problems = 0; int problems = 0;
uint32_t i, numSegments; uint32_t i, numSegments;
uint64_t totalFree, largestSegment; uint64_t totalFree, largestSegment;
char siTotal[255], siLargest[255];
// First, check for CRC errors in the GPT data.... // First, check for CRC errors in the GPT data....
if (!mainCrcOk) { if (!mainCrcOk) {
@@ -231,8 +229,6 @@ int GPTData::Verify(void) {
// problems could affect the results // problems could affect the results
if (problems == 0) { if (problems == 0) {
totalFree = FindFreeBlocks(&numSegments, &largestSegment); 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 (" cout << "No problems found. " << totalFree << " free sectors ("
<< BytesToSI(totalFree * (uint64_t) blockSize) << ") available in " << BytesToSI(totalFree * (uint64_t) blockSize) << ") available in "
<< numSegments << "\nsegments, the largest of which is " << numSegments << "\nsegments, the largest of which is "
@@ -386,10 +382,6 @@ void GPTData::RecomputeCRCs(void) {
ReverseHeaderBytes(&mainHeader); ReverseHeaderBytes(&mainHeader);
ReverseHeaderBytes(&secondHeader); ReverseHeaderBytes(&secondHeader);
} // if } // if
/* if ((littleEndian = IsLittleEndian()) == 0) {
ReverseBytes(&trueNumParts, 4);
ReverseBytes(&hSize, 4);
} // if */
// Compute CRC of partition tables & store in main and secondary headers // Compute CRC of partition tables & store in main and secondary headers
crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE); crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE);
@@ -566,10 +558,10 @@ void GPTData::PartitionScan(void) {
// Read GPT data from a disk. // Read GPT data from a disk.
int GPTData::LoadPartitions(const string & deviceFilename) { int GPTData::LoadPartitions(const string & deviceFilename) {
BSDData bsdDisklabel;
int err, allOK = 1; int err, allOK = 1;
uint32_t i; uint32_t i;
uint64_t firstBlock, lastBlock; uint64_t firstBlock, lastBlock;
BSDData bsdDisklabel;
MBRValidity mbrState; MBRValidity mbrState;
// First, do a test to see if writing will be possible later.... // 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)) { if ((err == 0) && (!justLooking)) {
cout << "\aNOTE: Write test failed with error number " << errno cout << "\aNOTE: Write test failed with error number " << errno
<< ". It will be impossible to save\nchanges to this disk's partition table!\n"; << ". 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" 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" << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
<< "program.\n"; << "program.\n";
@@ -604,7 +596,7 @@ int GPTData::LoadPartitions(const string & deviceFilename) {
// bsdDisklabel.DisplayBSDData(); // bsdDisklabel.DisplayBSDData();
ClearGPTData(); ClearGPTData();
protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1) protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
XFormDisklabel(&bsdDisklabel, 0); XFormDisklabel(&bsdDisklabel);
break; break;
case use_gpt: case use_gpt:
mbrState = protectiveMBR.GetValidity(); mbrState = protectiveMBR.GetValidity();
@@ -655,12 +647,12 @@ int GPTData::ForceLoadGPTData(void) {
if (mainCrcOk && (mainHeader.backupLBA < diskSize)) { if (mainCrcOk && (mainHeader.backupLBA < diskSize)) {
allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK; allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK;
} else { } 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" 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" << "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" << "verify disk integrity, and perhaps options on the experts' menu to repair\n"
<< "the disk.\n"; << "the disk.\n";
allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
} // if/else } // if/else
if (!allOK) if (!allOK)
state = gpt_invalid; state = gpt_invalid;
@@ -850,7 +842,6 @@ int GPTData::CheckTable(struct GPTHeader *header) {
int GPTData::SaveGPTData(int quiet) { int GPTData::SaveGPTData(int quiet) {
int allOK = 1, littleEndian; int allOK = 1, littleEndian;
char answer; char answer;
// uint64_t secondTable;
uint32_t numParts; uint32_t numParts;
littleEndian = IsLittleEndian(); littleEndian = IsLittleEndian();
@@ -1129,6 +1120,91 @@ int GPTData::LoadGPTBackup(const string & filename) {
return allOK; return allOK;
} // GPTData::LoadGPTBackup() } // 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.... // Tell user whether Apple Partition Map (APM) was discovered....
void GPTData::ShowAPMState(void) { void GPTData::ShowAPMState(void) {
if (apmFound) if (apmFound)
@@ -1176,20 +1252,6 @@ void GPTData::DisplayGPTData(void) {
} // for } // for
} // GPTData::DisplayGPTData() } // 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 // Show detailed information on the specified partition
void GPTData::ShowPartDetails(uint32_t partNum) { void GPTData::ShowPartDetails(uint32_t partNum) {
if (partitions[partNum].GetFirstLBA() != 0) { if (partitions[partNum].GetFirstLBA() != 0) {
@@ -1199,220 +1261,6 @@ void GPTData::ShowPartDetails(uint32_t partNum) {
} // if } // if
} // GPTData::ShowPartDetails() } // 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) * * 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 // Examines the MBR & GPT data to determine which set of data to use: the
// determine which set of data to use: the MBR (use_mbr), the GPT (use_gpt), // MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create
// or create a new set of partitions (use_new) // 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 GPTData::UseWhichPartitions(void) {
WhichToUse which = use_new; WhichToUse which = use_new;
MBRValidity mbrState; MBRValidity mbrState;
int answer;
mbrState = protectiveMBR.GetValidity(); mbrState = protectiveMBR.GetValidity();
@@ -1470,49 +1319,20 @@ WhichToUse GPTData::UseWhichPartitions(void) {
which = use_gpt; which = use_gpt;
} // if } // if
if ((state == gpt_valid) && (mbrState == mbr)) { if ((state == gpt_valid) && (mbrState == mbr)) {
if (!beQuiet) { which = use_abort;
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 } // if
// Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
// problems)
if (state == gpt_corrupt) { if (state == gpt_corrupt) {
if (beQuiet) { if (mbrState == gpt) {
which = use_abort;
} 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" cout << "\a\a****************************************************************************\n"
<< "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n" << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
<< "verification and recovery are STRONGLY recommended.\n" << "verification and recovery are STRONGLY recommended.\n"
<< "****************************************************************************\n"; << "****************************************************************************\n";
which = use_gpt; which = use_gpt;
} // if/else/else } else {
} // else (beQuiet) which = use_abort;
} // if (corrupt GPT) } // if/else MBR says disk is GPT
} // if GPT corrupt
if (which == use_new) if (which == use_new)
cout << "Creating new GPT entries.\n"; cout << "Creating new GPT entries.\n";
@@ -1520,13 +1340,14 @@ WhichToUse GPTData::UseWhichPartitions(void) {
return which; return which;
} // UseWhichPartitions() } // UseWhichPartitions()
// Convert MBR partition table into GPT form // Convert MBR partition table into GPT form.
int GPTData::XFormPartitions(void) { void GPTData::XFormPartitions(void) {
int i, numToConvert; int i, numToConvert;
uint8_t origType; uint8_t origType;
// Clear out old data & prepare basics.... // Clear out old data & prepare basics....
ClearGPTData(); ClearGPTData();
protectiveMBR.EmptyBootloader();
// Convert the smaller of the # of GPT or MBR partitions // Convert the smaller of the # of GPT or MBR partitions
if (mainHeader.numParts > (MAX_MBR_PARTS)) if (mainHeader.numParts > (MAX_MBR_PARTS))
@@ -1549,35 +1370,23 @@ int GPTData::XFormPartitions(void) {
// Record that all original CRCs were OK so as not to raise flags // Record that all original CRCs were OK so as not to raise flags
// when doing a disk verification // when doing a disk verification
mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
return (1);
} // GPTData::XFormPartitions() } // GPTData::XFormPartitions()
// Transforms BSD disklabel on the specified partition (numbered from 0). // Transforms BSD disklabel on the specified partition (numbered from 0).
// If an invalid partition number is given, the program prompts for one. // If an invalid partition number is given, the program does nothing.
// (Default for i is -1; called without an option, it therefore prompts.)
// Returns the number of new partitions created. // Returns the number of new partitions created.
int GPTData::XFormDisklabel(int i) { int GPTData::XFormDisklabel(uint32_t partNum) {
uint32_t low, high, partNum, startPart; uint32_t low, high;
uint16_t hexCode;
int goOn = 1, numDone = 0; int goOn = 1, numDone = 0;
BSDData disklabel; BSDData disklabel;
if (GetPartRange(&low, &high) != 0) { if (GetPartRange(&low, &high) == 0) {
if ((i < (int) low) || (i > (int) high)) goOn = 0;
partNum = GetPartNum(); cout << "No partitions!\n";
else } // if
partNum = (uint32_t) i; if (partNum > high) {
goOn = 0;
// Find the partition after the last used one cout << "Specified partition is invalid!\n";
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
// If all is OK, read the disklabel and convert it. // If all is OK, read the disklabel and convert it.
@@ -1585,9 +1394,9 @@ int GPTData::XFormDisklabel(int i) {
goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(), goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
partitions[partNum].GetLastLBA()); partitions[partNum].GetLastLBA());
if ((goOn) && (disklabel.IsDisklabel())) { if ((goOn) && (disklabel.IsDisklabel())) {
numDone = XFormDisklabel(&disklabel, startPart); numDone = XFormDisklabel(&disklabel);
if (numDone == 1) if (numDone == 1)
cout << "Converted " << numDone << " BSD partition.\n"; cout << "Converted 1 BSD partition.\n";
else else
cout << "Converted " << numDone << " BSD partitions.\n"; cout << "Converted " << numDone << " BSD partitions.\n";
} else { } else {
@@ -1597,23 +1406,24 @@ int GPTData::XFormDisklabel(int i) {
if (numDone > 0) { // converted partitions; delete carrier if (numDone > 0) { // converted partitions; delete carrier
partitions[partNum].BlankPartition(); partitions[partNum].BlankPartition();
} // if } // if
} else {
cout << "No partitions\n";
} // if/else
return numDone; return numDone;
} // GPTData::XFormDisklable(int i) } // GPTData::XFormDisklable(int i)
// Transform the partitions on an already-loaded BSD disklabel... // Transform the partitions on an already-loaded BSD disklabel...
int GPTData::XFormDisklabel(BSDData* disklabel, uint32_t startPart) { int GPTData::XFormDisklabel(BSDData* disklabel) {
int i, numDone = 0; int i, partNum = 0, numDone = 0;
if ((disklabel->IsDisklabel()) && (startPart >= 0) && if (disklabel->IsDisklabel()) {
(startPart < mainHeader.numParts)) {
for (i = 0; i < disklabel->GetNumParts(); i++) { for (i = 0; i < disklabel->GetNumParts(); i++) {
partitions[i + startPart] = disklabel->AsGPT(i); partNum = FindFirstFreePart();
if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0)) if (partNum >= 0) {
partitions[partNum] = disklabel->AsGPT(i);
if (partitions[partNum].IsUsed())
numDone++; numDone++;
} // if
} // for } // for
if (partNum == -1)
cerr << "Warning! Too many partitions to convert!\n";
} // if } // if
// Record that all original CRCs were OK so as not to raise flags // 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; return numDone;
} // GPTData::XFormDisklabel(BSDData* disklabel) } // GPTData::XFormDisklabel(BSDData* disklabel)
// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid() // Add one GPT partition to MBR. Used by PartsToMBR() functions. Created
// functions. Returns 1 if operation was successful. // 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 GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
int allOK = 1, typeCode, bootable; int allOK = 1;
uint64_t length;
char line[255];
char* junk;
cout.setf(ios::uppercase);
cout.fill('0');
if ((mbrPart < 0) || (mbrPart > 3)) { if ((mbrPart < 0) || (mbrPart > 3)) {
cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n"; 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) { if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
cout << "Caution: Partition end point past 32-bit pointer boundary;" cout << "Caution: Partition end point past 32-bit pointer boundary;"
<< " some OSes may\nreact strangely.\n"; << " some OSes may\nreact strangely.\n";
} // if partition ends past 32-bit (usually 2TiB) boundary } // if
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();
protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(), 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 } else { // partition out of range
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 " cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
<< "partitions, or is\n too big; omitting it.\n"; << "partitions, or is\n too big; omitting it.\n";
allOK = 0; allOK = 0;
} // if/else } // if/else
cout.fill(' ');
return allOK; return allOK;
} // GPTData::OnePartToMBR() } // GPTData::OnePartToMBR()
// Convert the GPT to MBR form. This function is necessarily limited; it // Convert up to four partitions to MBR form and return the number done.
// handles at most four partitions and creates layouts that ignore CHS // Partitions are specified in an array of GPT partition numbers,
// geometries. Returns the number of converted partitions; if this value // with an associated array of partition type codes. Both must be
// is over 0, the calling function should call DestroyGPT() to destroy // at least four elements in size (longer is OK, but will be ignored).
// the GPT data, and then exit. // A partition number of MBR_EFI_GPT means to place an EFI GPT
int GPTData::XFormToMBR(void) { // protective partition in that location in the table (the associated
char line[255]; // mbrType[] should be 0xEE), and MBR_EMPTY means not to create a
char* junk; // partition in that table position. If the mbrType[] entry for a
int j, numParts, numConverted = 0; // partition is 0, a default entry is used, based on the GPT
uint32_t i, partNums[4]; // 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;
// Get the numbers of up to four partitions to add to the if ((gptParts != NULL) && (mbrTypes != NULL)) {
// hybrid MBR.... protectiveMBR.EmptyMBR();
numParts = CountParts();
cout << "Counted " << numParts << " partitions.\n";
// Prepare the MBR for conversion (empty it of existing partitions).
protectiveMBR.EmptyMBR(0);
protectiveMBR.SetDiskSize(diskSize); protectiveMBR.SetDiskSize(diskSize);
// Do two passes, one to get "real" partitions and
if (numParts > 4) { // Over four partitions; engage in triage // the next to create EFI GPT protective partition(s)
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);
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....
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
if (protectiveMBR.GetType(i) == 0x00) { // unused entry.... if (gptParts[i] >= 0) {
if (fillItUp == 'M') { numConverted += OnePartToMBR((uint32_t) gptParts[i], i);
cout << "\nUnused partition space(s) found. Use one to protect more partitions? "; if (mbrTypes[i] != 0)
fillItUp = GetYN(); protectiveMBR.SetPartType(i, mbrTypes[i]);
typeCode = 0x00; // use this to flag a need to get type code
} // if } // if
if (fillItUp == 'Y') { } // for (regular partition pass)
while ((typeCode <= 0) || (typeCode > 255)) { for (i = 0; i < 4; i++) {
cout << "Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): "; if (gptParts[i] == MBR_EFI_GPT) {
// Comment on above: Mac OS treats disks with more than one if (protectiveMBR.FindFirstAvailable() == UINT32_C(1)) {
// 0xEE MBR partition as MBR disks, not as GPT disks. protectiveMBR.MakePart(i, 1, protectiveMBR.FindLastInFree(1), mbrTypes[i]);
junk = fgets(line, 255, stdin); protectiveMBR.SetHybrid();
sscanf(line, "%x", &typeCode); } else {
if (line[0] == '\n') protectiveMBR.MakeBiggestPart(i, mbrTypes[i]);
typeCode = 0; } // if/else
} // while } // if EFI GPT partition specified
protectiveMBR.MakeBiggestPart(i, typeCode); // make a partition } // for (0xEE pass)
} // if (fillItUp == 'Y') } // if arrays were passed
} // if unused entry return numConverted;
} // for (i = 0; i < 4; i++) } // GPTData::PartsToMBR()
} // if (numParts > 0)
} // GPTData::MakeHybrid()
/********************************************************************** /**********************************************************************
* * * *
@@ -1813,8 +1524,8 @@ void GPTData::MakeHybrid(void) {
// necessary, copies data if it already exists. Returns 1 if all goes // necessary, copies data if it already exists. Returns 1 if all goes
// well, 0 if an error is encountered. // well, 0 if an error is encountered.
int GPTData::SetGPTSize(uint32_t numEntries) { int GPTData::SetGPTSize(uint32_t numEntries) {
struct GPTPart* newParts; GPTPart* newParts;
struct GPTPart* trash; GPTPart* trash;
uint32_t i, high, copyNum; uint32_t i, high, copyNum;
int allOK = 1; int allOK = 1;
@@ -1907,10 +1618,8 @@ int GPTData::DeletePartition(uint32_t partNum) {
return retval; return retval;
} // GPTData::DeletePartition(uint32_t partNum) } // GPTData::DeletePartition(uint32_t partNum)
// Non-interactively create a partition. Note that this function is overloaded // Non-interactively create a partition.
// with another of the same name but different parameters; that one prompts // Returns 1 if the operation was successful, 0 if a problem was discovered.
// the user for data. This one 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) { uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
int retval = 1; // assume there'll be no problems 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 // Sort the GPT entries, eliminating gaps and making for a logical
// ordering. Relies on QuickSortGPT() for the bulk of the work // ordering. Relies on QuickSortGPT() for the bulk of the work
void GPTData::SortGPT(void) { void GPTData::SortGPT(void) {
GPTPart temp;
uint32_t i, numFound, firstPart, lastPart; uint32_t i, numFound, firstPart, lastPart;
// First, find the last partition with data, so as not to // First, find the last partition with data, so as not to
@@ -1943,9 +1651,7 @@ void GPTData::SortGPT(void) {
i = 0; i = 0;
while (i < lastPart) { while (i < lastPart) {
if (partitions[i].GetFirstLBA() == 0) { if (partitions[i].GetFirstLBA() == 0) {
temp = partitions[i]; SwapPartitions(i, lastPart);
partitions[i] = partitions[lastPart];
partitions[lastPart] = temp;
do { do {
lastPart--; lastPart--;
} while ((lastPart > 0) && (partitions[lastPart].GetFirstLBA() == 0)); } while ((lastPart > 0) && (partitions[lastPart].GetFirstLBA() == 0));
@@ -1959,9 +1665,51 @@ void GPTData::SortGPT(void) {
GetPartRange(&firstPart, &lastPart); GetPartRange(&firstPart, &lastPart);
// Now call the recursive quick sort routine to do the real work.... // Now call the recursive quick sort routine to do the real work....
QuickSortGPT(partitions, 0, lastPart); QuickSortGPT(0, lastPart);
} // GPTData::SortGPT() } // 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 // Set up data structures for entirely new set of partitions on the
// specified device. Returns 1 if OK, 0 if there were problems. // specified device. Returns 1 if OK, 0 if there were problems.
// Note that this function does NOT clear the protectiveMBR data // Note that this function does NOT clear the protectiveMBR data
@@ -2165,12 +1913,26 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
return numFound; return numFound;
} // GPTData::GetPartRange() } // 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. // Returns the number of defined partitions.
uint32_t GPTData::CountParts(void) { uint32_t GPTData::CountParts(void) {
uint32_t i, counted = 0; uint32_t i, counted = 0;
for (i = 0; i < mainHeader.numParts; i++) { for (i = 0; i < mainHeader.numParts; i++) {
if (partitions[i].GetFirstLBA() > 0) if (partitions[i].IsUsed())
counted++; counted++;
} // for } // for
return counted; return counted;
@@ -2331,9 +2093,8 @@ int GPTData::IsFree(uint64_t sector) {
int GPTData::IsFreePartNum(uint32_t partNum) { int GPTData::IsFreePartNum(uint32_t partNum) {
int retval = 1; int retval = 1;
if ((partNum >= 0) && (partNum < mainHeader.numParts)) { if ((partNum < mainHeader.numParts) && (partitions != NULL)) {
if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) || if (partitions[partNum].IsUsed()) {
(partitions[partNum].GetLastLBA() != UINT64_C(0))) {
retval = 0; retval = 0;
} // if partition is in use } // if partition is in use
} else retval = 0; } else retval = 0;
@@ -2361,8 +2122,7 @@ void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
ReverseBytes(&header->numParts, 4); ReverseBytes(&header->numParts, 4);
ReverseBytes(&header->sizeOfPartitionEntries, 4); ReverseBytes(&header->sizeOfPartitionEntries, 4);
ReverseBytes(&header->partitionEntriesCRC, 4); ReverseBytes(&header->partitionEntriesCRC, 4);
ReverseBytes(&header->reserved2, GPT_RESERVED); ReverseBytes(header->reserved2, GPT_RESERVED);
// header->diskGUID.ReverseGUIDBytes();
} // GPTData::ReverseHeaderBytes() } // GPTData::ReverseHeaderBytes()
// IMPORTANT NOTE: This function requires non-reversed mainHeader // IMPORTANT NOTE: This function requires non-reversed mainHeader

40
gpt.h
View File

@@ -15,7 +15,13 @@
#ifndef __GPTSTRUCTS #ifndef __GPTSTRUCTS
#define __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; using namespace std;
@@ -83,7 +89,7 @@ public:
// Basic necessary functions.... // Basic necessary functions....
GPTData(void); GPTData(void);
GPTData(string deviceFilename); GPTData(string deviceFilename);
~GPTData(void); virtual ~GPTData(void);
// Verify (or update) data integrity // Verify (or update) data integrity
int Verify(void); int Verify(void);
@@ -98,6 +104,7 @@ public:
// Load or save data from/to disk // Load or save data from/to disk
int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);} int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);}
int WriteProtectiveMBR(void) {return protectiveMBR.WriteMBRData(&myDisk);}
void PartitionScan(void); void PartitionScan(void);
int LoadPartitions(const string & deviceFilename); int LoadPartitions(const string & deviceFilename);
int ForceLoadGPTData(void); int ForceLoadGPTData(void);
@@ -106,32 +113,24 @@ public:
int SaveGPTData(int quiet = 0); int SaveGPTData(int quiet = 0);
int SaveGPTBackup(const string & filename); int SaveGPTBackup(const string & filename);
int LoadGPTBackup(const string & filename); int LoadGPTBackup(const string & filename);
int SaveMBR(void);
int DestroyGPT(void);
int DestroyMBR(void);
// Display data.... // Display data....
void ShowAPMState(void); void ShowAPMState(void);
void ShowGPTState(void); void ShowGPTState(void);
void DisplayGPTData(void); void DisplayGPTData(void);
void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();} void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();}
void ShowDetails(void);
void ShowPartDetails(uint32_t partNum); void ShowPartDetails(uint32_t partNum);
// Request information from the user (& possibly do something with it) // Convert between GPT and other formats
uint32_t GetPartNum(void); virtual WhichToUse UseWhichPartitions(void);
void ResizePartitionTable(void); void XFormPartitions(void);
void CreatePartition(void); virtual int XFormDisklabel(uint32_t partNum);
void DeletePartition(void); int XFormDisklabel(BSDData* disklabel);
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);
int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful 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 int PartsToMBR(const int *gptParts, const int *mbrTypes);
void MakeHybrid(void);
// Adjust GPT structures WITHOUT user interaction... // Adjust GPT structures WITHOUT user interaction...
int SetGPTSize(uint32_t numEntries); int SetGPTSize(uint32_t numEntries);
@@ -139,6 +138,8 @@ public:
int DeletePartition(uint32_t partNum); int DeletePartition(uint32_t partNum);
uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector); uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
void SortGPT(void); void SortGPT(void);
void QuickSortGPT(int start, int finish);
int SwapPartitions(uint32_t partNum1, uint32_t partNum2);
int ClearGPTData(void); int ClearGPTData(void);
void MoveSecondHeaderToEnd(); void MoveSecondHeaderToEnd();
int SetName(uint32_t partNum, const string & theName = ""); int SetName(uint32_t partNum, const string & theName = "");
@@ -150,6 +151,7 @@ public:
// Return data about the GPT structures.... // Return data about the GPT structures....
int GetPartRange(uint32_t* low, uint32_t* high); int GetPartRange(uint32_t* low, uint32_t* high);
int FindFirstFreePart(void);
uint32_t GetNumParts(void) {return mainHeader.numParts;} uint32_t GetNumParts(void) {return mainHeader.numParts;}
uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;} uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;}
uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;} uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;}

View File

@@ -66,6 +66,11 @@ string GPTPart::GetDescription(void) {
return theName; return theName;
} // GPTPart::GetDescription() } // 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 // 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 // name *IF* the current name is the generic one for the current partition
// type. // type.
@@ -139,6 +144,7 @@ void GPTPart::ShowSummary(int partNum, uint32_t blockSize) {
if (firstLBA != 0) { if (firstLBA != 0) {
sizeInSI = BytesToSI(blockSize * (lastLBA - firstLBA + 1)); sizeInSI = BytesToSI(blockSize * (lastLBA - firstLBA + 1));
cout.fill(' ');
cout.width(4); cout.width(4);
cout << partNum + 1 << " "; cout << partNum + 1 << " ";
cout.width(14); cout.width(14);
@@ -256,36 +262,3 @@ void GPTPart::ChangeType(void) {
SetDefaultDescription(); SetDefaultDescription();
} // if } // if
} // GPTPart::ChangeType() } // 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()

View File

@@ -59,6 +59,7 @@ class GPTPart {
uint64_t GetLengthLBA(void); uint64_t GetLengthLBA(void);
uint64_t GetAttributes(void) {return attributes;} uint64_t GetAttributes(void) {return attributes;}
string GetDescription(void); string GetDescription(void);
int IsUsed(void);
// Simple data assignment: // Simple data assignment:
void SetType(PartType t); void SetType(PartType t);
@@ -83,7 +84,4 @@ class GPTPart {
void ChangeType(void); // Change the type code void ChangeType(void); // Change the type code
}; // struct GPTPart }; // struct GPTPart
// A support function that doesn't quite belong in the class....
void QuickSortGPT(GPTPart* partitions, int start, int finish);
#endif #endif

58
mbr.cc
View File

@@ -10,7 +10,7 @@
#define __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS
#include <stdio.h> #include <stdio.h>
#include <unistd.h> //#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <fcntl.h> #include <fcntl.h>
@@ -273,7 +273,7 @@ int MBRData::WriteMBRData(void) {
int allOK = 1; int allOK = 1;
if (myDisk != NULL) { if (myDisk != NULL) {
if (myDisk->OpenForWrite(device) != 0) { if (myDisk->OpenForWrite() != 0) {
allOK = WriteMBRData(myDisk); allOK = WriteMBRData(myDisk);
} else { } else {
allOK = 0; allOK = 0;
@@ -493,10 +493,7 @@ void MBRData::EmptyMBR(int clearBootloader) {
// 2-byte nulls area only if requested to do so. (This is the // 2-byte nulls area only if requested to do so. (This is the
// default.) // default.)
if (clearBootloader == 1) { if (clearBootloader == 1) {
for (i = 0; i < 440; i++) EmptyBootloader();
code[i] = 0;
diskSignature = (uint32_t) rand();
nulls = 0;
} // if } // if
// Blank out the partitions // Blank out the partitions
@@ -515,6 +512,17 @@ void MBRData::EmptyMBR(int clearBootloader) {
MBRSignature = MBR_SIGNATURE; MBRSignature = MBR_SIGNATURE;
} // MBRData::EmptyMBR() } // 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. // Create a protective MBR. Clears the boot loader area if clearBoot > 0.
void MBRData::MakeProtectiveMBR(int clearBoot) { void MBRData::MakeProtectiveMBR(int clearBoot) {
@@ -523,6 +531,7 @@ void MBRData::MakeProtectiveMBR(int clearBoot) {
// Initialize variables // Initialize variables
nulls = 0; nulls = 0;
MBRSignature = MBR_SIGNATURE; MBRSignature = MBR_SIGNATURE;
diskSignature = (uint32_t) rand();
partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable 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, void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
int bootable) { int bootable) {
if ((num >= 0) && (num < MAX_MBR_PARTS)) { 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[0] = UINT8_C(0);
partitions[num].firstSector[1] = UINT8_C(0); partitions[num].firstSector[1] = UINT8_C(0);
partitions[num].firstSector[2] = 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, partitions[num].firstSector);
LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector); LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
} // if (length > 0) } // if (length > 0)
SetPartBootable(num, bootable);
} // if valid partition number } // if valid partition number
} // MBRData::MakePart() } // 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 // Create a partition that fills the most available space. Returns
// 1 if partition was created, 0 otherwise. Intended for use in // 1 if partition was created, 0 otherwise. Intended for use in
// creating hybrid MBRs. // creating hybrid MBRs.
@@ -637,7 +677,7 @@ int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
start32 = (uint32_t) start64; start32 = (uint32_t) start64;
length32 = (uint32_t) length64; length32 = (uint32_t) length64;
for (i = 0; i < MAX_MBR_PARTS; i++) { for (i = 0; i < MAX_MBR_PARTS; i++) {
if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) && if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA == length32) &&
(partitions[i].partitionType != 0xEE)) { (partitions[i].partitionType != 0xEE)) {
DeletePartition(i); DeletePartition(i);
if (state == hybrid) if (state == hybrid)
@@ -717,7 +757,7 @@ uint32_t MBRData::FindLastInFree(uint32_t start) {
uint32_t i; uint32_t i;
if ((diskSize <= UINT32_MAX) && (diskSize > 0)) if ((diskSize <= UINT32_MAX) && (diskSize > 0))
nearestStart = diskSize - 1; nearestStart = (uint32_t) diskSize - 1;
else else
nearestStart = UINT32_MAX - 1; nearestStart = UINT32_MAX - 1;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {

3
mbr.h
View File

@@ -105,9 +105,12 @@ public:
// Functions to create, delete, or change partitions // Functions to create, delete, or change partitions
// Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
void EmptyMBR(int clearBootloader = 1); void EmptyMBR(int clearBootloader = 1);
void EmptyBootloader(void);
void MakeProtectiveMBR(int clearBoot = 0); void MakeProtectiveMBR(int clearBoot = 0);
void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
int bootable = 0); int bootable = 0);
int SetPartType(int num, int type);
int SetPartBootable(int num, int bootable = 1);
int MakeBiggestPart(int i, int type); // Make partition filling most space int MakeBiggestPart(int i, int type); // Make partition filling most space
void DeletePartition(int i); void DeletePartition(int i);
int DeleteByLocation(uint64_t start64, uint64_t length64); int DeleteByLocation(uint64_t start64, uint64_t length64);

View File

@@ -2,7 +2,7 @@
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
#include <stdint.h> #include <stdint.h>
#include <unistd.h> //#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
#include "support.h" #include "support.h"

View File

@@ -1,6 +1,6 @@
.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
.\" May be distributed under the GNU General Public License .\" 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" .SH "NAME"
sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix
.SH "SYNOPSIS" .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 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 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 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 PowerPC\-based Macintoshes. If you specify any option that results in
to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes unless the changes to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes
\fI\-g\fR (\fI\-\-mbrtogpt\fR) or \fI\-z\fR (\fI\-\-zap\fR) option is used. unless the \fI\-g\fR (\fI\-\-mbrtogpt\fR), \fI\-z\fR (\fI\-\-zap\fR), or
If you use the \fI\-g\fR option, \fBsgdisk\fR replaces the MBR or disklabel \fI\-Z\fR (\fI\-\-zap\-all\fR) option is used. If you use the \fI\-g\fR
with a GPT. \fIThis action is potentially dangerous!\fR Your system may become option, \fBsgdisk\fR replaces the MBR or disklabel with a GPT. \fIThis
unbootable, and partition type codes may become corrupted if the disk uses action is potentially dangerous!\fR Your system may become unbootable, and
unrecognized type codes. Boot problems are particularly likely if you're partition type codes may become corrupted if the disk uses unrecognized
multi\-booting with any GPT\-unaware OS. 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 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 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 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. 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 .TP
.B \-i, \-\-info=partnum .B \-i, \-\-info=partnum
Show detailed partition information. The summary information produced by 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 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. 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 .TP
.B \-n, \-\-new=partnum:start:end .B \-n, \-\-new=partnum:start:end
Create a new partition. You enter a partition 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 altered according to other parameters, but changes are not written
to disk. 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 .TP
.B \-s, \-\-sort .B \-s, \-\-sort
Sort partition entries. GPT partition numbers need not match the order of Sort partition entries. GPT partition numbers need not match the order of
@@ -317,14 +349,22 @@ specifying a device filename.
.TP .TP
.B \-z, \-\-zap .B \-z, \-\-zap
Zap (destroy) the GPT data structures and exit. Use this option if you want to Zap (destroy) the GPT data structures and then exit. Use this option if you
repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program. want to repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware
You'll be given the choice of preserving the existing MBR, in case it's a program. This option destroys only the GPT data structures; it leaves the
hybrid MBR with salvageable partitions or if you've already created new MBR MBR intact. This makes it useful for wiping out GPT data structures after a
partitions and want to erase the remnants of your GPT partitions. \fIIf you've disk has been repartitioned for MBR using a GPT-unaware utility; however,
already created new MBR partitions, it's conceivable that this option will there's a risk that it will damage boot loaders or even the start of the
damage the first and/or last MBR partitions!\fR Such an event is unlikely, but first or end of the last MBR partition. If you use it on a valid GPT disk,
could occur if your new MBR partitions overlap the old GPT data structures. 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 .TP
.B \-?, \-\-help .B \-?, \-\-help
@@ -353,7 +393,7 @@ Non\-GPT disk detected and no \fI\-g\fR option
.B 4 .B 4
An error prevented saving changes An error prevented saving changes
.SH "BUGS" .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: should be considered beta software. Known bugs and limitations include:
.TP .TP

View File

@@ -25,6 +25,8 @@ using namespace std;
uint64_t GetInt(char* Info, int itemNum); uint64_t GetInt(char* Info, int itemNum);
string GetString(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[]) { int main(int argc, char *argv[]) {
GPTData theGPT; GPTData theGPT;
@@ -35,8 +37,8 @@ int main(int argc, char *argv[]) {
uint32_t tableSize = 128; uint32_t tableSize = 128;
uint64_t startSector, endSector; uint64_t startSector, endSector;
char *device = NULL; char *device = NULL;
char *newPartInfo = NULL, *typeCode = NULL, *partName; char *newPartInfo = NULL, *typeCode = NULL, *partName = NULL;
char *backupFile = NULL; char *backupFile = NULL, *twoParts = NULL, *hybrids = NULL, *mbrParts;
PartType typeHelper; PartType typeHelper;
poptContext poptCon; 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", ""}, {"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", ""}, {"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", ""}, {"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"}, {"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"}, {"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", ""}, {"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"}, {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"},
{"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""}, {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""},
{"print", 'p', POPT_ARG_NONE, NULL, 'p', "print 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", ""}, {"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", ""}, {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""},
{"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"}, {"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"}, {"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"}, {"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", ""}, {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""},
{"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, {"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 } POPT_AUTOHELP { NULL, 0, 0, NULL, 0 }
}; };
@@ -150,6 +156,11 @@ int main(int argc, char *argv[]) {
saveData = 1; saveData = 1;
saveNonGPT = 1; saveNonGPT = 1;
break; break;
case 'h':
theGPT.JustLooking(0);
if (BuildMBR(&theGPT, hybrids, 1) == 1)
saveData = 1;
break;
case 'i': case 'i':
theGPT.ShowPartDetails(infoPartNum - 1); theGPT.ShowPartDetails(infoPartNum - 1);
break; break;
@@ -165,6 +176,19 @@ int main(int argc, char *argv[]) {
break; break;
case 'L': case 'L':
break; 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': case 'n':
theGPT.JustLooking(0); theGPT.JustLooking(0);
partNum = (int) GetInt(newPartInfo, 1) - 1; partNum = (int) GetInt(newPartInfo, 1) - 1;
@@ -190,6 +214,16 @@ int main(int argc, char *argv[]) {
case 'P': case 'P':
pretend = 1; pretend = 1;
break; 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': case 's':
theGPT.JustLooking(0); theGPT.JustLooking(0);
theGPT.SortGPT(); theGPT.SortGPT();
@@ -217,16 +251,26 @@ int main(int argc, char *argv[]) {
break; break;
case 'T': case 'T':
theGPT.JustLooking(0); theGPT.JustLooking(0);
theGPT.XFormDisklabel(bsdPartNum); theGPT.XFormDisklabel(bsdPartNum - 1);
saveData = 1; saveData = 1;
break; break;
case 'v': case 'v':
theGPT.Verify(); theGPT.Verify();
break; break;
case 'z': case 'z':
if (!pretend) if (!pretend) {
theGPT.DestroyGPT(-1); theGPT.DestroyGPT();
} // if
saveNonGPT = 0; saveNonGPT = 0;
saveData = 0;
break;
case 'Z':
if (!pretend) {
theGPT.DestroyGPT();
theGPT.DestroyMBR();
} // if
saveNonGPT = 0;
saveData = 0;
break; break;
default: default:
cerr << "Unknown option (-" << opt << ")!\n"; cerr << "Unknown option (-" << opt << ")!\n";
@@ -287,3 +331,44 @@ string GetString(char* argument, int itemNum) {
return Info.substr(startPos, endPos - startPos + 1); return Info.substr(startPos, endPos - startPos + 1);
} // GetString() } // 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()

View File

@@ -17,6 +17,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <sstream>
#include "support.h" #include "support.h"
#include <sys/types.h> #include <sys/types.h>
@@ -166,10 +167,9 @@ uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, const string &
// form // form
string BytesToSI(uint64_t size) { string BytesToSI(uint64_t size) {
string units; string units;
char theValue[99]; ostringstream theValue;
float sizeInSI; float sizeInSI;
theValue[0] = '\0';
sizeInSI = (float) size; sizeInSI = (float) size;
units = " bytes"; units = " bytes";
if (sizeInSI > 1024.0) { if (sizeInSI > 1024.0) {
@@ -192,12 +192,14 @@ string BytesToSI(uint64_t size) {
sizeInSI /= 1024.0; sizeInSI /= 1024.0;
units = " PiB"; units = " PiB";
} // if } // if
theValue.setf(ios::fixed);
if (units == " bytes") { // in bytes, so no decimal point if (units == " bytes") { // in bytes, so no decimal point
sprintf(theValue, "%1.0f%s", sizeInSI, units.c_str()); theValue.precision(0);
} else { } else {
sprintf(theValue, "%1.1f%s", sizeInSI, units.c_str()); theValue.precision(1);
} // if/else } // if/else
return theValue; theValue << sizeInSI << units;
return theValue.str();
} // BlocksToSI() } // BlocksToSI()
// Converts two consecutive characters in the input string into a // Converts two consecutive characters in the input string into a

View File

@@ -2,14 +2,14 @@
under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
#include <stdint.h> #include <stdint.h>
#include <unistd.h> //#include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <string>
#ifndef __GPTSUPPORT #ifndef __GPTSUPPORT
#define __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 // Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64
// This used to use __DARWIN_UNIX03 rather than __APPLE__, but __APPLE__ // 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/ // is more general. If the code fails to work on older versions of OS X/