diff --git a/CHANGELOG b/CHANGELOG index 31ffc26..6b32097 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,27 @@ +0.6.8 (?/?/2010): +----------------- + +- Fixed compile problem for FreeBSD (its math library lacks a log2() + function). Also created separate Makefile.freebsd with a couple of + FreeBSD-specific options. + +- Added -N (--largest-new) command to sgdisk. This command creates a single + partition that fills the largest single unpartitioned block of space on + the disk. + +- Fixed sgdisk man page error: the --change-name option was incorrectly + listed as --change. + +- Added 'h' option to gdisk experts' menu (-C or --recompute-chs in sgdisk) + to recompute all protective/hybrid MBR CHS values. This option is + intended to work around a bug in at least one BIOS that prevents the + computer from booting when the GPT-mandated (but technically illegal) + 0xFFFFFF CHS value is used as the end point for a protective MBR. The + recomputed values will be legal (e.g., 0xFEFFFF instead of 0xFFFFFF), + but incorrect in GPT terms, and will therefore enable at least one + BIOS to boot with a GPT disk. See http://www.rodsbooks.com/gdisk/bios.html + for all I know about BIOS/GPT incompatibilities. + 0.6.7 (5/1/2010): ----------------- diff --git a/Makefile b/Makefile index 49be23a..b03f3bf 100644 --- a/Makefile +++ b/Makefile @@ -12,10 +12,10 @@ DEPEND= makedepend $(CXXFLAGS) all: gdisk sgdisk gdisk: $(LIB_OBJS) gdisk.o gpttext.o - $(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/opt/local/lib -L/usr/local/lib $(LDFLAGS) -luuid -o gdisk + $(CXX) $(LIB_OBJS) gdisk.o gpttext.o $(LDFLAGS) -luuid -o gdisk sgdisk: $(LIB_OBJS) sgdisk.o - $(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -L/usr/local/lib $(LDFLAGS) -luuid -lpopt -o sgdisk + $(CXX) $(LIB_OBJS) sgdisk.o $(LDFLAGS) -luuid -lpopt -o sgdisk lint: #no pre-reqs lint $(SRCS) diff --git a/README b/README index 2a41fe3..bca2f0e 100644 --- a/README +++ b/README @@ -69,14 +69,15 @@ compiler for C++. In addition, note these requirements: When all the necessary development tools and libraries are installed, you can uncompress the package and type "make" at the command prompt in the resulting directory. (You may need to type "make -f Makefile.mac" on Mac OS -X or "make -f Makefile.mingw" to compile using MinGW for Windows.) You may -also need to add header (include) directories or library directories by -setting the CXXFLAGS environment variable or by editing the Makefile. The -result should be program files called gdisk and sgdisk. Typing "make gdisk" -or "make sgdisk" will compile only the requested programs. You can use -these programs in place or copy the files to a suitable directory, such as -/usr/local/sbin. You can copy the man pages (gdisk.8 and sgdisk.8) to -/usr/local/man/man8 to make them available. +X, "make -f Makefile.freebsd" on FreeBSD, or "make -f Makefile.mingw" to +compile using MinGW for Windows.) You may also need to add header (include) +directories or library directories by setting the CXXFLAGS environment +variable or by editing the Makefile. The result should be program files +called gdisk and sgdisk. Typing "make gdisk" or "make sgdisk" will compile +only the requested programs. You can use these programs in place or copy +the files to a suitable directory, such as /usr/local/sbin. You can copy +the man pages (gdisk.8 and sgdisk.8) to /usr/local/man/man8 to make them +available. Caveats ------- @@ -93,10 +94,10 @@ RAID arrays over 2TiB in size, though. My main development platform is a system running the 64-bit version of Gentoo Linux (previously Ubuntu 8.04). I've also tested on 64-bit OpenSuSE, 32-bit Fedora 10, 32-bit Fedora 11, 32-bit Ubuntu 6.10, 64-bit Ubunut 9.10, -32-bit PowerPC Debian Linux, 32-bit Intel-based Mac OS X 10.5 and 10.6, and -64-bit FreeBSD 7.1. Problems relating to 64-bit integers on the 32-bit -Linux have been common during development and may crop up in the future. -The Mac OS X, FreeBSD, and big-endian (PowerPC) support are new. +32-bit PowerPC Debian Linux, 32-bit Intel-based Mac OS X 10.5 and 10.6, +64-bit FreeBSD 7.1, and Windows 7. Problems relating to 64-bit integers on +the 32-bit Linux have been common during development and may crop up in the +future. Redistribution -------------- @@ -113,8 +114,8 @@ other GPLed programs: Krzysztof Dabrowski and ElysiuM deeZine. (See the crc32.h and crc32.cc source code files.) -- A function to find the disk size is taken from Linux fdisk by - A. V. Le Blanc. +- A function to find the disk size is taken from Linux fdisk by A. V. Le + Blanc. This code has subsequently been heavily modified. Additional code contributors include: diff --git a/gdisk.8 b/gdisk.8 index 151f923..1a93c38 100644 --- a/gdisk.8 +++ b/gdisk.8 @@ -435,12 +435,29 @@ 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 f +Randomize the disk's GUID and all partitions' unique GUIDs (but not their +partition type code GUIDs). This function may be used after cloning a disk +with another utility in order to render all GUIDs once again unique. + .TP .B g Change disk GUID. Each disk has a unique GUID code, which \fBgdisk\fR assigns randomly upon creation of the GPT data structures. You can generate a fresh random GUID or enter one manually with this option. +.TP +.B h +Recompute CHS values in protective or hybrid MBR. This option can sometimes +help if a disk utility, OS, or BIOS doesn't like the CHS values used by the +partitions in the protective or hybrid MBR. In particular, the GPT +specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, +but this value is technically illegal by the usual standards. Some BIOSes +hang if they encounter this value. This option will recompute a more normal +CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to +boot. + .TP .B i Show detailed partition information. This option is identical to the 'i' diff --git a/gdisk.cc b/gdisk.cc index b372ebb..bc758a1 100644 --- a/gdisk.cc +++ b/gdisk.cc @@ -349,10 +349,16 @@ void ExpertsMenu(string filename, GPTDataTextUI* theGPT) { cout << "Relocating backup data structures to the end of the disk\n"; theGPT->MoveSecondHeaderToEnd(); break; + case 'f': case 'F': + theGPT->RandomizeGUIDs(); + break; case 'g': case 'G': cout << "Enter the disk's unique GUID:\n"; theGPT->SetDiskGUID(aGUID.GetGUIDFromUser()); break; + case 'h': case 'H': + theGPT->RecomputeCHS(); + break; case 'i': case 'I': theGPT->ShowDetails(); break; @@ -415,6 +421,7 @@ void ShowExpertCommands(void) { cout << "d\tdisplay the sector alignment value\n"; cout << "e\trelocate backup data structures to the end of the disk\n"; cout << "g\tchange disk GUID\n"; + cout << "h\trecompute CHS values in protective/hybrid MBR\n"; cout << "i\tshow detailed information on a partition\n"; cout << "l\tset the sector alignment value\n"; cout << "m\treturn to main menu\n"; diff --git a/gpt.cc b/gpt.cc index 532208f..4292035 100644 --- a/gpt.cc +++ b/gpt.cc @@ -30,6 +30,11 @@ using namespace std; +#ifdef __FreeBSD__ +#define log2(x) (log(x) / M_LN2) +#endif // __FreeBSD__ + + /**************************************** * * * GPTData class and related structures * @@ -1818,6 +1823,18 @@ int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) { return retval; } // GPTData::SetPartitionGUID() +// Set new random GUIDs for the disk and all partitions. Intended to be used +// after disk cloning or similar operations that don't randomize the GUIDs. +void GPTData::RandomizeGUIDs(void) { + uint32_t i; + + mainHeader.diskGUID.Randomize(); + secondHeader.diskGUID = mainHeader.diskGUID; + for (i = 0; i < numParts; i++) + if (partitions[i].IsUsed()) + partitions[i].RandomizeUniqueGUID(); +} // GPTData::RandomizeGUIDs() + // Change partition type code non-interactively. Returns 1 if // successful, 0 if not.... int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) { @@ -1829,6 +1846,16 @@ int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) { return retval; } // GPTData::ChangePartType() +// Recompute the CHS values of all the MBR partitions. Used to reset +// CHS values that some BIOSes require, despite the fact that the +// resulting CHS values violate the GPT standard. +void GPTData::RecomputeCHS(void) { + int i; + + for (i = 0; i < 4; i++) + protectiveMBR.RecomputeCHS(i); +} // GPTData::RecomputeCHS() + // 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 @@ -2140,10 +2167,7 @@ int GPTData::IsFreePartNum(uint32_t partNum) { // 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); } // GPTData::SetAlignment() // Compute sector alignment based on the current partitions (if any). Each diff --git a/gpt.h b/gpt.h index 09b51c5..f2399b4 100644 --- a/gpt.h +++ b/gpt.h @@ -16,7 +16,7 @@ #ifndef __GPTSTRUCTS #define __GPTSTRUCTS -#define GPTFDISK_VERSION "0.6.7" +#define GPTFDISK_VERSION "0.6.8-pre2" // Constants used by GPTData::PartsToMBR(). MBR_EMPTY must be the lowest- // numbered value to refer to partition numbers. (Most will be 0 or positive, @@ -160,8 +160,10 @@ public: int SetName(uint32_t partNum, const string & theName = ""); void SetDiskGUID(GUIDData newGUID); int SetPartitionGUID(uint32_t pn, GUIDData theGUID); + void RandomizeGUIDs(void); int ChangePartType(uint32_t pn, uint16_t hexCode); void MakeProtectiveMBR(void) {protectiveMBR.MakeProtectiveMBR();} + void RecomputeCHS(void); int Align(uint64_t* sector); // Return data about the GPT structures.... diff --git a/mbr.cc b/mbr.cc index 0b29bbb..3a5aec4 100644 --- a/mbr.cc +++ b/mbr.cc @@ -767,6 +767,23 @@ void MBRData::OptimizeEESize(void) { } // if } // MBRData::OptimizeEESize() +// Recomputes the CHS values for the specified partition and adjusts the value. +// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE) +// protective partitions, but this is required by some buggy BIOSes, so I'm +// providing a function to do this deliberately at the user's command. +// This function does nothing if the partition's length is 0. +void MBRData::RecomputeCHS(int partNum) { + uint64_t firstLBA, lengthLBA; + + firstLBA = (uint64_t) partitions[partNum].firstLBA; + lengthLBA = (uint64_t) partitions[partNum].lengthLBA; + + if (lengthLBA > 0) { + LBAtoCHS(firstLBA, partitions[partNum].firstSector); + LBAtoCHS(firstLBA + lengthLBA - 1, partitions[partNum].lastSector); + } // if +} // MBRData::RecomputeCHS() + // Creates an MBR extended partition holding logical partitions that // correspond to the list of GPT partitions in theList. The extended // partition is placed in position #4 (counting from 1) in the MBR. diff --git a/mbr.h b/mbr.h index 026aeec..a8e348e 100644 --- a/mbr.h +++ b/mbr.h @@ -124,6 +124,7 @@ public: void DeletePartition(int i); int DeleteByLocation(uint64_t start64, uint64_t length64); void OptimizeEESize(void); + void RecomputeCHS(int partNum); int CreateLogicals(PartNotes& notes); // Functions to find information on free space.... diff --git a/sgdisk.8 b/sgdisk.8 index 1685bf8..6a3a0b1 100644 --- a/sgdisk.8 +++ b/sgdisk.8 @@ -166,7 +166,7 @@ the backup may not accurately reflect the damaged state; instead, they will reflect GPT fdisk's first\-pass interpretation of the GPT. .TP -.B \-c, \-\-change=partnum:name +.B \-c, \-\-change\-name=partnum:name Change the GPT name of a partition. This name is encoded as a UTF\-16 string, but \fBsgdisk\fR supports only ASCII characters as names. For the most part, Linux ignores @@ -177,6 +177,17 @@ that includes a space, enclose it in quotation marks, as in partition is distinct from the filesystem name, which is encoded in the filesystem's data structures. +.TP +.B \-C, \-\-recompute-chs +Recompute CHS values in protective or hybrid MBR. This option can sometimes +help if a disk utility, OS, or BIOS doesn't like the CHS values used by the +partitions in the protective or hybrid MBR. In particular, the GPT +specification requires a CHS value of 0xFFFFFF for over-8GiB partitions, +but this value is technically illegal by the usual standards. Some BIOSes +hang if they encounter this value. This option will recompute a more normal +CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to +boot. + .TP .B \-d, \-\-delete=partnum Delete a partition. This action deletes the entry from the partition table @@ -218,6 +229,12 @@ 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 changes, in order to prevent accidentally damaging such disks. +.TP +.B \-G, \-\-randomize\-guids +Randomize the disk's GUID and all partitions' unique GUIDs (but not their +partition type code GUIDs). This function may be used after cloning a disk +in order to render all GUIDs once again unique. + .TP .B \-h, \-\-hybrid Create a hybrid MBR. This option takes from one to three partition numbers, @@ -286,6 +303,15 @@ sector. Pressing the Enter key with no input specifies the default value, which is the start of the largest available block for the start sector and the end of the same block for the end sector. +.TP +.B \-N, \-\-largest\-new +Create a new partition that fills the largest available block of space on +the disk. Note that if used on a completely blank disk, this is likely to +result in a sector-moved warning, since the first available sector +(normally 34) doesn't fall on a 2048-sector boundary (the default for +alignment). You can use the \fI\-a\fR (\fI\-\-set\-alignment\fR) option to +adjust the alignment, if desired. + .TP .B \-o, \-\-clear Clear out all partition data. This includes GPT header data, @@ -337,6 +363,14 @@ partitions that are unusable. The many BSD variants means that the probability of \fBsgdisk\fR being unable to convert a BSD disklabel is high compared to the likelihood of problems with an MBR conversion. +.TP +.B \-u, \-\-partition-guid=partnum:guid +Set the partition unique GUID for an individual partition. + +.TP +.B \-U, \-\-disk-guid=guid +Set the GUID for the disk. + .TP .B \-\-usage Print a brief summary of available options. diff --git a/sgdisk.cc b/sgdisk.cc index 99fa8ac..2576f42 100644 --- a/sgdisk.cc +++ b/sgdisk.cc @@ -32,7 +32,9 @@ int CountColons(char* argument); int main(int argc, char *argv[]) { GPTData theGPT; 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, largestPartNum = 0; + int saveNonGPT = 1; + uint32_t gptPartNum = 0; int alignment = DEFAULT_ALIGNMENT, retval = 0, pretend = 0; unsigned int hexCode; uint32_t tableSize = 128; @@ -40,6 +42,7 @@ int main(int argc, char *argv[]) { char *device = NULL; char *newPartInfo = NULL, *typeCode = NULL, *partName = NULL; char *backupFile = NULL, *twoParts = NULL, *hybrids = NULL, *mbrParts; + char *partGUID = NULL, *diskGUID = NULL; PartType typeHelper; poptContext poptCon; @@ -48,18 +51,21 @@ int main(int argc, char *argv[]) { {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"}, {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"}, {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"}, + {"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""}, {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"}, {"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""}, {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""}, {"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", ""}, {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""}, + {"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""}, {"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"}, {"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", ""}, {"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"}, + {"largest-new", 'N', POPT_ARG_INT, &largestPartNum, 'N', "create largest possible new partition", "partnum"}, {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear 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", ""}, @@ -68,6 +74,8 @@ int main(int argc, char *argv[]) { {"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"}, {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"}, + {"partition-guid", 'u', POPT_ARG_STRING, &partGUID, 'u', "set partition GUID", "partnum:guid"}, + {"disk-guid", 'U', POPT_ARG_STRING, &diskGUID, 'U', "set disk GUID", "guid"}, {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""}, {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""}, {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""}, @@ -161,6 +169,11 @@ int main(int argc, char *argv[]) { saveData = 1; saveNonGPT = 1; break; + case 'G': + theGPT.JustLooking(0); + saveData = 1; + theGPT.RandomizeGUIDs(); + break; case 'h': theGPT.JustLooking(0); if (BuildMBR(&theGPT, hybrids, 1) == 1) @@ -208,6 +221,18 @@ int main(int argc, char *argv[]) { } // if/else free(newPartInfo); break; + case 'N': + theGPT.JustLooking(0); + startSector = theGPT.FindFirstInLargest(); + endSector = theGPT.FindLastInFree(startSector); + if (theGPT.CreatePartition(largestPartNum - 1, startSector, endSector)) { + saveData = 1; + } else { + cerr << "Could not create partition " << largestPartNum << " from " + << startSector << " to " << endSector << "\n"; + neverSaveData = 1; + } // if/else + break; case 'o': theGPT.JustLooking(0); theGPT.ClearGPTData(); @@ -229,6 +254,11 @@ int main(int argc, char *argv[]) { cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n"; } else saveData = 1; break; + case 'R': + theGPT.JustLooking(0); + theGPT.RecomputeCHS(); + saveData = 1; + break; case 's': theGPT.JustLooking(0); theGPT.SortGPT(); @@ -259,6 +289,17 @@ int main(int argc, char *argv[]) { theGPT.XFormDisklabel(bsdPartNum - 1); saveData = 1; break; + case 'u': + theGPT.JustLooking(0); + saveData = 1; + gptPartNum = (int) GetInt(partGUID, 1) - 1; + theGPT.SetPartitionGUID(gptPartNum, GetString(partGUID, 2).c_str()); + break; + case 'U': + theGPT.JustLooking(0); + saveData = 1; + theGPT.SetDiskGUID(diskGUID); + break; case 'v': theGPT.Verify(); break;