disks.c revision 14670
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.35 1996/01/23 21:05:22 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
138/* Partition a disk based wholly on which variables are set */
139static void
140scriptPartition(Device *dev, Disk *d)
141{
142    char *cp;
143    int i, sz;
144
145    record_chunks(d);
146    cp = variable_get(VAR_GEOMETRY);
147    if (cp) {
148	msgDebug("Setting geometry from script to: %s\n", cp);
149	d->bios_cyl = strtol(cp, &cp, 0);
150	d->bios_hd = strtol(cp + 1, &cp, 0);
151	d->bios_sect = strtol(cp + 1, 0, 0);
152    }
153
154    cp = variable_get(VAR_DISKSPACE);
155    if (cp) {
156	if (!strcmp(cp, "free")) {
157	    /* Do free disk space case */
158	    for (i = 0; chunk_info[i]; i++) {
159		/* If a chunk is at least 10MB in size, use it. */
160		if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
161		    Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
162				 (chunk_info[i]->flags & CHUNK_ALIGN));
163		    variable_set2(DISK_PARTITIONED, "yes");
164		    break;
165		}
166	    }
167	    if (!chunk_info[i]) {
168		dialog_clear();
169		msgConfirm("Unable to find any free space on this disk!");
170		return;
171	    }
172	}
173	else if (!strcmp(cp, "all")) {
174	    /* Do all disk space case */
175	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
176
177	    All_FreeBSD(d, FALSE);
178	}
179	else if (!strcmp(cp, "exclusive")) {
180	    /* Do really-all-the-disk-space case */
181	    msgDebug("Warning:  Devoting all of disk %s to FreeBSD.\n", d->name);
182
183	    All_FreeBSD(d, TRUE);
184	}
185	else if ((sz = strtol(cp, &cp, 0))) {
186	    /* Look for sz bytes free */
187	    if (*cp && toupper(*cp) == 'M')
188		sz *= ONE_MEG;
189	    for (i = 0; chunk_info[i]; i++) {
190		/* If a chunk is at least sz MB, use it. */
191		if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
192		    Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN));
193		    variable_set2(DISK_PARTITIONED, "yes");
194		    break;
195		}
196	    }
197	    if (!chunk_info[i]) {
198		dialog_clear();
199		msgConfirm("Unable to find %d free blocks on this disk!", sz);
200		return;
201	    }
202	}
203	else if (!strcmp(cp, "existing")) {
204	    /* Do existing FreeBSD case */
205	    for (i = 0; chunk_info[i]; i++) {
206		if (chunk_info[i]->type == freebsd)
207		    break;
208	    }
209	    if (!chunk_info[i]) {
210		dialog_clear();
211		msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
212		return;
213	    }
214	}
215	else {
216	    dialog_clear();
217	    msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_DISKSPACE);
218	    return;
219	}
220	variable_set2(DISK_PARTITIONED, "yes");
221    }
222}
223
224static u_char *
225getBootMgr(char *dname)
226{
227    extern u_char mbr[], bteasy17[];
228    char str[80];
229    char *cp;
230    int i = 0;
231
232    cp = variable_get(VAR_BOOTMGR);
233    if (!cp) {
234	/* Figure out what kind of MBR the user wants */
235	sprintf(str, "Install Boot Manager for drive %s?", dname);
236	MenuMBRType.title = str;
237	dialog_clear();
238	i = dmenuOpenSimple(&MenuMBRType);
239    }
240    else {
241	if (!strncmp(cp, "boot", 4))
242	    BootMgr = 0;
243	else if (!strcmp(cp, "standard"))
244	    BootMgr = 1;
245	else
246	    BootMgr = 2;
247    }
248    if (cp || i) {
249	switch (BootMgr) {
250	case 0:
251	    return bteasy17;
252
253	case 1:
254	    return mbr;
255
256	case 2:
257	default:
258	    break;
259	}
260    }
261    return NULL;
262}
263
264void
265diskPartition(Device *dev, Disk *d)
266{
267    char *p;
268    int key = 0;
269    Boolean chunking;
270    char *msg = NULL;
271    u_char *mbrContents;
272
273    chunking = TRUE;
274    keypad(stdscr, TRUE);
275
276    clear();
277    record_chunks(d);
278    while (chunking) {
279	print_chunks(d);
280	print_command_summary();
281	if (msg) {
282	    standout(); mvprintw(23, 0, msg); standend();
283	    beep();
284	    msg = NULL;
285	}
286
287	key = toupper(getch());
288	switch (key) {
289
290	case '\014':	/* ^L */
291	    clear();
292	    print_command_summary();
293	    continue;
294
295	case KEY_UP:
296	case '-':
297	    if (current_chunk != 0)
298		--current_chunk;
299	    break;
300
301	case KEY_DOWN:
302	case '+':
303	case '\r':
304	case '\n':
305	    if (chunk_info[current_chunk + 1])
306		++current_chunk;
307	    break;
308
309	case KEY_HOME:
310	    current_chunk = 0;
311	    break;
312
313	case KEY_END:
314	    while (chunk_info[current_chunk + 1])
315		++current_chunk;
316	    break;
317
318	case KEY_F(1):
319	case '?':
320	    systemDisplayHelp("slice");
321	    break;
322
323	case 'A': {
324	    int rv;
325
326	    rv = msgYesNo("Do you want to do this with a true partition entry\n"
327			  "so as to remain cooperative with any future possible\n"
328			  "operating systems on the drive(s)?");
329	    if (rv) {
330		rv = !msgYesNo("This is dangerous in that it will make the drive totally\n"
331			       "uncooperative with other potential operating systems on the\n"
332			       "same disk.  It will lead instead to a totally dedicated disk,\n"
333			       "starting at the very first sector, bypassing all BIOS geometry\n"
334			       "considerations.  This precludes the existance of any boot\n"
335			       "manager or other stuff in sector 0, since the BSD bootstrap\n"
336			       "will live there.\n"
337			       "You will run into serious trouble with ST-506 and ESDI drives\n"
338			       "and possibly some IDE drives (e.g. drives running under the\n"
339			       "control of sort of disk manager).  SCSI drives are considerably\n"
340			       "less at risk.\n\n"
341			       "Do you insist on dedicating the entire disk this way?");
342	    }
343	    All_FreeBSD(d, rv);
344	    if (rv)
345		d->bios_hd = d->bios_sect = d->bios_cyl = 1;
346	    variable_set2(DISK_PARTITIONED, "yes");
347	    record_chunks(d);
348	}
349	    break;
350
351	case 'B':
352	    if (chunk_info[current_chunk]->type != freebsd)
353		msg = "Can only scan for bad blocks in FreeBSD partition.";
354	    else if (strncmp(d->name, "sd", 2) ||
355		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
356			       "Are you sure you want to do this on a SCSI disk?")) {
357		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
358		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
359		else
360		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
361	    }
362	    break;
363
364	case 'C':
365	    if (chunk_info[current_chunk]->type != unused)
366		msg = "Partition in use, delete it first or move to an unused one.";
367	    else {
368		char *val, tmp[20], *cp;
369		int size;
370
371		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
372		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\n"
373				  "a trailing `M' for megabytes (e.g. 20M).");
374		if (val && (size = strtol(val, &cp, 0)) > 0) {
375		    if (*cp && toupper(*cp) == 'M')
376			size *= ONE_MEG;
377		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3,
378				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
379		    variable_set2(DISK_PARTITIONED, "yes");
380		    record_chunks(d);
381		}
382	    }
383	    break;
384
385	case 'D':
386	    if (chunk_info[current_chunk]->type == unused)
387		msg = "Partition is already unused!";
388	    else {
389		Delete_Chunk(d, chunk_info[current_chunk]);
390		variable_set2(DISK_PARTITIONED, "yes");
391		record_chunks(d);
392	    }
393	    break;
394
395	case 'G': {
396	    char *val, geometry[80];
397
398	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
399	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
400			      "Don't forget to use the two slash (/) separator characters!\n"
401			      "It's not possible to parse the field without them.");
402	    if (val) {
403		d->bios_cyl = strtol(val, &val, 0);
404		d->bios_hd = strtol(val + 1, &val, 0);
405		d->bios_sect = strtol(val + 1, 0, 0);
406	    }
407	}
408	break;
409
410    case 'S':
411	/* Set Bootable */
412	chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
413	break;
414
415    case 'U':
416	    clear();
417	    if (msgYesNo("Are you SURE you want to Undo everything?"))
418		break;
419	    d = Open_Disk(d->name);
420	    if (!d) {
421		dialog_clear();
422		msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", d->name);
423		return;
424	    }
425	    Free_Disk(dev->private);
426	    dev->private = d;
427	    variable_unset(DISK_PARTITIONED);
428	    record_chunks(d);
429	    break;
430
431	case 'W':
432	    if (!msgYesNo("Are you SURE you want to write this now?  You do also\n"
433			  "have the option of not modifying the disk until *all*\n"
434			  "configuration information has been entered, at which\n"
435			  "point you can do it all at once.  If you're unsure, then\n"
436			  "PLEASE CHOOSE NO at this dialog!  This option is DANGEROUS\n"
437			  "if you do not know EXACTLY what you are doing!")) {
438		variable_set2(DISK_PARTITIONED, "yes");
439		clear();
440
441		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
442		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
443		 * booteasy or a "standard" MBR -- both would be fatal in this case.
444		 */
445		if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
446		    && (mbrContents = getBootMgr(d->name)) != NULL)
447		    Set_Boot_Mgr(d, mbrContents);
448
449		if (diskPartitionWrite(NULL) != RET_SUCCESS) {
450		    dialog_clear();
451		    msgConfirm("Disk partition write returned an error status!");
452		}
453		else {
454		    msgConfirm("Wrote FDISK partition information out successfully.");
455		}
456	    }
457	    break;
458
459	case '|':
460	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
461			  "No seat belts whatsoever are provided!")) {
462		dialog_clear();
463		end_dialog();
464		DialogActive = FALSE;
465		slice_wizard(d);
466		variable_set2(DISK_PARTITIONED, "yes");
467		dialog_clear();
468		DialogActive = TRUE;
469		record_chunks(d);
470	    }
471	    else
472		msg = "Wise choice!";
473	    break;
474
475	case 'Q':
476	    chunking = FALSE;
477	    clear();
478	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
479	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
480	     * booteasy or a "standard" MBR -- both would be fatal in this case.
481	     */
482	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
483		&& (mbrContents = getBootMgr(d->name)) != NULL)
484		Set_Boot_Mgr(d, mbrContents);
485	    break;
486
487	default:
488	    beep();
489	    msg = "Type F1 or ? for help";
490	    break;
491	}
492    }
493    p = CheckRules(d);
494    if (p) {
495	dialog_clear();
496	msgConfirm(p);
497	free(p);
498    }
499    dialog_clear();
500}
501
502static int
503partitionHook(char *str)
504{
505    Device **devs = NULL;
506
507    /* Clip garbage off the ends */
508    string_prune(str);
509    str = string_skipwhite(str);
510    /* Try and open all the disks */
511    while (str) {
512	char *cp;
513
514	cp = index(str, '\n');
515	if (cp)
516	   *cp++ = 0;
517	if (!*str) {
518	    beep();
519	    return 0;
520	}
521	devs = deviceFind(str, DEVICE_TYPE_DISK);
522	if (!devs) {
523	    dialog_clear();
524	    msgConfirm("Unable to find disk %s!", str);
525	    return 0;
526	}
527	devs[0]->enabled = TRUE;
528	diskPartition(devs[0], (Disk *)devs[0]->private);
529	str = cp;
530    }
531    return devs ? 1 : 0;
532}
533
534int
535diskPartitionEditor(char *str)
536{
537    DMenu *menu;
538    Device **devs;
539    int i, cnt;
540    char *cp;
541
542    cp = variable_get(VAR_DISK);
543    devs = deviceFind(cp, DEVICE_TYPE_DISK);
544    cnt = deviceCount(devs);
545    if (!cnt) {
546	dialog_clear();
547	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
548		   "properly probed at boot time.  See the Hardware Guide on the\n"
549		   "Documentation menu for clues on diagnosing this type of problem.");
550	i = RET_FAIL;
551    }
552    else if (cnt == 1) {
553	devs[0]->enabled = TRUE;
554	if (str && !strcmp(str, "script"))
555	    scriptPartition(devs[0], (Disk *)devs[0]->private);
556	else
557	    diskPartition(devs[0], (Disk *)devs[0]->private);
558	i = RET_SUCCESS;
559	variable_set2(DISK_SELECTED, "yes");
560    }
561    else {
562	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook);
563	if (!menu) {
564	    dialog_clear();
565	    msgConfirm("No devices suitable for installation found!\n\n"
566		       "Please verify that your disk controller (and attached drives)\n"
567		       "were detected properly.  This can be done by pressing the\n"
568		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
569		       "the boot messages.  Press [Scroll Lock] again to return.");
570	    i = RET_FAIL;
571	}
572	else {
573	    if (!dmenuOpenSimple(menu))
574		i = RET_FAIL;
575	    else  {
576		i = RET_SUCCESS;
577		variable_set2(DISK_SELECTED, "yes");
578	    }
579	    free(menu);
580	}
581    }
582    return i;
583}
584
585int
586diskPartitionWrite(char *str)
587{
588    extern u_char boot1[], boot2[];
589    Device **devs;
590    char *cp;
591    int i;
592
593    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
594	return RET_SUCCESS;
595    else if (!cp) {
596	dialog_clear();
597	msgConfirm("You must partition the disk(s) before this option can be used.");
598	return RET_FAIL;
599    }
600
601    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
602    if (!devs) {
603	dialog_clear();
604	msgConfirm("Unable to find any disks to write to??");
605	return RET_FAIL;
606    }
607
608    for (i = 0; devs[i]; i++) {
609	Chunk *c1;
610	Disk *d = (Disk *)devs[i]->private;
611
612	if (!devs[i]->enabled)
613	    continue;
614
615	Set_Boot_Blocks(d, boot1, boot2);
616	msgNotify("Writing partition information to drive %s", d->name);
617	if (Write_Disk(d)) {
618	    dialog_clear();
619	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
620	    return RET_FAIL;
621	}
622	/* Now scan for bad blocks, if necessary */
623	for (c1 = d->chunks->part; c1; c1 = c1->next) {
624	    if (c1->flags & CHUNK_BAD144) {
625		int ret;
626
627		msgNotify("Running bad block scan on partition %s", c1->name);
628		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
629		if (ret) {
630		    dialog_clear();
631		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
632		}
633		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
634		if (ret) {
635		    dialog_clear();
636		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
637		}
638	    }
639	}
640    }
641    /* Now it's not "yes", but "written" */
642    variable_set2(DISK_PARTITIONED, "written");
643    return RET_SUCCESS;
644}
645