disks.c revision 15091
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.38 1996/03/24 18:57:35 joerg 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 * 3. All advertising materials mentioning features or use of this software
23 *    must display the following acknowledgement:
24 *	This product includes software developed by Jordan Hubbard
25 *	for the FreeBSD Project.
26 * 4. The name of Jordan Hubbard or the FreeBSD project may not be used to
27 *    endorse or promote products derived from this software without specific
28 *    prior written permission.
29 *
30 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
41 *
42 */
43
44#include "sysinstall.h"
45#include <ctype.h>
46#include <sys/disklabel.h>
47
48/* Where we start displaying chunk information on the screen */
49#define CHUNK_START_ROW		5
50
51/* Where we keep track of MBR chunks */
52static struct chunk *chunk_info[16];
53static int current_chunk;
54
55static void
56record_chunks(Disk *d)
57{
58    struct chunk *c1;
59    int i = 0;
60    int last_free = 0;
61    if (!d->chunks)
62	msgFatal("No chunk list found for %s!", d->name);
63    current_chunk = 0;
64    for (c1 = d->chunks->part; c1; c1 = c1->next) {
65	if (c1->type == unused && c1->size > last_free) {
66	    last_free = c1->size;
67	    current_chunk = i;
68	}
69	chunk_info[i++] = c1;
70    }
71    chunk_info[i] = NULL;
72}
73
74static void
75print_chunks(Disk *d)
76{
77    int row;
78    int i;
79
80    if ((!d->bios_cyl || d->bios_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64)) {
81	int sz;
82
83	dialog_clear();
84	msgConfirm("WARNING:  The current geometry for %s is incorrect.  Using\n"
85		   "a default geometry of 64 heads and 32 sectors.  If this geometry\n"
86		   "is incorrect or you are unsure as to whether or not it's correct,\n"
87		   "please consult the Hardware Guide in the Documentation submenu\n"
88		   "or use the (G)eometry command to change it now.", d->name);
89	d->bios_hd = 64;
90	d->bios_sect = 32;
91	sz = 0;
92	for (i = 0; chunk_info[i]; i++)
93	    sz += chunk_info[i]->size;
94	if (sz)
95	    d->bios_cyl = sz / ONE_MEG;
96	else
97	    msgConfirm("Couldn't set geometry!  You'll have to do it by hand.");
98    }
99    attrset(A_NORMAL);
100    mvaddstr(0, 0, "Disk name:\t");
101    clrtobot();
102    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
103    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
104    mvprintw(1, 0,
105	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors",
106	     d->bios_cyl, d->bios_hd, d->bios_sect);
107    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
108	     "Offset", "Size", "End", "Name", "PType", "Desc",
109	     "Subtype", "Flags");
110    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
111	if (i == current_chunk)
112	    attrset(A_REVERSE);
113	mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s",
114		 chunk_info[i]->offset, chunk_info[i]->size,
115		 chunk_info[i]->end, chunk_info[i]->name,
116		 chunk_info[i]->type, chunk_n[chunk_info[i]->type],
117		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
118	if (i == current_chunk)
119	    attrset(A_NORMAL);
120    }
121}
122
123static void
124print_command_summary()
125{
126    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
127    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan     C = Create Partition");
128    mvprintw(17, 0, "D = Delete Partition   G = Set Drive Geometry S = Set Bootable");
129    mvprintw(18, 0, "U = Undo All Changes   Q = Finish");
130    if (!RunningAsInit)
131	mvprintw(18, 48, "W = Write Changes");
132    mvprintw(20, 0, "The currently selected partition is displayed in ");
133    attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video.");
134    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move.");
135    move(0, 0);
136}
137
138static u_char *
139getBootMgr(char *dname)
140{
141    extern u_char mbr[], bteasy17[];
142    char str[80];
143    char *cp;
144    int i = 0;
145
146    cp = variable_get(VAR_BOOTMGR);
147    if (!cp) {
148	/* Figure out what kind of MBR the user wants */
149	sprintf(str, "Install Boot Manager for drive %s?", dname);
150	MenuMBRType.title = str;
151	dialog_clear();
152	i = dmenuOpenSimple(&MenuMBRType);
153    }
154    else {
155	if (!strncmp(cp, "boot", 4))
156	    BootMgr = 0;
157	else if (!strcmp(cp, "standard"))
158	    BootMgr = 1;
159	else
160	    BootMgr = 2;
161    }
162    if (cp || i) {
163	switch (BootMgr) {
164	case 0:
165	    return bteasy17;
166
167	case 1:
168	    return mbr;
169
170	case 2:
171	default:
172	    break;
173	}
174    }
175    return NULL;
176}
177
178void
179diskPartition(Device *dev, Disk *d)
180{
181    char *p;
182    int key = 0;
183    Boolean chunking;
184    char *msg = NULL;
185    u_char *mbrContents;
186
187    chunking = TRUE;
188    keypad(stdscr, TRUE);
189
190    clear();
191    record_chunks(d);
192    while (chunking) {
193	print_chunks(d);
194	print_command_summary();
195	if (msg) {
196	    standout(); mvprintw(23, 0, msg); standend();
197	    beep();
198	    msg = NULL;
199	}
200
201	key = toupper(getch());
202	switch (key) {
203
204	case '\014':	/* ^L */
205	    clear();
206	    print_command_summary();
207	    continue;
208
209	case KEY_UP:
210	case '-':
211	    if (current_chunk != 0)
212		--current_chunk;
213	    break;
214
215	case KEY_DOWN:
216	case '+':
217	case '\r':
218	case '\n':
219	    if (chunk_info[current_chunk + 1])
220		++current_chunk;
221	    break;
222
223	case KEY_HOME:
224	    current_chunk = 0;
225	    break;
226
227	case KEY_END:
228	    while (chunk_info[current_chunk + 1])
229		++current_chunk;
230	    break;
231
232	case KEY_F(1):
233	case '?':
234	    systemDisplayHelp("slice");
235	    break;
236
237	case 'A': {
238	    int rv;
239
240	    rv = msgYesNo("Do you want to do this with a true partition entry\n"
241			  "so as to remain cooperative with any future possible\n"
242			  "operating systems on the drive(s)?");
243	    if (rv) {
244		rv = !msgYesNo("This is dangerous in that it will make the drive totally\n"
245			       "uncooperative with other potential operating systems on the\n"
246			       "same disk.  It will lead instead to a totally dedicated disk,\n"
247			       "starting at the very first sector, bypassing all BIOS geometry\n"
248			       "considerations.  This precludes the existance of any boot\n"
249			       "manager or other stuff in sector 0, since the BSD bootstrap\n"
250			       "will live there.\n"
251			       "You will run into serious trouble with ST-506 and ESDI drives\n"
252			       "and possibly some IDE drives (e.g. drives running under the\n"
253			       "control of sort of disk manager).  SCSI drives are considerably\n"
254			       "less at risk.\n\n"
255			       "Do you insist on dedicating the entire disk this way?");
256	    }
257	    All_FreeBSD(d, rv);
258	    if (rv)
259		d->bios_hd = d->bios_sect = d->bios_cyl = 1;
260	    variable_set2(DISK_PARTITIONED, "yes");
261	    record_chunks(d);
262	}
263	    break;
264
265	case 'B':
266	    if (chunk_info[current_chunk]->type != freebsd)
267		msg = "Can only scan for bad blocks in FreeBSD partition.";
268	    else if (strncmp(d->name, "sd", 2) ||
269		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
270			       "Are you sure you want to do this on a SCSI disk?")) {
271		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
272		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
273		else
274		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
275	    }
276	    break;
277
278	case 'C':
279	    if (chunk_info[current_chunk]->type != unused)
280		msg = "Partition in use, delete it first or move to an unused one.";
281	    else {
282		char *val, tmp[20], *cp;
283		int size;
284
285		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
286		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n"
287				  "a trailing `M' for megabytes (e.g. 20M).");
288		if (val && (size = strtol(val, &cp, 0)) > 0) {
289		    if (*cp && toupper(*cp) == 'M')
290			size *= ONE_MEG;
291		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3,
292				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
293		    variable_set2(DISK_PARTITIONED, "yes");
294		    record_chunks(d);
295		}
296	    }
297	    break;
298
299	case '\177':
300	case 'D':
301	    if (chunk_info[current_chunk]->type == unused)
302		msg = "Partition is already unused!";
303	    else {
304		Delete_Chunk(d, chunk_info[current_chunk]);
305		variable_set2(DISK_PARTITIONED, "yes");
306		record_chunks(d);
307	    }
308	    break;
309
310	case 'G': {
311	    char *val, geometry[80];
312
313	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
314	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
315			      "Don't forget to use the two slash (/) separator characters!\n"
316			      "It's not possible to parse the field without them.");
317	    if (val) {
318		d->bios_cyl = strtol(val, &val, 0);
319		d->bios_hd = strtol(val + 1, &val, 0);
320		d->bios_sect = strtol(val + 1, 0, 0);
321	    }
322	}
323	break;
324
325    case 'S':
326	/* Set Bootable */
327	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
328	break;
329
330    case 'U':
331	    clear();
332	    if (msgYesNo("Are you SURE you want to Undo everything?"))
333		break;
334	    d = Open_Disk(d->name);
335	    if (!d) {
336		dialog_clear();
337		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
338		return;
339	    }
340	    Free_Disk(dev->private);
341	    dev->private = d;
342	    variable_unset(DISK_PARTITIONED);
343	    record_chunks(d);
344	    break;
345
346	case 'W':
347	    if (!msgYesNo("Are you SURE you want to write this now?  You do also\n"
348			  "have the option of not modifying the disk until *all*\n"
349			  "configuration information has been entered, at which\n"
350			  "point you can do it all at once.  If you're unsure, then\n"
351			  "PLEASE CHOOSE NO at this dialog!  This option is DANGEROUS\n"
352			  "if you do not know EXACTLY what you are doing!")) {
353		variable_set2(DISK_PARTITIONED, "yes");
354		clear();
355
356		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
357		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
358		 * booteasy or a "standard" MBR -- both would be fatal in this case.
359		 */
360		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
361		    && (mbrContents = getBootMgr(d->name)) != NULL)
362		    Set_Boot_Mgr(d, mbrContents);
363
364		if (diskPartitionWrite(NULL) != RET_SUCCESS) {
365		    dialog_clear();
366		    msgConfirm("Disk partition write returned an error status!");
367		}
368		else {
369		    msgConfirm("Wrote FDISK partition information out successfully.");
370		}
371	    }
372	    break;
373
374	case '|':
375	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
376			  "No seat belts whatsoever are provided!")) {
377		dialog_clear();
378		end_dialog();
379		DialogActive = FALSE;
380		slice_wizard(d);
381		variable_set2(DISK_PARTITIONED, "yes");
382		dialog_clear();
383		DialogActive = TRUE;
384		record_chunks(d);
385	    }
386	    else
387		msg = "Wise choice!";
388	    break;
389
390	case 'Q':
391	    chunking = FALSE;
392	    clear();
393	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
394	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
395	     * booteasy or a "standard" MBR -- both would be fatal in this case.
396	     */
397	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
398		&& (mbrContents = getBootMgr(d->name)) != NULL)
399		Set_Boot_Mgr(d, mbrContents);
400	    break;
401
402	default:
403	    beep();
404	    msg = "Type F1 or ? for help";
405	    break;
406	}
407    }
408    p = CheckRules(d);
409    if (p) {
410	dialog_clear();
411	msgConfirm(p);
412	free(p);
413    }
414    dialog_clear();
415}
416
417static int
418partitionHook(char *str)
419{
420    Device **devs = NULL;
421
422    /* Clip garbage off the ends */
423    string_prune(str);
424    str = string_skipwhite(str);
425    /* Try and open all the disks */
426    while (str) {
427	char *cp;
428
429	cp = index(str, '\n');
430	if (cp)
431	   *cp++ = 0;
432	if (!*str) {
433	    beep();
434	    return 0;
435	}
436	devs = deviceFind(str, DEVICE_TYPE_DISK);
437	if (!devs) {
438	    dialog_clear();
439	    msgConfirm("Unable to find disk %s!", str);
440	    return 0;
441	}
442	devs[0]->enabled = TRUE;
443	diskPartition(devs[0], (Disk *)devs[0]->private);
444	str = cp;
445    }
446    return devs ? 1 : 0;
447}
448
449int
450diskPartitionEditor(dialogMenuItem *self)
451{
452    DMenu *menu;
453    Device **devs;
454    int i, cnt;
455    char *cp, *str;
456
457    cp = variable_get(VAR_DISK);
458    str= variable_get(SYSTEM_STATE);
459    devs = deviceFind(cp, DEVICE_TYPE_DISK);
460    cnt = deviceCount(devs);
461    if (!cnt) {
462	dialog_clear();
463	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
464		   "properly probed at boot time.  See the Hardware Guide on the\n"
465		   "Documentation menu for clues on diagnosing this type of problem.");
466	i = RET_FAIL;
467    }
468    else if (cnt == 1) {
469	devs[0]->enabled = TRUE;
470	diskPartition(devs[0], (Disk *)devs[0]->private);
471	i = RET_SUCCESS;
472	variable_set2(DISK_SELECTED, "yes");
473    }
474    else {
475	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook);
476	if (!menu) {
477	    dialog_clear();
478	    msgConfirm("No devices suitable for installation found!\n\n"
479		       "Please verify that your disk controller (and attached drives)\n"
480		       "were detected properly.  This can be done by pressing the\n"
481		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
482		       "the boot messages.  Press [Scroll Lock] again to return.");
483	    i = RET_FAIL;
484	}
485	else {
486	    if (!dmenuOpenSimple(menu))
487		i = RET_FAIL;
488	    else  {
489		i = RET_SUCCESS;
490		variable_set2(DISK_SELECTED, "yes");
491	    }
492	    free(menu);
493	}
494    }
495    return i;
496}
497
498int
499diskPartitionWrite(dialogMenuItem *self)
500{
501    Device **devs;
502    char *cp;
503    int i;
504
505    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
506	return RET_SUCCESS;
507    else if (!cp) {
508	dialog_clear();
509	msgConfirm("You must partition the disk(s) before this option can be used.");
510	return RET_FAIL;
511    }
512
513    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
514    if (!devs) {
515	dialog_clear();
516	msgConfirm("Unable to find any disks to write to??");
517	return RET_FAIL;
518    }
519
520    for (i = 0; devs[i]; i++) {
521	Chunk *c1;
522	Disk *d = (Disk *)devs[i]->private;
523
524	if (!devs[i]->enabled)
525	    continue;
526
527	Set_Boot_Blocks(d, boot1, boot2);
528	msgNotify("Writing partition information to drive %s", d->name);
529	if (Write_Disk(d)) {
530	    dialog_clear();
531	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
532	    return RET_FAIL;
533	}
534	/* Now scan for bad blocks, if necessary */
535	for (c1 = d->chunks->part; c1; c1 = c1->next) {
536	    if (c1->flags & CHUNK_BAD144) {
537		int ret;
538
539		msgNotify("Running bad block scan on partition %s", c1->name);
540		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
541		if (ret) {
542		    dialog_clear();
543		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
544		}
545		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
546		if (ret) {
547		    dialog_clear();
548		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
549		}
550	    }
551	}
552    }
553    /* Now it's not "yes", but "written" */
554    variable_set2(DISK_PARTITIONED, "written");
555    return RET_SUCCESS;
556}
557