@@ -85,6 +85,8 @@ def __init__(self):
8585 self .osi = None
8686 self .m1n1 = "boot/m1n1.bin"
8787 self .m1n1_ver = m1n1 .get_version (self .m1n1 )
88+ self .sys_disk = None
89+ self .cur_disk = None
8890
8991 def input (self ):
9092 self .flush_input ()
@@ -225,6 +227,33 @@ def action_install_into_container(self, avail_parts):
225227
226228 self .do_install ()
227229
230+ def action_wipe (self ):
231+ p_warning ("This will wipe all data on the currently selected disk." )
232+ p_warning ("Are you sure you want to continue?" )
233+ if not self .yesno ("Wipe my disk" ):
234+ return True
235+
236+ print ()
237+
238+ template = self .choose_os ()
239+
240+ self .osins = osinstall .OSInstaller (self .dutil , self .data , template )
241+ self .osins .load_package ()
242+
243+ min_size = STUB_SIZE + self .osins .min_size
244+ print ()
245+ p_message (f"Minimum required space for this OS: { ssize (min_size )} " )
246+
247+ start , end = self .dutil .get_disk_usable_range (self .cur_disk )
248+ os_size = self .get_os_size_and_info (end - start , min_size , template )
249+
250+ p_progress (f"Partitioning the whole disk ({ self .cur_disk } )" )
251+ self .part = self .dutil .partitionDisk (self .cur_disk , "apfs" , self .osins .name , STUB_SIZE )
252+
253+ p_progress (f"Creating new stub macOS named { self .osins .name } " )
254+ logging .info (f"Creating stub macOS: { self .osins .name } " )
255+ self .do_install (os_size )
256+
228257 def action_install_into_free (self , avail_free ):
229258 template = self .choose_os ()
230259
@@ -256,32 +285,41 @@ def action_install_into_free(self, avail_free):
256285 print ()
257286 p_message (f"Available free space: { ssize (free_part .size )} " )
258287
288+ os_size = self .get_os_size_and_info (free_part .size , min_size , template )
289+
290+ p_progress (f"Creating new stub macOS named { self .osins .name } " )
291+ logging .info (f"Creating stub macOS: { self .osins .name } " )
292+ self .part = self .dutil .addPartition (free_part .name , "apfs" , self .osins .name , STUB_SIZE )
293+
294+ self .do_install (os_size )
295+
296+ def get_os_size_and_info (self , free_size , min_size , template ):
259297 os_size = None
260298 if self .osins .expandable :
261299 print ()
262300 p_question ("How much space should be allocated to the new OS?" )
263301 p_message (" You can enter a size such as '1GB', a fraction such as '50%'," )
264302 p_message (" the word 'min' for the smallest allowable size, or" )
265303 p_message (" the word 'max' to use all available space." )
266- min_perc = 100 * min_size / free_part . size
304+ min_perc = 100 * min_size / free_size
267305 while True :
268306 os_size = self .get_size ("New OS size" , default = "max" ,
269- min = min_size , max = free_part . size ,
270- total = free_part . size )
307+ min = min_size , max = free_size ,
308+ total = free_size )
271309 if os_size is None :
272310 continue
273311 os_size = align_down (os_size , PART_ALIGN )
274312 if os_size < min_size :
275313 p_error (f"Size is too small, please enter a value > { ssize (min_size )} ({ min_perc :.2f} %)" )
276314 continue
277- if os_size > free_part . size :
278- p_error (f"Size is too large, please enter a value < { ssize (free_part . size )} " )
315+ if os_size > free_size :
316+ p_error (f"Size is too large, please enter a value < { ssize (free_size )} " )
279317 continue
280318 break
281319
282320 print ()
283321 p_message (f"The new OS will be allocated { ssize (os_size )} of space," )
284- p_message (f"leaving { ssize (free_part . size - os_size )} of free space." )
322+ p_message (f"leaving { ssize (free_size - os_size )} of free space." )
285323 os_size -= STUB_SIZE
286324
287325 print ()
@@ -296,12 +334,7 @@ def action_install_into_free(self, avail_free):
296334 logging .info (f"Chosen IPSW version: { ipsw .version } " )
297335 self .ins = stub .StubInstaller (self .sysinfo , self .dutil , self .osinfo )
298336 self .ins .load_ipsw (ipsw )
299-
300- p_progress (f"Creating new stub macOS named { label } " )
301- logging .info (f"Creating stub macOS: { label } " )
302- self .part = self .dutil .addPartition (free_part .name , "apfs" , label , STUB_SIZE )
303-
304- self .do_install (os_size )
337+ return os_size
305338
306339 def action_resume_or_upgrade (self , oses , upgrade ):
307340 choices = {str (i ): f"{ p .desc } \n { str (o )} " for i , (p , o ) in enumerate (oses )}
@@ -422,6 +455,8 @@ def choose_os(self):
422455 os_list = self .data ["os_list" ]
423456 if not self .expert :
424457 os_list = [i for i in os_list if not i .get ("expert" , False )]
458+ if self .cur_disk != self .sys_disk :
459+ os_list = [i for i in os_list if i .get ("external_boot" , False )]
425460 p_question ("Choose an OS to install:" )
426461 idx = self .choice ("OS" , [i ["name" ] for i in os_list ])
427462 os = os_list [idx ]
@@ -736,6 +771,22 @@ def action_resize(self, resizable):
736771
737772 return True
738773
774+ def action_select_disk (self ):
775+ choices = {"1" : "Internal storage" }
776+
777+ for i , disk in enumerate (self .external_disks ):
778+ choices [str (i + 2 )] = f"{ disk ['IORegistryEntryName' ]} ({ ssize (disk ['Size' ])} )"
779+
780+ print ()
781+ p_question ("Choose a disk:" )
782+ idx = int (self .choice ("Disk" , choices ))
783+ if idx == 1 :
784+ self .cur_disk = self .sys_disk
785+ else :
786+ self .cur_disk = self .external_disks [idx - 2 ]["DeviceIdentifier" ]
787+
788+ return True
789+
739790 def main (self ):
740791 print ()
741792 p_message ("Welcome to the Asahi Linux installer!" )
@@ -803,13 +854,24 @@ def main_loop(self):
803854 p_progress ("Collecting partition information..." )
804855 self .dutil = diskutil .DiskUtil ()
805856 self .dutil .get_info ()
806- self .sysdsk = self .dutil .find_system_disk ()
807- p_info (f" System disk: { col ()} { self .sysdsk } " )
808- self .parts = self .dutil .get_partitions (self .sysdsk )
857+ if self .sys_disk is None :
858+ self .cur_disk = self .sys_disk = self .dutil .find_system_disk ()
859+
860+ p_info (f" System disk: { col ()} { self .sys_disk } " )
861+
862+ if self .expert :
863+ self .external_disks = self .dutil .find_external_disks ()
864+ else :
865+ self .external_disks = None
866+
867+ if self .external_disks :
868+ p_info (f" Found { len (self .external_disks )} external disk(s)" )
869+
870+ self .parts = self .dutil .get_partitions (self .cur_disk )
809871 print ()
810872
811873 p_progress ("Collecting OS information..." )
812- self .osinfo = osenum .OSEnum (self .sysinfo , self .dutil , self .sysdsk )
874+ self .osinfo = osenum .OSEnum (self .sysinfo , self .dutil , self .cur_disk )
813875 self .osinfo .collect (self .parts )
814876
815877 parts_free = []
@@ -842,9 +904,14 @@ def main_loop(self):
842904 p .desc = f"{ p .type } ({ ssize (p .size )} )"
843905
844906 print ()
845- p_message (f"Partitions in system disk ({ self .sysdsk } ):" )
907+ if self .cur_disk == self .sys_disk :
908+ t = "system"
909+ else :
910+ t = "external"
911+ p_message (f"Partitions in { t } disk ({ self .cur_disk } ):" )
846912
847- self .cur_os = None
913+ if self .cur_disk == self .sys_disk :
914+ self .cur_os = None
848915 self .is_sfr_recovery = self .sysinfo .boot_vgid in (osenum .UUID_SROS , osenum .UUID_FROS )
849916 default_os = None
850917
@@ -853,6 +920,8 @@ def main_loop(self):
853920 u = col (RED ) + "?" + col ()
854921 d = col (BRIGHT ) + "*" + col ()
855922
923+ is_gpt = self .dutil .disks [self .cur_disk ]["Content" ] == "GUID_partition_scheme"
924+
856925 for i , p in enumerate (self .parts ):
857926 if p .desc is None :
858927 continue
@@ -898,14 +967,20 @@ def main_loop(self):
898967 if oses_incomplete :
899968 actions ["p" ] = "Repair an incomplete installation"
900969 default = default or "p"
901- if parts_free :
970+ if parts_free and is_gpt :
902971 actions ["f" ] = "Install an OS into free space"
903972 default = default or "f"
904- if parts_empty_apfs and False : # This feature is confusing, disable it for now
973+ if parts_empty_apfs and is_gpt and False : # This feature is confusing, disable it for now
905974 actions ["a" ] = "Install an OS into an existing APFS container"
906- if parts_resizable :
975+ if parts_resizable and is_gpt :
907976 actions ["r" ] = "Resize an existing partition to make space for a new OS"
908977 default = default or "r"
978+ if self .cur_disk != self .sys_disk :
979+ actions ["w" ] = "Wipe and install into the whole disk"
980+ # Never make this default!
981+ if self .external_disks :
982+ actions ["d" ] = "Select another disk for installation"
983+ default = default or "d"
909984 if oses_upgradable :
910985 actions ["m" ] = "Upgrade m1n1 on an existing OS"
911986 default = default or "m"
@@ -931,6 +1006,10 @@ def main_loop(self):
9311006 return self .action_repair_or_upgrade (oses_upgradable , upgrade = True )
9321007 elif act == "p" :
9331008 return self .action_repair_or_upgrade (oses_incomplete , upgrade = False )
1009+ elif act == "d" :
1010+ return self .action_select_disk ()
1011+ elif act == "w" :
1012+ return self .action_wipe ()
9341013 elif act == "q" :
9351014 return False
9361015
0 commit comments