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