Skip to content

Commit 3b7b8f5

Browse files
committed
main: Properly get and check APFS resize limits
Turns out `diskutil apfs resizeContainer $foo limits` has been staring at us in our face all along... sigh. This should fix things for all those people reporting resize failures caused by an excessively small size. Fixes: #86 Fixes: #116 Signed-off-by: Hector Martin <[email protected]>
1 parent 7544dc0 commit 3b7b8f5

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/diskutil.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ def changeVolumeRole(self, volume, role):
203203
def rename(self, volume, name):
204204
self.action("rename", volume, name, verbose=True)
205205

206+
def get_resize_limits(self, name):
207+
return self.get("apfs", "resizeContainer", name, "limits", "-plist")
208+
206209
def resizeContainer(self, name, size):
207210
size = str(size)
208211
self.action("apfs", "resizeContainer", name, size, verbose=2)

src/main.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,10 +581,19 @@ def action_resize(self, resizable):
581581
else:
582582
target = resizable[0]
583583

584+
limits = self.dutil.get_resize_limits(target.name)
585+
584586
total = target.container["CapacityCeiling"]
585587
free = target.container["CapacityFree"]
586588
min_free = self.get_min_free_space(target)
587-
min_size = align_up(total - free + min_free, PART_ALIGN)
589+
# Minimum size, ignoring APFS snapshots & co, but with a conservative buffer
590+
min_size_raw = align_up(total - free + min_free, PART_ALIGN)
591+
# Minimum size reported by diskutil, considering APFS snapshots & co but with a less conservative buffer
592+
min_size_safe = limits["MinimumSizePreferred"]
593+
min_size = max(min_size_raw, min_size_safe)
594+
overhead = min_size - min_size_raw
595+
avail = total - min_size
596+
588597
min_perc = 100 * min_size / total
589598

590599
assert free > min_free
@@ -593,9 +602,22 @@ def action_resize(self, resizable):
593602
p_message(f" {target.desc}")
594603
p_info( f" Total size: {col()}{ssize(total)}")
595604
p_info( f" Free space: {col()}{ssize(free)}")
596-
p_info( f" Minimum free space: {col()}{ssize(min_free)}")
605+
p_info( f" Available space: {col()}{ssize(avail)}")
606+
p_info( f" Overhead: {col()}{ssize(overhead)}")
597607
p_info( f" Minimum total size: {col()}{ssize(min_size)} ({min_perc:.2f}%)")
598608
print()
609+
if overhead > 1000000000:
610+
p_warning(" Note: The selected partition has significant disk space overhead.")
611+
p_message(" This is usually caused by APFS snapshots used by Time Machine, which")
612+
p_message(" use up free disk space and prevent resizing the partition to a smaller")
613+
p_message(" size. It can also be caused by having a pending macOS upgrade.")
614+
print()
615+
p_message(" If you want to resize your partition to a smaller size, please complete")
616+
p_message(" any pending macOS upgrades and visit this link to learn how to manually")
617+
p_message(" delete Time Machine snapshots:")
618+
print()
619+
p_plain( f" {col(BLUE, BRIGHT)}https://alx.sh/tmcleanup{col()}")
620+
print()
599621
p_question("Enter the new size for your existing partition:")
600622
p_message( " You can enter a size such as '1GB', a fraction such as '50%',")
601623
p_message( " or the word 'min' for the smallest allowable size.")

0 commit comments

Comments
 (0)