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