disks.c revision 17372
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.59 1996/07/31 10:55:19 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();
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	if (islower(key))
193	    key = toupper(key);
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		WINDOW *save = savescr();
278
279		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
280		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks\n"
281				       "or append a trailing `M' for megabytes (e.g. 20M).");
282		if (val && (size = strtol(val, &cp, 0)) > 0) {
283		    if (*cp && toupper(*cp) == 'M')
284			size *= ONE_MEG;
285		    strcpy(tmp, "165");
286		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
287				      "Pressing Enter will choose the default, a native FreeBSD\n"
288				      "partition (type 165).  You can choose other types, 6 for a\n"
289				      "DOS partition or 131 for a Linux partition, for example.\n\n"
290				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
291				      "be formatted or otherwise prepared, it will simply reserve space\n"
292				      "for you to use another tool, such as DOS FORMAT, to later format\n"
293				      "and use the partition.");
294		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
295			if (subtype==165)
296				partitiontype=freebsd;
297			else if (subtype==6)
298				partitiontype=fat;
299			else
300				partitiontype=unknown;
301		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
302				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
303		    variable_set2(DISK_PARTITIONED, "yes");
304		    record_chunks(d);
305		    }
306		}
307		restorescr(save);
308	    }
309	    break;
310
311	case '\177':
312	case 'D':
313	    if (chunk_info[current_chunk]->type == unused)
314		msg = "Partition is already unused!";
315	    else {
316		Delete_Chunk(d, chunk_info[current_chunk]);
317		variable_set2(DISK_PARTITIONED, "yes");
318		record_chunks(d);
319	    }
320	    break;
321
322	case 'G': {
323	    char *val, geometry[80];
324
325	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
326	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
327			      "Don't forget to use the two slash (/) separator characters!\n"
328			      "It's not possible to parse the field without them.");
329	    if (val) {
330		d->bios_cyl = strtol(val, &val, 0);
331		d->bios_hd = strtol(val + 1, &val, 0);
332		d->bios_sect = strtol(val + 1, 0, 0);
333	    }
334	}
335	break;
336
337    case 'S':
338	/* Set Bootable */
339	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
340	break;
341
342    case 'U':
343	    clear();
344	    if (msgYesNo("Are you SURE you want to Undo everything?"))
345		break;
346	    d = Open_Disk(d->name);
347	    if (!d) {
348		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
349		break;
350	    }
351	    Free_Disk(dev->private);
352	    dev->private = d;
353	    variable_unset(DISK_PARTITIONED);
354	    variable_unset(DISK_LABELLED);
355	    record_chunks(d);
356	    break;
357
358	case 'W':
359	    if (!msgYesNo("WARNING:  This should only be used for modifying an\n"
360			  "EXISTING installation - DO NOT USE this option if you\n"
361			  "are installing FreeBSD for the first time!  This is not\n"
362			  "an option for use during the standard install.\n\n"
363			  "Are you absolutely sure you want to do this now?")) {
364		WINDOW *save = savescr();
365
366		variable_set2(DISK_PARTITIONED, "yes");
367		clear();
368
369		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
370		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
371		 * booteasy or a "standard" MBR -- both would be fatal in this case.
372		 */
373		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
374		    && (mbrContents = getBootMgr(d->name)) != NULL)
375		    Set_Boot_Mgr(d, mbrContents);
376
377		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
378		    msgConfirm("Disk partition write returned an error status!");
379		else
380		    msgConfirm("Wrote FDISK partition information out successfully.");
381		restorescr(save);
382	    }
383	    break;
384
385	case '|':
386	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
387			  "No seat belts whatsoever are provided!")) {
388		WINDOW *w;
389
390		w = savescr();
391		dialog_clear();
392		end_dialog();
393		DialogActive = FALSE;
394		slice_wizard(d);
395		variable_set2(DISK_PARTITIONED, "yes");
396		dialog_clear();
397		DialogActive = TRUE;
398		record_chunks(d);
399		restorescr(w);
400	    }
401	    else
402		msg = "Wise choice!";
403	    break;
404
405	case 'Q':
406	    chunking = FALSE;
407	    clear();
408	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
409	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
410	     * booteasy or a "standard" MBR -- both would be fatal in this case.
411	     */
412	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
413		&& (mbrContents = getBootMgr(d->name)) != NULL)
414		Set_Boot_Mgr(d, mbrContents);
415	    break;
416
417	default:
418	    beep();
419	    msg = "Type F1 or ? for help";
420	    break;
421	}
422    }
423    p = CheckRules(d);
424    if (p) {
425	char buf[FILENAME_MAX];
426
427	dialog_clear();
428        use_helpline("Press F1 to read more about disk partitioning.");
429	use_helpfile(systemHelpFile("partition", buf));
430	dialog_mesgbox("Disk partitioning warning:", p, -1, -1);
431	free(p);
432    }
433    restorescr(w);
434}
435
436static int
437partitionHook(dialogMenuItem *selected)
438{
439    Device **devs = NULL;
440
441    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
442    if (!devs) {
443	msgConfirm("Unable to find disk %s!", selected->prompt);
444	return DITEM_FAILURE;
445    }
446    /* Toggle enabled status? */
447    if (!devs[0]->enabled) {
448	devs[0]->enabled = TRUE;
449	diskPartition(devs[0], (Disk *)devs[0]->private);
450    }
451    else
452	devs[0]->enabled = FALSE;
453    return DITEM_SUCCESS | DITEM_REDRAW;
454}
455
456static int
457partitionCheck(dialogMenuItem *selected)
458{
459    Device **devs = NULL;
460
461    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
462    if (!devs || devs[0]->enabled == FALSE)
463	return FALSE;
464    return TRUE;
465}
466
467int
468diskPartitionEditor(dialogMenuItem *self)
469{
470    DMenu *menu;
471    Device **devs;
472    int i, cnt;
473    char *cp;
474
475    cp = variable_get(VAR_DISK);
476    devs = deviceFind(cp, DEVICE_TYPE_DISK);
477    cnt = deviceCount(devs);
478    if (!cnt) {
479	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
480		   "properly probed at boot time.  See the Hardware Guide on the\n"
481		   "Documentation menu for clues on diagnosing this type of problem.");
482	i = DITEM_FAILURE;
483    }
484    else if (cnt == 1) {
485	devs[0]->enabled = TRUE;
486	diskPartition(devs[0], (Disk *)devs[0]->private);
487	i = DITEM_SUCCESS;
488    }
489    else {
490	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
491	if (!menu) {
492	    msgConfirm("No devices suitable for installation found!\n\n"
493		       "Please verify that your disk controller (and attached drives)\n"
494		       "were detected properly.  This can be done by pressing the\n"
495		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
496		       "the boot messages.  Press [Scroll Lock] again to return.");
497	    i = DITEM_FAILURE;
498	}
499	else {
500	    i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
501	    free(menu);
502	}
503	i = i | DITEM_RECREATE;
504    }
505    return i;
506}
507
508int
509diskPartitionWrite(dialogMenuItem *self)
510{
511    Device **devs;
512    char *cp;
513    int i;
514
515    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
516	return DITEM_SUCCESS;
517    else if (!cp) {
518	msgConfirm("You must partition the disk(s) before this option can be used.");
519	return DITEM_FAILURE;
520    }
521
522    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
523    if (!devs) {
524	msgConfirm("Unable to find any disks to write to??");
525	return DITEM_FAILURE;
526    }
527    if (isDebug())
528	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
529
530    for (i = 0; devs[i]; i++) {
531	Chunk *c1;
532	Disk *d = (Disk *)devs[i]->private;
533
534	if (!devs[i]->enabled)
535	    continue;
536
537	Set_Boot_Blocks(d, boot1, boot2);
538	msgNotify("Writing partition information to drive %s", d->name);
539	if (!Fake && Write_Disk(d)) {
540	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
541	    return DITEM_FAILURE;
542	}
543	/* Now scan for bad blocks, if necessary */
544	for (c1 = d->chunks->part; c1; c1 = c1->next) {
545	    if (c1->flags & CHUNK_BAD144) {
546		int ret;
547
548		msgNotify("Running bad block scan on partition %s", c1->name);
549		if (!Fake) {
550		    ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
551		    if (ret)
552			msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
553		    ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
554		    if (ret)
555			msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
556		}
557	    }
558	}
559    }
560    /* Now it's not "yes", but "written" */
561    variable_set2(DISK_PARTITIONED, "written");
562    return DITEM_SUCCESS;
563}
564