disks.c revision 10882
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.31.2.2 1995/07/21 11:45:38 rgrimes 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[10];
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    c1 = d->chunks->part;
64    current_chunk = 0;
65    while (c1) {
66	if (c1->type == unused && c1->size > last_free) {
67	    last_free = c1->size;
68	    current_chunk = i;
69	}
70	chunk_info[i++] = c1;
71	c1 = c1->next;
72    }
73    chunk_info[i] = NULL;
74}
75
76static void
77print_chunks(Disk *d)
78{
79    int row;
80    int i;
81
82    if ((!d->bios_cyl || d->bios_cyl > 65536) || (!d->bios_hd || d->bios_hd > 256) || (!d->bios_sect || d->bios_sect >= 64))
83	msgConfirm("WARNING:  The detected geometry is incorrect!  Please adjust it to\nthe correct values manually with the (G)eometry command.  If you are\nunsure about the correct geometry (which may be \"translated\"), please\nconsult the Hardware Guide in the Documentation submenu.");
84
85    attrset(A_NORMAL);
86    mvaddstr(0, 0, "Disk name:\t");
87    clrtobot();
88    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
89    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
90    mvprintw(1, 0,
91	     "BIOS Geometry:\t%lu cyls/%lu heads/%lu sectors",
92	     d->bios_cyl, d->bios_hd, d->bios_sect);
93    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
94	     "Offset", "Size", "End", "Name", "PType", "Desc",
95	     "Subtype", "Flags");
96    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
97	if (i == current_chunk)
98	    attrset(A_REVERSE);
99	mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s",
100		 chunk_info[i]->offset, chunk_info[i]->size,
101		 chunk_info[i]->end, chunk_info[i]->name,
102		 chunk_info[i]->type, chunk_n[chunk_info[i]->type],
103		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
104	if (i == current_chunk)
105	    attrset(A_NORMAL);
106    }
107}
108
109static void
110print_command_summary()
111{
112    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
113    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan     C = Create Partition");
114    mvprintw(17, 0, "D = Delete Partition   G = Set BIOS Geometry  S = Set Bootable");
115    mvprintw(18, 0, "U = Undo All Changes   Q = Finish             W = Write Changes");
116    mvprintw(20, 0, "The currently selected partition is displayed in ");
117    attrset(A_REVERSE); addstr("reverse"); attrset(A_NORMAL); addstr(" video.");
118    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to move.");
119    move(0, 0);
120}
121
122static Disk *
123diskPartition(Disk *d)
124{
125    char *p;
126    int key = 0;
127    Boolean chunking;
128    char *msg = NULL;
129    char name[40];
130
131    chunking = TRUE;
132    strncpy(name, d->name, 40);
133    keypad(stdscr, TRUE);
134
135    clear();
136    record_chunks(d);
137    while (chunking) {
138	print_chunks(d);
139	print_command_summary();
140	if (msg) {
141	    standout(); mvprintw(23, 0, msg); standend();
142	    beep();
143	    msg = NULL;
144	}
145
146	key = toupper(getch());
147	switch (key) {
148
149	case '\014':	/* ^L */
150	    clear();
151	    continue;
152
153	case KEY_UP:
154	case '-':
155	    if (current_chunk != 0)
156		--current_chunk;
157	    break;
158
159	case KEY_DOWN:
160	case '+':
161	case '\r':
162	case '\n':
163	    if (chunk_info[current_chunk + 1])
164		++current_chunk;
165	    break;
166
167	case KEY_HOME:
168	    current_chunk = 0;
169	    break;
170
171	case KEY_END:
172	    while (chunk_info[current_chunk + 1])
173		++current_chunk;
174	    break;
175
176	case KEY_F(1):
177	case '?':
178	    systemDisplayFile("slice.hlp");
179	    break;
180
181	case 'A':
182	    All_FreeBSD(d);
183	    record_chunks(d);
184	    break;
185
186	case 'B':
187	    if (chunk_info[current_chunk]->type != freebsd)
188		msg = "Can only scan for bad blocks in FreeBSD partition.";
189	    else if (strncmp(name, "sd", 2) ||
190		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\nAre you sure you want to do this on a SCSI disk?"))
191		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
192		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
193		else
194		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
195	    break;
196
197	case 'C':
198	    if (chunk_info[current_chunk]->type != unused)
199		msg = "Partition in use, delete it first or move to an unused one.";
200	    else {
201		char *val, tmp[20], *cp;
202		int size;
203
204		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
205		val = msgGetInput(tmp, "Please specify the size for new FreeBSD partition in blocks, or append\na trailing `M' for megabytes (e.g. 20M).");
206		if (val && (size = strtol(val, &cp, 0)) > 0) {
207		    if (*cp && toupper(*cp) == 'M')
208			size *= 2048;
209		    Create_Chunk(d, chunk_info[current_chunk]->offset, size, freebsd, 3,
210				 (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
211		    record_chunks(d);
212		}
213	    }
214	    break;
215
216	case 'D':
217	    if (chunk_info[current_chunk]->type == unused)
218		msg = "Partition is already unused!";
219	    else {
220		Delete_Chunk(d, chunk_info[current_chunk]);
221		record_chunks(d);
222	    }
223	    break;
224
225	case 'G': {
226	    char *val, geometry[80];
227
228	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
229	    val = msgGetInput(geometry,
230"Please specify the new geometry in cyl/hd/sect format.\nDon't forget to use the two slash (/) separator characters!\nIt's not possible to parse the field without them.");
231	    if (val) {
232		d->bios_cyl = strtol(val, &val, 0);
233		d->bios_hd = strtol(val + 1, &val, 0);
234		d->bios_sect = strtol(val + 1, 0, 0);
235	    }
236	}
237	    break;
238
239	case 'S':
240	    /* Set Bootable */
241	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
242	    break;
243
244	case 'U':
245	    Free_Disk(d);
246	    d = Open_Disk(name);
247	    if (!d)
248		msgFatal("Can't reopen disk %s!", name);
249	    record_chunks(d);
250	    break;
251
252	case 'W':
253	    if (!msgYesNo("Are you sure you want to write this now?  You do also\nhave the option of not modifying the disk until *all*\nconfiguration information has been entered, at which\npoint you can do it all at once.  If you're unsure, then\nchoose No at this dialog."))
254	      diskPartitionWrite(NULL);
255	    break;
256
257	case '|':
258	    if (!msgYesNo("Are you sure you want to go into Wizard mode?\nNo seat belts whatsoever are provided!")) {
259		dialog_clear();
260		end_dialog();
261		DialogActive = FALSE;
262		slice_wizard(d);
263		dialog_clear();
264		DialogActive = TRUE;
265		record_chunks(d);
266	    }
267	    else
268		msg = "Wise choice!";
269	    break;
270
271	case 'Q':
272	    chunking = FALSE;
273	    break;
274
275	default:
276	    beep();
277	    msg = "Type F1 or ? for help";
278	    break;
279	}
280    }
281    p = CheckRules(d);
282    if (p) {
283	msgConfirm(p);
284	free(p);
285    }
286    dialog_clear();
287    variable_set2(DISK_PARTITIONED, "yes");
288    return d;
289}
290
291static int
292partitionHook(char *str)
293{
294    Device **devs = NULL;
295
296    /* Clip garbage off the ends */
297    string_prune(str);
298    str = string_skipwhite(str);
299    /* Try and open all the disks */
300    while (str) {
301	char *cp;
302
303	cp = index(str, '\n');
304	if (cp)
305	   *cp++ = 0;
306	if (!*str) {
307	    beep();
308	    return 0;
309	}
310	devs = deviceFind(str, DEVICE_TYPE_DISK);
311	if (!devs) {
312	    msgConfirm("Unable to find disk %s!", str);
313	    return 0;
314	}
315	else if (devs[1])
316	    msgConfirm("Bizarre multiple match for %s!", str);
317	devs[0]->private = diskPartition((Disk *)devs[0]->private);
318	devs[0]->enabled = TRUE;
319	str = cp;
320    }
321    return devs ? 1 : 0;
322}
323
324int
325diskPartitionEditor(char *str)
326{
327    DMenu *menu;
328    Device **devs;
329    int cnt;
330
331    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
332    cnt = deviceCount(devs);
333    if (!cnt) {
334	msgConfirm("No disks found!  Please verify that your disk controller is being\nproperly probed at boot time.  See the Hardware Guide on the Documentation menu\nfor clues on diagnosing this type of problem.");
335	return 0;
336    }
337    else if (cnt == 1) {
338	devs[0]->private = diskPartition((Disk *)devs[0]->private);
339	devs[0]->enabled = TRUE;
340    }
341    else {
342	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook);
343	if (!menu)
344	    msgConfirm("No devices suitable for installation found!\n\nPlease verify that your disk controller (and attached drives) were detected properly.  This can be done by selecting the ``Bootmsg'' option on the main menu and reviewing the boot messages carefully.");
345	else {
346	    dmenuOpenSimple(menu);
347	    free(menu);
348	}
349    }
350    return 0;
351}
352
353static u_char *
354getBootMgr(void)
355{
356    extern u_char mbr[], bteasy17[];
357
358    /* Figure out what kind of MBR the user wants */
359    if (dmenuOpenSimple(&MenuMBRType)) {
360	switch (BootMgr) {
361	case 0:
362	    return bteasy17;
363
364	case 1:
365	    return mbr;
366
367	case 2:
368	default:
369	    break;
370	}
371    }
372    return NULL;
373}
374
375int
376diskPartitionWrite(char *str)
377{
378    extern u_char boot1[], boot2[];
379    u_char *mbrContents;
380    Device **devs;
381    int i;
382
383    mbrContents = getBootMgr();
384    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
385    if (!devs) {
386	msgConfirm("Unable to find any disks to write to??");
387	return 0;
388    }
389
390    for (i = 0; devs[i]; i++) {
391	Chunk *c1;
392	Disk *d = (Disk *)devs[i]->private;
393
394	if (!devs[i]->enabled)
395	    continue;
396
397	/* Do it once so that it only goes on the first drive */
398	if (mbrContents) {
399	    Set_Boot_Mgr(d, mbrContents);
400	    mbrContents = NULL;
401	}
402
403	Set_Boot_Blocks(d, boot1, boot2);
404	msgNotify("Writing partition information to drive %s", d->name);
405	Write_Disk(d);
406
407	/* Now scan for bad blocks, if necessary */
408	for (c1 = d->chunks->part; c1; c1 = c1->next) {
409	    if (c1->flags & CHUNK_BAD144) {
410		int ret;
411
412		msgNotify("Running bad block scan on partition %s", c1->name);
413		ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
414		if (ret)
415		    msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
416		ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
417		if (ret)
418		    msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
419	    }
420	}
421    }
422    return 0;
423}
424