disks.c revision 15242
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.39 1996/04/07 03:52:20 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(A_REVERSE);
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, 48, "W = Write Changes");
125    mvprintw(20, 0, "The currently selected partition is displayed in ");
126    attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video.");
127    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move.");
128    move(0, 0);
129}
130
131static u_char *
132getBootMgr(char *dname)
133{
134    extern u_char mbr[], bteasy17[];
135    char str[80];
136    char *cp;
137    int i = 0;
138
139    cp = variable_get(VAR_BOOTMGR);
140    if (!cp) {
141	/* Figure out what kind of MBR the user wants */
142	sprintf(str, "Install Boot Manager for drive %s?", dname);
143	MenuMBRType.title = str;
144	i = dmenuOpenSimple(&MenuMBRType);
145    }
146    else {
147	if (!strncmp(cp, "boot", 4))
148	    BootMgr = 0;
149	else if (!strcmp(cp, "standard"))
150	    BootMgr = 1;
151	else
152	    BootMgr = 2;
153    }
154    if (cp || i) {
155	switch (BootMgr) {
156	case 0:
157	    return bteasy17;
158
159	case 1:
160	    return mbr;
161
162	case 2:
163	default:
164	    break;
165	}
166    }
167    return NULL;
168}
169
170void
171diskPartition(Device *dev, Disk *d)
172{
173    char *p;
174    int key = 0;
175    Boolean chunking;
176    char *msg = NULL;
177    u_char *mbrContents;
178
179    chunking = TRUE;
180    keypad(stdscr, TRUE);
181
182    clear();
183    record_chunks(d);
184    while (chunking) {
185	print_chunks(d);
186	print_command_summary();
187	if (msg) {
188	    standout(); mvprintw(23, 0, msg); standend();
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;
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, or append\n"
279				  "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		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3,
284				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
285		    variable_set2(DISK_PARTITIONED, "yes");
286		    record_chunks(d);
287		}
288	    }
289	    break;
290
291	case '\177':
292	case 'D':
293	    if (chunk_info[current_chunk]->type == unused)
294		msg = "Partition is already unused!";
295	    else {
296		Delete_Chunk(d, chunk_info[current_chunk]);
297		variable_set2(DISK_PARTITIONED, "yes");
298		record_chunks(d);
299	    }
300	    break;
301
302	case 'G': {
303	    char *val, geometry[80];
304
305	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
306	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
307			      "Don't forget to use the two slash (/) separator characters!\n"
308			      "It's not possible to parse the field without them.");
309	    if (val) {
310		d->bios_cyl = strtol(val, &val, 0);
311		d->bios_hd = strtol(val + 1, &val, 0);
312		d->bios_sect = strtol(val + 1, 0, 0);
313	    }
314	}
315	break;
316
317    case 'S':
318	/* Set Bootable */
319	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
320	break;
321
322    case 'U':
323	    clear();
324	    if (msgYesNo("Are you SURE you want to Undo everything?"))
325		break;
326	    d = Open_Disk(d->name);
327	    if (!d) {
328		dialog_clear();
329		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
330		return;
331	    }
332	    Free_Disk(dev->private);
333	    dev->private = d;
334	    variable_unset(DISK_PARTITIONED);
335	    record_chunks(d);
336	    break;
337
338	case 'W':
339	    if (!msgYesNo("Are you SURE you want to write this now?  You do also\n"
340			  "have the option of not modifying the disk until *all*\n"
341			  "configuration information has been entered, at which\n"
342			  "point you can do it all at once.  If you're unsure, then\n"
343			  "PLEASE CHOOSE NO at this dialog!  This option is DANGEROUS\n"
344			  "if you do not know EXACTLY what you are doing!")) {
345		variable_set2(DISK_PARTITIONED, "yes");
346		clear();
347
348		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
349		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
350		 * booteasy or a "standard" MBR -- both would be fatal in this case.
351		 */
352		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
353		    && (mbrContents = getBootMgr(d->name)) != NULL)
354		    Set_Boot_Mgr(d, mbrContents);
355
356		if (diskPartitionWrite(NULL) != DITEM_SUCCESS) {
357		    dialog_clear();
358		    msgConfirm("Disk partition write returned an error status!");
359		}
360		else {
361		    msgConfirm("Wrote FDISK partition information out successfully.");
362		}
363	    }
364	    break;
365
366	case '|':
367	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
368			  "No seat belts whatsoever are provided!")) {
369		dialog_clear();
370		end_dialog();
371		DialogActive = FALSE;
372		slice_wizard(d);
373		variable_set2(DISK_PARTITIONED, "yes");
374		dialog_clear();
375		DialogActive = TRUE;
376		record_chunks(d);
377	    }
378	    else
379		msg = "Wise choice!";
380	    break;
381
382	case 'Q':
383	    chunking = FALSE;
384	    clear();
385	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
386	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
387	     * booteasy or a "standard" MBR -- both would be fatal in this case.
388	     */
389	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
390		&& (mbrContents = getBootMgr(d->name)) != NULL)
391		Set_Boot_Mgr(d, mbrContents);
392	    break;
393
394	default:
395	    beep();
396	    msg = "Type F1 or ? for help";
397	    break;
398	}
399    }
400    p = CheckRules(d);
401    if (p) {
402	dialog_clear();
403	msgConfirm(p);
404	free(p);
405    }
406    dialog_clear();
407}
408
409static int
410partitionHook(dialogMenuItem *selected)
411{
412    Device **devs = NULL;
413    WINDOW *w;
414
415    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
416    if (!devs) {
417	dialog_clear();
418	msgConfirm("Unable to find disk %s!", selected->prompt);
419	return DITEM_FAILURE;
420    }
421    devs[0]->enabled = TRUE;
422    w = savescr();
423    diskPartition(devs[0], (Disk *)devs[0]->private);
424    restorescr(w);
425    return DITEM_SUCCESS;
426}
427
428static int
429partitionCheck(dialogMenuItem *selected)
430{
431    Device **devs = NULL;
432
433    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
434    if (!devs || devs[0]->enabled == FALSE)
435	return FALSE;
436    return TRUE;
437}
438
439int
440diskPartitionEditor(dialogMenuItem *self)
441{
442    DMenu *menu;
443    Device **devs;
444    int i, cnt;
445    char *cp;
446
447    cp = variable_get(VAR_DISK);
448    devs = deviceFind(cp, DEVICE_TYPE_DISK);
449    cnt = deviceCount(devs);
450    if (!cnt) {
451	dialog_clear();
452	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
453		   "properly probed at boot time.  See the Hardware Guide on the\n"
454		   "Documentation menu for clues on diagnosing this type of problem.");
455	i = DITEM_FAILURE;
456    }
457    else if (cnt == 1) {
458	devs[0]->enabled = TRUE;
459	diskPartition(devs[0], (Disk *)devs[0]->private);
460	i = DITEM_SUCCESS;
461    }
462    else {
463	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
464	if (!menu) {
465	    dialog_clear();
466	    msgConfirm("No devices suitable for installation found!\n\n"
467		       "Please verify that your disk controller (and attached drives)\n"
468		       "were detected properly.  This can be done by pressing the\n"
469		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
470		       "the boot messages.  Press [Scroll Lock] again to return.");
471	    i = DITEM_FAILURE;
472	}
473	else {
474	    WINDOW *w;
475
476	    w = savescr();
477	    if (dmenuOpenSimple(menu))
478		i = DITEM_SUCCESS;
479	    else
480		i = DITEM_FAILURE;
481	    restorescr(w);
482	    free(menu);
483	}
484    }
485    return i;
486}
487
488int
489diskPartitionWrite(dialogMenuItem *self)
490{
491    Device **devs;
492    char *cp;
493    int i;
494
495    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
496	return DITEM_SUCCESS;
497    else if (!cp) {
498	dialog_clear();
499	msgConfirm("You must partition the disk(s) before this option can be used.");
500	return DITEM_FAILURE;
501    }
502
503    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
504    if (!devs) {
505	dialog_clear();
506	msgConfirm("Unable to find any disks to write to??");
507	return DITEM_FAILURE;
508    }
509
510    for (i = 0; devs[i]; i++) {
511	Chunk *c1;
512	Disk *d = (Disk *)devs[i]->private;
513
514	if (!devs[i]->enabled)
515	    continue;
516
517	Set_Boot_Blocks(d, boot1, boot2);
518	msgNotify("Writing partition information to drive %s", d->name);
519	if (Write_Disk(d)) {
520	    dialog_clear();
521	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
522	    return DITEM_FAILURE;
523	}
524	/* Now scan for bad blocks, if necessary */
525	for (c1 = d->chunks->part; c1; c1 = c1->next) {
526	    if (c1->flags & CHUNK_BAD144) {
527		int ret;
528
529		msgNotify("Running bad block scan on partition %s", c1->name);
530		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
531		if (ret) {
532		    dialog_clear();
533		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
534		}
535		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
536		if (ret) {
537		    dialog_clear();
538		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
539		}
540	    }
541	}
542    }
543    /* Now it's not "yes", but "written" */
544    variable_set2(DISK_PARTITIONED, "written");
545    return DITEM_SUCCESS;
546}
547