diff --git a/src/lib/y2storage/partition.rb b/src/lib/y2storage/partition.rb index 77c9ff8b0..dbf844513 100644 --- a/src/lib/y2storage/partition.rb +++ b/src/lib/y2storage/partition.rb @@ -281,6 +281,25 @@ def windows_suitable? !disk.nil? && type.is?(:primary) && id.is?(:windows_system) end + # Whether the given disk space is located right after this partition + # + # @param disk_space [FreeDiskSpace] + # @return [Boolean] + def subsequent_slot?(disk_space) + return false if disk_space.disk != partition_table.partitionable + return false unless disk_space.region.start > region.end + + # The simplest case can be easily evaluated + return true if disk_space.region.start == (region.end + 1) + + # But if the end of the partition is not properly aligned, we may need to look closer + # FIXME: this can likely be both simpler and more efficient. But since it's used + # only for missaligned partitions is not a priority + slots = partition_table.partitions + partition_table.unused_partition_slots + break_points = slots.flat_map { |s| [s.region.start, s.region.end] } + break_points.none? { |p| p > region.end && p < disk_space.region.start } + end + protected # Values for volume specification matching diff --git a/src/lib/y2storage/planned/assigned_space.rb b/src/lib/y2storage/planned/assigned_space.rb index 9e291d3ff..3659612a7 100644 --- a/src/lib/y2storage/planned/assigned_space.rb +++ b/src/lib/y2storage/planned/assigned_space.rb @@ -161,6 +161,15 @@ def total_missing_size end end + # Space that can be sustracted from the start of the region without invalidating this + # valid assignation + # + # @return [DiskSize] + def disposable_size + # FIXME: This is more based on trial and error than on a real rationale + usable_extra_size - align_grain + end + # Space consumed by the EBR of one logical partition in a given disk # See https://en.wikipedia.org/wiki/Extended_boot_record # diff --git a/src/lib/y2storage/planned/can_be_resized.rb b/src/lib/y2storage/planned/can_be_resized.rb index 773e2fed1..16fb44384 100644 --- a/src/lib/y2storage/planned/can_be_resized.rb +++ b/src/lib/y2storage/planned/can_be_resized.rb @@ -44,6 +44,15 @@ def shrink?(devicegraph) max_size <= device_to_reuse(devicegraph).size end + # Limit the max size to ensure the device does not grow more than a give margin + # + # @param max_grow [DiskSize] max margin to grow the device + # @param devicegraph [Devicegraph] + def limit_grow(max_grow, devicegraph) + limit = device_to_reuse(devicegraph).size + max_grow + self.max_size = [max_size, limit].min + end + protected # Implements reuse_device! hook diff --git a/src/lib/y2storage/planned/partition.rb b/src/lib/y2storage/planned/partition.rb index bc40987ee..241d9e62a 100644 --- a/src/lib/y2storage/planned/partition.rb +++ b/src/lib/y2storage/planned/partition.rb @@ -87,6 +87,19 @@ def initialize(mount_point, filesystem_type = nil) @primary = false end + # Whether this corresponds to a reused partition that is located right before the given + # assigned space + # + # @param assigned_space [AssignedSpace] + # @return [Boolean] + def subsequent_slot?(assigned_space) + devicegraph = assigned_space.disk_space.disk.devicegraph + dev = device_to_reuse(devicegraph) + return false unless dev + + dev.subsequent_slot?(assigned_space.disk_space) + end + def self.to_string_attrs [ :mount_point, :reuse_name, :reuse_sid, :min_size, :max_size, diff --git a/test/y2storage/partition_test.rb b/test/y2storage/partition_test.rb index 2aae0a61e..fc4932278 100755 --- a/test/y2storage/partition_test.rb +++ b/test/y2storage/partition_test.rb @@ -446,6 +446,55 @@ end end + describe "#subsequent_slot?" do + let(:disk) { fake_devicegraph.find_by_name(disk_name) } + let(:region) { disk.partition_table.unused_partition_slots[slot].region } + let(:slot) { 0 } + let(:space) { Y2Storage::FreeDiskSpace.new(disk, region) } + subject(:partition) { fake_devicegraph.find_by_name(part_name) } + + context "when the space starts at the sector right after the partition" do + let(:scenario) { "spaces_5_5_10" } + let(:part_name) { "/dev/sda1" } + let(:disk_name) { "/dev/sda" } + + it "returns true" do + expect(partition.subsequent_slot?(space)).to eq true + end + end + + context "when the space is right after a partition with missaligned end" do + let(:scenario) { "alignment" } + let(:part_name) { "/dev/sdb1" } + let(:disk_name) { "/dev/sdb" } + + it "returns true" do + expect(partition.subsequent_slot?(space)).to eq true + end + end + + context "when the space is not adyacent to the partition" do + let(:scenario) { "spaces_5_5_10" } + let(:part_name) { "/dev/sda1" } + let(:disk_name) { "/dev/sda" } + let(:slot) { 1 } + + it "returns false" do + expect(partition.subsequent_slot?(space)).to eq false + end + end + + context "when the space starts at an appropriate sector but is in another disk" do + let(:scenario) { "alignment" } + let(:part_name) { "/dev/sdb1" } + let(:disk_name) { "/dev/sdc" } + + it "returns false" do + expect(partition.subsequent_slot?(space)).to eq false + end + end + end + # Only basic cases are tested here. More exhaustive tests can be found in tests # for Y2Storage::MatchVolumeSpec describe "#match_volume?" do diff --git a/test/y2storage/planned/can_be_resized_test.rb b/test/y2storage/planned/can_be_resized_test.rb index 4bce2b99f..fcd241a31 100755 --- a/test/y2storage/planned/can_be_resized_test.rb +++ b/test/y2storage/planned/can_be_resized_test.rb @@ -172,4 +172,18 @@ class ResizableDevice < Y2Storage::Planned::Device end end end + + describe "#limit_grow" do + before { planned.max_size = 70.GiB } + + it "limits the max size if the sum of the new limit and the original size is smaller" do + planned.limit_grow(5.GiB, devicegraph) + expect(planned.max_size).to eq 55.GiB + end + + it "leaves the max size untouched if the sum of original size and limit is bigger" do + planned.limit_grow(50.GiB, devicegraph) + expect(planned.max_size).to eq 70.GiB + end + end end diff --git a/test/y2storage/planned/partition_test.rb b/test/y2storage/planned/partition_test.rb index 518f24360..ae7885d2e 100755 --- a/test/y2storage/planned/partition_test.rb +++ b/test/y2storage/planned/partition_test.rb @@ -111,4 +111,53 @@ end end end + + describe "#subsequent_slot?" do + let(:assigned_space) { Y2Storage::Planned::AssignedSpace.new(space, []) } + let(:space) do + instance_double( + "Y2Storage::FreeDiskSpace", + disk: disk, + disk_size: 500.GiB, + align_grain: 1.MiB, + require_end_alignment?: false + ) + end + + let(:disk) { instance_double("Y2Storage::Disk", devicegraph: devicegraph) } + let(:devicegraph) { instance_double("Y2Storage::Devicegraph") } + + context "when the plan is to create a new partition" do + it "returns false" do + expect(partition.subsequent_slot?(assigned_space)).to eq false + end + end + + context "when the plan is to reuse an existing partition" do + let(:real_partition) { instance_double("Y2Storage::Partition", sid: 123) } + + before do + partition.assign_reuse(real_partition) + + allow(devicegraph).to receive(:find_device).and_return real_partition + allow(real_partition).to receive(:subsequent_slot?).with(space).and_return subsequent + end + + context "if the reused partition is next to the region of the assigned space" do + let(:subsequent) { true } + + it "returns true" do + expect(partition.subsequent_slot?(assigned_space)).to eq true + end + end + + context "if the reused partition is not adjacent to the region of the assigned space" do + let(:subsequent) { false } + + it "returns false" do + expect(partition.subsequent_slot?(assigned_space)).to eq false + end + end + end + end end