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