From fc0e014beaa6935576e93cf440f8b3d960b2d3f7 Mon Sep 17 00:00:00 2001 From: Rod Smith Date: Tue, 25 Jul 2017 21:33:18 -0400 Subject: [PATCH] Added code to read physical block size on Linux. --- NEWS | 6 ++++++ diskio-unix.cc | 21 +++++++++++++++++++++ diskio-windows.cc | 7 +++++++ diskio.h | 1 + gpt.cc | 29 ++++++++++++++++++++++++----- gpt.h | 5 +++-- 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 6ff81cc..5771634 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,12 @@ 1.0.2 (?/??/2017): ------------------ +- GPT fdisk can now report both the physical and logical sector sizes of + disks, but only on 2.6.32 and later Linux kernels. The verify feature now + uses the larger of the set alignment and physical/logical block sizes for + testing alignment, and setting alignment to something other than an exact + multiple of the physical/logical block size results in a warning. + - Addition of new verification checks, mostly (but not exclusively) related to the new j/-j/--move-main-table option. diff --git a/diskio-unix.cc b/diskio-unix.cc index af71cdb..a7c4724 100644 --- a/diskio-unix.cc +++ b/diskio-unix.cc @@ -177,6 +177,27 @@ int DiskIO::GetBlockSize(void) { return (blockSize); } // DiskIO::GetBlockSize() +// Returns the physical block size of the device, if possible. If this is +// not supported, or if an error occurs, this function returns 0. +// TODO: Get this working in more OSes than Linux. +int DiskIO::GetPhysBlockSize(void) { + int err = -1, physBlockSize = 0; + + // If disk isn't open, try to open it.... + if (!isOpen) { + OpenForRead(); + } // if + + if (isOpen) { +#if defined __linux__ && !defined(EFI) + err = ioctl(fd, BLKPBSZGET, &physBlockSize); +#endif + } // if (isOpen) + if (err == -1) + physBlockSize = 0; + return (physBlockSize); +} // DiskIO::GetPhysBlockSize(void) + // Returns the number of heads, according to the kernel, or 255 if the // correct value can't be determined. uint32_t DiskIO::GetNumHeads(void) { diff --git a/diskio-windows.cc b/diskio-windows.cc index 5535f49..9cff0d7 100644 --- a/diskio-windows.cc +++ b/diskio-windows.cc @@ -144,6 +144,13 @@ int DiskIO::GetBlockSize(void) { return (blockSize); } // DiskIO::GetBlockSize() +// In theory, returns the physical block size. In practice, this is only +// supported in Linux, as of yet. +// TODO: Get this working in Windows. +int DiskIO:GetPhysBlockSize(void) { + return 0; +} // DiskIO::GetPhysBlockSize() + // Returns the number of heads, according to the kernel, or 255 if the // correct value can't be determined. uint32_t DiskIO::GetNumHeads(void) { diff --git a/diskio.h b/diskio.h index 631a43a..26f113d 100644 --- a/diskio.h +++ b/diskio.h @@ -71,6 +71,7 @@ class DiskIO { int Write(void* buffer, int numBytes); int DiskSync(void); // resync disk caches to use new partitions int GetBlockSize(void); + int GetPhysBlockSize(void); uint32_t GetNumHeads(void); uint32_t GetNumSecsPerTrack(void); int IsOpen(void) {return isOpen;} diff --git a/gpt.cc b/gpt.cc index 015da65..a86dd2e 100644 --- a/gpt.cc +++ b/gpt.cc @@ -64,6 +64,7 @@ static inline uint32_t log2_32(uint32_t v) { // Default constructor GPTData::GPTData(void) { blockSize = SECTOR_SIZE; // set a default + physBlockSize = 0; // 0 = can't be determined diskSize = 0; partitions = NULL; state = gpt_valid; @@ -125,6 +126,7 @@ GPTData & GPTData::operator=(const GPTData & orig) { protectiveMBR = orig.protectiveMBR; device = orig.device; blockSize = orig.blockSize; + physBlockSize = orig.physBlockSize; diskSize = orig.diskSize; state = orig.state; justLooking = orig.justLooking; @@ -166,7 +168,7 @@ GPTData & GPTData::operator=(const GPTData & orig) { // problems identified. int GPTData::Verify(void) { int problems = 0, alignProbs = 0; - uint32_t i, numSegments; + uint32_t i, numSegments, testAlignment = sectorAlignment; uint64_t totalFree, largestSegment; // First, check for CRC errors in the GPT data.... @@ -355,10 +357,15 @@ int GPTData::Verify(void) { // Check that partitions are aligned on proper boundaries (for WD Advanced // Format and similar disks).... + if ((physBlockSize != 0) && (blockSize != 0)) + testAlignment = physBlockSize / blockSize; + testAlignment = max(testAlignment, sectorAlignment); + if (testAlignment == 0) // Should not happen; just being paranoid. + testAlignment = sectorAlignment; for (i = 0; i < numParts; i++) { - if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % sectorAlignment) != 0) { + if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % testAlignment) != 0) { cout << "\nCaution: Partition " << i + 1 << " doesn't begin on a " - << sectorAlignment << "-sector boundary. This may\nresult " + << testAlignment << "-sector boundary. This may\nresult " << "in degraded performance on some modern (2009 and later) hard disks.\n"; alignProbs++; } // if @@ -722,6 +729,7 @@ int GPTData::SetDisk(const string & deviceFilename) { // store disk information.... diskSize = myDisk.DiskSize(&err); blockSize = (uint32_t) myDisk.GetBlockSize(); + physBlockSize = (uint32_t) myDisk.GetPhysBlockSize(); } // if protectiveMBR.SetDisk(&myDisk); protectiveMBR.SetDiskSize(diskSize); @@ -801,6 +809,7 @@ int GPTData::LoadPartitions(const string & deviceFilename) { // store disk information.... diskSize = myDisk.DiskSize(&err); blockSize = (uint32_t) myDisk.GetBlockSize(); + physBlockSize = (uint32_t) myDisk.GetPhysBlockSize(); device = deviceFilename; PartitionScan(); // Check for partition types, load GPT, & print summary @@ -1478,6 +1487,8 @@ void GPTData::DisplayGPTData(void) { cout << "Disk " << device << ": " << diskSize << " sectors, " << BytesToIeee(diskSize, blockSize) << "\n"; cout << "Logical sector size: " << blockSize << " bytes\n"; + if (physBlockSize > 0) + cout << "Physical sector size: " << physBlockSize << " bytes\n"; cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n"; cout << "Partition table holds up to " << numParts << " entries\n"; cout << "Main partition table begins at sector " << mainHeader.partitionEntriesLBA @@ -2334,10 +2345,18 @@ int GPTData::IsUsedPartNum(uint32_t partNum) { // Set partition alignment value; partitions will begin on multiples of // the specified value void GPTData::SetAlignment(uint32_t n) { - if (n > 0) + if (n > 0) { sectorAlignment = n; - else + if ((physBlockSize > 0) && (n % (physBlockSize / blockSize) != 0)) { + cout << "Warning: Setting alignment to a value that does not match the disk's\n" + << "physical block size! Performance degradation may result!\n" + << "Physical block size = " << physBlockSize << "\n" + << "Logical block size = " << blockSize << "\n" + << "Optimal alignment = " << physBlockSize / blockSize << " or multiples thereof.\n"; + } // if + } else { cerr << "Attempt to set partition alignment to 0!\n"; + } // if/else } // GPTData::SetAlignment() // Compute sector alignment based on the current partitions (if any). Each diff --git a/gpt.h b/gpt.h index 2528b92..2d7a1ce 100644 --- a/gpt.h +++ b/gpt.h @@ -68,8 +68,9 @@ protected: MBRData protectiveMBR; string device; // device filename DiskIO myDisk; - uint32_t blockSize; // device block size - uint64_t diskSize; // size of device, in blocks + uint32_t blockSize; // device logical block size + uint32_t physBlockSize; // device physical block size (or 0 if it can't be determined) + uint64_t diskSize; // size of device, in logical blocks GPTValidity state; // is GPT valid? int justLooking; // Set to 1 if program launched with "-l" or if read-only int mainCrcOk;