Skip to content

Commit da724b6

Browse files
committed
main: Provide the option to resume a missed step2
Signed-off-by: Hector Martin <[email protected]>
1 parent 81a3085 commit da724b6

2 files changed

Lines changed: 59 additions & 9 deletions

File tree

src/diskutil.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ def mount(self, target):
166166
info = self.get("info", "-plist", target)
167167
return info["MountPoint"]
168168

169+
def remount_rw(self, target):
170+
logging.info(f"DiskUtil.remount_rw({target})")
171+
subprocess.run(["mount", "-u", "-w", target], check=True)
172+
169173
def addVolume(self, container, name, **kwargs):
170174
args = []
171175
for k, v in kwargs.items():

src/main.py

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ def __init__(self):
9090
self.data = json.load(open("installer_data.json"))
9191
self.credentials_validated = False
9292
self.expert = False
93+
self.ipsw = None
94+
self.osins = None
95+
self.osi = None
9396

9497
def input(self):
9598
self.flush_input()
@@ -306,11 +309,42 @@ def action_install_into_free(self, avail_free):
306309

307310
self.do_install(os_size)
308311

312+
def action_resume(self, oses):
313+
choices = {str(i): f"{p.desc}\n {str(o)}" for i, (p, o) in enumerate(oses)}
314+
315+
if len(choices) > 1:
316+
print()
317+
p_question("Choose an incomplete install to resume:")
318+
idx = self.choice("Installed OS", choices)
319+
else:
320+
idx = list(choices.keys())[0]
321+
322+
self.part, self.osi = oses[int(idx)]
323+
324+
p_progress(f"Resuming installation into {self.part.name} ({self.part.label})")
325+
326+
self.dutil.remount_rw(self.osi.system)
327+
328+
# Unhide the SystemVersion, if hidden
329+
cs = os.path.join(self.osi.system, "System/Library/CoreServices")
330+
sv_path = os.path.join(cs, "SystemVersion.plist")
331+
sv_dis_path = os.path.join(cs, "SystemVersion-disabled.plist")
332+
if not os.path.exists(sv_path):
333+
if not os.path.exists(sv_dis_path):
334+
p_error("Could not find SystemVersion.plist.")
335+
p_error("Cannot resume this installation.")
336+
return False
337+
os.rename(sv_dis_path, sv_path)
338+
339+
# Go for step2 again
340+
self.step2()
341+
309342
def do_install(self, total_size=None):
310343
p_progress(f"Installing stub macOS into {self.part.name} ({self.part.label})")
311344

312345
self.ins.prepare_volume(self.part)
313346
self.ins.check_volume()
347+
self.osi = self.ins.osi
314348
self.ins.install_files(self.cur_os)
315349

316350
self.osins.partition_disk(self.part.name, total_size)
@@ -379,7 +413,7 @@ def set_reduced_security(self):
379413
print()
380414
p_progress("Preparing the new OS for booting in Reduced Security mode...")
381415
try:
382-
subprocess.run(["bputil", "-g", "-v", self.ins.osi.vgid,
416+
subprocess.run(["bputil", "-g", "-v", self.osi.vgid,
383417
"-u", self.admin_user, "-p", self.admin_password], check=True)
384418
break
385419
except subprocess.CalledProcessError:
@@ -396,7 +430,7 @@ def bless(self):
396430
p_progress("Setting the new OS as the default boot volume...")
397431
try:
398432
subprocess.run(["bless", "--setBoot",
399-
"--device", "/dev/" + self.ins.osi.sys_volume,
433+
"--device", "/dev/" + self.osi.sys_volume,
400434
"--user", self.admin_user, "--stdinpass"],
401435
input=self.admin_password.encode("utf-8"),
402436
check=True)
@@ -409,7 +443,7 @@ def bless(self):
409443
p_warning("Let's try a different way. Sorry, you'll have to type it in again.")
410444
try:
411445
subprocess.run(["bless", "--setBoot",
412-
"--device", "/dev/" + self.ins.osi.sys_volume,
446+
"--device", "/dev/" + self.osi.sys_volume,
413447
"--user", self.admin_user], check=True)
414448
print()
415449
return
@@ -424,7 +458,10 @@ def bless(self):
424458
def step2(self):
425459
is_1tr = self.sysinfo.boot_mode == "one true recoveryOS"
426460
is_recovery = "recoveryOS" in self.sysinfo.boot_mode
427-
bootpicker_works = split_ver(self.sysinfo.macos_ver) >= split_ver(self.ipsw.min_macos)
461+
sys_ver = split_ver(self.sysinfo.macos_ver)
462+
bootpicker_works = sys_ver >= (12, 3)
463+
if not bootpicker_works and self.ipsw:
464+
bootpicker_works = sys_ver >= split_ver(self.ipsw.min_macos)
428465

429466
if is_1tr and self.is_sfr_recovery and self.ipsw.paired_sfr:
430467
subprocess.run([self.ins.step2_sh], check=True)
@@ -454,15 +491,16 @@ def flush_input(self):
454491

455492
def step2_indirect(self):
456493
# Hide the new volume until step2 is done
457-
os.rename(self.ins.systemversion_path,
458-
self.ins.systemversion_path.replace("SystemVersion.plist",
459-
"SystemVersion-disabled.plist"))
494+
cs = os.path.join(self.osi.system, "System/Library/CoreServices")
495+
sv_path = os.path.join(cs, "SystemVersion.plist")
496+
sv_dis_path = os.path.join(cs, "SystemVersion-disabled.plist")
497+
os.rename(sv_path, sv_dis_path)
460498

461499
p_success( "Installation successful!")
462500
print()
463501
p_progress("Install information:")
464-
p_info( f" APFS VGID: {col()}{self.ins.osi.vgid}")
465-
if self.osins.efi_part:
502+
p_info( f" APFS VGID: {col()}{self.osi.vgid}")
503+
if self.osins and self.osins.efi_part:
466504
p_info(f" EFI PARTUUID: {col()}{self.osins.efi_part.uuid.lower()}")
467505
print()
468506
p_message( "To be able to boot your new OS, you will need to complete one more step.")
@@ -761,6 +799,7 @@ def main_loop(self):
761799
parts_free = []
762800
parts_empty_apfs = []
763801
parts_resizable = []
802+
oses_incomplete = []
764803

765804
for i, p in enumerate(self.parts):
766805
if p.type in ("Apple_APFS_ISC",):
@@ -822,6 +861,8 @@ def main_loop(self):
822861
else:
823862
state += " "
824863
p_plain(f" OS: [{state}] {os}")
864+
if os.stub and not (os.bp and os.bp.get("coih", None)):
865+
oses_incomplete.append((p, os))
825866

826867
print()
827868
p_plain(f" [{b} ] = Booted OS, [{r} ] = Booted recovery, [{u} ] = Unknown")
@@ -835,6 +876,9 @@ def main_loop(self):
835876
actions = {}
836877

837878
default = None
879+
if oses_incomplete:
880+
actions["p"] = "Resume an incomplete installation (reboot step)"
881+
default = default or "p"
838882
if parts_free:
839883
actions["f"] = "Install an OS into free space"
840884
default = default or "f"
@@ -866,6 +910,8 @@ def main_loop(self):
866910
elif act == "m":
867911
p_error("Unimplemented")
868912
sys.exit(1)
913+
elif act == "p":
914+
return self.action_resume(oses_incomplete)
869915
elif act == "q":
870916
return False
871917

0 commit comments

Comments
 (0)