disks.c revision 16887
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.52 1996/06/25 18:41: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	dialog_clear();
77	msgConfirm("WARNING:  The current geometry for %s is incorrect.  Using\n"
78		   "a default geometry of 64 heads and 32 sectors.  If this geometry\n"
79		   "is incorrect or you are unsure as to whether or not it's correct,\n"
80		   "please consult the Hardware Guide in the Documentation submenu\n"
81		   "or use the (G)eometry command to change it now.", d->name);
82	d->bios_hd = 64;
83	d->bios_sect = 32;
84	sz = 0;
85	for (i = 0; chunk_info[i]; i++)
86	    sz += chunk_info[i]->size;
87	if (sz)
88	    d->bios_cyl = sz / ONE_MEG;
89	else
90	    msgConfirm("Couldn't set geometry!  You'll have to do it by hand.");
91    }
92    attrset(A_NORMAL);
93    mvaddstr(0, 0, "Disk name:\t");
94    clrtobot();
95    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
96    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
97    mvprintw(1, 0,
98	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors",
99	     d->bios_cyl, d->bios_hd, d->bios_sect);
100    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
101	     "Offset", "Size", "End", "Name", "PType", "Desc",
102	     "Subtype", "Flags");
103    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
104	if (i == current_chunk)
105	    attrset(ATTR_SELECTED);
106	mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s",
107		 chunk_info[i]->offset, chunk_info[i]->size,
108		 chunk_info[i]->end, chunk_info[i]->name,
109		 chunk_info[i]->type, chunk_n[chunk_info[i]->type],
110		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
111	if (i == current_chunk)
112	    attrset(A_NORMAL);
113    }
114}
115
116static void
117print_command_summary()
118{
119    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
120    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan     C = Create Partition");
121    mvprintw(17, 0, "D = Delete Partition   G = Set Drive Geometry S = Set Bootable");
122    mvprintw(18, 0, "U = Undo All Changes   Q = Finish");
123    if (!RunningAsInit)
124	mvprintw(18, 46, "W = Write Changes");
125    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
126    move(0, 0);
127}
128
129static u_char *
130getBootMgr(char *dname)
131{
132    extern u_char mbr[], bteasy17[];
133    char str[80];
134    char *cp;
135    int i = 0;
136
137    cp = variable_get(VAR_BOOTMGR);
138    if (!cp) {
139	/* Figure out what kind of MBR the user wants */
140	sprintf(str, "Install Boot Manager for drive %s?", dname);
141	MenuMBRType.title = str;
142	i = dmenuOpenSimple(&MenuMBRType, FALSE);
143    }
144    else {
145	if (!strncmp(cp, "boot", 4))
146	    BootMgr = 0;
147	else if (!strcmp(cp, "standard"))
148	    BootMgr = 1;
149	else
150	    BootMgr = 2;
151    }
152    if (cp || i) {
153	switch (BootMgr) {
154	case 0:
155	    return bteasy17;
156
157	case 1:
158	    return mbr;
159
160	case 2:
161	default:
162	    break;
163	}
164    }
165    return NULL;
166}
167
168void
169diskPartition(Device *dev, Disk *d)
170{
171    char *p;
172    int key = 0;
173    Boolean chunking;
174    char *msg = NULL;
175    u_char *mbrContents;
176    WINDOW *w;
177
178    chunking = TRUE;
179    keypad(stdscr, TRUE);
180
181    w = savescr();
182    clear();
183    record_chunks(d);
184    while (chunking) {
185	print_chunks(d);
186	print_command_summary();
187	if (msg) {
188	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
189	    beep();
190	    msg = NULL;
191	}
192
193	key = toupper(getch());
194	switch (key) {
195
196	case '\014':	/* ^L */
197	    clear();
198	    print_command_summary();
199	    continue;
200
201	case KEY_UP:
202	case '-':
203	    if (current_chunk != 0)
204		--current_chunk;
205	    break;
206
207	case KEY_DOWN:
208	case '+':
209	case '\r':
210	case '\n':
211	    if (chunk_info[current_chunk + 1])
212		++current_chunk;
213	    break;
214
215	case KEY_HOME:
216	    current_chunk = 0;
217	    break;
218
219	case KEY_END:
220	    while (chunk_info[current_chunk + 1])
221		++current_chunk;
222	    break;
223
224	case KEY_F(1):
225	case '?':
226	    systemDisplayHelp("slice");
227	    break;
228
229	case 'A': {
230	    int rv;
231
232	    rv = msgYesNo("Do you want to do this with a true partition entry\n"
233			  "so as to remain cooperative with any future possible\n"
234			  "operating systems on the drive(s)?");
235	    if (rv) {
236		rv = !msgYesNo("This is dangerous in that it will make the drive totally\n"
237			       "uncooperative with other potential operating systems on the\n"
238			       "same disk.  It will lead instead to a totally dedicated disk,\n"
239			       "starting at the very first sector, bypassing all BIOS geometry\n"
240			       "considerations.  This precludes the existance of any boot\n"
241			       "manager or other stuff in sector 0, since the BSD bootstrap\n"
242			       "will live there.\n"
243			       "You will run into serious trouble with ST-506 and ESDI drives\n"
244			       "and possibly some IDE drives (e.g. drives running under the\n"
245			       "control of sort of disk manager).  SCSI drives are considerably\n"
246			       "less at risk.\n\n"
247			       "Do you insist on dedicating the entire disk this way?");
248	    }
249	    All_FreeBSD(d, rv);
250	    if (rv)
251		d->bios_hd = d->bios_sect = d->bios_cyl = 1;
252	    variable_set2(DISK_PARTITIONED, "yes");
253	    record_chunks(d);
254	}
255	break;
256
257	case 'B':
258	    if (chunk_info[current_chunk]->type != freebsd)
259		msg = "Can only scan for bad blocks in FreeBSD partition.";
260	    else if (strncmp(d->name, "sd", 2) ||
261		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
262			       "Are you sure you want to do this on a SCSI disk?")) {
263		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
264		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
265		else
266		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
267	    }
268	    break;
269
270	case 'C':
271	    if (chunk_info[current_chunk]->type != unused)
272		msg = "Partition in use, delete it first or move to an unused one.";
273	    else {
274		char *val, tmp[20], *cp;
275		int size, subtype;
276		chunk_e partitiontype;
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\n"
280				       "or append 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		    strcpy(tmp, "165");
285		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
286				      "Pressing Enter will choose the default, a native FreeBSD\n"
287				      "partition (type 165).  You can choose other types, 6 for a\n"
288				      "DOS partition or 131 for a Linux partition, for example.\n\n"
289				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
290				      "be formatted or otherwise prepared, it will simply reserve space\n"
291				      "for you to use another tool, such as DOS FORMAT, to later format\n"
292				      "and use the partition.");
293		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
294			if (subtype==165)
295				partitiontype=freebsd;
296			else if (subtype==6)
297				partitiontype=fat;
298			else
299				partitiontype=unknown;
300		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
301				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
302		    variable_set2(DISK_PARTITIONED, "yes");
303		    record_chunks(d);
304		    }
305		}
306	    }
307	    break;
308
309	case '\177':
310	case 'D':
311	    if (chunk_info[current_chunk]->type == unused)
312		msg = "Partition is already unused!";
313	    else {
314		Delete_Chunk(d, chunk_info[current_chunk]);
315		variable_set2(DISK_PARTITIONED, "yes");
316		record_chunks(d);
317	    }
318	    break;
319
320	case 'G': {
321	    char *val, geometry[80];
322
323	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
324	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
325			      "Don't forget to use the two slash (/) separator characters!\n"
326			      "It's not possible to parse the field without them.");
327	    if (val) {
328		d->bios_cyl = strtol(val, &val, 0);
329		d->bios_hd = strtol(val + 1, &val, 0);
330		d->bios_sect = strtol(val + 1, 0, 0);
331	    }
332	}
333	break;
334
335    case 'S':
336	/* Set Bootable */
337	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
338	break;
339
340    case 'U':
341	    clear();
342	    if (msgYesNo("Are you SURE you want to Undo everything?"))
343		break;
344	    d = Open_Disk(d->name);
345	    if (!d) {
346		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
347		break;
348	    }
349	    Free_Disk(dev->private);
350	    dev->private = d;
351	    variable_unset(DISK_PARTITIONED);
352	    record_chunks(d);
353	    break;
354
355	case 'W':
356	    if (!msgYesNo("WARNING:  This should only be used for modifying an\n"
357			  "EXISTING installation - DO NOT USE this option if you\n"
358			  "are installing FreeBSD for the first time!  This is not\n"
359			  "an option for use during the standard install.\n\n"
360			  "Are you absolutely sure you want to do this now?")) {
361		WINDOW *save = savescr();
362
363		variable_set2(DISK_PARTITIONED, "yes");
364		clear();
365
366		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
367		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
368		 * booteasy or a "standard" MBR -- both would be fatal in this case.
369		 */
370		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
371		    && (mbrContents = getBootMgr(d->name)) != NULL)
372		    Set_Boot_Mgr(d, mbrContents);
373
374		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
375		    msgConfirm("Disk partition write returned an error status!");
376		else
377		    msgConfirm("Wrote FDISK partition information out successfully.");
378		restorescr(save);
379	    }
380	    break;
381
382	case '|':
383	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
384			  "No seat belts whatsoever are provided!")) {
385		WINDOW *w;
386
387		w = savescr();
388		dialog_clear();
389		end_dialog();
390		DialogActive = FALSE;
391		slice_wizard(d);
392		variable_set2(DISK_PARTITIONED, "yes");
393		dialog_clear();
394		DialogActive = TRUE;
395		record_chunks(d);
396		restorescr(w);
397	    }
398	    else
399		msg = "Wise choice!";
400	    break;
401
402	case 'Q':
403	    chunking = FALSE;
404	    clear();
405	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
406	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
407	     * booteasy or a "standard" MBR -- both would be fatal in this case.
408	     */
409	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
410		&& (mbrContents = getBootMgr(d->name)) != NULL)
411		Set_Boot_Mgr(d, mbrContents);
412	    break;
413
414	default:
415	    beep();
416	    msg = "Type F1 or ? for help";
417	    break;
418	}
419    }
420    p = CheckRules(d);
421    if (p) {
422	char buf[FILENAME_MAX];
423
424	dialog_clear();
425        use_helpline("Press F1 to read more about disk partitioning.");
426	use_helpfile(systemHelpFile("partition", buf));
427	dialog_mesgbox("Disk partitioning warning:", p, -1, -1);
428	free(p);
429    }
430    restorescr(w);
431}
432
433static int
434partitionHook(dialogMenuItem *selected)
435{
436    Device **devs = NULL;
437
438    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
439    if (!devs) {
440	msgConfirm("Unable to find disk %s!", selected->prompt);
441	return DITEM_FAILURE;
442    }
443    /* Toggle enabled status? */
444    if (!devs[0]->enabled) {
445	devs[0]->enabled = TRUE;
446	diskPartition(devs[0], (Disk *)devs[0]->private);
447    }
448    else
449	devs[0]->enabled = FALSE;
450    return DITEM_SUCCESS | DITEM_REDRAW;
451}
452
453static int
454partitionCheck(dialogMenuItem *selected)
455{
456    Device **devs = NULL;
457
458    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
459    if (!devs || devs[0]->enabled == FALSE)
460	return FALSE;
461    return TRUE;
462}
463
464int
465diskPartitionEditor(dialogMenuItem *self)
466{
467    DMenu *menu;
468    Device **devs;
469    int i, cnt;
470    char *cp;
471
472    cp = variable_get(VAR_DISK);
473    devs = deviceFind(cp, DEVICE_TYPE_DISK);
474    cnt = deviceCount(devs);
475    if (!cnt) {
476	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
477		   "properly probed at boot time.  See the Hardware Guide on the\n"
478		   "Documentation menu for clues on diagnosing this type of problem.");
479	i = DITEM_FAILURE;
480    }
481    else if (cnt == 1) {
482	devs[0]->enabled = TRUE;
483	diskPartition(devs[0], (Disk *)devs[0]->private);
484	i = DITEM_SUCCESS;
485    }
486    else {
487	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
488	if (!menu) {
489	    msgConfirm("No devices suitable for installation found!\n\n"
490		       "Please verify that your disk controller (and attached drives)\n"
491		       "were detected properly.  This can be done by pressing the\n"
492		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
493		       "the boot messages.  Press [Scroll Lock] again to return.");
494	    i = DITEM_FAILURE;
495	}
496	else {
497	    i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
498	    free(menu);
499	}
500	i = i | DITEM_RESTORE | DITEM_RECREATE;
501    }
502    return i;
503}
504
505int
506diskPartitionWrite(dialogMenuItem *self)
507{
508    Device **devs;
509    char *cp;
510    int i;
511
512    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
513	return DITEM_SUCCESS;
514    else if (!cp) {
515	msgConfirm("You must partition the disk(s) before this option can be used.");
516	return DITEM_FAILURE;
517    }
518
519    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
520    if (!devs) {
521	msgConfirm("Unable to find any disks to write to??");
522	return DITEM_FAILURE;
523    }
524    if (isDebug())
525	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
526
527    for (i = 0; devs[i]; i++) {
528	Chunk *c1;
529	Disk *d = (Disk *)devs[i]->private;
530
531	if (!devs[i]->enabled)
532	    continue;
533
534	Set_Boot_Blocks(d, boot1, boot2);
535	msgNotify("Writing partition information to drive %s", d->name);
536	if (!Fake && Write_Disk(d)) {
537	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
538	    return DITEM_FAILURE;
539	}
540	/* Now scan for bad blocks, if necessary */
541	for (c1 = d->chunks->part; c1; c1 = c1->next) {
542	    if (c1->flags & CHUNK_BAD144) {
543		int ret;
544
545		msgNotify("Running bad block scan on partition %s", c1->name);
546		if (!Fake) {
547		    ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
548		    if (ret)
549			msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
550		    ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
551		    if (ret)
552			msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
553		}
554	    }
555	}
556    }
557    /* Now it's not "yes", but "written" */
558    variable_set2(DISK_PARTITIONED, "written");
559    return DITEM_SUCCESS;
560}
561