diff --git a/CHANGELOG b/CHANGELOG index 65229e77a0f763ebcbfaac06f74b9a3560f84e6d..420291b323ec7301b63e6591501b201b845a7130 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,34 @@ +0.4.0: +------ + +- Added support for BSD disklabels. The program can now convert disks that + use "raw" disklabels, with the caveat that the first partition will + almost certainly need to be deleted because it'll overlap the main GPT + header; and convert disklabels contained within a GPT (or a former MBR, + converted to GPT) partition. In the latter case, the 'b' main menu option + is used. + +- Added support for compiling on FreeBSD. + +- Fixed bug that could cause crashes or incomplete sorts when sorting + the partition table. + +- New partitions, including converted ones, now take on the name of the + partition type as a default name. + +- Reorganized some code; created a separate C++ class for GPT partitions + (GPTPart), which replaced a struct and enabled moving code from the + bloated GPTData class into GPTPart. + +- Fixed a bug that produced spurious warnings about unknown sector sizes + when loading a backup file. + 0.3.5: ------ +Note: This version was not officially publicly released; I wanted to test +the big-endian support while developing 0.4.0. + - Tweaked the disk type identification code to warn users to re-sync their hybrid MBRs when one is detected. diff --git a/Makefile b/Makefile index 2c553323d04d62d7b79719db84e15d3793da59b4..eb17c78022d4345add018407a58db1001ee6a22e 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CXX=g++ #CFLAGS=-O2 -fpack-struct CFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g CXXFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g -LIB_NAMES=support crc32 mbr gpt parttypes attributes +LIB_NAMES=support crc32 gptpart mbr gpt bsd parttypes attributes LIB_SRCS=$(NAMES:=.cc) LIB_OBJS=$(LIB_NAMES:=.o) LIB_HEADERS=$(LIB_NAMES:=.h) diff --git a/README b/README index a212e8f7005771170094bc6e909dfb4a772b064b..042418bc1d7bf4aafd5a86bacb47364b4456d6b4 100644 --- a/README +++ b/README @@ -13,6 +13,9 @@ include: * The ability to convert MBR-partitioned disks in-place to GPT format, without losing data +* The ability to convert BSD disklabels in-place to create GPT + partitions, without losing data + * The ability to specify sector-exact partition sizes * More flexible specification of filesystem type code GUIDs, which @@ -27,6 +30,9 @@ include: * The MBR boot loader code is left alone (GNU Parted tends to wipe it out with every change) +* The ability to create a hybrid MBR, which permits GPT-unaware + OSes to access up to three GPT partitions on the disk + Of course, gdisk isn't without its limitations. Most notably, it lacks the filesystem awareness and filesystem-related features of GNU Parted. You can't resize a partition's filesystem or create a partition with a @@ -60,10 +66,10 @@ with >2TiB drives, though. My main development platform is a system running the 64-bit version of Ubuntu 8.04. I've also tested on 64-bit OpenSuSE, 32-bit Fedora 10, 32-bit -Ubuntu 6.10, 64-bit Gentoo, 32-bit PowerPC Linux, and 32-bit Intel-based -Mac OS X. 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 and big-endian (PowerPC) support is new. +Ubuntu 6.10, 64-bit Gentoo, 32-bit PowerPC Linux, 32-bit Intel-based Mac +OS X, and 64-bit Fedora 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. Redistribution -------------- diff --git a/attributes.cc b/attributes.cc index 1a299ea9ee1601208154526fbfe29349e746b09c..73560d6f6b5de2a83a64b6124f539ae2a4c0ff89 100644 --- a/attributes.cc +++ b/attributes.cc @@ -2,6 +2,9 @@ // Class to manage partition attribute codes. These are binary bit fields, // of which only three are currently (2/2009) documented on Wikipedia. +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS diff --git a/attributes.h b/attributes.h index a7538a1c4ad8e04246d3aa90f89d4f534882e384..14329c66408bb095d345845ae88800a1ba674b0f 100644 --- a/attributes.h +++ b/attributes.h @@ -1,3 +1,6 @@ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #include <stdint.h> #include <unistd.h> #include <stdlib.h> diff --git a/crc32.cc b/crc32.cc index a446ffca3bd3af4062af23ce7d05e8a9235b32e7..2b9ee9a384ca1ce57a22f50b5aff34bda7fac6de 100644 --- a/crc32.cc +++ b/crc32.cc @@ -15,6 +15,7 @@ #include <stdio.h> #include <stdlib.h> +#include <sys/types.h> /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). * so make sure, you call it before using the other diff --git a/gdisk.8 b/gdisk.8 index 1207f87467ca1986b6473a81c2d3376bc9bc18af..714d3001e7359e8f46810b90f31322d57f2f10ce 100644 --- a/gdisk.8 +++ b/gdisk.8 @@ -1,13 +1,15 @@ .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com) .\" May be distributed under the GNU General Public License -.TH GDISK 8 "August 2009" "Linux 2.6" "GPT fdisk Manual" +.TH GDISK 8 "August 2009" "0.4.0" "GPT fdisk Manual" .SH NAME -gdisk \- GPT partition table manipulator for Linux +gdisk \- GPT partition table manipulator for Linux and Unix .SH SYNOPSIS .BI "gdisk " [ \-l ] .I device + .SH DESCRIPTION + Hard disks can be divided into one or more segments, known as .IR partitions . This division is described in the @@ -110,7 +112,7 @@ program employs a user interface similar to that of Linux's but .B "gdisk" modifies GPT partitions. It also has the capability of transforming MBR -partitions into GPT partitions. Like the original +partitions or BSD disklabels into GPT partitions. Like the original .B fdisk program, .B gdisk @@ -120,8 +122,9 @@ save your partitions. .B gdisk is a text-mode menu-driven program for creation and manipulation of -partition tables. It will automatically convert an MBR partition table to -GPT format, or will load a GPT partition table. When used with the +partition tables. It will automatically convert an MBR partition table or +BSD disklabel stored without an MBR carrier partition to GPT format, or +will load a GPT partition table. When used with the .IR "\-l" command-line option, the program displays the current partition table and then exits. @@ -173,6 +176,13 @@ will note that .B "gdisk" lacks the options and limitations associated with CHS geometries. +For best results, you should always use an OS-specific partition table +program. For example, you should make Mac OS X partitions with the Mac OS +X Disk Utility +program and Linux partitions with the Linux +.B "gdisk" +or GNU Parted program. + Upon start, .B gdisk attempts to identify the partition type in use on the specified disk. If it @@ -180,10 +190,14 @@ finds valid GPT data, .B gdisk will use it. If .B gdisk -finds a valid MBR but no GPT data, it will attempt to convert the MBR into -GPT form. Upon exiting with the 'w' option, +finds a valid MBR or BSD disklabel but no GPT data, it will attempt to +convert the MBR or disklabel into GPT form. (BSD disklabels are likely to +have unusable first and/or final partitions because they overlap with the +GPT data structures, though.) GPT fdisk can identify, but not use data in, +Apple Partition Map (APM) disks, which are used on 680x0- and PowerPC-based +Macintoshes. Upon exiting with the 'w' option, .B gdisk -will then replace the MBR with a GPT. +will then replace the MBR or disklabel with a GPT. .IR "This action is potentially dangerous!" Your system may become unbootable, and partition type codes may become corrupted if the disk uses unrecognized type codes. Boot problems are @@ -257,13 +271,26 @@ Most interactions with occur with its interactive text-mode menus. The main menu provides the following options: +.TP +.B b +Convert BSD partitions into GPT partitions. This option works on BSD +disklabels held within GPT (or converted MBR) partitions. Converted +partitions' type codes are likely to need manual adjustment. +.B gdisk +will attempt to convert BSD disklabels stored on the main disk when +launched, but this conversion is likely to produce first and/or last +partitions that are unusable. The many BSD variants means that the +probability of GPT fdisk being unable to convert a BSD disklabel are high +compared to the likelihood of problems with an MBR conversion. + .TP .B c Change the GPT name of a partition. This name is encoded as a UTF-16 string, but .B gdisk supports only ASCII characters as names. For the most part, Linux ignores -the partition name, but it may be important in some OSes. +the partition name, but it may be important in some OSes. GPT fdisk sets +a default name based on the partition type code. .TP .B d @@ -342,7 +369,7 @@ Sort partition entries. GPT partition numbers need not match the order of partitions on the disk. If you want them to match, you can use this option. Note that some partitioning utilities, such as GNU Parted, will sort partitions whenever they make changes. Such changes will be reflected in -your Linux device filenames, so you may need to edit +your device filenames, so you may need to edit .IR "/etc/fstab" if you use this option. diff --git a/gdisk.cc b/gdisk.cc index 428111cdd221da6ded18ff7dec2487f824f9c3de..5baf9fee9bda81515d89f089d3b5208f7524a9ae 100644 --- a/gdisk.cc +++ b/gdisk.cc @@ -4,6 +4,9 @@ // // by Rod Smith, February 2009 +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + //#include <iostream> #include <stdio.h> #include <string.h> @@ -24,7 +27,7 @@ int main(int argc, char* argv[]) { int doMore = 1; char* device = NULL; - printf("GPT fdisk (gdisk) version 0.3.5\n\n"); + printf("GPT fdisk (gdisk) version 0.4.0\n\n"); if (argc == 2) { // basic usage if (SizesOK()) { @@ -65,9 +68,9 @@ int DoCommand(char* filename, struct GPTData* theGPT) { fgets(line, 255, stdin); sscanf(line, "%c", &command); switch (command) { -/* case 'b': case 'B': - GetGUID(); - break; */ + case 'b': case 'B': + theGPT->XFormDisklabel(); + break; case 'c': case 'C': if (theGPT->GetPartRange(&temp1, &temp2) > 0) theGPT->SetName(theGPT->GetPartNum()); @@ -125,6 +128,7 @@ int DoCommand(char* filename, struct GPTData* theGPT) { } // DoCommand() void ShowCommands(void) { + printf("b\tconvert BSD disklabel partitions\n"); printf("c\tchange a partition's name\n"); printf("d\tdelete a partition\n"); printf("i\tshow detailed information on a partition\n"); diff --git a/gpt.cc b/gpt.cc index c06edfcf47f3a588c10730ccd8748f876803d566..af3da2ad894f7c884a6215462903b2a51f668c72 100644 --- a/gpt.cc +++ b/gpt.cc @@ -3,6 +3,9 @@ /* By Rod Smith, January to February, 2009 */ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS @@ -17,6 +20,7 @@ #include <errno.h> #include "crc32.h" #include "gpt.h" +#include "bsd.h" #include "support.h" #include "parttypes.h" #include "attributes.h" @@ -39,6 +43,8 @@ GPTData::GPTData(void) { secondCrcOk = 0; mainPartsCrcOk = 0; secondPartsCrcOk = 0; + apmFound = 0; + bsdFound = 0; srand((unsigned int) time(NULL)); SetGPTSize(NUM_GPT_ENTRIES); } // GPTData default constructor @@ -54,6 +60,8 @@ GPTData::GPTData(char* filename) { secondCrcOk = 0; mainPartsCrcOk = 0; secondPartsCrcOk = 0; + apmFound = 0; + bsdFound = 0; srand((unsigned int) time(NULL)); LoadPartitions(filename); } // GPTData(char* filename) constructor @@ -65,8 +73,8 @@ GPTData::~GPTData(void) { // Resizes GPT to specified number of entries. Creates a new table if // necessary, copies data if it already exists. int GPTData::SetGPTSize(uint32_t numEntries) { - struct GPTPartition* newParts; - struct GPTPartition* trash; + struct GPTPart* newParts; + struct GPTPart* trash; uint32_t i, high, copyNum; int allOK = 1; @@ -79,7 +87,7 @@ int GPTData::SetGPTSize(uint32_t numEntries) { printf("to %lu to fill the sector\n", (unsigned long) numEntries); } // if - newParts = (struct GPTPartition*) calloc(numEntries, sizeof (struct GPTPartition)); + newParts = (GPTPart*) calloc(numEntries, sizeof (GPTPart)); if (newParts != NULL) { if (partitions != NULL) { // existing partitions; copy them over GetPartRange(&i, &high); @@ -120,22 +128,22 @@ int GPTData::SetGPTSize(uint32_t numEntries) { } // GPTData::SetGPTSize() // Checks to see if the GPT tables overrun existing partitions; if they -// do, issues a warning but takes no action. Returns 1 if all is OK, 0 -// if problems were detected. +// do, issues a warning but takes no action. Returns number of problems +// detected (0 if OK, 1 to 2 if problems). int GPTData::CheckGPTSize(void) { uint64_t overlap, firstUsedBlock, lastUsedBlock; uint32_t i; - int allOK = 1; + int numProbs = 0; // first, locate the first & last used blocks firstUsedBlock = UINT64_MAX; lastUsedBlock = 0; for (i = 0; i < mainHeader.numParts; i++) { - if ((partitions[i].firstLBA < firstUsedBlock) && - (partitions[i].firstLBA != 0)) - firstUsedBlock = partitions[i].firstLBA; - if (partitions[i].lastLBA > lastUsedBlock) - lastUsedBlock = partitions[i].lastLBA; + if ((partitions[i].GetFirstLBA() < firstUsedBlock) && + (partitions[i].GetFirstLBA() != 0)) + firstUsedBlock = partitions[i].GetFirstLBA(); + if (partitions[i].GetLastLBA() > lastUsedBlock) + lastUsedBlock = partitions[i].GetLastLBA(); } // for // If the disk size is 0 (the default), then it means that various @@ -144,45 +152,121 @@ int GPTData::CheckGPTSize(void) { if (diskSize != 0) { if (mainHeader.firstUsableLBA > firstUsedBlock) { overlap = mainHeader.firstUsableLBA - firstUsedBlock; - printf("Warning! Main partition table overlaps the first partition by %lu\n" - "blocks! Try reducing the partition table size by %lu entries.\n", - (unsigned long) overlap, (unsigned long) (overlap * 4)); - printf("(Use the 's' item on the experts' menu.)\n"); - allOK = 0; + printf("Warning! Main partition table overlaps the first partition by %lu blocks!\n", + (unsigned long) overlap); + if (firstUsedBlock > 2) { + printf("Try reducing the partition table size by %lu entries.\n", + (unsigned long) (overlap * 4)); + printf("(Use the 's' item on the experts' menu.)\n"); + } else { + printf("You will need to delete this partition or resize it in another utility.\n"); + } // if/else + numProbs++; } // Problem at start of disk if (mainHeader.lastUsableLBA < lastUsedBlock) { overlap = lastUsedBlock - mainHeader.lastUsableLBA; - printf("Warning! Secondary partition table overlaps the last partition by %lu\n" - "blocks! Try reducing the partition table size by %lu entries.\n", - (unsigned long) overlap, (unsigned long) (overlap * 4)); - printf("(Use the 's' item on the experts' menu.)\n"); - allOK = 0; + printf("Warning! Secondary partition table overlaps the last partition by %lu blocks\n", + (unsigned long) overlap); + if (lastUsedBlock > (diskSize - 2)) { + printf("You will need to delete this partition or resize it in another utility.\n"); + } else { + printf("Try reducing the partition table size by %lu entries.\n", + (unsigned long) (overlap * 4)); + printf("(Use the 's' item on the experts' menu.)\n"); + } // if/else + numProbs++; } // Problem at end of disk } // if (diskSize != 0) - return allOK; + return numProbs; } // GPTData::CheckGPTSize() +// Tell user whether Apple Partition Map (APM) was discovered.... +void GPTData::ShowAPMState(void) { + if (apmFound) + printf(" APM: present\n"); + else + printf(" APM: not present\n"); +} // GPTData::ShowAPMState() + +// Tell user about the state of the GPT data.... +void GPTData::ShowGPTState(void) { + switch (state) { + case gpt_invalid: + printf(" GPT: not present\n"); + break; + case gpt_valid: + printf(" GPT: present\n"); + break; + case gpt_corrupt: + printf(" GPT: damaged\n"); + break; + default: + printf("\a GPT: unknown -- bug!\n"); + break; + } // switch +} // GPTData::ShowGPTState() + +// Scan for partition data. This function loads the MBR data (regular MBR or +// protective MBR) and loads BSD disklabel data (which is probably invalid). +// It also looks for APM data, forces a load of GPT data, and summarizes +// the results. +void GPTData::PartitionScan(int fd) { + BSDData bsdDisklabel; +// int bsdFound; + + printf("Partition table scan:\n"); + + // Read the MBR & check for BSD disklabel + protectiveMBR.ReadMBRData(fd); + protectiveMBR.ShowState(); + bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1); + bsdFound = bsdDisklabel.ShowState(); +// bsdDisklabel.DisplayBSDData(); + + // Load the GPT data, whether or not it's valid + ForceLoadGPTData(fd); + ShowAPMState(); // Show whether there's an Apple Partition Map present + ShowGPTState(); // Show GPT status + printf("\n"); + + if (apmFound) { + printf("\n*******************************************************************\n"); + printf("This disk appears to contain an Apple-format (APM) partition table!\n"); + printf("It will be destroyed if you continue!\n"); + printf("*******************************************************************\n\n\a"); + } // if +/* if (bsdFound) { + printf("\n*************************************************************************\n"); + printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n" + "continue!\n"); + printf("*************************************************************************\n\n\a"); + } // if */ +} // GPTData::PartitionScan() + // Read GPT data from a disk. int GPTData::LoadPartitions(char* deviceFilename) { int fd, err; int allOK = 1, i; uint64_t firstBlock, lastBlock; + BSDData bsdDisklabel; if ((fd = open(deviceFilename, O_RDONLY)) != -1) { // store disk information.... diskSize = disksize(fd, &err); blockSize = (uint32_t) GetBlockSize(fd); strcpy(device, deviceFilename); - - // Read the MBR - protectiveMBR.ReadMBRData(fd); - - // Load the GPT data, whether or not it's valid - ForceLoadGPTData(fd); + PartitionScan(fd); // Check for partition types & print summary switch (UseWhichPartitions()) { case use_mbr: - XFormPartitions(&protectiveMBR); + XFormPartitions(); + break; + case use_bsd: + bsdDisklabel.ReadBSDData(fd, 0, diskSize - 1); +// bsdDisklabel.DisplayBSDData(); + ClearGPTData(); + protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1) + XFormDisklabel(&bsdDisklabel, 0); break; case use_gpt: break; @@ -197,11 +281,11 @@ int GPTData::LoadPartitions(char* deviceFilename) { firstBlock = mainHeader.backupLBA; // start high lastBlock = 0; // start low for (i = 0; i < mainHeader.numParts; i++) { - if ((partitions[i].firstLBA < firstBlock) && - (partitions[i].firstLBA > 0)) - firstBlock = partitions[i].firstLBA; - if (partitions[i].lastLBA > lastBlock) - lastBlock = partitions[i].lastLBA; + if ((partitions[i].GetFirstLBA() < firstBlock) && + (partitions[i].GetFirstLBA() > 0)) + firstBlock = partitions[i].GetFirstLBA(); + if (partitions[i].GetLastLBA() > lastBlock) + lastBlock = partitions[i].GetLastLBA(); } // for } // if CheckGPTSize(); @@ -340,20 +424,30 @@ WhichToUse GPTData::UseWhichPartitions(void) { if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) { printf("\n\a***************************************************************\n" - "Found invalid GPT and valid MBR; converting MBR to GPT format.\n" + "Found invalid GPT and valid MBR; converting MBR to GPT format.\n" "THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Exit by typing 'q' if\n" "you don't want to convert your MBR partitions to GPT format!\n" "***************************************************************\n\n"); which = use_mbr; } // if + if ((state == gpt_invalid) && bsdFound) { + printf("\n\a**********************************************************************\n" + "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n" + "to GPT format. THIS OPERATON IS POTENTIALLY DESTRUCTIVE! Your first\n" + "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n" + "want to convert your BSD partitions to GPT format!\n" + "**********************************************************************\n\n"); + which = use_bsd; + } // if + if ((state == gpt_valid) && (mbrState == gpt)) { printf("Found valid GPT with protective MBR; using GPT.\n"); which = use_gpt; } // if if ((state == gpt_valid) && (mbrState == hybrid)) { printf("Found valid GPT with hybrid MBR; using GPT.\n"); - printf("\aIf you change GPT partitions' sizes, you may need to re-create the hybrid MBR!\n"); + printf("\aIf you change GPT partitions, you may need to re-create the hybrid MBR!\n"); which = use_gpt; } // if if ((state == gpt_valid) && (mbrState == invalid)) { @@ -445,7 +539,7 @@ int GPTData::GetPartRange(uint32_t *low, uint32_t *high) { *high = 0; if (mainHeader.numParts > 0) { // only try if partition table exists... for (i = 0; i < mainHeader.numParts; i++) { - if (partitions[i].firstLBA != UINT64_C(0)) { // it exists + if (partitions[i].GetFirstLBA() != UINT64_C(0)) { // it exists *high = i; // since we're counting up, set the high value // Set the low value only if it's not yet found... if (*low == (mainHeader.numParts + 1)) *low = i; @@ -481,20 +575,7 @@ void GPTData::DisplayGPTData(void) { BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI)); printf("\nNumber Start (sector) End (sector) Size Code Name\n"); for (i = 0; i < mainHeader.numParts; i++) { - if (partitions[i].firstLBA != 0) { - BytesToSI(blockSize * (partitions[i].lastLBA - partitions[i].firstLBA + 1), - sizeInSI); - printf("%4d %14lu %14lu", i + 1, (unsigned long) partitions[i].firstLBA, - (unsigned long) partitions[i].lastLBA); - printf(" %-10s %04X ", sizeInSI, - typeHelper.GUIDToID(partitions[i].partitionType)); - j = 0; - while ((partitions[i].name[j] != '\0') && (j < 44)) { - printf("%c", partitions[i].name[j]); - j += 2; - } // while - printf("\n"); - } // if + partitions[i].ShowSummary(i, blockSize, sizeInSI); } // for } // GPTData::DisplayGPTData() @@ -514,33 +595,8 @@ void GPTData::ShowDetails(void) { // Show detailed information on the specified partition void GPTData::ShowPartDetails(uint32_t partNum) { - char temp[255]; - int i; - uint64_t size; - - if (partitions[partNum].firstLBA != 0) { - printf("Partition GUID code: %s ", GUIDToStr(partitions[partNum].partitionType, temp)); - printf("(%s)\n", typeHelper.GUIDToName(partitions[partNum].partitionType, temp)); - printf("Partition unique GUID: %s\n", GUIDToStr(partitions[partNum].uniqueGUID, temp)); - - printf("First sector: %llu (at %s)\n", (unsigned long long) - partitions[partNum].firstLBA, - BytesToSI(partitions[partNum].firstLBA * blockSize, temp)); - printf("Last sector: %llu (at %s)\n", (unsigned long long) - partitions[partNum].lastLBA, - BytesToSI(partitions[partNum].lastLBA * blockSize, temp)); - size = (partitions[partNum].lastLBA - partitions[partNum].firstLBA + 1); - printf("Partition size: %llu sectors (%s)\n", (unsigned long long) - size, BytesToSI(size * ((uint64_t) blockSize), temp)); - printf("Attribute flags: %016llx\n", (unsigned long long) - partitions[partNum].attributes); - printf("Partition name: "); - i = 0; - while ((partitions[partNum].name[i] != '\0') && (i < NAME_SIZE)) { - printf("%c", partitions[partNum].name[i]); - i += 2; - } // while - printf("\n"); + if (partitions[partNum].GetFirstLBA() != 0) { + partitions[partNum].ShowDetails(blockSize); } else { printf("Partition #%d does not exist.", (int) (partNum + 1)); } // if @@ -553,7 +609,7 @@ void GPTData::CreatePartition(void) { int partNum, firstFreePart = 0; // Find first free partition... - while (partitions[firstFreePart].firstLBA != 0) { + while (partitions[firstFreePart].GetFirstLBA() != 0) { firstFreePart++; } // while @@ -567,9 +623,9 @@ void GPTData::CreatePartition(void) { mainHeader.numParts, firstFreePart + 1); partNum = GetNumber(firstFreePart + 1, mainHeader.numParts, firstFreePart + 1, prompt) - 1; - if (partitions[partNum].firstLBA != 0) + if (partitions[partNum].GetFirstLBA() != 0) printf("partition %d is in use.\n", partNum + 1); - } while (partitions[partNum].firstLBA != 0); + } while (partitions[partNum].GetFirstLBA() != 0); // Get first block for new partition... sprintf(prompt, "First sector (%llu-%llu, default = %llu): ", firstBlock, @@ -588,14 +644,12 @@ void GPTData::CreatePartition(void) { } while (IsFree(sector) == 0); lastBlock = sector; - partitions[partNum].firstLBA = firstBlock; - partitions[partNum].lastLBA = lastBlock; + partitions[partNum].SetFirstLBA(firstBlock); + partitions[partNum].SetLastLBA(lastBlock); - // rand() is only 32 bits on 32-bit systems, so multiply together to - // fill a 64-bit value. - partitions[partNum].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); - partitions[partNum].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); - ChangeGPTType(&partitions[partNum]); + partitions[partNum].SetUniqueGUID(1); + partitions[partNum].ChangeType(); + partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt)); } else { printf("No free sectors available\n"); } // if/else @@ -610,11 +664,11 @@ void GPTData::DeletePartition(void) { if (GetPartRange(&low, &high) > 0) { sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1); partNum = GetNumber(low + 1, high + 1, low, prompt); - BlankPartition(&partitions[partNum - 1]); + partitions[partNum - 1].BlankPartition(); } else { printf("No partitions\n"); } // if/else -} // GPTData::DeletePartition +} // GPTData::DeletePartition() // Find the first available block after the starting point; returns 0 if // there are no available blocks left @@ -638,9 +692,9 @@ uint64_t GPTData::FindFirstAvailable(uint64_t start) { do { firstMoved = 0; for (i = 0; i < mainHeader.numParts; i++) { - if ((first >= partitions[i].firstLBA) && - (first <= partitions[i].lastLBA)) { // in existing part. - first = partitions[i].lastLBA + 1; + if ((first >= partitions[i].GetFirstLBA()) && + (first <= partitions[i].GetLastLBA())) { // in existing part. + first = partitions[i].GetLastLBA() + 1; firstMoved = 1; } // if } // for @@ -668,9 +722,9 @@ uint64_t GPTData::FindLastAvailable(uint64_t start) { do { lastMoved = 0; for (i = 0; i < mainHeader.numParts; i++) { - if ((last >= partitions[i].firstLBA) && - (last <= partitions[i].lastLBA)) { // in existing part. - last = partitions[i].firstLBA - 1; + if ((last >= partitions[i].GetFirstLBA()) && + (last <= partitions[i].GetLastLBA())) { // in existing part. + last = partitions[i].GetFirstLBA() - 1; lastMoved = 1; } // if } // for @@ -687,9 +741,9 @@ uint64_t GPTData::FindLastInFree(uint64_t start) { nearestStart = mainHeader.lastUsableLBA; for (i = 0; i < mainHeader.numParts; i++) { - if ((nearestStart > partitions[i].firstLBA) && - (partitions[i].firstLBA > start)) { - nearestStart = partitions[i].firstLBA - 1; + if ((nearestStart > partitions[i].GetFirstLBA()) && + (partitions[i].GetFirstLBA() > start)) { + nearestStart = partitions[i].GetFirstLBA() - 1; } // if } // for return (nearestStart); @@ -701,8 +755,8 @@ int GPTData::IsFree(uint64_t sector) { uint32_t i; for (i = 0; i < mainHeader.numParts; i++) { - if ((sector >= partitions[i].firstLBA) && - (sector <= partitions[i].lastLBA)) { + if ((sector >= partitions[i].GetFirstLBA()) && + (sector <= partitions[i].GetLastLBA())) { isFree = 0; } // if } // for @@ -713,10 +767,11 @@ int GPTData::IsFree(uint64_t sector) { return (isFree); } // GPTData::IsFree() -int GPTData::XFormPartitions(MBRData* origParts) { - int i, j; - int numToConvert; +int GPTData::XFormPartitions(void) { + int i, numToConvert; uint8_t origType; + struct newGUID; + char name[NAME_SIZE]; // Clear out old data & prepare basics.... ClearGPTData(); @@ -728,24 +783,12 @@ int GPTData::XFormPartitions(MBRData* origParts) { numToConvert = mainHeader.numParts; for (i = 0; i < numToConvert; i++) { - origType = origParts->GetType(i); - - // don't convert extended, hybrid protective, or null (non-existent) partitions + origType = protectiveMBR.GetType(i); + // don't waste CPU time trying to convert extended, hybrid protective, or + // null (non-existent) partitions if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && - (origType != 0x00) && (origType != 0xEE)) { - partitions[i].firstLBA = (uint64_t) origParts->GetFirstSector(i); - partitions[i].lastLBA = partitions[i].firstLBA + (uint64_t) - origParts->GetLength(i) - 1; - partitions[i].partitionType = typeHelper.IDToGUID(((uint16_t) origType) * 0x0100); - - // Create random unique GUIDs for the partitions - // rand() is only 32 bits, so multiply together to fill a 64-bit value - partitions[i].uniqueGUID.data1 = (uint64_t) rand() * (uint64_t) rand(); - partitions[i].uniqueGUID.data2 = (uint64_t) rand() * (uint64_t) rand(); - partitions[i].attributes = 0; - for (j = 0; j < NAME_SIZE; j++) - partitions[i].name[j] = '\0'; - } // if + (origType != 0x00) && (origType != 0xEE)) + partitions[i] = protectiveMBR.AsGPT(i); } // for // Convert MBR into protective MBR @@ -756,18 +799,87 @@ int GPTData::XFormPartitions(MBRData* origParts) { mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; return (1); -} // XFormPartitions() +} // GPTData::XFormPartitions() + +// Transforms BSD disklable on the specified partition (numbered from 0). +// If an invalid partition number is given, the program prompts for one. +// Returns the number of new partitions created. +int GPTData::XFormDisklabel(int i) { + uint32_t low, high, partNum, startPart; + uint16_t hexCode; + int goOn = 1, numDone = 0; + BSDData disklabel; + + if (GetPartRange(&low, &high) != 0) { + if ((i < low) || (i > high)) + partNum = GetPartNum(); + else + partNum = (uint32_t) i; + + // Find the partition after the last used one + startPart = high + 1; + + // Now see if the specified partition has a BSD type code.... + hexCode = partitions[partNum].GetHexType(); + if ((hexCode != 0xa500) && (hexCode != 0xa900)) { + printf("Specified partition doesn't have a disklabel partition type " + "code.\nContinue anyway?"); + goOn = (GetYN() == 'Y'); + } // if + + // If all is OK, read the disklabel and convert it. + if (goOn) { + goOn = disklabel.ReadBSDData(device, partitions[partNum].GetFirstLBA(), + partitions[partNum].GetLastLBA()); + if ((goOn) && (disklabel.IsDisklabel())) { + numDone = XFormDisklabel(&disklabel, startPart); + if (numDone == 1) + printf("Converted %d BSD partition.\n", numDone); + else + printf("Converted %d BSD partitions.\n", numDone); + } else { + printf("Unable to convert partitions! Unrecognized BSD disklabel.\n"); + } // if/else + } // if + if (numDone > 0) { // converted partitions; delete carrier + partitions[partNum].BlankPartition(); + } // if + } else { + printf("No partitions\n"); + } // if/else + return numDone; +} // GPTData::XFormDisklable(int i) + +// Transform the partitions on an already-loaded BSD disklabel... +int GPTData::XFormDisklabel(BSDData* disklabel, int startPart) { + int i, numDone = 0; + + if ((disklabel->IsDisklabel()) && (startPart >= 0) && + (startPart < mainHeader.numParts)) { + for (i = 0; i < disklabel->GetNumParts(); i++) { + partitions[i + startPart] = disklabel->AsGPT(i); + if (partitions[i + startPart].GetFirstLBA() != UINT64_C(0)) + numDone++; + } // for + } // if + + // Record that all original CRCs were OK so as not to raise flags + // when doing a disk verification + mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1; + + return numDone; +} // GPTData::XFormDisklabel(BSDData* disklabel) // Sort the GPT entries, eliminating gaps and making for a logical // ordering. Relies on QuickSortGPT() for the bulk of the work void GPTData::SortGPT(void) { int i, lastPart = 0; - struct GPTPartition temp; + GPTPart temp; // First, find the last partition with data, so as not to // spend needless time sorting empty entries.... - for (i = 0; i < GPT_SIZE; i++) { - if (partitions[i].firstLBA > 0) + for (i = 0; i < mainHeader.numParts; i++) { + if (partitions[i].GetFirstLBA() > 0) lastPart = i; } // for @@ -775,7 +887,7 @@ void GPTData::SortGPT(void) { // in the Quicksort function.... i = 0; while (i < lastPart) { - if (partitions[i].firstLBA == 0) { + if (partitions[i].GetFirstLBA() == 0) { temp = partitions[i]; partitions[i] = partitions[lastPart]; partitions[lastPart] = temp; @@ -788,56 +900,12 @@ void GPTData::SortGPT(void) { QuickSortGPT(partitions, 0, lastPart); } // GPTData::SortGPT() -// Recursive quick sort algorithm for GPT partitions. Note that if there -// are any empties in the specified range, they'll be sorted to the -// start, resulting in a sorted set of partitions that begins with -// partition 2, 3, or higher. -void QuickSortGPT(struct GPTPartition* partitions, int start, int finish) { - uint64_t starterValue; // starting location of median partition - int left, right; - struct GPTPartition temp; - - left = start; - right = finish; - starterValue = partitions[(start + finish) / 2].firstLBA; - do { - while (partitions[left].firstLBA < starterValue) - left++; - while (partitions[right].firstLBA > starterValue) - right--; - if (left <= right) { - temp = partitions[left]; - partitions[left] = partitions[right]; - partitions[right] = temp; - left++; - right--; - } // if - } while (left <= right); - if (start < right) QuickSortGPT(partitions, start, right); - if (finish > left) QuickSortGPT(partitions, left, finish); -} // QuickSortGPT() - -// Blank (delete) a single partition -void BlankPartition(struct GPTPartition* partition) { - int j; - - partition->uniqueGUID.data1 = 0; - partition->uniqueGUID.data2 = 0; - partition->partitionType.data1 = 0; - partition->partitionType.data2 = 0; - partition->firstLBA = 0; - partition->lastLBA = 0; - partition->attributes = 0; - for (j = 0; j < NAME_SIZE; j++) - partition->name[j] = '\0'; -} // BlankPartition - // Blank the partition array void GPTData::BlankPartitions(void) { uint32_t i; for (i = 0; i < mainHeader.numParts; i++) { - BlankPartition(&partitions[i]); + partitions[i].BlankPartition(); } // for } // GPTData::BlankPartitions() @@ -877,49 +945,16 @@ int GPTData::ClearGPTData(void) { // Blank out the partitions array.... BlankPartitions(); + + // Flag all CRCs as being OK.... + mainCrcOk = 1; + secondCrcOk = 1; + mainPartsCrcOk = 1; + secondPartsCrcOk = 1; + return (goOn); } // GPTData::ClearGPTData() -// Returns 1 if the two partitions overlap, 0 if they don't -int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second) { - int theyDo = 0; - - // Don't bother checking unless these are defined (both start and end points - // are 0 for undefined partitions, so just check the start points) - if ((first->firstLBA != 0) && (second->firstLBA != 0)) { - if ((first->firstLBA < second->lastLBA) && (first->lastLBA >= second->firstLBA)) - theyDo = 1; - if ((second->firstLBA < first->lastLBA) && (second->lastLBA >= first->firstLBA)) - theyDo = 1; - } // if - return (theyDo); -} // Overlap() - -// Change the type code on the specified partition. -// Note: The GPT CRCs must be recomputed after calling this function! -void ChangeGPTType(struct GPTPartition* part) { - char typeName[255], line[255]; - uint16_t typeNum = 0xFFFF; - PartTypes typeHelper; - GUIDData newType; - - printf("Current type is '%s'\n", typeHelper.GUIDToName(part->partitionType, typeName)); - while ((!typeHelper.Valid(typeNum)) && (typeNum != 0)) { - printf("Hex code (L to show codes, 0 to enter raw code): "); - fgets(line, 255, stdin); - sscanf(line, "%x", &typeNum); - if (line[0] == 'L') - typeHelper.ShowTypes(); - } // while - if (typeNum != 0) // user entered a code, so convert it - newType = typeHelper.IDToGUID(typeNum); - else // user wants to enter the GUID directly, so do that - newType = GetGUID(); - part->partitionType = newType; - printf("Changed system type of partition to '%s'\n", - typeHelper.GUIDToName(part->partitionType, typeName)); -} // ChangeGPTType() - // Prompt user for a partition number, then change its type code // using ChangeGPTType(struct GPTPartition*) function. void GPTData::ChangePartType(void) { @@ -928,7 +963,7 @@ void GPTData::ChangePartType(void) { if (GetPartRange(&low, &high) > 0) { partNum = GetPartNum(); - ChangeGPTType(&partitions[partNum]); + partitions[partNum].ChangeType(); } else { printf("No partitions\n"); } // if/else @@ -947,51 +982,20 @@ uint32_t GPTData::GetPartNum(void) { return (partNum - 1); } // GPTData::GetPartNum() -// Prompt user for attributes to change on the specified partition -// and change them. void GPTData::SetAttributes(uint32_t partNum) { Attributes theAttr; - theAttr.SetAttributes(partitions[partNum].attributes); + theAttr.SetAttributes(partitions[partNum].GetAttributes()); theAttr.DisplayAttributes(); theAttr.ChangeAttributes(); - partitions[partNum].attributes = theAttr.GetAttributes(); + partitions[partNum].SetAttributes(theAttr.GetAttributes()); } // GPTData::SetAttributes() -// Set the name for a partition to theName, or prompt for a name if -// theName is a NULL pointer. Note that theName is a standard C-style -// string, although the GUID partition definition requires a UTF-16LE -// string. This function creates a simple-minded copy for this. void GPTData::SetName(uint32_t partNum, char* theName) { - char newName[NAME_SIZE]; // New name - int i; - - // Blank out new name string, just to be on the safe side.... - for (i = 0; i < NAME_SIZE; i++) - newName[i] = '\0'; - - if (theName == NULL) { // No name specified, so get one from the user - printf("Enter name: "); - fgets(newName, NAME_SIZE / 2, stdin); - - // Input is likely to include a newline, so remove it.... - i = strlen(newName); - if (newName[i - 1] == '\n') - newName[i - 1] = '\0'; - } else { - strcpy(newName, theName); - } // if - - // Copy the C-style ASCII string from newName into a form that the GPT - // table will accept.... - for (i = 0; i < NAME_SIZE; i++) { - if ((i % 2) == 0) { - partitions[partNum].name[i] = newName[(i / 2)]; - } else { - partitions[partNum].name[i] = '\0'; - } // if/else - } // for -} // GPTData::SetName() + if ((partNum >= 0) && (partNum < mainHeader.numParts)) + if (partitions[partNum].GetFirstLBA() > 0) + partitions[partNum].SetName((unsigned char*) theName); +} // GPTData::SetName // Set the disk GUID to the specified value. Note that the header CRCs must // be recomputed after calling this function. @@ -1007,8 +1011,8 @@ int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) { int retval = 0; if (pn < mainHeader.numParts) { - if (partitions[pn].firstLBA != UINT64_C(0)) { - partitions[pn].uniqueGUID = theGUID; + if (partitions[pn].GetFirstLBA() != UINT64_C(0)) { + partitions[pn].SetUniqueGUID(theGUID); retval = 1; } // if } // if @@ -1024,8 +1028,8 @@ int GPTData::CheckHeaderValidity(void) { if (mainHeader.signature != GPT_SIGNATURE) { valid -= 1; - printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", - (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE); +// printf("Main GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", +// (unsigned long long) mainHeader.signature, (unsigned long long) GPT_SIGNATURE); } else if ((mainHeader.revision != 0x00010000) && valid) { valid -= 1; printf("Unsupported GPT version in main header; read 0x%08lX, should be\n0x%08lX\n", @@ -1034,8 +1038,8 @@ int GPTData::CheckHeaderValidity(void) { if (secondHeader.signature != GPT_SIGNATURE) { valid -= 2; - printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", - (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE); +// printf("Secondary GPT signature invalid; read 0x%016llX, should be\n0x%016llX\n", +// (unsigned long long) secondHeader.signature, (unsigned long long) GPT_SIGNATURE); } else if ((secondHeader.revision != 0x00010000) && valid) { valid -= 2; printf("Unsupported GPT version in backup header; read 0x%08lX, should be\n0x%08lX\n", @@ -1046,9 +1050,7 @@ int GPTData::CheckHeaderValidity(void) { if ((protectiveMBR.GetValidity() == invalid) && (((mainHeader.signature << 32) == APM_SIGNATURE1) || (mainHeader.signature << 32) == APM_SIGNATURE2)) { - printf("\n*******************************************************************\n"); - printf("This disk appears to contain an Apple-format (APM) partition table!\n"); - printf("*******************************************************************\n\n\a"); + apmFound = 1; // Will display warning message later } // if return valid; @@ -1064,7 +1066,7 @@ int GPTData::CheckHeaderCRC(struct GPTHeader* header) { // computation to be valid oldCRC = header->headerCRC; if (IsLittleEndian() == 0) - ReverseBytes((char*) &oldCRC, 4); + ReverseBytes(&oldCRC, 4); header->headerCRC = UINT32_C(0); // Initialize CRC functions... @@ -1091,13 +1093,13 @@ void GPTData::RecomputeCRCs(void) { // Compute CRC of partition tables & store in main and secondary headers trueNumParts = mainHeader.numParts; if (littleEndian == 0) - ReverseBytes((char*) &trueNumParts, 4); // unreverse this key piece of data.... + ReverseBytes(&trueNumParts, 4); // unreverse this key piece of data.... crc = chksum_crc32((unsigned char*) partitions, trueNumParts * GPT_SIZE); mainHeader.partitionEntriesCRC = crc; secondHeader.partitionEntriesCRC = crc; if (littleEndian == 0) { - ReverseBytes((char*) &mainHeader.partitionEntriesCRC, 4); - ReverseBytes((char*) &secondHeader.partitionEntriesCRC, 4); + ReverseBytes(&mainHeader.partitionEntriesCRC, 4); + ReverseBytes(&secondHeader.partitionEntriesCRC, 4); } // if // Zero out GPT tables' own CRCs (required for correct computation) @@ -1107,11 +1109,11 @@ void GPTData::RecomputeCRCs(void) { // Compute & store CRCs of main & secondary headers... crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE); if (littleEndian == 0) - ReverseBytes((char*) &crc, 4); + ReverseBytes(&crc, 4); mainHeader.headerCRC = crc; crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE); if (littleEndian == 0) - ReverseBytes((char*) &crc, 4); + ReverseBytes(&crc, 4); secondHeader.headerCRC = crc; } // GPTData::RecomputeCRCs() @@ -1213,19 +1215,22 @@ int GPTData::Verify(void) { // Check for overlapping partitions.... for (i = 1; i < mainHeader.numParts; i++) { for (j = 0; j < i; j++) { - if (TheyOverlap(&partitions[i], &partitions[j])) { + if (partitions[i].DoTheyOverlap(&partitions[j])) { problems++; printf("\nProblem: partitions %d and %d overlap:\n", i + 1, j + 1); printf(" Partition %d: %llu to %llu\n", i, - (unsigned long long) partitions[i].firstLBA, - (unsigned long long) partitions[i].lastLBA); + (unsigned long long) partitions[i].GetFirstLBA(), + (unsigned long long) partitions[i].GetLastLBA()); printf(" Partition %d: %llu to %llu\n", j, - (unsigned long long) partitions[j].firstLBA, - (unsigned long long) partitions[j].lastLBA); + (unsigned long long) partitions[j].GetFirstLBA(), + (unsigned long long) partitions[j].GetLastLBA()); } // if } // for j... } // for i... + // Verify that partitions don't run into GPT data areas.... + problems += CheckGPTSize(); + // Now compute available space, but only if no problems found, since // problems could affect the results if (problems == 0) { @@ -1382,24 +1387,24 @@ void GPTData::MakeHybrid(void) { j = partNums[i] - 1; printf("\nCreating entry for partition #%d\n", j + 1); if ((j >= 0) && (j < mainHeader.numParts)) { - if (partitions[j].lastLBA < UINT32_MAX) { + if (partitions[j].GetLastLBA() < UINT32_MAX) { do { printf("Enter an MBR hex code (default %02X): ", - typeHelper.GUIDToID(partitions[j].partitionType) / 256); + typeHelper.GUIDToID(partitions[j].GetType()) / 256); fgets(line, 255, stdin); sscanf(line, "%x", &typeCode); if (line[0] == '\n') - typeCode = typeHelper.GUIDToID(partitions[j].partitionType) / 256; + typeCode = partitions[j].GetHexType() / 256; } while ((typeCode <= 0) || (typeCode > 255)); printf("Set the bootable flag? "); bootable = (GetYN() == 'Y'); - length = partitions[j].lastLBA - partitions[j].firstLBA + UINT64_C(1); + length = partitions[j].GetLengthLBA(); if (eeFirst == 'Y') mbrNum = i + 1; else mbrNum = i; - protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].firstLBA, - (uint32_t) length, typeCode, bootable); + protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(), + (uint32_t) length, typeCode, bootable); } else { // partition out of range printf("Partition %d ends beyond the 2TiB limit of MBR partitions; omitting it.\n", j + 1); @@ -1472,7 +1477,7 @@ int GPTData::SaveGPTData(void) { // First do some final sanity checks.... // Is there enough space to hold the GPT headers and partition tables, // given the partition sizes? - if (CheckGPTSize() == 0) { + if (CheckGPTSize() > 0) { allOK = 0; } // if @@ -1488,14 +1493,14 @@ int GPTData::SaveGPTData(void) { // Check for overlapping partitions.... for (i = 1; i < mainHeader.numParts; i++) { for (j = 0; j < i; j++) { - if (TheyOverlap(&partitions[i], &partitions[j])) { + if (partitions[i].DoTheyOverlap(&partitions[j])) { fprintf(stderr, "\Error: partitions %d and %d overlap:\n", i + 1, j + 1); fprintf(stderr, " Partition %d: %llu to %llu\n", i, - (unsigned long long) partitions[i].firstLBA, - (unsigned long long) partitions[i].lastLBA); + (unsigned long long) partitions[i].GetFirstLBA(), + (unsigned long long) partitions[i].GetLastLBA()); fprintf(stderr, " Partition %d: %llu to %llu\n", j, - (unsigned long long) partitions[j].firstLBA, - (unsigned long long) partitions[j].lastLBA); + (unsigned long long) partitions[j].GetFirstLBA(), + (unsigned long long) partitions[j].GetLastLBA()); fprintf(stderr, "Aborting write operation!\n"); allOK = 0; } // if @@ -1582,12 +1587,18 @@ int GPTData::SaveGPTData(void) { * it definitely will get things on disk though: * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */ i = ioctl(fd, DKIOCSYNCHRONIZECACHE); +#else +#ifdef __FreeBSD__ + sleep(2); + i = ioctl(fd, DIOCGFLUSH); + printf("Warning: The kernel is still using the old partition table.\n"); #else sleep(2); i = ioctl(fd, BLKRRPART); if (i) printf("Warning: The kernel is still using the old partition table.\n" "The new table will be used at the next reboot.\n"); +#endif #endif } // if @@ -1691,7 +1702,7 @@ int GPTData::LoadGPTBackup(char* filename) { littleEndian = 0; // Let the MBRData class load the saved MBR... - protectiveMBR.ReadMBRData(fd); + protectiveMBR.ReadMBRData(fd, 0); // 0 = don't check block size // Load the main GPT header, check its vaility, and set the GPT // size based on the data @@ -1815,22 +1826,22 @@ int GPTData::DestroyGPT(void) { } // GPTData::DestroyGPT() void GPTData::ReverseHeaderBytes(struct GPTHeader* header) { - ReverseBytes((char*) &header->signature, 8); - ReverseBytes((char*) &header->revision, 4); - ReverseBytes((char*) &header->headerSize, 4); - ReverseBytes((char*) &header->headerCRC, 4); - ReverseBytes((char*) &header->reserved, 4); - ReverseBytes((char*) &header->currentLBA, 8); - ReverseBytes((char*) &header->backupLBA, 8); - ReverseBytes((char*) &header->firstUsableLBA, 8); - ReverseBytes((char*) &header->lastUsableLBA, 8); - ReverseBytes((char*) &header->partitionEntriesLBA, 8); - ReverseBytes((char*) &header->numParts, 4); - ReverseBytes((char*) &header->sizeOfPartitionEntries, 4); - ReverseBytes((char*) &header->partitionEntriesCRC, 4); - ReverseBytes((char*) header->reserved2, GPT_RESERVED); - ReverseBytes((char*) &header->diskGUID.data1, 8); - ReverseBytes((char*) &header->diskGUID.data2, 8); + ReverseBytes(&header->signature, 8); + ReverseBytes(&header->revision, 4); + ReverseBytes(&header->headerSize, 4); + ReverseBytes(&header->headerCRC, 4); + ReverseBytes(&header->reserved, 4); + ReverseBytes(&header->currentLBA, 8); + ReverseBytes(&header->backupLBA, 8); + ReverseBytes(&header->firstUsableLBA, 8); + ReverseBytes(&header->lastUsableLBA, 8); + ReverseBytes(&header->partitionEntriesLBA, 8); + ReverseBytes(&header->numParts, 4); + ReverseBytes(&header->sizeOfPartitionEntries, 4); + ReverseBytes(&header->partitionEntriesCRC, 4); + ReverseBytes(&header->reserved2, GPT_RESERVED); + ReverseBytes(&header->diskGUID.data1, 8); + ReverseBytes(&header->diskGUID.data2, 8); } // GPTData::ReverseHeaderBytes() // IMPORTANT NOTE: This function requires non-reversed mainHeader @@ -1846,13 +1857,7 @@ void GPTData::ReversePartitionBytes() { "data corruption or a misplaced call to this function.\n"); } // if signature mismatch.... for (i = 0; i < mainHeader.numParts; i++) { - ReverseBytes((char*) &partitions[i].partitionType.data1, 8); - ReverseBytes((char*) &partitions[i].partitionType.data2, 8); - ReverseBytes((char*) &partitions[i].uniqueGUID.data1, 8); - ReverseBytes((char*) &partitions[i].uniqueGUID.data2, 8); - ReverseBytes((char*) &partitions[i].firstLBA, 8); - ReverseBytes((char*) &partitions[i].lastLBA, 8); - ReverseBytes((char*) &partitions[i].attributes, 8); + partitions[i].ReversePartBytes(); } // for } // GPTData::ReversePartitionBytes() @@ -1886,18 +1891,22 @@ int SizesOK(void) { allOK = 0; } // if if (sizeof(struct MBRRecord) != 16) { - fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(uint32_t)); + fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord)); allOK = 0; } // if if (sizeof(struct EBRRecord) != 512) { - fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t)); + fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord)); allOK = 0; } // if if (sizeof(struct GPTHeader) != 512) { - fprintf(stderr, "GPTHeader is %d bytes, should be 512 bytes; aborting!\n", sizeof(uint32_t)); + fprintf(stderr, "GPTHeader is %d 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)); allOK = 0; } // if - // Determine endianness; set allOK = 0 if running on big-endian hardware +// Determine endianness; set allOK = 0 if running on big-endian hardware if (IsLittleEndian() == 0) { fprintf(stderr, "\aRunning on big-endian hardware. Big-endian support is new and poorly" " tested!\nBeware!\n"); diff --git a/gpt.h b/gpt.h index d5057f6203190607aa9c817f07c705dc76cff8bc..2f405d5ec113c2c7adadebca6249c6831b9145dc 100644 --- a/gpt.h +++ b/gpt.h @@ -1,34 +1,21 @@ /* gpt.h -- GPT and data structure definitions, types, and functions */ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #include <stdint.h> #include <sys/types.h> #include <sys/ioctl.h> #include "support.h" #include "parttypes.h" #include "mbr.h" +#include "bsd.h" +#include "gptpart.h" #ifndef __GPTSTRUCTS #define __GPTSTRUCTS -#define GPT_SIGNATURE UINT64_C(0x5452415020494645) -// Signatures for Apple (APM) disks, multiplied by 0x100000000 -#define APM_SIGNATURE1 UINT64_C(0x00004D5000000000) -#define APM_SIGNATURE2 UINT64_C(0x0000535400000000) - -/* Number and size of GPT entries... */ -#define NUM_GPT_ENTRIES 128 -#define GPT_SIZE 128 -/* Offset, in 512-byte sectors, for GPT table and partition data. - Note this is above two multiplied together, divided by 512, with 2 - added -#define GPT_OFFSET (((NUM_GPT_ENTRIES * GPT_SIZE) / SECTOR_SIZE) + 2) -*/ - -#define HEADER_SIZE 92 - -#define GPT_RESERVED 420 -#define NAME_SIZE 72 using namespace std; @@ -42,7 +29,7 @@ using namespace std; enum GPTValidity {gpt_valid, gpt_corrupt, gpt_invalid}; // Which set of partition data to use -enum WhichToUse {use_gpt, use_mbr, use_new}; +enum WhichToUse {use_gpt, use_mbr, use_bsd, use_new}; // Header (first 512 bytes) of GPT table struct GPTHeader { @@ -63,20 +50,11 @@ struct GPTHeader { unsigned char reserved2[GPT_RESERVED]; }; // struct GPTHeader -struct GPTPartition { - struct GUIDData partitionType; - struct GUIDData uniqueGUID; - uint64_t firstLBA; - uint64_t lastLBA; - uint64_t attributes; - unsigned char name[NAME_SIZE]; -}; // struct GPTPartition - // Data in GPT format class GPTData { protected: struct GPTHeader mainHeader; - struct GPTPartition *partitions; + struct GPTPart *partitions; struct GPTHeader secondHeader; MBRData protectiveMBR; char device[256]; // device filename @@ -87,6 +65,8 @@ protected: int secondCrcOk; int mainPartsCrcOk; int secondPartsCrcOk; + int apmFound; // set to 1 if APM detected + int bsdFound; // set to 1 if BSD disklabel detected in MBR // uint32_t units; // display units, in multiples of sectors PartTypes typeHelper; public: @@ -95,6 +75,9 @@ public: ~GPTData(void); int SetGPTSize(uint32_t numEntries); int CheckGPTSize(void); + void ShowAPMState(void); + void ShowGPTState(void); + void PartitionScan(int fd); int LoadPartitions(char* deviceFilename); int ForceLoadGPTData(int fd); int LoadMainTable(void); @@ -112,7 +95,9 @@ public: uint64_t FindLastAvailable(uint64_t start); uint64_t FindLastInFree(uint64_t start); int IsFree(uint64_t sector); - int XFormPartitions(MBRData* origParts); + int XFormPartitions(void); + int XFormDisklabel(int OnGptPart = -1); + int XFormDisklabel(BSDData* disklabel, int startPart); void SortGPT(void); int ClearGPTData(void); void ChangePartType(void); @@ -135,6 +120,8 @@ public: int SaveGPTBackup(char* filename); int LoadGPTBackup(char* filename); int DestroyGPT(void); // Returns 1 if user proceeds + + // Endianness functions void ReverseHeaderBytes(struct GPTHeader* header); // for endianness void ReversePartitionBytes(); // for endianness @@ -150,8 +137,6 @@ public: // Function prototypes.... void BlankPartition(struct GPTPartition* partition); -//int XFormType(uint8_t oldType, struct GUIDData* newType, int partNum); -void QuickSortGPT(struct GPTPartition* partitions, int start, int finish); int TheyOverlap(struct GPTPartition* first, struct GPTPartition* second); void ChangeGPTType(struct GPTPartition* part); int SizesOK(void); diff --git a/mbr.cc b/mbr.cc index bf76d2c243bab0ef7eaf89a81b1889583bfa80f1..b4bc6f42903b290766926f81b1ab682a2beff74a 100644 --- a/mbr.cc +++ b/mbr.cc @@ -3,6 +3,9 @@ /* By Rod Smith, January to February, 2009 */ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS @@ -118,9 +121,10 @@ int MBRData::ReadMBRData(char* deviceFilename) { } // MBRData::ReadMBRData(char* deviceFilename) // Read data from MBR. -void MBRData::ReadMBRData(int fd) { - int allOK = 1, i, maxLogicals = 0; +void MBRData::ReadMBRData(int fd, int checkBlockSize) { + int allOK = 1, i, j, maxLogicals = 0; int err; + TempMBR tempMBR; // Clear logical partition array for (i = 0; i < NUM_LOGICALS; i++) { @@ -136,39 +140,51 @@ void MBRData::ReadMBRData(int fd) { logicals[i].lengthLBA = UINT32_C(0); } // for - read(fd, code, 440); - read(fd, &diskSignature, 4); - read(fd, &nulls, 2); - read(fd, partitions, 64); - read(fd, &MBRSignature, 2); + err = lseek64(fd, 0, SEEK_SET); + err = read(fd, &tempMBR, 512); + for (i = 0; i < 440; i++) + code[i] = tempMBR.code[i]; + diskSignature = tempMBR.diskSignature; + nulls = tempMBR.nulls; + for (i = 0; i < 4; i++) { + partitions[i].status = tempMBR.partitions[i].status; + partitions[i].partitionType = tempMBR.partitions[i].partitionType; + partitions[i].firstLBA = tempMBR.partitions[i].firstLBA; + partitions[i].lengthLBA = tempMBR.partitions[i].lengthLBA; + for (j = 0; j < 3; j++) { + partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j]; + partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j]; + } // for j... + } // for i... + MBRSignature = tempMBR.MBRSignature; // Reverse the byte order, if necessary if (IsLittleEndian() == 0) { - ReverseBytes((char*) &diskSignature, 4); - ReverseBytes((char*) &nulls, 2); - ReverseBytes((char*) &MBRSignature, 2); + ReverseBytes(&diskSignature, 4); + ReverseBytes(&nulls, 2); + ReverseBytes(&MBRSignature, 2); for (i = 0; i < 4; i++) { - ReverseBytes((char*) &partitions[i].firstLBA, 4); - ReverseBytes((char*) &partitions[i].lengthLBA, 4); + ReverseBytes(&partitions[i].firstLBA, 4); + ReverseBytes(&partitions[i].lengthLBA, 4); } // for } // if if (MBRSignature != MBR_SIGNATURE) { allOK = 0; state = invalid; - fprintf(stderr, "MBR signature invalid; read 0x%04X, but should be 0x%04X\n", - (unsigned int) MBRSignature, (unsigned int) MBR_SIGNATURE); - } /* if */ + } // if // Find disk size diskSize = disksize(fd, &err); // Find block size - if ((blockSize = GetBlockSize(fd)) == -1) { - blockSize = SECTOR_SIZE; - printf("Unable to determine sector size; assuming %lu bytes!\n", - (unsigned long) SECTOR_SIZE); - } // if + if (checkBlockSize) { + if ((blockSize = GetBlockSize(fd)) == -1) { + blockSize = SECTOR_SIZE; + printf("Unable to determine sector size; assuming %lu bytes!\n", + (unsigned long) SECTOR_SIZE); + } // if + } // if (checkBlockSize) // Load logical partition data, if any is found.... if (allOK) { @@ -242,33 +258,55 @@ int MBRData::WriteMBRData(void) { // Save the MBR data to a file. Note that this function writes ONLY the // MBR data, not the logical partitions (if any are defined). void MBRData::WriteMBRData(int fd) { - int i; + int i, j; + TempMBR tempMBR; // Reverse the byte order, if necessary if (IsLittleEndian() == 0) { - ReverseBytes((char*) &diskSignature, 4); - ReverseBytes((char*) &nulls, 2); - ReverseBytes((char*) &MBRSignature, 2); + ReverseBytes(&diskSignature, 4); + ReverseBytes(&nulls, 2); + ReverseBytes(&MBRSignature, 2); for (i = 0; i < 4; i++) { - ReverseBytes((char*) &partitions[i].firstLBA, 4); - ReverseBytes((char*) &partitions[i].lengthLBA, 4); + ReverseBytes(&partitions[i].firstLBA, 4); + ReverseBytes(&partitions[i].lengthLBA, 4); } // for } // if - write(fd, code, 440); + // Copy MBR data to a 512-byte data structure for writing, to + // work around a FreeBSD limitation.... + for (i = 0; i < 440; i++) + tempMBR.code[i] = code[i]; + tempMBR.diskSignature = diskSignature; + tempMBR.nulls = nulls; + tempMBR.MBRSignature = MBRSignature; + for (i = 0; i < 4; i++) { + tempMBR.partitions[i].status = partitions[i].status; + tempMBR.partitions[i].partitionType = partitions[i].partitionType; + tempMBR.partitions[i].firstLBA = partitions[i].firstLBA; + tempMBR.partitions[i].lengthLBA = partitions[i].lengthLBA; + for (j = 0; j < 3; j++) { + tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j]; + tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j]; + } // for j... + } // for i... + + // Now write that data structure... + write(fd, &tempMBR, 512); + +/* write(fd, code, 440); write(fd, &diskSignature, 4); write(fd, &nulls, 2); write(fd, partitions, 64); - write(fd, &MBRSignature, 2); + write(fd, &MBRSignature, 2); */ - // Reverse the byte order, if necessary + // Reverse the byte order back, if necessary if (IsLittleEndian() == 0) { - ReverseBytes((char*) &diskSignature, 4); - ReverseBytes((char*) &nulls, 2); - ReverseBytes((char*) &MBRSignature, 2); + ReverseBytes(&diskSignature, 4); + ReverseBytes(&nulls, 2); + ReverseBytes(&MBRSignature, 2); for (i = 0; i < 4; i++) { - ReverseBytes((char*) &partitions[i].firstLBA, 4); - ReverseBytes((char*) &partitions[i].lengthLBA, 4); + ReverseBytes(&partitions[i].firstLBA, 4); + ReverseBytes(&partitions[i].lengthLBA, 4); } // for }// if } // MBRData::WriteMBRData(int fd) @@ -293,11 +331,11 @@ int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart, (unsigned long) offset); partNum = -1; } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data.... - ReverseBytes((char*) &ebr.MBRSignature, 2); - ReverseBytes((char*) &ebr.partitions[0].firstLBA, 4); - ReverseBytes((char*) &ebr.partitions[0].lengthLBA, 4); - ReverseBytes((char*) &ebr.partitions[1].firstLBA, 4); - ReverseBytes((char*) &ebr.partitions[1].lengthLBA, 4); + ReverseBytes(&ebr.MBRSignature, 2); + ReverseBytes(&ebr.partitions[0].firstLBA, 4); + ReverseBytes(&ebr.partitions[0].lengthLBA, 4); + ReverseBytes(&ebr.partitions[1].firstLBA, 4); + ReverseBytes(&ebr.partitions[1].lengthLBA, 4); } // if/else/if if (ebr.MBRSignature != MBR_SIGNATURE) { @@ -356,14 +394,19 @@ void MBRData::DisplayMBRData(void) { BytesToSI(diskSize * (uint64_t) blockSize, tempStr)); } // MBRData::DisplayMBRData() -// Create a protective MBR -void MBRData::MakeProtectiveMBR(void) { +// Create a protective MBR. Clears the boot loader area if clearBoot > 0. +void MBRData::MakeProtectiveMBR(int clearBoot) { int i; // Initialize variables nulls = 0; MBRSignature = MBR_SIGNATURE; + if (clearBoot > 0) { + for (i = 0; i < 440; i++) + code[i] = (uint8_t) 0; + } // if + partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable // Write CHS data. This maxes out the use of the disk, as much as @@ -467,23 +510,24 @@ struct MBRRecord* MBRData::GetPartition(int i) { return thePart; } // GetPartition() -// Displays the state, as a word, on stdout. Used for debugging +// Displays the state, as a word, on stdout. Used for debugging & to +// tell the user about the MBR state when the program launches.... void MBRData::ShowState(void) { switch (state) { case invalid: - printf("invalid"); + printf(" MBR: not present\n"); break; case gpt: - printf("gpt"); + printf(" MBR: protective\n"); break; case hybrid: - printf("hybrid"); + printf(" MBR: hybrid\n"); break; case mbr: - printf("mbr"); + printf(" MBR: MBR only\n"); break; default: - printf("unknown -- bug!"); + printf("\a MBR: unknown -- bug!\n"); break; } // switch } // MBRData::ShowState() @@ -602,3 +646,36 @@ uint32_t MBRData::GetLength(int i) { retval = UINT32_C(0); return retval; } // MBRData::GetLength() + +// Return the MBR data as a GPT partition.... +GPTPart MBRData::AsGPT(int i) { + MBRRecord* origPart; + GPTPart newPart; + uint8_t origType; + uint64_t firstSector, lastSector; + char tempStr[NAME_SIZE]; + + newPart.BlankPartition(); + origPart = GetPartition(i); + if (origPart != NULL) { + origType = origPart->partitionType; + + // don't convert extended, hybrid protective, or null (non-existent) + // partitions (Note similar protection is in GPTData::XFormPartitions(), + // but I want it here too in case I call this function in another + // context in the future....) + if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) && + (origType != 0x00) && (origType != 0xEE)) { + firstSector = (uint64_t) origPart->firstLBA; + newPart.SetFirstLBA(firstSector); + lastSector = firstSector + (uint64_t) origPart->lengthLBA; + if (lastSector > 0) lastSector--; + newPart.SetLastLBA(lastSector); + newPart.SetType(((uint16_t) origType) * 0x0100); + newPart.SetUniqueGUID(1); + newPart.SetAttributes(0); + newPart.SetName((unsigned char*) newPart.GetNameType(tempStr)); + } // if + } // if + return newPart; +} // MBRData::AsGPT() diff --git a/mbr.h b/mbr.h index 95ff4dfb461998b05525c0371a7a143a0c07e973..45159c1d7b14577ca881e8a4f3841cc1363dd825 100644 --- a/mbr.h +++ b/mbr.h @@ -1,8 +1,12 @@ /* mbr.h -- MBR data structure definitions, types, and functions */ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #include <stdint.h> #include <sys/types.h> #include <sys/ioctl.h> +#include "gptpart.h" #ifndef __MBRSTRUCTS #define __MBRSTRUCTS @@ -32,6 +36,17 @@ struct MBRRecord { uint32_t lengthLBA; }; // struct MBRRecord +// Create a 512-byte data structure into which the MBR can be loaded in one +// go, for the benefit of FreeBSD which seems to flake out when loading +// from block devices in multiples other than the block size.... +struct TempMBR { + uint8_t code[440]; + uint32_t diskSignature; + uint16_t nulls; + struct MBRRecord partitions[4]; + uint16_t MBRSignature; +}; // struct TempMBR + // Extended Boot Record (EBR) data, used to hold one logical partition's // data within an extended partition. Includes pointer to next record for // in-memory linked-list access. This is similar to MBRData, but with a @@ -75,7 +90,7 @@ public: void EmptyMBR(int clearBootloader = 1); void SetDiskSize(uint64_t ds) {diskSize = ds;} int ReadMBRData(char* deviceFilename); - void ReadMBRData(int fd); + void ReadMBRData(int fd, int checkBlockSize = 1); int WriteMBRData(void); void WriteMBRData(int fd); // ReadLogicalPart() returns last partition # read to logicals[] array, @@ -83,8 +98,9 @@ public: int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset, int partNum); void DisplayMBRData(void); - void MakeProtectiveMBR(void); + void MakeProtectiveMBR(int clearBoot = 0); MBRValidity GetValidity(void) {return state;} + void ShowValidity(void); void ShowState(void); void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07, int bootable = 0); @@ -99,6 +115,7 @@ public: uint8_t GetType(int i); uint32_t GetFirstSector(int i); uint32_t GetLength(int i); + GPTPart AsGPT(int i); }; // struct MBRData #endif diff --git a/parttypes.cc b/parttypes.cc index cb61621b3864f058bc3ba603ab16e5295cef92cf..7e5a2a38b6ce5e0995612b0f6d583871c3551780 100644 --- a/parttypes.cc +++ b/parttypes.cc @@ -2,6 +2,9 @@ // Class to manage partition type codes -- a slight variant on MBR type // codes, GUID type codes, and associated names. +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS diff --git a/parttypes.h b/parttypes.h index ad62027604cea35fb3c87b4f217d5b667462e6ef..7c336a4e8d7a38a4eb3a3c601111d7fb7c982a22 100644 --- a/parttypes.h +++ b/parttypes.h @@ -1,3 +1,6 @@ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #include <stdint.h> #include <unistd.h> #include <stdlib.h> diff --git a/support.cc b/support.cc index 1cef7d67ea3ee56591022dff6e2b24091f66a759..7ebec9acf8f8c233ce23b778540be24bb5eb179c 100644 --- a/support.cc +++ b/support.cc @@ -3,6 +3,9 @@ // Primarily by Rod Smith, February 2009, but with a few functions // copied from other sources (see attributions below). +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #define __STDC_LIMIT_MACROS #define __STDC_CONSTANT_MACROS @@ -176,8 +179,12 @@ int GetBlockSize(int fd) { #ifdef __APPLE__ err = ioctl(fd, DKIOCGETBLOCKSIZE, &result); +#else +#ifdef __FreeBSD__ + err = ioctl(fd, DIOCGSECTORSIZE, &result); #else err = ioctl(fd, BLKSSZGET, &result); +#endif #endif if (result != 512) { @@ -319,15 +326,17 @@ int IsLittleEndian(void) { } // IsLittleEndian() // Reverse the byte order of theValue; numBytes is number of bytes -void ReverseBytes(char* theValue, int numBytes) { +void ReverseBytes(void* theValue, int numBytes) { + char* origValue; char* tempValue; int i; + origValue = (char*) theValue; tempValue = (char*) malloc(numBytes); for (i = 0; i < numBytes; i++) - tempValue[i] = theValue[i]; + tempValue[i] = origValue[i]; for (i = 0; i < numBytes; i++) - theValue[i] = tempValue[numBytes - i - 1]; + origValue[i] = tempValue[numBytes - i - 1]; free(tempValue); } // ReverseBytes() @@ -345,6 +354,7 @@ uint64_t PowerOf2(int value) { return retval; } // PowerOf2() + /************************************************************************************** * * * Below functions are lifted from various sources, as documented in comments before * @@ -366,6 +376,11 @@ uint64_t disksize(int fd, int *err) { // 32/64-bit issues on MacOS.... #ifdef __APPLE__ *err = ioctl(fd, DKIOCGETBLOCKCOUNT, §ors); +#else +#ifdef __FreeBSD__ + *err = ioctl(fd, DIOCGMEDIASIZE, &sz); + b = GetBlockSize(fd); + sectors = sz / b; #else *err = ioctl(fd, BLKGETSIZE, &sz); if (*err) { @@ -378,6 +393,7 @@ uint64_t disksize(int fd, int *err) { sectors = sz; else sectors = (b >> 9); +#endif #endif return sectors; } diff --git a/support.h b/support.h index d0b3e572ea8893763d38abc2ac403b295891592f..d812d3c211792c1c11c653cb150b57b4e12abdce 100644 --- a/support.h +++ b/support.h @@ -1,8 +1,11 @@ +/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed + under the terms of the GNU GPL version 2, as detailed in the COPYING file. */ + #include <stdint.h> #include <unistd.h> #include <stdlib.h> -#ifdef __APPLE__ +#if defined (__FreeBSD__) || defined (__APPLE__) // Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64 // This used to use __DARWIN_UNIX03 rather than __APPLE__, but __APPLE__ // is more general. If the code fails to work on older versions of OS X/ @@ -23,6 +26,23 @@ // Set this as a default #define SECTOR_SIZE UINT32_C(512) +// Signatures for Apple (APM) disks, multiplied by 0x100000000 +#define APM_SIGNATURE1 UINT64_C(0x00004D5000000000) +#define APM_SIGNATURE2 UINT64_C(0x0000535400000000) + +/************************** + * Some GPT constants.... * + **************************/ + +#define GPT_SIGNATURE UINT64_C(0x5452415020494645) + +// Number and size of GPT entries... +#define NUM_GPT_ENTRIES 128 +#define GPT_SIZE 128 +#define HEADER_SIZE 92 +#define GPT_RESERVED 420 +#define NAME_SIZE 72 + using namespace std; // a GUID @@ -39,7 +59,7 @@ int GetBlockSize(int fd); char* GUIDToStr(struct GUIDData theGUID, char* theString); GUIDData GetGUID(void); int IsLittleEndian(void); // Returns 1 if CPU is little-endian, 0 if it's big-endian -void ReverseBytes(char* 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 disksize(int fd, int* err);