disks.c revision 15465
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $Id: disks.c,v 1.46 1996/04/29 06:47:08 jkh Exp $
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include <ctype.h>
39#include <sys/disklabel.h>
40
41/* Where we start displaying chunk information on the screen */
42#define CHUNK_START_ROW		5
43
44/* Where we keep track of MBR chunks */
45static struct chunk *chunk_info[16];
46static int current_chunk;
47
48static void
49record_chunks(Disk *d)
50{
51    struct chunk *c1;
52    int i = 0;
53    int last_free = 0;
54    if (!d->chunks)
55	msgFatal("No chunk list found for %s!", d->name);
56    current_chunk = 0;
57    for (c1 = d->chunks->part; c1; c1 = c1->next) {
58	if (c1->type == unused && c1->size > last_free) {
59	    last_free = c1->size;
60	    current_chunk = i;
61	}
62	chunk_info[i++] = c1;
63    }
64    chunk_info[i] = NULL;
65}
66
67static void
68print_chunks(Disk *d)
69{
70    int row;
71    int i;
72
73    if ((!d->bios_cyl || d->bios_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64)) {
74	int sz;
75
76	msgConfirm("WARNING:  The current geometry for %s is incorrect.  Using\n"
77		   "a default geometry of 64 heads and 32 sectors.  If this geometry\n"
78		   "is incorrect or you are unsure as to whether or not it's correct,\n"
79		   "please consult the Hardware Guide in the Documentation submenu\n"
80		   "or use the (G)eometry command to change it now.", d->name);
81	d->bios_hd = 64;
82	d->bios_sect = 32;
83	sz = 0;
84	for (i = 0; chunk_info[i]; i++)
85	    sz += chunk_info[i]->size;
86	if (sz)
87	    d->bios_cyl = sz / ONE_MEG;
88	else
89	    msgConfirm("Couldn't set geometry!  You'll have to do it by hand.");
90    }
91    attrset(A_NORMAL);
92    mvaddstr(0, 0, "Disk name:\t");
93    clrtobot();
94    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
95    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
96    mvprintw(1, 0,
97	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors",
98	     d->bios_cyl, d->bios_hd, d->bios_sect);
99    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
100	     "Offset", "Size", "End", "Name", "PType", "Desc",
101	     "Subtype", "Flags");
102    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
103	if (i == current_chunk)
104	    attrset(A_REVERSE);
105	mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s",
106		 chunk_info[i]->offset, chunk_info[i]->size,
107		 chunk_info[i]->end, chunk_info[i]->name,
108		 chunk_info[i]->type, chunk_n[chunk_info[i]->type],
109		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
110	if (i == current_chunk)
111	    attrset(A_NORMAL);
112    }
113}
114
115static void
116print_command_summary()
117{
118    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
119    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan     C = Create Partition");
120    mvprintw(17, 0, "D = Delete Partition   G = Set Drive Geometry S = Set Bootable");
121    mvprintw(18, 0, "U = Undo All Changes   Q = Finish");
122    if (!RunningAsInit)
123	mvprintw(18, 48, "W = Write Changes");
124    mvprintw(20, 0, "The currently selected partition is displayed in ");
125    attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video.");
126    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move.");
127    move(0, 0);
128}
129
130static u_char *
131getBootMgr(char *dname)
132{
133    extern u_char mbr[], bteasy17[];
134    char str[80];
135    char *cp;
136    int i = 0;
137
138    cp = variable_get(VAR_BOOTMGR);
139    if (!cp) {
140	/* Figure out what kind of MBR the user wants */
141	sprintf(str, "Install Boot Manager for drive %s?", dname);
142	MenuMBRType.title = str;
143	i = dmenuOpenSimple(&MenuMBRType);
144    }
145    else {
146	if (!strncmp(cp, "boot", 4))
147	    BootMgr = 0;
148	else if (!strcmp(cp, "standard"))
149	    BootMgr = 1;
150	else
151	    BootMgr = 2;
152    }
153    if (cp || i) {
154	switch (BootMgr) {
155	case 0:
156	    return bteasy17;
157
158	case 1:
159	    return mbr;
160
161	case 2:
162	default:
163	    break;
164	}
165    }
166    return NULL;
167}
168
169void
170diskPartition(Device *dev, Disk *d)
171{
172    char *p;
173    int key = 0;
174    Boolean chunking;
175    char *msg = NULL;
176    u_char *mbrContents;
177    WINDOW *w;
178
179    chunking = TRUE;
180    keypad(stdscr, TRUE);
181
182    w = savescr();
183    clear();
184    record_chunks(d);
185    while (chunking) {
186	print_chunks(d);
187	print_command_summary();
188	if (msg) {
189	    standout(); mvprintw(23, 0, msg); standend();
190	    beep();
191	    msg = NULL;
192	}
193
194	key = toupper(getch());
195	switch (key) {
196
197	case '\014':	/* ^L */
198	    clear();
199	    print_command_summary();
200	    continue;
201
202	case KEY_UP:
203	case '-':
204	    if (current_chunk != 0)
205		--current_chunk;
206	    break;
207
208	case KEY_DOWN:
209	case '+':
210	case '\r':
211	case '\n':
212	    if (chunk_info[current_chunk + 1])
213		++current_chunk;
214	    break;
215
216	case KEY_HOME:
217	    current_chunk = 0;
218	    break;
219
220	case KEY_END:
221	    while (chunk_info[current_chunk + 1])
222		++current_chunk;
223	    break;
224
225	case KEY_F(1):
226	case '?':
227	    systemDisplayHelp("slice");
228	    break;
229
230	case 'A': {
231	    int rv;
232
233	    rv = msgYesNo("Do you want to do this with a true partition entry\n"
234			  "so as to remain cooperative with any future possible\n"
235			  "operating systems on the drive(s)?");
236	    if (rv) {
237		rv = !msgYesNo("This is dangerous in that it will make the drive totally\n"
238			       "uncooperative with other potential operating systems on the\n"
239			       "same disk.  It will lead instead to a totally dedicated disk,\n"
240			       "starting at the very first sector, bypassing all BIOS geometry\n"
241			       "considerations.  This precludes the existance of any boot\n"
242			       "manager or other stuff in sector 0, since the BSD bootstrap\n"
243			       "will live there.\n"
244			       "You will run into serious trouble with ST-506 and ESDI drives\n"
245			       "and possibly some IDE drives (e.g. drives running under the\n"
246			       "control of sort of disk manager).  SCSI drives are considerably\n"
247			       "less at risk.\n\n"
248			       "Do you insist on dedicating the entire disk this way?");
249	    }
250	    All_FreeBSD(d, rv);
251	    if (rv)
252		d->bios_hd = d->bios_sect = d->bios_cyl = 1;
253	    variable_set2(DISK_PARTITIONED, "yes");
254	    record_chunks(d);
255	}
256	break;
257
258	case 'B':
259	    if (chunk_info[current_chunk]->type != freebsd)
260		msg = "Can only scan for bad blocks in FreeBSD partition.";
261	    else if (strncmp(d->name, "sd", 2) ||
262		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
263			       "Are you sure you want to do this on a SCSI disk?")) {
264		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
265		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
266		else
267		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
268	    }
269	    break;
270
271	case 'C':
272	    if (chunk_info[current_chunk]->type != unused)
273		msg = "Partition in use, delete it first or move to an unused one.";
274	    else {
275		char *val, tmp[20], *cp;
276		int size;
277
278		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
279		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n"
280				  "a trailing `M' for megabytes (e.g. 20M).");
281		if (val && (size = strtol(val, &cp, 0)) > 0) {
282		    if (*cp && toupper(*cp) == 'M')
283			size *= ONE_MEG;
284		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3,
285				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
286		    variable_set2(DISK_PARTITIONED, "yes");
287		    record_chunks(d);
288		}
289	    }
290	    break;
291
292	case '\177':
293	case 'D':
294	    if (chunk_info[current_chunk]->type == unused)
295		msg = "Partition is already unused!";
296	    else {
297		Delete_Chunk(d, chunk_info[current_chunk]);
298		variable_set2(DISK_PARTITIONED, "yes");
299		record_chunks(d);
300	    }
301	    break;
302
303	case 'G': {
304	    char *val, geometry[80];
305
306	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
307	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
308			      "Don't forget to use the two slash (/) separator characters!\n"
309			      "It's not possible to parse the field without them.");
310	    if (val) {
311		d->bios_cyl = strtol(val, &val, 0);
312		d->bios_hd = strtol(val + 1, &val, 0);
313		d->bios_sect = strtol(val + 1, 0, 0);
314	    }
315	}
316	break;
317
318    case 'S':
319	/* Set Bootable */
320	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
321	break;
322
323    case 'U':
324	    clear();
325	    if (msgYesNo("Are you SURE you want to Undo everything?"))
326		break;
327	    d = Open_Disk(d->name);
328	    if (!d) {
329		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
330		break;
331	    }
332	    Free_Disk(dev->private);
333	    dev->private = d;
334	    variable_unset(DISK_PARTITIONED);
335	    record_chunks(d);
336	    break;
337
338	case 'W':
339	    if (!msgYesNo("WARNING:  This should only be used for modifying an\n"
340			  "EXISTING installation - DO NOT USE this option if you\n"
341			  "are installing FreeBSD for the first time!  This is not\n"
342			  "an option for use during the standard install.\n\n"
343			  "Are you absolutely sure you want to do this now?")) {
344		variable_set2(DISK_PARTITIONED, "yes");
345		clear();
346
347		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
348		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
349		 * booteasy or a "standard" MBR -- both would be fatal in this case.
350		 */
351		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
352		    && (mbrContents = getBootMgr(d->name)) != NULL)
353		    Set_Boot_Mgr(d, mbrContents);
354
355		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
356		    msgConfirm("Disk partition write returned an error status!");
357		else
358		    msgConfirm("Wrote FDISK partition information out successfully.");
359	    }
360	    break;
361
362	case '|':
363	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
364			  "No seat belts whatsoever are provided!")) {
365		WINDOW *w;
366
367		w = savescr();
368		dialog_clear();
369		end_dialog();
370		DialogActive = FALSE;
371		slice_wizard(d);
372		variable_set2(DISK_PARTITIONED, "yes");
373		dialog_clear();
374		DialogActive = TRUE;
375		record_chunks(d);
376		restorescr(w);
377	    }
378	    else
379		msg = "Wise choice!";
380	    break;
381
382	case 'Q':
383	    chunking = FALSE;
384	    clear();
385	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
386	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
387	     * booteasy or a "standard" MBR -- both would be fatal in this case.
388	     */
389	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
390		&& (mbrContents = getBootMgr(d->name)) != NULL)
391		Set_Boot_Mgr(d, mbrContents);
392	    break;
393
394	default:
395	    beep();
396	    msg = "Type F1 or ? for help";
397	    break;
398	}
399    }
400    p = CheckRules(d);
401    if (p) {
402	char buf[FILENAME_MAX];
403
404	dialog_clear();
405        use_helpline("Press F1 to read more about disk partitioning.");
406	use_helpfile(systemHelpFile("partition", buf));
407	dialog_mesgbox("Disk partitioning warning:", p, -1, -1);
408	free(p);
409    }
410    restorescr(w);
411}
412
413static int
414partitionHook(dialogMenuItem *selected)
415{
416    Device **devs = NULL;
417
418    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
419    if (!devs) {
420	msgConfirm("Unable to find disk %s!", selected->prompt);
421	return DITEM_FAILURE;
422    }
423    devs[0]->enabled = TRUE;
424    diskPartition(devs[0], (Disk *)devs[0]->private);
425    return DITEM_SUCCESS;
426}
427
428static int
429partitionCheck(dialogMenuItem *selected)
430{
431    Device **devs = NULL;
432
433    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
434    if (!devs || devs[0]->enabled == FALSE)
435	return FALSE;
436    return TRUE;
437}
438
439int
440diskPartitionEditor(dialogMenuItem *self)
441{
442    DMenu *menu;
443    Device **devs;
444    int i, cnt;
445    char *cp;
446
447    cp = variable_get(VAR_DISK);
448    devs = deviceFind(cp, DEVICE_TYPE_DISK);
449    cnt = deviceCount(devs);
450    if (!cnt) {
451	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
452		   "properly probed at boot time.  See the Hardware Guide on the\n"
453		   "Documentation menu for clues on diagnosing this type of problem.");
454	i = DITEM_FAILURE;
455    }
456    else if (cnt == 1) {
457	devs[0]->enabled = TRUE;
458	diskPartition(devs[0], (Disk *)devs[0]->private);
459	i = DITEM_SUCCESS;
460    }
461    else {
462	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
463	if (!menu) {
464	    msgConfirm("No devices suitable for installation found!\n\n"
465		       "Please verify that your disk controller (and attached drives)\n"
466		       "were detected properly.  This can be done by pressing the\n"
467		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
468		       "the boot messages.  Press [Scroll Lock] again to return.");
469	    i = DITEM_FAILURE;
470	}
471	else {
472	    i = dmenuOpenSimple(menu) ? DITEM_SUCCESS : DITEM_FAILURE;
473	    free(menu);
474	}
475	i = i | DITEM_RESTORE | DITEM_RECREATE;
476    }
477    return i;
478}
479
480int
481diskPartitionWrite(dialogMenuItem *self)
482{
483    Device **devs;
484    char *cp;
485    int i;
486
487    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
488	return DITEM_SUCCESS;
489    else if (!cp) {
490	msgConfirm("You must partition the disk(s) before this option can be used.");
491	return DITEM_FAILURE;
492    }
493
494    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
495    if (!devs) {
496	msgConfirm("Unable to find any disks to write to??");
497	return DITEM_FAILURE;
498    }
499    if (isDebug())
500	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
501
502    for (i = 0; devs[i]; i++) {
503	Chunk *c1;
504	Disk *d = (Disk *)devs[i]->private;
505
506	if (!devs[i]->enabled)
507	    continue;
508
509	Set_Boot_Blocks(d, boot1, boot2);
510	msgNotify("Writing partition information to drive %s", d->name);
511	if (!Fake && Write_Disk(d)) {
512	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
513	    return DITEM_FAILURE;
514	}
515	/* Now scan for bad blocks, if necessary */
516	for (c1 = d->chunks->part; c1; c1 = c1->next) {
517	    if (c1->flags & CHUNK_BAD144) {
518		int ret;
519
520		msgNotify("Running bad block scan on partition %s", c1->name);
521		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
522		if (ret)
523		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
524		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
525		if (ret)
526		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
527	    }
528	}
529    }
530    /* Now it's not "yes", but "written" */
531    variable_set2(DISK_PARTITIONED, "written");
532    return DITEM_SUCCESS;
533}
534