From 1d1448a82d62ad32a8d597ed9ade46b4f37d8eb5 Mon Sep 17 00:00:00 2001 From: srs5694 Date: Thu, 31 Dec 2009 21:20:19 -0500 Subject: [PATCH] Update to version 0.5.2; adds support for Advanced Format disk partition alignment --- CHANGELOG | 11 ++++++ gdisk.8 | 36 +++++++++++++++---- gdisk.cc | 12 ++++++- gpt.cc | 104 ++++++++++++++++++++++++++++++++++++++++++++++------- gpt.h | 5 ++- support.cc | 4 +-- 6 files changed, 149 insertions(+), 23 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9667715..ce293a9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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: ------ diff --git a/gdisk.8 b/gdisk.8 index 6c87d0f..f65ec7e 100644 --- a/gdisk.8 +++ b/gdisk.8 @@ -1,6 +1,6 @@ .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License -.TH "GDISK" "8" "0.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 diff --git a/gdisk.cc b/gdisk.cc index bfee217..3414d89 100644 --- a/gdisk.cc +++ b/gdisk.cc @@ -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"); diff --git a/gpt.cc b/gpt.cc index 2e99714..c34c862 100644 --- a/gpt.cc +++ b/gpt.cc @@ -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(§or); // 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 diff --git a/gpt.h b/gpt.h index f22bd61..c0bf53c 100644 --- a/gpt.h +++ b/gpt.h @@ -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); diff --git a/support.cc b/support.cc index bb419ed..0902617 100644 --- a/support.cc +++ b/support.cc @@ -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