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