Update to version 0.5.2; adds support for Advanced Format disk partition

alignment
This commit is contained in:
srs5694
2009-12-31 21:20:19 -05:00
parent 247657a5ac
commit 1d1448a82d
6 changed files with 149 additions and 23 deletions

View File

@@ -1,3 +1,14 @@
0.5.2 (12/31/2009):
-------------------
- Modified partition creation function to begin partitions on 8-sector
boundaries by default. This improves performance on the new Western
Digital Advanced Format drives. The new 'd' and 'l' options on the
experts' menu display and change, respectively, the boundary size.
- Tweaked code to produce fewer warnings on the latest versions of
GCC.
0.5.1:
------

36
gdisk.8
View File

@@ -1,6 +1,6 @@
.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
.\" May be distributed under the GNU General Public License
.TH "GDISK" "8" "0.5.0" "Roderick W. Smith" "GPT fdisk Manual"
.TH "GDISK" "8" "0.5.1" "Roderick W. Smith" "GPT fdisk Manual"
.SH "NAME"
gdisk \- GUID partition table (GPT) manipulator for Linux and Unix
.SH "SYNOPSIS"
@@ -416,12 +416,20 @@ you might want to adjust the number manually if you've wound up with the
same GUID on two partitions because of buggy GUID assignments (hopefully
not in \fBgdisk\fR) or sheer incredible coincidence.
.TP
.B d
Display the number of logical sectors per physical sector. This value
determines the sector alignment that GPT fdisk enforces. See the
description of the 'l' option for more details. Note that this value
is not actually detected on a disk-by-disk basis; it's set to 8 as a
blanket default.
.TP
.B e
Relocate backup GPT data structures. Use this command if you've added
disks to a RAID array, thus creating a virtual disk with space that follows
the backup GPT data structures. This command moves the backup GPT data
structures to the end of the disk, where they belong.
Move backup GPT data structures to the end of the disk. Use this command if
you've added disks to a RAID array, thus creating a virtual disk with space
that follows the backup GPT data structures. This command moves the backup
GPT data structures to the end of the disk, where they belong.
.TP
.B g
@@ -434,6 +442,22 @@ a fresh random GUID or enter one manually with this option.
Show detailed partition information. This option is identical to the 'i'
option on the main menu.
.TP
.B l
Change the number of logical sectors per physical sector. Prior to December
of 2009, most hard disks used 512-byte physical sectors. Starting in
December of 2009, disk manufacturers began transitioning to disks with
larger physical sectors, but their firmware translated to 512-byte logical
sectors to maintain compatibility with older OSes. If partitions begin
mid-physical-sector, though, performance can suffer on such drives, since
important filesystem data structures can span physical sectors on the disk.
To minimize such problems, GPT fdisk aligns the start of partitions on the
boundary of presumed physical sectors. You can set the number of logical
sectors per physical sector with this option. The default is 8, which is
set blindly; GPT fdisk does not currently read this information from the
disk. The default value will result in a tiny amount of wasted disk space
on older disks with true 512-byte sectors but will otherwise be harmless.
.TP
.B m
Return to the main menu. This option enables you to enter main\-menu commands.
@@ -501,7 +525,7 @@ entering data. When only one option is possible, \fBgdisk\fR
usually bypasses the prompt entirely.
.SH "BUGS"
As of September 2009 (version 0.5.0), \fBgdisk\fR
As of November 2009 (version 0.5.1), \fBgdisk\fR
should be considered beta software. Known bugs and limitations include:
.TP

View File

@@ -29,7 +29,7 @@ int main(int argc, char* argv[]) {
int doMore = 1;
char* device = NULL;
printf("GPT fdisk (gdisk) version 0.5.1-pre3\n\n");
printf("GPT fdisk (gdisk) version 0.5.2\n\n");
if (argc == 2) { // basic usage
if (SizesOK()) {
@@ -309,6 +309,10 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
theGPT->SetPartitionGUID(pn, GetGUID());
} else printf("No partitions\n");
break;
case 'd': case 'D':
printf("The number of logical sectors per physical sector is set to %d.\n",
theGPT->GetAlignment());
break;
case 'e': case 'E':
printf("Relocating backup data structures to the end of the disk\n");
theGPT->MoveSecondHeaderToEnd();
@@ -320,6 +324,10 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
case 'i': case 'I':
theGPT->ShowDetails();
break;
case 'l': case 'L':
temp1 = GetNumber(1, 128, 8, "Enter the number of logical sectors in a physical sector on the\ndisk (1-128, default = 8): ");
theGPT->SetAlignment(temp1);
break;
case 'm': case 'M':
MainMenu(filename, theGPT);
goOn = 0;
@@ -366,9 +374,11 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
void ShowExpertCommands(void) {
printf("a\tset attributes\n");
printf("c\tchange partition GUID\n");
printf("d\tdisplay the number of logical sectors per physical sector\n");
printf("e\trelocate backup data structures to the end of the disk\n");
printf("g\tchange disk GUID\n");
printf("i\tshow detailed information on a partition\n");
printf("b\tset the number of logical sectors per physical sector\n");
printf("m\treturn to main menu\n");
printf("n\tcreate a new protective MBR\n");
printf("o\tprint protective MBR data\n");

104
gpt.cc
View File

@@ -46,6 +46,7 @@ GPTData::GPTData(void) {
secondPartsCrcOk = 0;
apmFound = 0;
bsdFound = 0;
sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srand((unsigned int) time(NULL));
SetGPTSize(NUM_GPT_ENTRIES);
} // GPTData default constructor
@@ -63,6 +64,7 @@ GPTData::GPTData(char* filename) {
secondPartsCrcOk = 0;
apmFound = 0;
bsdFound = 0;
sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
srand((unsigned int) time(NULL));
LoadPartitions(filename);
} // GPTData(char* filename) constructor
@@ -83,8 +85,8 @@ GPTData::~GPTData(void) {
// do *NOT* recover from these problems. Returns the total number of
// problems identified.
int GPTData::Verify(void) {
int problems = 0, numSegments;
uint64_t totalFree, largestSegment;
int problems = 0, numSegments, i;
uint64_t totalFree, largestSegment, firstSector;
char tempStr[255], siTotal[255], siLargest[255];
// First, check for CRC errors in the GPT data....
@@ -199,6 +201,16 @@ int GPTData::Verify(void) {
// Verify that partitions don't run into GPT data areas....
problems += CheckGPTSize();
// Check that partitions are aligned on proper boundaries (for WD Advanced
// Format and similar disks)....
for (i = 0; i < mainHeader.numParts; i++) {
if ((partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
printf("\nCaution: Partition %d doesn't begin on a %d-sector boundary. This may\n"
"result in degraded performance on some modern (2010 and later) hard disks.\n",
i + 1, sectorAlignment);
} // if
} // for
// Now compute available space, but only if no problems found, since
// problems could affect the results
if (problems == 0) {
@@ -726,7 +738,7 @@ void GPTData::LoadSecondTableAsMain(void) {
printf("Error! Couldn't seek to backup partition table!\n");
} // if/else
} else {
printf("Error! Couldn't open device %s when recovering backup partition table!\n");
printf("Error! Couldn't open device %s when recovering backup partition table!\n", device);
} // if/else
} // GPTData::LoadSecondTableAsMain()
@@ -755,7 +767,7 @@ int GPTData::SaveGPTData(void) {
if (mainHeader.backupLBA > diskSize) {
fprintf(stderr, "Error! Disk is too small! The 'e' option on the experts' menu might fix the\n"
"problem (or it might not). Aborting!\n");
printf("(Disk size is %ld sectors, needs to be %ld sectors.)\n", diskSize,
printf("(Disk size is %llu sectors, needs to be %llu sectors.)\n", diskSize,
mainHeader.backupLBA);
allOK = 0;
} // if
@@ -1183,6 +1195,7 @@ void GPTData::CreatePartition(void) {
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...
@@ -1315,7 +1328,7 @@ int GPTData::DestroyGPT(int prompt) {
printf("GPT data structures destroyed! You may now partition the disk using fdisk or\n"
"other utilities. Program will now terminate.\n");
} else {
printf("Problem opening %s for writing! Program will now terminate.\n");
printf("Problem opening %s for writing! Program will now terminate.\n", device);
} // if/else (fd != -1)
} // if (goOn == 'Y')
return (goOn == 'Y');
@@ -1885,6 +1898,71 @@ int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
return retval;
} // GPTData::SetPartitionGUID()
// Adjust sector number so that it falls on a sector boundary that's a
// multiple of sectorAlignment. This is done to improve the performance
// of Western Digital Advanced Format disks and disks with similar
// technology from other companies, which use 4096-byte sectors
// internally although they translate to 512-byte sectors for the
// benefit of the OS. If partitions aren't properly aligned on these
// disks, some filesystem data structures can span multiple physical
// sectors, degrading performance. This function should be called
// only on the FIRST sector of the partition, not the last!
// This function returns 1 if the alignment was altered, 0 if it
// was unchanged.
int GPTData::Align(uint64_t* sector) {
int retval = 0, sectorOK = 0;
uint64_t earlier, later, testSector, original;
if ((*sector % sectorAlignment) != 0) {
original = *sector;
retval = 1;
earlier = (*sector / sectorAlignment) * sectorAlignment;
later = earlier + (uint64_t) sectorAlignment;
// Check to see that every sector between the earlier one and the
// requested one is clear, and that it's not too early....
if (earlier >= mainHeader.firstUsableLBA) {
// printf("earlier is %llu, first usable is %llu\n", earlier, mainHeader.firstUsableLBA);
sectorOK = 1;
testSector = earlier;
do {
sectorOK = IsFree(testSector++);
} while ((sectorOK == 1) && (testSector < *sector));
if (sectorOK == 1) {
*sector = earlier;
// printf("Moved sector earlier.\n");
} // if
} // if firstUsableLBA check
// If couldn't move the sector earlier, try to move it later instead....
if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
sectorOK = 1;
testSector = later;
do {
sectorOK = IsFree(testSector--);
} while ((sectorOK == 1) && (testSector > *sector));
if (sectorOK == 1) {
*sector = later;
// printf("Moved sector later\n");
} // if
} // if
// If sector was changed successfully, inform the user of this fact.
// Otherwise, notify the user that it couldn't be done....
if (sectorOK == 1) {
printf("Information: Moved requested sector from %llu to %llu for\n"
"alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
original, *sector);
} else {
printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
"If you're using a Western Digital Advanced Format or similar disk with\n"
"underlying 4096-byte sectors, performance may suffer.\n", sectorAlignment);
retval = 0;
} // if/else
} // if
return retval;
} // GPTData::Align()
/********************************************************
* *
* Functions that return data about GPT data structures *
@@ -2141,35 +2219,35 @@ int SizesOK(void) {
int allOK = 1;
if (sizeof(uint8_t) != 1) {
fprintf(stderr, "uint8_t is %d bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
fprintf(stderr, "uint8_t is %lu bytes, should be 1 byte; aborting!\n", sizeof(uint8_t));
allOK = 0;
} // if
if (sizeof(uint16_t) != 2) {
fprintf(stderr, "uint16_t is %d bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
fprintf(stderr, "uint16_t is %lu bytes, should be 2 bytes; aborting!\n", sizeof(uint16_t));
allOK = 0;
} // if
if (sizeof(uint32_t) != 4) {
fprintf(stderr, "uint32_t is %d bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
fprintf(stderr, "uint32_t is %lu bytes, should be 4 bytes; aborting!\n", sizeof(uint32_t));
allOK = 0;
} // if
if (sizeof(uint64_t) != 8) {
fprintf(stderr, "uint64_t is %d bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
fprintf(stderr, "uint64_t is %lu bytes, should be 8 bytes; aborting!\n", sizeof(uint64_t));
allOK = 0;
} // if
if (sizeof(struct MBRRecord) != 16) {
fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
fprintf(stderr, "MBRRecord is %lu bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
allOK = 0;
} // if
if (sizeof(struct TempMBR) != 512) {
fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
fprintf(stderr, "TempMBR is %lu bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
allOK = 0;
} // if
if (sizeof(struct GPTHeader) != 512) {
fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
fprintf(stderr, "GPTHeader is %lu bytes, should be 512 bytes; aborting!\n", sizeof(GPTHeader));
allOK = 0;
} // if
if (sizeof(GPTPart) != 128) {
fprintf(stderr, "GPTPart is %d bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
fprintf(stderr, "GPTPart is %lu bytes, should be 128 bytes; aborting!\n", sizeof(GPTPart));
allOK = 0;
} // if
// Determine endianness; set allOK = 0 if running on big-endian hardware

5
gpt.h
View File

@@ -67,6 +67,7 @@ protected:
int secondPartsCrcOk;
int apmFound; // set to 1 if APM detected
int bsdFound; // set to 1 if BSD disklabel detected in MBR
int sectorAlignment; // Start & end partitions at multiples of sectorAlignment
PartTypes typeHelper;
public:
// Basic necessary functions....
@@ -132,6 +133,8 @@ public:
void SetDiskGUID(GUIDData newGUID);
int SetPartitionGUID(uint32_t pn, GUIDData theGUID);
void MakeProtectiveMBR(void) {protectiveMBR.MakeProtectiveMBR();}
int Align(uint64_t* sector);
void SetAlignment(int n) {sectorAlignment = n;}
// Return data about the GPT structures....
int GetPartRange(uint32_t* low, uint32_t* high);
@@ -143,7 +146,7 @@ public:
uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts *
mainHeader.sizeOfPartitionEntries) / blockSize;}
uint32_t CountParts(void);
int GetAlignment(void) {return sectorAlignment;}
// Find information about free space
uint64_t FindFirstAvailable(uint64_t start = 0);

View File

@@ -34,7 +34,7 @@ int GetNumber(int low, int high, int def, const char prompt[]) {
if (low != high) { // bother only if low and high differ...
response = low - 1; // force one loop by setting response outside range
while ((response < low) || (response > high)) {
printf(prompt);
printf("%s", prompt);
fgets(line, 255, stdin);
num = sscanf(line, "%d", &response);
if (num == 1) { // user provided a response
@@ -83,7 +83,7 @@ uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, char prompt[])
response = low - 1; // Ensure one pass by setting a too-low initial value
while ((response < low) || (response > high)) {
printf(prompt);
printf("%s", prompt);
fgets(line, 255, stdin);
// Remove leading spaces, if present