Changes to sector alignment policies and behavior when restoring a

backup fails
This commit is contained in:
srs5694
2010-03-19 14:21:59 -04:00
parent 55d926192a
commit a8582cfe6c
14 changed files with 122 additions and 34 deletions

View File

@@ -1,3 +1,22 @@
0.6.6 (?/?/2010):
-----------------
- More alignment changes: GPT fdisk now attempts to determine the
alignment value based on alignment of current partitions, if any are
defined. If no partitions are defined, a default value of 2048 is
set. If the computed value is less than 8 on drives over about 596GiB,
it's reset to 8, since the drive might be a WD Advanced Format unit
that requires an 8-sector (or larger power-of-2) alignment value
for proper functioning. The 2048-sector default provides better
alignment in some RAID configurations.
- Changed behavior when a backup restore fails. Previously, GPT fdisk
would create a fresh blank set of partitions. Now it does so only
if the failure occurs when interpreting the backup's contents; if the
user typed the wrong filename, the in-memory data structures aren't
touched.
0.6.5 (3/7/2010): 0.6.5 (3/7/2010):
----------------- -----------------

View File

@@ -73,7 +73,7 @@ void Attributes::ChangeAttributes(void) {
do { do {
response = GetNumber(0, 64, -1, (string) "Toggle which attribute field (0-63, 64 to exit): "); response = GetNumber(0, 64, -1, (string) "Toggle which attribute field (0-63, 64 to exit): ");
if (response != 64) { if (response != 64) {
bitValue = PowerOf2(NUM_ATR - response - 1); // Find the integer value of the bit bitValue = PowerOf2(uint32_t (NUM_ATR - response - 1)); // Find the integer value of the bit
if ((bitValue & attributes) == bitValue) { // bit is set if ((bitValue & attributes) == bitValue) { // bit is set
attributes -= bitValue; // so unset it attributes -= bitValue; // so unset it
cout << "Have disabled the '" << atNames[response] << "' attribute.\n"; cout << "Have disabled the '" << atNames[response] << "' attribute.\n";

View File

@@ -34,6 +34,7 @@
#include "support.h" #include "support.h"
#include "diskio.h" #include "diskio.h"
#include "gpt.h"
using namespace std; using namespace std;
@@ -93,11 +94,12 @@ int DiskIO::OpenForWrite(const string & filename) {
// returns 1-sector alignment for unusual sector sizes and drives smaller than // returns 1-sector alignment for unusual sector sizes and drives smaller than
// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for // a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for
// larger drives with 512-byte sectors. // larger drives with 512-byte sectors.
int DiskIO::FindAlignment(void) { uint32_t DiskIO::FindAlignment(void) {
int err, result; int err;
uint32_t result;
if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) { if ((GetBlockSize() == 512) && (DiskSize(&err) >= SMALLEST_ADVANCED_FORMAT)) {
result = 8; // play it safe; align for 4096-byte sectors result = DEFAULT_ALIGNMENT; // play it safe; align for 4096-byte sectors
} else { } else {
result = 1; // unusual sector size; assume it's the real physical size result = 1; // unusual sector size; assume it's the real physical size
} // if/else } // if/else

View File

@@ -35,10 +35,6 @@
using namespace std; using namespace std;
// Below constant corresponds to an 800GB disk -- a somewhat arbitrary
// cutoff
#define SMALLEST_ADVANCED_FORMAT UINT64_C(1677721600)
/*************************************** /***************************************
* * * *
* DiskIO class and related structures * * DiskIO class and related structures *
@@ -71,7 +67,7 @@ class DiskIO {
int Write(void* buffer, int numBytes); int Write(void* buffer, int numBytes);
void DiskSync(void); // resync disk caches to use new partitions void DiskSync(void); // resync disk caches to use new partitions
int GetBlockSize(void); int GetBlockSize(void);
int FindAlignment(void); uint32_t FindAlignment(void);
int FindAlignment(const string & filename); int FindAlignment(const string & filename);
int IsOpen(void) {return isOpen;} int IsOpen(void) {return isOpen;}
int IsOpenForWrite(void) {return openForWrite;} int IsOpenForWrite(void) {return openForWrite;}

View File

@@ -458,7 +458,7 @@ important filesystem data structures can span physical sectors on the disk.
To minimize such problems, GPT fdisk aligns the start of partitions on the 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 boundary of presumed physical sectors. You can set the number of logical
sectors per physical sector with this option. The default is 1 on disks sectors per physical sector with this option. The default is 1 on disks
smaller than 800GB and 8 on larger disks. smaller than 596 GiB and 8 on larger disks with 512\-byte logical sectors.
.TP .TP
.B m .B m

View File

@@ -318,6 +318,7 @@ void ExpertsMenu(string filename, GPTDataTextUI* theGPT) {
uint32_t pn, temp1, temp2; uint32_t pn, temp1, temp2;
int goOn = 1; int goOn = 1;
GUIDData aGUID; GUIDData aGUID;
ostringstream prompt;
do { do {
cout << "\nExpert command (? for help): "; cout << "\nExpert command (? for help): ";
@@ -355,8 +356,10 @@ void ExpertsMenu(string filename, GPTDataTextUI* theGPT) {
theGPT->ShowDetails(); theGPT->ShowDetails();
break; break;
case 'l': case 'L': case 'l': case 'L':
temp1 = GetNumber(1, 128, 8, (string) prompt.seekp(0);
"Enter the sector alignment value (1-128, default = 8): "); prompt << "Enter the sector alignment value (1-" << MAX_ALIGNMENT << ", default = "
<< DEFAULT_ALIGNMENT << "): ";
temp1 = GetNumber(1, MAX_ALIGNMENT, DEFAULT_ALIGNMENT, prompt.str());
theGPT->SetAlignment(temp1); theGPT->SetAlignment(temp1);
break; break;
case 'm': case 'M': case 'm': case 'M':

68
gpt.cc
View File

@@ -14,6 +14,7 @@
#include <stdint.h> #include <stdint.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <math.h>
#include <time.h> #include <time.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
@@ -641,6 +642,7 @@ int GPTData::LoadPartitions(const string & deviceFilename) {
if (allOK) if (allOK)
CheckGPTSize(); CheckGPTSize();
myDisk.Close(); myDisk.Close();
ComputeAlignment();
} else { } else {
allOK = 0; allOK = 0;
} // if/else } // if/else
@@ -1116,20 +1118,20 @@ int GPTData::LoadGPTBackup(const string & filename) {
if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup))) if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup)))
cerr << "Warning! Read error " << errno cerr << "Warning! Read error " << errno
<< " loading partition table; strange behavior now likely!\n"; << " loading partition table; strange behavior now likely!\n";
} else { } else {
allOK = 0; allOK = 0;
} // if/else } // if/else
// Something went badly wrong, so blank out partitions
if (allOK == 0) {
cerr << "Improper backup file! Clearing all partition data!\n";
ClearGPTData();
protectiveMBR.MakeProtectiveMBR();
} // if
} else { } else {
allOK = 0; allOK = 0;
cerr << "Unable to open file " << filename << " for reading! Aborting!\n"; cerr << "Unable to open file " << filename << " for reading! Aborting!\n";
} // if/else } // if/else
// Something went badly wrong, so blank out partitions
if (allOK == 0) {
ClearGPTData();
protectiveMBR.MakeProtectiveMBR();
} // if
return allOK; return allOK;
} // GPTData::LoadGPTBackup() } // GPTData::LoadGPTBackup()
@@ -2123,6 +2125,60 @@ int GPTData::IsFreePartNum(uint32_t partNum) {
return retval; return retval;
} // GPTData::IsFreePartNum() } // GPTData::IsFreePartNum()
/***********************************************************
* *
* Change how functions work or return information on them *
* *
***********************************************************/
// Set partition alignment value; partitions will begin on multiples of
// the specified value
void GPTData::SetAlignment(uint32_t n) {
uint32_t l2;
sectorAlignment = n;
l2 = (uint32_t) log2(n);
if (PowerOf2(l2) != n)
cout << "Information: Your alignment value is not a power of 2.\n";
} // GPTData::SetAlignment()
// Compute sector alignment based on the current partitions (if any). Each
// partition's starting LBA is examined, and if it's divisible by a power-of-2
// value less than the maximum found so far (or 2^31 for the first partition
// found), then the alignment value is adjusted down. If the computed
// alignment is less than 8 and the disk is bigger than SMALLEST_ADVANCED_FORMAT,
// resets it to 8. This is a safety measure for WD Advanced Format and
// similar drives. If no partitions are defined, the alignment value is set
// to DEFAULT_ALIGNMENT (2048). The result is that new drives are aligned to
// 2048-sector multiples but the program won't complain about other alignments
// on existing disks unless a smaller-than-8 alignment is used on small disks
// (as safety for WD Advanced Format drives).
// Returns the computed alignment value.
uint32_t GPTData::ComputeAlignment(void) {
uint32_t i = 0, found, exponent = 31;
uint64_t align = DEFAULT_ALIGNMENT;
for (i = 0; i < mainHeader.numParts; i++) {
if (partitions[i].IsUsed()) {
found = 0;
while (!found) {
align = PowerOf2(exponent);
if ((partitions[i].GetFirstLBA() % align) == 0) {
found = 1;
} else {
exponent--;
} // if/else
} // while
} // if
} // for
if ((align < 8) && (diskSize >= SMALLEST_ADVANCED_FORMAT))
align = 8;
// cout << "Setting alignment to " << align << "\n";
SetAlignment(align);
return align;
} // GPTData::ComputeAlignment()
/******************************** /********************************
* * * *
* Endianness support functions * * Endianness support functions *

19
gpt.h
View File

@@ -16,7 +16,7 @@
#ifndef __GPTSTRUCTS #ifndef __GPTSTRUCTS
#define __GPTSTRUCTS #define __GPTSTRUCTS
#define GPTFDISK_VERSION "0.6.5" #define GPTFDISK_VERSION "0.6.6-pre1"
// Constants used by GPTData::PartsToMBR(). MBR_EMPTY must be the lowest- // Constants used by GPTData::PartsToMBR(). MBR_EMPTY must be the lowest-
// numbered value to refer to partition numbers. (Most will be 0 or positive, // numbered value to refer to partition numbers. (Most will be 0 or positive,
@@ -24,6 +24,16 @@
#define MBR_EFI_GPT -1 #define MBR_EFI_GPT -1
#define MBR_EMPTY -2 #define MBR_EMPTY -2
// Default values for sector alignment
#define DEFAULT_ALIGNMENT 2048
#define MAX_ALIGNMENT 32768
// Below constant corresponds to an 800GB disk -- a somewhat arbitrary
// cutoff
//#define SMALLEST_ADVANCED_FORMAT UINT64_C(1677721600)
// Now ~596GiB (640MB), since WD has introduced a smaller Advanced Format drive
#define SMALLEST_ADVANCED_FORMAT UINT64_C(1250263728)
using namespace std; using namespace std;
class PartNotes; class PartNotes;
@@ -79,7 +89,7 @@ protected:
int secondPartsCrcOk; int secondPartsCrcOk;
int apmFound; // set to 1 if APM detected int apmFound; // set to 1 if APM detected
int bsdFound; // set to 1 if BSD disklabel detected in MBR int bsdFound; // set to 1 if BSD disklabel detected in MBR
int sectorAlignment; // Start & end partitions at multiples of sectorAlignment uint32_t sectorAlignment; // Start & end partitions at multiples of sectorAlignment
int beQuiet; int beQuiet;
WhichToUse whichWasUsed; WhichToUse whichWasUsed;
@@ -177,8 +187,9 @@ public:
int IsFreePartNum(uint32_t partNum); int IsFreePartNum(uint32_t partNum);
// Change how functions work, or return information on same // Change how functions work, or return information on same
void SetAlignment(int n) {sectorAlignment = n;} void SetAlignment(uint32_t n);
int GetAlignment(void) {return sectorAlignment;} uint32_t ComputeAlignment(void); // Set alignment based on current partitions
uint32_t GetAlignment(void) {return sectorAlignment;}
void JustLooking(int i = 1) {justLooking = i;} void JustLooking(int i = 1) {justLooking = i;}
void BeQuiet(int i = 1) {beQuiet = i;} void BeQuiet(int i = 1) {beQuiet = i;}
WhichToUse WhichWasUsed(void) {return whichWasUsed;} WhichToUse WhichWasUsed(void) {return whichWasUsed;}

5
mbr.cc
View File

@@ -85,7 +85,7 @@ int MBRData::ReadMBRData(const string & deviceFilename) {
canDeleteMyDisk = 1; canDeleteMyDisk = 1;
} // if } // if
if (myDisk->OpenForRead(deviceFilename)) { if (myDisk->OpenForRead(deviceFilename)) {
ReadMBRData(myDisk); allOK = ReadMBRData(myDisk);
} else { } else {
allOK = 0; allOK = 0;
} // if } // if
@@ -101,7 +101,7 @@ int MBRData::ReadMBRData(const string & deviceFilename) {
// Note that any extended partition(s) present will be explicitly stored // Note that any extended partition(s) present will be explicitly stored
// in the partitions[] array, along with their contained partitions; the // in the partitions[] array, along with their contained partitions; the
// extended container partition(s) should be ignored by other functions. // extended container partition(s) should be ignored by other functions.
void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) { int MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
int allOK = 1, i, j, logicalNum; int allOK = 1, i, j, logicalNum;
int err = 1; int err = 1;
TempMBR tempMBR; TempMBR tempMBR;
@@ -201,6 +201,7 @@ void MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
} // for } // for
} // if (hybrid detection code) } // if (hybrid detection code)
} // no initial error } // no initial error
return allOK;
} // MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) } // MBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
// This is a recursive function to read all the logical partitions, following the // This is a recursive function to read all the logical partitions, following the

2
mbr.h
View File

@@ -89,7 +89,7 @@ public:
// File I/O functions... // File I/O functions...
int ReadMBRData(const string & deviceFilename); int ReadMBRData(const string & deviceFilename);
void ReadMBRData(DiskIO * theDisk, int checkBlockSize = 1); int ReadMBRData(DiskIO * theDisk, int checkBlockSize = 1);
// ReadLogicalPart() returns last partition # read to logicals[] array, // ReadLogicalPart() returns last partition # read to logicals[] array,
// or -1 if there was a problem.... // or -1 if there was a problem....
int ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset, int ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset,

View File

@@ -149,10 +149,10 @@ sibling. Options available in \fBsgdisk\fR are:
.B \-a, \-\-set\-alignment=value .B \-a, \-\-set\-alignment=value
Set the sector alignment multiple. GPT fdisk aligns the start of partitions Set the sector alignment multiple. GPT fdisk aligns the start of partitions
to sectors that are multiples of this value, which defaults to 8 on disks to sectors that are multiples of this value, which defaults to 8 on disks
larger than 800GiB with 512\-byte sectors and to 1 on smaller disks or those larger than 596 GiB with 512\-byte logical sectors and to 1 on smaller
with non\-512\-byte sectors. This alignment value is necessary to obtain disks or those with non\-512\-byte sectors. This alignment value is
optimum performance with Western Digital Advanced Format and similar drives necessary to obtain optimum performance with Western Digital Advanced
with larger physical than logical sector sizes. Format and similar drives with larger physical than logical sector sizes.
.TP .TP
.B \-b, \-\-backup=file .B \-b, \-\-backup=file

View File

@@ -33,7 +33,7 @@ int main(int argc, char *argv[]) {
GPTData theGPT; GPTData theGPT;
int opt, numOptions = 0, saveData = 0, neverSaveData = 0; int opt, numOptions = 0, saveData = 0, neverSaveData = 0;
int partNum = 0, deletePartNum = 0, infoPartNum = 0, bsdPartNum = 0, saveNonGPT = 1; int partNum = 0, deletePartNum = 0, infoPartNum = 0, bsdPartNum = 0, saveNonGPT = 1;
int alignment = 8, retval = 0, pretend = 0; int alignment = DEFAULT_ALIGNMENT, retval = 0, pretend = 0;
unsigned int hexCode; unsigned int hexCode;
uint32_t tableSize = 128; uint32_t tableSize = 128;
uint64_t startSector, endSector; uint64_t startSector, endSector;

View File

@@ -246,11 +246,11 @@ void ReverseBytes(void* theValue, int numBytes) {
// Compute (2 ^ value). Given the return type, value must be 63 or less. // Compute (2 ^ value). Given the return type, value must be 63 or less.
// Used in some bit-fiddling functions // Used in some bit-fiddling functions
uint64_t PowerOf2(int value) { uint64_t PowerOf2(uint32_t value) {
uint64_t retval = 1; uint64_t retval = 1;
int i; uint32_t i;
if ((value < 64) && (value >= 0)) { if (value < 64) {
for (i = 0; i < value; i++) { for (i = 0; i < value; i++) {
retval *= 2; retval *= 2;
} // for } // for

View File

@@ -55,6 +55,6 @@ string BytesToSI(uint64_t size);
unsigned char StrToHex(const string & input, unsigned int position); unsigned char StrToHex(const string & input, unsigned int position);
int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-endian int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-endian
void ReverseBytes(void* theValue, int numBytes); // Reverses byte-order of theValue void ReverseBytes(void* theValue, int numBytes); // Reverses byte-order of theValue
uint64_t PowerOf2(int value); uint64_t PowerOf2(uint32_t value);
#endif #endif