From 391a12ec74e1d2c03d19b768ae29acc8d2ee603e Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Tue, 21 Oct 2014 13:41:05 -0500
Subject: [PATCH 9/9] Add a method to adjust target sizes as needed for
 alignment, &c. (#1120964)

Callers should pass new target sizes through this method before setting
a new target size.
---
 blivet/devices.py        | 33 ++++++++++++++++++++++++---------
 tests/storagetestcase.py |  1 +
 2 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/blivet/devices.py b/blivet/devices.py
index 1519059..a9c8bb0 100644
--- a/blivet/devices.py
+++ b/blivet/devices.py
@@ -717,6 +717,10 @@ class StorageDevice(Device):
 
         super(StorageDevice, self)._setName(value)
 
+    def alignTargetSize(self, newsize):
+        """ Return a proposed target size adjusted for device specifics. """
+        return newsize
+
     def _getTargetSize(self):
         return self._targetSize
 
@@ -729,6 +733,9 @@ class StorageDevice(Device):
                       newsize, self.maxSize)
             raise ValueError("size is larger than the maximum for this device")
 
+        if self.alignTargetSize(newsize) != newsize:
+            raise ValueError("new size would violate alignment requirements")
+
         self._targetSize = newsize
 
     targetSize = property(lambda s: s._getTargetSize(),
@@ -1028,12 +1035,12 @@ class StorageDevice(Device):
     @property
     def minSize(self):
         """ The minimum size this device can be. """
-        return self.format.minSize if self.resizable else self.currentSize
+        return self.alignTargetSize(self.format.minSize) if self.resizable else self.currentSize
 
     @property
     def maxSize(self):
         """ The maximum size this device can be. """
-        return self.format.maxSize if self.resizable else self.currentSize
+        return self.alignTargetSize(self.format.maxSize) if self.resizable else self.currentSize
 
     @property
     def status(self):
@@ -1502,11 +1509,23 @@ class PartitionDevice(StorageDevice):
                       "flags": self.partedPartition.getFlagsAsString()})
         return d
 
+    def alignTargetSize(self, newsize):
+        """ Return newsize adjusted to allow for an end-aligned partition. """
+        if newsize == 0:
+            return newsize
+
+        (_constraint, geometry) = self._computeResize(self.partedPartition,
+                                                      newsize=newsize)
+        return Size(geometry.getLength(unit="B"))
+
     def _setTargetSize(self, newsize):
         if not isinstance(newsize, Size):
             raise ValueError("new size must of type Size")
 
         if newsize != self.size:
+            if self.alignTargetSize(newsize) != newsize:
+                raise ValueError("new size will not yield an aligned partition")
+
             # change this partition's geometry in-memory so that other
             # partitioning operations can complete (e.g., autopart)
             super(PartitionDevice, self)._setTargetSize(newsize)
@@ -2038,6 +2057,8 @@ class PartitionDevice(StorageDevice):
             # won't drive the actual size below the formatting's minimum.
             # This has the added benefit that we're always returning an aligned
             # size.
+            # Call computeResize directly so we can reuse the geometry it
+            # returns.
             (_constraint, geometry) = self._computeResize(self.partedPartition,
                                                           newsize=min_size)
             if Size(geometry.getLength(unit="B")) < min_size:
@@ -2067,16 +2088,10 @@ class PartitionDevice(StorageDevice):
             pass
         else:
             if partition.type == parted.PARTITION_FREESPACE:
-                # align the end sector
-                geometry = partition.geometry
-                free_start = geometry.start
-                free_end = self.disk.format.endAlignment.alignDown(geometry,
-                                                                   geometry.end)
-                geometry.end = free_end
                 maxPartSize = self.size + Size(geometry.getLength(unit="B"))
 
         maxFormatSize = self.format.maxSize
-        return min(maxFormatSize, maxPartSize) if maxFormatSize else maxPartSize
+        return self.alignTargetSize(min(maxFormatSize, maxPartSize)) if maxFormatSize else self.alignTargetSize(maxPartSize)
 
     @property
     def currentSize(self):
diff --git a/tests/storagetestcase.py b/tests/storagetestcase.py
index e1481f6..c7aab84 100644
--- a/tests/storagetestcase.py
+++ b/tests/storagetestcase.py
@@ -36,6 +36,7 @@ class StorageTestCase(unittest.TestCase):
         # prevent PartitionDevice from trying to dig around in the partition's
         # geometry
         blivet.devices.PartitionDevice._setTargetSize = StorageDevice._setTargetSize
+        blivet.devices.PartitionDevice.alignTargetSize = StorageDevice.alignTargetSize
         blivet.devices.PartitionDevice.maxSize = StorageDevice.maxSize
         blivet.devices.PartitionDevice.minSize = StorageDevice.minSize
 
-- 
1.9.3

