From ba00fed2efd6c0cba60da9afb0ce3dff84fc69f9 Mon Sep 17 00:00:00 2001
From: srs5694 <srs5694@users.sourceforge.net>
Date: Tue, 12 Jan 2010 18:18:36 -0500
Subject: [PATCH] sgdisk program, misc. bug fixes

---
 CHANGELOG  |   6 ++
 Makefile   |  11 ++--
 README     |  21 +++++--
 bsd.cc     |  37 ++++++-------
 bsd.h      |  12 ++--
 gdisk.8    |  13 ++---
 gdisk.cc   |  10 ++--
 gpt.cc     | 158 ++++++++++++++++++++++++++++++++++++++---------------
 gpt.h      |  23 ++++++--
 gptpart.cc |   3 +-
 gptpart.h  |   6 +-
 mbr.cc     |   5 ++
 mbr.h      |   7 ++-
 support.cc |  64 +++++++++++++++++++---
 14 files changed, 264 insertions(+), 112 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 4280d4e..6d4d8e8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,16 @@
 0.6.0 (1/??/2009):
 ------------------
 
+- Fixed bug that caused the convert to MBR function to fail.
+
 - Added support for disks with other than 512-byte sectors.
 
 - Created embryonic sgdisk program.
 
+- Fixed bug that caused relative sector numbers entered by users (e.g,
+  "+128M") to be misinterpreted as from the start of the range rather than
+  from the default value.
+
 0.5.3 (1/4/2009):
 -----------------
 
diff --git a/Makefile b/Makefile
index 9c73a7a..33a2ffc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,7 @@
 CC=gcc
 CXX=g++
-#CFLAGS=-O2 -fpack-struct
-CFLAGS=-O2 -fpack-struct -D_FILE_OFFSET_BITS=64 -g
-CXXFLAGS=-O2 -fpack-struct -Wuninitialized -Wreturn-type -D_FILE_OFFSET_BITS=64 -g
+CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g
+CXXFLAGS=-O2 -Wuninitialized -Wreturn-type -D_FILE_OFFSET_BITS=64 -I/opt/local/include -g
 LIB_NAMES=crc32 support gptpart mbr gpt bsd parttypes attributes
 LIB_SRCS=$(NAMES:=.cc)
 LIB_OBJS=$(LIB_NAMES:=.o)
@@ -12,11 +11,13 @@ DEPEND= makedepend $(CFLAGS)
 #$(APPNAME):	$(MBR2GPT_OBJS)
 #	$(CC) $(MBR2GPT_OBJS) -o $@
 
+all:	gdisk sgdisk
+
 gdisk:	$(LIB_OBJS) gdisk.o
 	$(CXX) $(LIB_OBJS) gdisk.o -o gdisk
 
 sgdisk: $(LIB_OBJS) sgdisk.o
-	$(CXX) $(LIB_OBJS) sgdisk.o -o sgdisk
+	$(CXX) $(LIB_OBJS) sgdisk.o -L/opt/local/lib -lpopt -o sgdisk
 
 wipegpt:	$(LIB_OBJS) wipegpt.o
 	$(CXX) $(LIB_OBJS) wipegpt.o -o wipegpt
@@ -25,7 +26,7 @@ lint:	#no pre-reqs
 	lint $(SRCS)
 
 clean:	#no pre-reqs
-	rm -f core *.o *~ gdisk
+	rm -f core *.o *~ gdisk sgdisk
 
 # what are the source dependencies
 depend: $(SRCS)
diff --git a/README b/README
index 41718ca..ce7ffb5 100644
--- a/README
+++ b/README
@@ -44,11 +44,22 @@ Installing
 
 To compile gdisk, you must have appropriate development tools installed,
 most notably the GNU Compiler Collection (GCC) and its g++ compiler for
-C++. Uncompress the package and type "make" at the command prompt in the
-resulting directory. The result should be a program file called gdisk. You
-can use this in place or copy the file to a suitable directory, such as
-/usr/local/sbin. You can copy the man page (gdisk.8) to /usr/local/man/man8
-to make it available.
+C++. The sgdisk program also requires the popt library and its development
+files (headers). Most Linux distributions install popt by default, but you
+may need to install a package called popt-dev, popt-devel, or something
+similar to obtain the development libraries. Mac OS users can find a version
+of popt for Mac OS from http://popt.darwinports.com; however, you'll first
+need to install DarwinPorts (instructions exist on the preceding page).
+Alternatively, you can compile gdisk alone, without sgdisk; gdisk doesn't
+require popt.
+
+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. 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
 -------
diff --git a/bsd.cc b/bsd.cc
index ccac953..b931d35 100644
--- a/bsd.cc
+++ b/bsd.cc
@@ -70,12 +70,15 @@ void BSDData::ReadBSDData(int fd, uint64_t startSector, uint64_t endSector) {
    uint32_t* temp32;
    uint16_t* temp16;
    BSDRecord* tempRecords;
+   int offset[3] = { LABEL_OFFSET1, LABEL_OFFSET2, LABEL_OFFSET3 };
 
    labelFirstLBA = startSector;
    labelLastLBA = endSector;
 
-   // Read eight sectors into memory; we'll extract data from
-   // this buffer. (Done to work around FreeBSD limitation)
+   // Read 4096 bytes (eight 512-byte sectors or equivalent)
+   // into memory; we'll extract data from this buffer.
+   // (Done to work around FreeBSD limitation on size of reads
+   // from block devices.)
    lseek64(fd, startSector * GetBlockSize(fd), SEEK_SET);
    err = read(fd, buffer, 4096);
 
@@ -85,27 +88,23 @@ void BSDData::ReadBSDData(int fd, uint64_t startSector, uint64_t endSector) {
    if (bigEnd)
       ReverseBytes(&realSig, 4);
 
-   // Look for the signature at one of two locations
-   labelStart = LABEL_OFFSET1;
-   temp32 = (uint32_t*) &buffer[labelStart];
-   signature = *temp32;
-   if (signature == realSig) {
-      temp32 = (uint32_t*) &buffer[labelStart + 132];
-      signature2 = *temp32;
-      if (signature2 == realSig)
-         foundSig = 1;
-   } // if/else
-   if (!foundSig) { // look in second location
-      labelStart = LABEL_OFFSET2;
-      temp32 = (uint32_t*) &buffer[labelStart];
+   // Look for the signature at any of three locations.
+   // Note that the signature is repeated at both the original
+   // offset and 132 bytes later, so we need two checks....
+   i = 0;
+   do {
+      temp32 = (uint32_t*) &buffer[offset[i]];
       signature = *temp32;
-      if (signature == realSig) {
-         temp32 = (uint32_t*) &buffer[labelStart + 132];
+      if (signature == realSig) { // found first, look for second
+         temp32 = (uint32_t*) &buffer[offset[i] + 132];
          signature2 = *temp32;
-         if (signature2 == realSig)
+         if (signature2 == realSig) {
             foundSig = 1;
+            labelStart = offset[i];
+         } // if found signature
       } // if/else
-   } // if
+      i++;
+   } while ((!foundSig) && (i < 3));
 
    // Load partition metadata from the buffer....
    temp32 = (uint32_t*) &buffer[labelStart + 40];
diff --git a/bsd.h b/bsd.h
index c92cfb0..afdb60c 100644
--- a/bsd.h
+++ b/bsd.h
@@ -13,14 +13,16 @@
 
 #define BSD_SIGNATURE UINT32_C(0x82564557)  /* BSD disklabel signature ("magic") */
 
-#define LABEL_OFFSET1 64   /* BSD disklabels can start at one of these two */
-#define LABEL_OFFSET2 512  /* values; check both for valid signatures */
+#define LABEL_OFFSET1 64    /* BSD disklabels can start at any of these three */
+#define LABEL_OFFSET2 512   /* values; check all for valid signatures */
+#define LABEL_OFFSET3 2048
 
 // FreeBSD documents a maximum # of partitions of 8, but I saw 16 on a NetBSD
 // disk. I'm quadrupling that for further safety. Note that BSDReadData()
-// uses a 2048-byte I/O buffer. In combination with LABEL_OFFSET2 and the
+// uses a 4096-byte I/O buffer. In combination with LABEL_OFFSET3 and the
 // additional 148-byte offset to the actual partition data, that gives a
-// theoretical maximum of 86.75 partitions that the program can handle.
+// theoretical maximum of 118.75 partitions that the program can handle before
+// memory errors will occur.
 #define MAX_BSD_PARTS 64
 
 
@@ -47,7 +49,7 @@ struct  BSDRecord {      // the partition table
    uint16_t pcpg;        // filesystem cylinders per group
 };
 
-// Full data in tweaked MBR format
+// Full data in tweaked BSD format
 class BSDData {
    protected:
       // We only need a few items from the main BSD disklabel data structure....
diff --git a/gdisk.8 b/gdisk.8
index 63a72aa..525fba5 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -2,7 +2,7 @@
 .\" May be distributed under the GNU General Public License
 .TH "GDISK" "8" "0.5.3" "Roderick W. Smith" "GPT fdisk Manual"
 .SH "NAME"
-gdisk \- GUID partition table (GPT) manipulator for Linux and Unix
+gdisk \- Interactive GUID partition table (GPT) manipulator for Linux and Unix
 .SH "SYNOPSIS"
 .BI "gdisk "
 [ \-l ]
@@ -198,12 +198,12 @@ can be specified in absolute terms as sector numbers or as positions
 measured in kilobytes (K), megabytes (M), gigabytes (G), or terabytes (T);
 for instance, \fI\fB40M\fR\fR specifies a position 40MiB from the start of
 the disk. You can specify locations relative to the start or end of the
-specified range by preceding the number by a '+' or '\-' symbol, as in
-\fI\fB+2G\fR\fR to specify a point 2GiB after the first available sector,
+specified default range by preceding the number by a '+' or '\-' symbol, as
+in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start sector,
 or \fI\fB\-200M\fR\fR to specify a point 200MiB before the last available
 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 last available block for the end sector.
+the end of the same block for the end sector.
 
 .TP 
 .B o
@@ -532,10 +532,7 @@ should be considered beta software. Known bugs and limitations include:
 .B *
 The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Linux
 versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been
-tested, with the x86\-64 version having seen the most testing. The Mac OS X
-support was added with version 0.3.1 and has not been as thoroughly tested.
-FreeBSD support was added with version 0.4.0 and has seen even less
-testing.
+tested, with the x86\-64 version having seen the most testing.
 
 .TP 
 .B *
diff --git a/gdisk.cc b/gdisk.cc
index bc238a0..120e68e 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -170,7 +170,7 @@ void RecoveryMenu(char* filename, struct GPTData* theGPT) {
    int goOn = 1;
 
    do {
-      printf("\nrecovery/transformation command (? for help): ");
+      printf("\nRecovery/transformation command (? for help): ");
       junk = fgets(line, 255, stdin);
       sscanf(line, "%c", &command);
       switch (command) {
@@ -313,7 +313,7 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
             } else printf("No partitions\n");
             break;
          case 'd': case 'D':
-            printf("The number of logical sectors per physical sector is %d.\n",
+            printf("Partitions will begin on %d-sector boundaries.\n",
                    theGPT->GetAlignment());
             break;
          case 'e': case 'E':
@@ -328,7 +328,7 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
             theGPT->ShowDetails();
             break;
          case 'l': case 'L':
-            temp1 = GetNumber(1, 128, 8, "Enter the number of logical sectors in a physical sector on the\ndisk (1-128, default = 8): ");
+            temp1 = GetNumber(1, 128, 8, "Enter the sector alignment value (1-128, default = 8): ");
             theGPT->SetAlignment(temp1);
             break;
          case 'm': case 'M':
@@ -377,11 +377,11 @@ void ExpertsMenu(char* filename, struct GPTData* theGPT) {
 void ShowExpertCommands(void) {
    printf("a\tset attributes\n");
    printf("c\tchange partition GUID\n");
-   printf("d\tdisplay the number of logical sectors per physical sector\n");
+   printf("d\tdisplay the sector alignment value\n");
    printf("e\trelocate backup data structures to the end of the disk\n");
    printf("g\tchange disk GUID\n");
    printf("i\tshow detailed information on a partition\n");
-   printf("b\tset the number of logical sectors per physical sector\n");
+   printf("l\tset the sector alignment value\n");
    printf("m\treturn to main menu\n");
    printf("n\tcreate a new protective MBR\n");
    printf("o\tprint protective MBR data\n");
diff --git a/gpt.cc b/gpt.cc
index 7352edc..7788aa8 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -48,6 +48,8 @@ GPTData::GPTData(void) {
    apmFound = 0;
    bsdFound = 0;
    sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
+   beQuiet = 0;
+   whichWasUsed = use_new;
    srand((unsigned int) time(NULL));
    mainHeader.numParts = 0;
    SetGPTSize(NUM_GPT_ENTRIES);
@@ -68,6 +70,8 @@ GPTData::GPTData(char* filename) {
    apmFound = 0;
    bsdFound = 0;
    sectorAlignment = 8; // Align partitions on 4096-byte boundaries by default
+   beQuiet = 0;
+   whichWasUsed = use_new;
    srand((unsigned int) time(NULL));
    mainHeader.numParts = 0;
    LoadPartitions(filename);
@@ -499,22 +503,22 @@ int GPTData::FindOverlaps(void) {
 // 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 (!beQuiet) {
+      printf("Partition table scan:\n");
+      protectiveMBR.ShowState();
+      bsdDisklabel.ShowState();
+      ShowAPMState(); // Show whether there's an Apple Partition Map present
+      ShowGPTState(); // Show GPT status
+      printf("\n");
+   } // if
 
    if (apmFound) {
       printf("\n*******************************************************************\n");
@@ -549,9 +553,10 @@ int GPTData::LoadPartitions(char* deviceFilename) {
       blockSize = (uint32_t) GetBlockSize(fd);
       sectorAlignment = FindAlignment(fd);
       strcpy(device, deviceFilename);
-      PartitionScan(fd); // Check for partition types & print summary
+      PartitionScan(fd); // Check for partition types, load GPT, & print summary
 
-      switch (UseWhichPartitions()) {
+      whichWasUsed = UseWhichPartitions();
+      switch (whichWasUsed) {
          case use_mbr:
             XFormPartitions();
             break;
@@ -696,7 +701,7 @@ int GPTData::ForceLoadGPTData(int fd) {
            (secondPartsCrcOk == 0)) {
          printf("Warning! One or more CRCs don't match. You should repair the disk!\n");
          state = gpt_corrupt;
-           } // if
+      } // if
    } else {
       state = gpt_invalid;
    } // if/else
@@ -763,7 +768,7 @@ void GPTData::LoadSecondTableAsMain(void) {
 
 // Writes GPT (and protective MBR) to disk. Returns 1 on successful
 // write, 0 if there was a problem.
-int GPTData::SaveGPTData(void) {
+int GPTData::SaveGPTData(int quiet) {
    int allOK = 1;
    char answer, line[256];
    int fd;
@@ -799,7 +804,7 @@ int GPTData::SaveGPTData(void) {
    } // if
    // Check that second header is properly placed. Warn and ask if this should
    // be corrected if the test fails....
-   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
+   if ((mainHeader.backupLBA < (diskSize - UINT64_C(1))) && (quiet == 0)) {
       printf("Warning! Secondary header is placed too early on the disk! Do you want to\n"
              "correct this problem? ");
       if (GetYN() == 'Y') {
@@ -833,7 +838,7 @@ int GPTData::SaveGPTData(void) {
    } // if
    RecomputeCRCs();
 
-   if (allOK) {
+   if ((allOK) && (!quiet)) {
       printf("\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n");
       printf("MBR PARTITIONS!! THIS PROGRAM IS BETA QUALITY AT BEST. IF YOU LOSE ALL YOUR\n");
       printf("DATA, YOU HAVE ONLY YOURSELF TO BLAME IF YOU ANSWER 'Y' BELOW!\n\n");
@@ -1251,36 +1256,24 @@ void GPTData::CreatePartition(void) {
       } while (IsFree(sector) == 0);
       lastBlock = sector;
 
-      partitions[partNum].SetFirstLBA(firstBlock);
-      partitions[partNum].SetLastLBA(lastBlock);
-
-      partitions[partNum].SetUniqueGUID(1);
+      firstFreePart = CreatePartition(partNum, firstBlock, lastBlock);
       partitions[partNum].ChangeType();
       partitions[partNum].SetName((unsigned char*) partitions[partNum].GetNameType(prompt));
-         } else {
-            printf("No free sectors available\n");
-         } // if/else
+   } else {
+      printf("No free sectors available\n");
+   } // if/else
 } // GPTData::CreatePartition()
 
 // Interactively delete a partition (duh!)
 void GPTData::DeletePartition(void) {
    int partNum;
    uint32_t low, high;
-   uint64_t startSector, length;
    char prompt[255];
 
    if (GetPartRange(&low, &high) > 0) {
       sprintf(prompt, "Partition number (%d-%d): ", low + 1, high + 1);
       partNum = GetNumber(low + 1, high + 1, low, prompt);
-
-      // In case there's a protective MBR, look for & delete matching
-      // MBR partition....
-      startSector = partitions[partNum - 1].GetFirstLBA();
-      length = partitions[partNum - 1].GetLengthLBA();
-      protectiveMBR.DeleteByLocation(startSector, length);
-
-      // Now delete the GPT partition
-      partitions[partNum - 1].BlankPartition();
+      DeletePartition(partNum - 1);
    } else {
       printf("No partitions\n");
    } // if/else
@@ -1315,6 +1308,8 @@ void GPTData::SetAttributes(uint32_t partNum) {
 // user confirms destruction, 0 if the user aborts.
 // If prompt == 0, don't ask user about proceeding and do NOT wipe out
 // MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
+// If prompt == -1, don't ask user about proceeding and DO wipe out
+// MBR.
 int GPTData::DestroyGPT(int prompt) {
    int fd, i, sum, tableSize;
    char blankSector[512], goOn = 'Y', blank = 'N';
@@ -1324,11 +1319,11 @@ int GPTData::DestroyGPT(int prompt) {
       blankSector[i] = '\0';
    } // for
 
-   if (((apmFound) || (bsdFound)) && prompt) {
+   if (((apmFound) || (bsdFound)) && (prompt > 0)) {
       printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
              "damage any APM or BSD partitions on this disk!\n");
    } // if APM or BSD
-   if (prompt) {
+   if (prompt > 0) {
       printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
       goOn = GetYN();
    } // if
@@ -1361,16 +1356,16 @@ int GPTData::DestroyGPT(int prompt) {
          if (myWrite(fd, blankSector, 512) != 512) { // blank it out
             fprintf(stderr, "Warning! GPT backup header not overwritten! Error is %d\n", errno);
          } // if
-         if (prompt) {
+         if (prompt > 0) {
             printf("Blank out MBR? ");
             blank = GetYN();
-         }// if
+         } // if
          // Note on below: Touch the MBR only if the user wants it completely
          // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
          // the MBR, but this could wipe out a valid MBR that the program
          // had subsequently discarded (say, if it conflicted with older GPT
          // structures).
-         if (blank == 'Y') {
+         if ((blank == 'Y') || (prompt < 0)) {
             lseek64(fd, 0, SEEK_SET);
             if (myWrite(fd, blankSector, 512) != 512) { // blank it out
                fprintf(stderr, "Warning! MBR not overwritten! Error is %d!\n", errno);
@@ -1681,6 +1676,7 @@ int GPTData::XFormToMBR(void) {
       printf("\nCreating entry for partition #%d\n", j + 1);
       numConverted += OnePartToMBR(j, i);
    } // for
+   printf("MBR writing returned %d\n", protectiveMBR.WriteMBRData(device));
    return numConverted;
 } // GPTData::XFormToMBR()
 
@@ -1774,7 +1770,8 @@ void GPTData::MakeHybrid(void) {
  **********************************************************************/
 
 // Resizes GPT to specified number of entries. Creates a new table if
-// necessary, copies data if it already exists.
+// necessary, copies data if it already exists. Returns 1 if all goes
+// well, 0 if an error is encountered.
 int GPTData::SetGPTSize(uint32_t numEntries) {
    struct GPTPart* newParts;
    struct GPTPart* trash;
@@ -1844,6 +1841,51 @@ void GPTData::BlankPartitions(void) {
    } // for
 } // GPTData::BlankPartitions()
 
+// Delete a partition by number. Returns 1 if successful,
+// 0 if there was a problem. Returns 1 if partition was in
+// range, 0 if it was out of range.
+int GPTData::DeletePartition(uint32_t partNum) {
+   uint64_t startSector, length;
+   uint32_t low, high, numParts, retval = 1;;
+
+   numParts = GetPartRange(&low, &high);
+   if ((numParts > 0) && (partNum >= low) && (partNum <= high)) {
+      // In case there's a protective MBR, look for & delete matching
+      // MBR partition....
+      startSector = partitions[partNum].GetFirstLBA();
+      length = partitions[partNum].GetLengthLBA();
+      protectiveMBR.DeleteByLocation(startSector, length);
+
+      // Now delete the GPT partition
+      partitions[partNum].BlankPartition();
+   } else {
+      fprintf(stderr, "Partition number %d out of range!\n", partNum + 1);
+      retval = 0;
+   } // if/else
+   return retval;
+} // GPTData::DeletePartition(uint32_t partNum)
+
+// Non-interactively create a partition. Note that this function is overloaded
+// with another of the same name but different parameters; that one prompts
+// the user for data. This one returns 1 if the operation was successful, 0
+// if a problem was discovered.
+int GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
+   int retval = 1; // assume there'll be no problems
+
+   if (IsFreePartNum(partNum)) {
+      Align(&startSector); // Align sector to correct multiple
+      if (IsFree(startSector) && (startSector <= endSector)) {
+         if (FindLastInFree(startSector) >= endSector) {
+            partitions[partNum].SetFirstLBA(startSector);
+            partitions[partNum].SetLastLBA(endSector);
+            partitions[partNum].SetType(0x0700);
+            partitions[partNum].SetUniqueGUID(1);
+         } else retval = 0; // if free space until endSector
+      } else retval = 0; // if startSector is free
+   } else retval = 0; // if legal partition number
+   return retval;
+} // GPTData::CreatePartition(partNum, startSector, endSector)
+
 // 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) {
@@ -1934,10 +1976,13 @@ void GPTData::MoveSecondHeaderToEnd() {
    secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
 } // GPTData::FixSecondHeaderLocation()
 
-void GPTData::SetName(uint32_t partNum, char* theName) {
-   if ((partNum >= 0) && (partNum < mainHeader.numParts))
-      if (partitions[partNum].GetFirstLBA() > 0)
-         partitions[partNum].SetName((unsigned char*) theName);
+int GPTData::SetName(uint32_t partNum, char* theName) {
+   int retval = 1;
+   if (!IsFreePartNum(partNum))
+      partitions[partNum].SetName((unsigned char*) theName);
+   else retval = 0;
+
+   return retval;
 } // GPTData::SetName
 
 // Set the disk GUID to the specified value. Note that the header CRCs must
@@ -1962,6 +2007,17 @@ int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
    return retval;
 } // GPTData::SetPartitionGUID()
 
+// Change partition type code non-interactively. Returns 1 if
+// successful, 0 if not....
+int GPTData::ChangePartType(uint32_t partNum, uint16_t hexCode) {
+   int retval = 1;
+
+   if (!IsFreePartNum(partNum)) {
+      partitions[partNum].SetType(hexCode);
+   } else retval = 0;
+   return retval;
+} // GPTData::ChangePartType()
+
 // 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
@@ -2015,8 +2071,10 @@ int GPTData::Align(uint64_t* sector) {
       // Otherwise, notify the user that it couldn't be done....
       if (sectorOK == 1) {
          printf("Information: Moved requested sector from %llu to %llu for\n"
-                "alignment purposes. Use 'l' on the experts' menu to adjust alignment.\n",
+                "alignment purposes.\n",
                 (unsigned long long) original, (unsigned long long) *sector);
+         if (!beQuiet)
+            printf("Use 'l' on the experts' menu to adjust alignment\n");
       } else {
          printf("Information: Sector not aligned on %d-sector boundary and could not be moved.\n"
                 "If you're using a Western Digital Advanced Format or similar disk with\n"
@@ -2227,6 +2285,20 @@ int GPTData::IsFree(uint64_t sector) {
         return (isFree);
 } // GPTData::IsFree()
 
+// Returns 1 if partNum is unused.
+int GPTData::IsFreePartNum(uint32_t partNum) {
+   int retval = 1;
+
+   if ((partNum >= 0) && (partNum < mainHeader.numParts)) {
+      if ((partitions[partNum].GetFirstLBA() != UINT64_C(0)) ||
+          (partitions[partNum].GetLastLBA() != UINT64_C(0))) {
+         retval = 0;
+      } // if partition is in use
+   } else retval = 0;
+
+   return retval;
+} // GPTData::IsFreePartNum()
+
 /********************************
  *                              *
  * Endianness support functions *
diff --git a/gpt.h b/gpt.h
index 68baf1e..3fa1f5d 100644
--- a/gpt.h
+++ b/gpt.h
@@ -32,6 +32,7 @@ enum GPTValidity {gpt_valid, gpt_corrupt, gpt_invalid};
 enum WhichToUse {use_gpt, use_mbr, use_bsd, use_new};
 
 // Header (first 512 bytes) of GPT table
+#pragma pack(1)
 struct GPTHeader {
    uint64_t signature;
    uint32_t revision;
@@ -54,7 +55,7 @@ struct GPTHeader {
 class GPTData {
 protected:
    struct GPTHeader mainHeader;
-   struct GPTPart *partitions;
+   GPTPart *partitions;
    struct GPTHeader secondHeader;
    MBRData protectiveMBR;
    char device[256]; // device filename
@@ -70,6 +71,8 @@ protected:
    int bsdFound; // set to 1 if BSD disklabel detected in MBR
    int sectorAlignment; // Start & end partitions at multiples of sectorAlignment
    PartTypes typeHelper;
+   int beQuiet;
+   WhichToUse whichWasUsed;
 public:
    // Basic necessary functions....
    GPTData(void);
@@ -94,7 +97,7 @@ public:
    int ForceLoadGPTData(int fd);
    int LoadMainTable(void);
    void LoadSecondTableAsMain(void);
-   int SaveGPTData(void);
+   int SaveGPTData(int quiet = 0);
    int SaveGPTBackup(char* filename);
    int LoadGPTBackup(char* filename);
 
@@ -127,16 +130,17 @@ public:
    // Adjust GPT structures WITHOUT user interaction...
    int SetGPTSize(uint32_t numEntries);
    void BlankPartitions(void);
+   int DeletePartition(uint32_t partNum);
+   int CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
    void SortGPT(void);
    int ClearGPTData(void);
    void MoveSecondHeaderToEnd();
-   void SetName(uint32_t partNum, char* theName = NULL);
+   int SetName(uint32_t partNum, char* theName = NULL);
    void SetDiskGUID(GUIDData newGUID);
    int SetPartitionGUID(uint32_t pn, GUIDData theGUID);
+   int ChangePartType(uint32_t pn, uint16_t hexCode);
    void MakeProtectiveMBR(void) {protectiveMBR.MakeProtectiveMBR();}
    int Align(uint64_t* sector);
-   void SetAlignment(int n) {sectorAlignment = n;}
-   void JustLooking(int i = 1) {justLooking = i;}
 
    // Return data about the GPT structures....
    int GetPartRange(uint32_t* low, uint32_t* high);
@@ -146,7 +150,6 @@ public:
    uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;}
    uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;}
    uint32_t CountParts(void);
-   int GetAlignment(void) {return sectorAlignment;}
 
    // Find information about free space
    uint64_t FindFirstAvailable(uint64_t start = 0);
@@ -155,6 +158,14 @@ public:
    uint64_t FindLastInFree(uint64_t start);
    uint64_t FindFreeBlocks(int *numSegments, uint64_t *largestSegment);
    int IsFree(uint64_t sector);
+   int IsFreePartNum(uint32_t partNum);
+
+   // Change how functions work, or return information on same
+   void SetAlignment(int n) {sectorAlignment = n;}
+   int GetAlignment(void) {return sectorAlignment;}
+   void JustLooking(int i = 1) {justLooking = i;}
+   void BeQuiet(int i = 1) {beQuiet = i;}
+   WhichToUse WhichWasUsed(void) {return whichWasUsed;}
 
    // Endianness functions
    void ReverseHeaderBytes(struct GPTHeader* header); // for endianness
diff --git a/gptpart.cc b/gptpart.cc
index fd24dfa..5ee7d5d 100644
--- a/gptpart.cc
+++ b/gptpart.cc
@@ -206,7 +206,7 @@ void GPTPart::ChangeType(void) {
    else // user wants to enter the GUID directly, so do that
       newType = GetGUID();
    partitionType = newType;
-   printf("Changed system type of partition to '%s'\n",
+   printf("Changed type of partition to '%s'\n",
           typeHelper.GUIDToName(partitionType, typeName));
 } // GPTPart::ChangeType()
 
@@ -278,4 +278,3 @@ void QuickSortGPT(GPTPart* partitions, int start, int finish) {
    if (start < right) QuickSortGPT(partitions, start, right);
    if (finish > left) QuickSortGPT(partitions, left, finish);
 } // QuickSortGPT()
-
diff --git a/gptpart.h b/gptpart.h
index 8d396df..b1a580f 100644
--- a/gptpart.h
+++ b/gptpart.h
@@ -31,13 +31,13 @@ using namespace std;
 
 class GPTPart {
    protected:
-      // Caution: The non-static data in GUIDPart is precisely the right size
+      // Caution: The non-static data in GPTPart is precisely the right size
       // to enable easy loading of the data directly from disk. If any
       // non-static variables are added to the below, the data size will
       // change and the program will stop working. This can be corrected by
       // adjusting the data-load operation in GPTData::LoadMainTable() and
-      // GPTData::LoadSecondTableAsMain() and then removing the GUIDPart
-      // size check in SizesOK().
+      // GPTData::LoadSecondTableAsMain() and then removing the GPTPart
+      // size check in SizesOK() (in gpt.cc file).
       struct GUIDData partitionType;
       struct GUIDData uniqueGUID;
       uint64_t firstLBA;
diff --git a/mbr.cc b/mbr.cc
index efd6fd3..f51d5c7 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -305,6 +305,11 @@ void MBRData::WriteMBRData(int fd) {
    }// if
 } // MBRData::WriteMBRData(int fd)
 
+int MBRData::WriteMBRData(char* deviceFilename) {
+   strcpy(device, deviceFilename);
+   return WriteMBRData();
+} // MBRData::WriteMBRData(char* deviceFilename)
+
 /********************************************
  *                                          *
  * Functions that display data for the user *
diff --git a/mbr.h b/mbr.h
index 8f7dfbc..0cd4d2a 100644
--- a/mbr.h
+++ b/mbr.h
@@ -30,6 +30,7 @@ using namespace std;
 // Data for a single MBR partition record
 // Note that firstSector and lastSector are in CHS addressing, which
 // splits the bits up in a weird way.
+#pragma pack(1)
 struct MBRRecord {
    uint8_t status;
    uint8_t firstSector[3];
@@ -43,6 +44,7 @@ struct MBRRecord {
 // go, for the benefit of FreeBSD which seems to flake out when loading
 // from block devices in multiples other than the block size.
 // Also used when loading logical partitions.
+#pragma pack(1)
 struct TempMBR {
    uint8_t code[440];
    uint32_t diskSignature;
@@ -81,12 +83,13 @@ public:
    // File I/O functions...
    int ReadMBRData(char* deviceFilename);
    void ReadMBRData(int fd, int checkBlockSize = 1);
+   // ReadLogicalPart() returns last partition # read to logicals[] array,
+   // or -1 if there was a problem....
    int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
                        int partNum);
    int WriteMBRData(void);
    void WriteMBRData(int fd);
-   // ReadLogicalPart() returns last partition # read to logicals[] array,
-   // or -1 if there was a problem....
+   int WriteMBRData(char* deviceFilename);
 
    // Display data for user...
    void DisplayMBRData(void);
diff --git a/support.cc b/support.cc
index 8ab9a2e..c30f4e7 100644
--- a/support.cc
+++ b/support.cc
@@ -27,6 +27,10 @@
 #define BLKPBSZGET _IO(0x12,123)
 #endif
 
+// Below constant corresponds to an 800GB disk -- a somewhat arbitrary
+// cutoff
+#define SMALLEST_ADVANCED_FORMAT UINT64_C(1677721600)
+
 using namespace std;
 
 // Get a numeric value from the user, between low and high (inclusive).
@@ -84,8 +88,7 @@ char GetYN(void) {
  //value as the default if the user just hits Enter
 uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, char prompt[]) {
    unsigned long long response;
-   int num;
-   int plusFlag = 0;
+   int num, plusFlag = 0;
    uint64_t mult = 1;
    char suffix;
    char line[255];
@@ -147,7 +150,14 @@ uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, char prompt[])
       // Adjust response based on multiplier and plus flag, if present
       response *= (unsigned long long) mult;
       if (plusFlag == 1) {
-         response = response + (unsigned long long) low - UINT64_C(1);
+         // Recompute response based on low part of range (if default = high
+         // value, which should be the case when prompting for the end of a
+         // range) or the defaut value (if default != high, which should be
+         // the case for the first sector of a partition).
+         if (def == high)
+            response = response + (unsigned long long) low - UINT64_C(1);
+         else
+            response = response + (unsigned long long) def - UINT64_C(1);
       } // if
       if (plusFlag == -1) {
          response = (unsigned long long) high - response;
@@ -231,29 +241,65 @@ int GetBlockSize(int fd) {
    return (result);
 } // GetBlockSize()
 
+// My original FindAlignment() function (after this one) isn't working, since
+// the BLKPBSZGET ioctl() isn't doing what I expected (it returns 512 even on
+// a WD Advanced Format drive). Therefore, I'm using a simpler function that
+// returns 1-sector alignment for unusual sector sizes and drives smaller than
+// a size defined by SMALLEST_ADVANCED_FORMAT, and 8-sector alignment for
+// larger drives with 512-byte sectors.
+int FindAlignment(int fd) {
+   int err, result;
+
+   if ((GetBlockSize(fd) == 512) && (disksize(fd, &err) >= SMALLEST_ADVANCED_FORMAT)) {
+      result = 8; // play it safe; align for 4096-byte sectors
+   } else {
+      result = 1; // unusual sector size; assume it's the real physical size
+   } // if/else
+   return result;
+} // FindAlignment
+
 // Return the partition alignment value in sectors. Right now this works
 // only for Linux 2.6.32 and later, since I can't find equivalent ioctl()s
 // for OS X or FreeBSD, and the Linux ioctl is new
-int FindAlignment(int fd) {
-   int err = -2, result = 8, physicalSectorSize = 4096;
+/* int FindAlignment(int fd) {
+   int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096;
+   uint64_t diskSize;
 
+   printf("Entering FindAlignment()\n");
 #if defined (__linux__) && defined (BLKPBSZGET)
    err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
+   printf("In FindAlignment(), physicalSectorSize = %d, err = %d\n", physicalSectorSize, err);
 //   printf("Tried to get hardware alignment; err is %d, sector size is %d\n", err, physicalSectorSize);
 #else
    err = -1;
 #endif
 
    if (err < 0) { // ioctl didn't work; have to guess....
-		if (GetBlockSize(fd) == 512)
+      if (GetBlockSize(fd) == 512) {
          result = 8; // play it safe; align for 4096-byte sectors
-		else
-			result = 1; // unusual sector size; assume it's the real physical size
+      } else {
+         result = 1; // unusual sector size; assume it's the real physical size
+      } // if/else
    } else { // ioctl worked; compute alignment
       result = physicalSectorSize / GetBlockSize(fd);
+      // Disks with larger physical than logical sectors must theoretically
+      // have a total disk size that's a multiple of the physical sector
+      // size; however, some such disks have compatibility jumper settings
+      // meant for one-partition MBR setups, and these reduce the total
+      // number of sectors by 1. If such a setting is used, it'll result
+      // in improper alignment, so look for this condition and warn the
+      // user if it's found....
+      diskSize = disksize(fd, &errnum);
+      if ((diskSize % (uint64_t) result) != 0) {
+         fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n"
+                         "size (%d), but it should be! Check disk manual and jumper settings!\n",
+                         (unsigned long long) diskSize, result);
+      } // if
    } // if/else
+   if (result <= 0) // can happen if physical sector size < logical sector size
+      result = 1;
    return result;
-} // FindAlignment(int)
+} // FindAlignment(int) */
 
 // The same as FindAlignment(int), but opens and closes a device by filename
 int FindAlignment(char deviceFilename[]) {
-- 
GitLab