disks.c revision 25665
176195Sbrian/*
276195Sbrian * The new sysinstall program.
376358Sbrian *
476358Sbrian * This is probably the last program in the `sysinstall' line - the next
576358Sbrian * generation being essentially a complete rewrite.
676195Sbrian *
776195Sbrian * $Id: disks.c,v 1.84 1997/05/05 05:16:00 pst Exp $
876195Sbrian *
976195Sbrian * Copyright (c) 1995
1076195Sbrian *	Jordan Hubbard.  All rights reserved.
1176195Sbrian *
1276195Sbrian * Redistribution and use in source and binary forms, with or without
1376195Sbrian * modification, are permitted provided that the following conditions
1476195Sbrian * are met:
1576195Sbrian * 1. Redistributions of source code must retain the above copyright
1676195Sbrian *    notice, this list of conditions and the following disclaimer,
1776195Sbrian *    verbatim and that no modifications are made prior to this
1876195Sbrian *    point in the file.
1976195Sbrian * 2. Redistributions in binary form must reproduce the above copyright
2076195Sbrian *    notice, this list of conditions and the following disclaimer in the
2176195Sbrian *    documentation and/or other materials provided with the distribution.
2276195Sbrian *
2376195Sbrian * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
2476195Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2576195Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2676195Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
2776195Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2876195Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2976195Sbrian * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
3076195Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3176195Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3276195Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3376195Sbrian * SUCH DAMAGE.
3476195Sbrian *
3576195Sbrian */
3676195Sbrian
3776195Sbrian#include "sysinstall.h"
3876195Sbrian#include <ctype.h>
3976195Sbrian#include <sys/disklabel.h>
4076195Sbrian
4176195Sbrian/* Where we start displaying chunk information on the screen */
4276195Sbrian#define CHUNK_START_ROW		5
43119277Simp
4476195Sbrian/* Where we keep track of MBR chunks */
4576848Sbrianstatic struct chunk *chunk_info[16];
4676853Sbrianstatic int current_chunk;
4776853Sbrian
4876853Sbrianstatic void
4976195Sbrianrecord_chunks(Disk *d)
5076195Sbrian{
5176195Sbrian    struct chunk *c1 = NULL;
5276195Sbrian    int i = 0;
5376195Sbrian    int last_free = 0;
5476195Sbrian
5576195Sbrian    if (!d->chunks)
5676195Sbrian	msgFatal("No chunk list found for %s!", d->name);
5776195Sbrian
5876195Sbrian    for (c1 = d->chunks->part; c1; c1 = c1->next) {
5976195Sbrian	if (c1->type == unused && c1->size > last_free) {
6076195Sbrian	    last_free = c1->size;
6176195Sbrian	    current_chunk = i;
6276195Sbrian	}
6376195Sbrian	chunk_info[i++] = c1;
6476195Sbrian    }
6576195Sbrian    chunk_info[i] = NULL;
6676195Sbrian    if (current_chunk >= i)
6776195Sbrian	current_chunk = i - 1;
6876195Sbrian}
6976195Sbrian
7076195Sbrianstatic int Total;
7176195Sbrian
7276195Sbrianstatic void
7376195Sbrianprint_chunks(Disk *d)
7476195Sbrian{
7576195Sbrian    int row;
7676195Sbrian    int i;
7776195Sbrian
7876195Sbrian    for (i = Total = 0; chunk_info[i]; i++)
7976195Sbrian	Total += chunk_info[i]->size;
8076195Sbrian    if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
8176195Sbrian	dialog_clear_norefresh();
8276195Sbrian	msgConfirm("WARNING:  A geometry of %d/%d/%d for %s is incorrect.  Using\n"
8376195Sbrian		   "a more likely geometry.  If this geometry is incorrect or you\n"
8476195Sbrian		   "are unsure as to whether or not it's correct, please consult\n"
8576195Sbrian		   "the Hardware Guide in the Documentation submenu or use the\n"
8676195Sbrian		   "(G)eometry command to change it now.\n\n"
8776195Sbrian		   "Remember: you need to enter whatever your BIOS thinks the\n"
8876195Sbrian		   "geometry is!  For IDE, it's what you were told in the BIOS\n"
8976195Sbrian		   "setup. For SCSI, it's the translation mode your controller is\n"
9076195Sbrian		   "using.  Do NOT use a ``physical geometry''.",
9176195Sbrian	  d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
9276358Sbrian	Sanitize_Bios_Geom(d);
9376195Sbrian    }
9476195Sbrian    attrset(A_NORMAL);
9576358Sbrian    mvaddstr(0, 0, "Disk name:\t");
9676195Sbrian    clrtobot();
9776195Sbrian    attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
9876195Sbrian    attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
9976195Sbrian    mvprintw(1, 0,
10076195Sbrian	     "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors",
10176195Sbrian	     d->bios_cyl, d->bios_hd, d->bios_sect,
10276195Sbrian	     d->bios_cyl * d->bios_hd * d->bios_sect);
10376195Sbrian    mvprintw(3, 1, "%10s %10s %10s %8s %8s %8s %8s %8s",
10476195Sbrian	     "Offset", "Size", "End", "Name", "PType", "Desc",
10576195Sbrian	     "Subtype", "Flags");
10676195Sbrian    for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
10776195Sbrian	if (i == current_chunk)
10876195Sbrian	    attrset(ATTR_SELECTED);
10976195Sbrian	mvprintw(row, 2, "%10ld %10lu %10lu %8s %8d %8s %8d\t%-6s",
11076195Sbrian		 chunk_info[i]->offset, chunk_info[i]->size,
11176195Sbrian		 chunk_info[i]->end, chunk_info[i]->name,
11276195Sbrian		 chunk_info[i]->type,
11376195Sbrian		 slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
11476195Sbrian		 chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
11576195Sbrian	if (i == current_chunk)
11676195Sbrian	    attrset(A_NORMAL);
11776195Sbrian    }
11876195Sbrian}
11976195Sbrian
12076195Sbrianstatic void
12176195Sbrianprint_command_summary()
12276195Sbrian{
12376195Sbrian    mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
12476195Sbrian    mvprintw(16, 0, "A = Use Entire Disk    B = Bad Block Scan       C = Create Slice");
12576195Sbrian    mvprintw(17, 0, "D = Delete Slice       G = Set Drive Geometry   S = Set Bootable");
12676195Sbrian    mvprintw(18, 0, "U = Undo All Changes   Q = Finish");
12776195Sbrian    if (!RunningAsInit)
12876195Sbrian	mvprintw(18, 48, "W = Write Changes");
12976195Sbrian    mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
13076195Sbrian    move(0, 0);
13176195Sbrian}
13276195Sbrian
13376195Sbrianstatic u_char *
13476195SbriangetBootMgr(char *dname)
13576195Sbrian{
13676195Sbrian    extern u_char mbr[], bteasy17[];
13776195Sbrian    char str[80];
13876195Sbrian    char *cp;
13976195Sbrian    int i = 0;
14076195Sbrian
14176195Sbrian    cp = variable_get(VAR_BOOTMGR);
14276195Sbrian    if (!cp) {
14376195Sbrian	/* Figure out what kind of MBR the user wants */
14476195Sbrian	sprintf(str, "Install Boot Manager for drive %s?", dname);
14576195Sbrian	MenuMBRType.title = str;
14676195Sbrian	i = dmenuOpenSimple(&MenuMBRType, FALSE);
14776195Sbrian    }
14876195Sbrian    else {
14976195Sbrian	if (!strncmp(cp, "boot", 4))
15076195Sbrian	    BootMgr = 0;
15176195Sbrian	else if (!strcmp(cp, "standard"))
15276195Sbrian	    BootMgr = 1;
15376195Sbrian	else
15476195Sbrian	    BootMgr = 2;
15576195Sbrian    }
15676195Sbrian    if (cp || i) {
15776195Sbrian	switch (BootMgr) {
15876195Sbrian	case 0:
15976195Sbrian	    return bteasy17;
16076195Sbrian
16176195Sbrian	case 1:
16276195Sbrian	    return mbr;
16376195Sbrian
16476195Sbrian	case 2:
16576195Sbrian	default:
16676195Sbrian	    break;
16776195Sbrian	}
16876195Sbrian    }
16976195Sbrian    return NULL;
17076195Sbrian}
17176195Sbrian
17276195Sbrianvoid
17376195SbriandiskPartition(Device *dev, Disk *d)
17476195Sbrian{
17576195Sbrian    char *cp, *p;
17676195Sbrian    int rv, key = 0;
17776195Sbrian    Boolean chunking;
17876195Sbrian    char *msg = NULL;
17976195Sbrian    u_char *mbrContents;
18076195Sbrian    WINDOW *w = savescr();
18176195Sbrian
18276195Sbrian    chunking = TRUE;
18376195Sbrian    keypad(stdscr, TRUE);
18476195Sbrian
18576195Sbrian    /* Flush both the dialog and curses library views of the screen
18676195Sbrian       since we don't always know who called us */
18776195Sbrian    dialog_clear_norefresh(), clear();
18876358Sbrian    current_chunk = 0;
18976195Sbrian
19076195Sbrian    /* Set up the chunk array */
19176195Sbrian    record_chunks(d);
19276195Sbrian
19376195Sbrian    while (chunking) {
19476195Sbrian	char *val, geometry[80];
19576195Sbrian
19676195Sbrian	/* Now print our overall state */
19776195Sbrian	if (d)
19876195Sbrian	    print_chunks(d);
19976195Sbrian	print_command_summary();
20076195Sbrian	if (msg) {
20176195Sbrian	    attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
20276195Sbrian	    beep();
20376195Sbrian	    msg = NULL;
20476195Sbrian	}
20576195Sbrian	else {
20676195Sbrian	    move(23, 0);
20776195Sbrian	    clrtoeol();
20876195Sbrian	}
20976195Sbrian
21076195Sbrian	/* Get command character */
21176195Sbrian	key = getch();
21276195Sbrian	switch (toupper(key)) {
21376195Sbrian	case '\014':	/* ^L (redraw) */
21476195Sbrian	    clear();
21576195Sbrian	    msg = NULL;
21676195Sbrian	    break;
21776195Sbrian
21876195Sbrian	case '\020':	/* ^P */
21976195Sbrian	case KEY_UP:
22076195Sbrian	case '-':
22176195Sbrian	    if (current_chunk != 0)
22276195Sbrian		--current_chunk;
22376195Sbrian	    break;
22476195Sbrian
22576195Sbrian	case '\016':	/* ^N */
22676195Sbrian	case KEY_DOWN:
22776195Sbrian	case '+':
22891445Speter	case '\r':
229	case '\n':
230	    if (chunk_info[current_chunk + 1])
231		++current_chunk;
232	    break;
233
234	case KEY_HOME:
235	    current_chunk = 0;
236	    break;
237
238	case KEY_END:
239	    while (chunk_info[current_chunk + 1])
240		++current_chunk;
241	    break;
242
243	case KEY_F(1):
244	case '?':
245	    systemDisplayHelp("slice");
246	    clear();
247	    break;
248
249	case 'A':
250	    cp = variable_get(VAR_DEDICATE_DISK);
251	    if (cp && !strcasecmp(cp, "always"))
252		rv = 1;
253	    else {
254		rv = msgYesNo("Do you want to do this with a true partition entry\n"
255			      "so as to remain cooperative with any future possible\n"
256			      "operating systems on the drive(s)?");
257		if (rv != 0 && (!cp || strcasecmp(cp, "nowarn"))) {
258		    rv = !msgYesNo("This is dangerous in that it will make the drive totally\n"
259				   "uncooperative with other potential operating systems on the\n"
260				   "same disk.  It will lead instead to a totally dedicated disk,\n"
261				   "starting at the very first sector, bypassing all BIOS geometry\n"
262				   "considerations.  This precludes the existance of any boot\n"
263				   "manager or other stuff in sector 0, since the BSD bootstrap\n"
264				   "will live there.\n"
265				   "You will run into serious trouble with ST-506 and ESDI drives\n"
266				   "and possibly some IDE drives (e.g. drives running under the\n"
267				   "control of sort of disk manager).  SCSI drives are considerably\n"
268				   "less at risk.\n\n"
269				   "If, on the other hand, your goal is a dedicated FreeBSD machine\n"
270				   "and nothing else, this option is for you.\n\n"
271				   "Do you insist on dedicating the entire disk this way?");
272		}
273		if (rv == -1)
274		    rv = 0;
275	    }
276	    All_FreeBSD(d, rv);
277	    variable_set2(DISK_PARTITIONED, "yes");
278	    record_chunks(d);
279	    clear();
280	    break;
281
282	case 'B':
283	    if (chunk_info[current_chunk]->type != freebsd)
284		msg = "Can only scan for bad blocks in FreeBSD slice.";
285	    else if (strncmp(d->name, "sd", 2) ||
286		     !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
287			       "Are you sure you want to do this on a SCSI disk?")) {
288		if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
289		    chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
290		else
291		    chunk_info[current_chunk]->flags |= CHUNK_BAD144;
292	    }
293	    clear();
294	    break;
295
296	case 'C':
297	    if (chunk_info[current_chunk]->type != unused)
298		msg = "Slice in use, delete it first or move to an unused one.";
299	    else {
300		char *val, tmp[20], *cp;
301		int size, subtype;
302		chunk_e partitiontype;
303
304		snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
305		val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
306				  "or append a trailing `M' for megabytes (e.g. 20M).");
307		if (val && (size = strtol(val, &cp, 0)) > 0) {
308		    if (*cp && toupper(*cp) == 'M')
309			size *= ONE_MEG;
310		    strcpy(tmp, "165");
311		    val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
312				      "Pressing Enter will choose the default, a native FreeBSD\n"
313				      "slice (type 165).  You can choose other types, 6 for a\n"
314				      "DOS partition or 131 for a Linux partition, for example.\n\n"
315				      "Note:  If you choose a non-FreeBSD partition type, it will not\n"
316				      "be formatted or otherwise prepared, it will simply reserve space\n"
317				      "for you to use another tool, such as DOS FORMAT, to later format\n"
318				      "and use the partition.");
319		    if (val && (subtype = strtol(val, NULL, 0)) > 0) {
320			if (subtype==165)
321			    partitiontype=freebsd;
322			else if (subtype==6)
323			    partitiontype=fat;
324			else
325			    partitiontype=unknown;
326			Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
327				     (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
328			variable_set2(DISK_PARTITIONED, "yes");
329			record_chunks(d);
330		    }
331		}
332		clear();
333	    }
334	    break;
335
336	case KEY_DC:
337	case 'D':
338	    if (chunk_info[current_chunk]->type == unused)
339		msg = "Slice is already unused!";
340	    else {
341		Delete_Chunk(d, chunk_info[current_chunk]);
342		variable_set2(DISK_PARTITIONED, "yes");
343		record_chunks(d);
344	    }
345	    break;
346
347	case 'G':
348	    snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
349	    val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
350			      "Don't forget to use the two slash (/) separator characters!\n"
351			      "It's not possible to parse the field without them.");
352	    if (val) {
353		long nc, nh, ns;
354		nc = strtol(val, &val, 0);
355		nh = strtol(val + 1, &val, 0);
356		ns = strtol(val + 1, 0, 0);
357		Set_Bios_Geom(d, nc, nh, ns);
358	    }
359	    clear();
360	    break;
361
362	case 'S':
363	    /* Set Bootable */
364	    chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
365	    break;
366
367	case 'U':
368	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
369		msgConfirm("You've already written this information out - you\n"
370			   "can't undo it.");
371	    }
372	    else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
373		char cp[BUFSIZ];
374
375		sstrncpy(cp, d->name, sizeof cp);
376		Free_Disk(dev->private);
377		d = Open_Disk(cp);
378		if (!d)
379		    msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
380		dev->private = d;
381		variable_unset(DISK_PARTITIONED);
382		variable_unset(DISK_LABELLED);
383		if (d)
384		    record_chunks(d);
385	    }
386	    clear();
387	    break;
388
389	case 'W':
390	    if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
391		msgConfirm("You've already written this information out - if\n"
392			   "you wish to overwrite it, you'll have to restart.");
393	    }
394	    else if (!msgYesNo("WARNING:  This should only be used when modifying an EXISTING\n"
395			       "installation.  If you are installing FreeBSD for the first time\n"
396			       "then you should simply type Q when you're finished here and your\n"
397			       "changes will be committed in one batch automatically at the end of\n"
398			       "these questions.\n\n"
399			       "Are you absolutely sure you want to do this now?")) {
400		variable_set2(DISK_PARTITIONED, "yes");
401
402		/* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
403		 * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
404		 * booteasy or a "standard" MBR -- both would be fatal in this case.
405		 */
406		if (!(d->chunks->part->flags & CHUNK_FORCE_ALL) && (mbrContents = getBootMgr(d->name)) != NULL)
407		    Set_Boot_Mgr(d, mbrContents);
408
409		if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
410		    msgConfirm("Disk partition write returned an error status!");
411		else
412		    msgConfirm("Wrote FDISK partition information out successfully.");
413	    }
414	    clear();
415	    break;
416
417	case '|':
418	    if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
419			  "No seat belts whatsoever are provided!")) {
420		clear();
421		refresh();
422		slice_wizard(d);
423		variable_set2(DISK_PARTITIONED, "yes");
424		record_chunks(d);
425	    }
426	    else
427		msg = "Wise choice!";
428	    clear();
429	    break;
430
431	case '\033':	/* ESC */
432	case 'Q':
433	    chunking = FALSE;
434	    /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
435	     * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
436	     * booteasy or a "standard" MBR -- both would be fatal in this case.
437	     */
438	    if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
439		&& (mbrContents = getBootMgr(d->name)) != NULL)
440		Set_Boot_Mgr(d, mbrContents);
441	    break;
442
443	default:
444	    beep();
445	    msg = "Type F1 or ? for help";
446	    break;
447	}
448    }
449    p = CheckRules(d);
450    if (p) {
451	char buf[FILENAME_MAX];
452
453	dialog_clear_norefresh();
454        use_helpline("Press F1 to read more about disk slices.");
455	use_helpfile(systemHelpFile("partition", buf));
456	dialog_mesgbox("Disk slicing warning:", p, -1, -1);
457	free(p);
458    }
459    restorescr(w);
460}
461
462static int
463partitionHook(dialogMenuItem *selected)
464{
465    Device **devs = NULL;
466
467    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
468    if (!devs) {
469	msgConfirm("Unable to find disk %s!", selected->prompt);
470	return DITEM_FAILURE;
471    }
472    /* Toggle enabled status? */
473    if (!devs[0]->enabled) {
474	devs[0]->enabled = TRUE;
475	diskPartition(devs[0], (Disk *)devs[0]->private);
476    }
477    else
478	devs[0]->enabled = FALSE;
479    return DITEM_SUCCESS | DITEM_REDRAW;
480}
481
482static int
483partitionCheck(dialogMenuItem *selected)
484{
485    Device **devs = NULL;
486
487    devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
488    if (!devs || devs[0]->enabled == FALSE)
489	return FALSE;
490    return TRUE;
491}
492
493int
494diskPartitionEditor(dialogMenuItem *self)
495{
496    DMenu *menu;
497    Device **devs;
498    int i, cnt;
499    char *cp;
500
501    cp = variable_get(VAR_DISK);
502    devs = deviceFind(cp, DEVICE_TYPE_DISK);
503    cnt = deviceCount(devs);
504    if (!cnt) {
505	msgConfirm("No disks found!  Please verify that your disk controller is being\n"
506		   "properly probed at boot time.  See the Hardware Guide on the\n"
507		   "Documentation menu for clues on diagnosing this type of problem.");
508	i = DITEM_FAILURE;
509    }
510    else if (cnt == 1) {
511	devs[0]->enabled = TRUE;
512	diskPartition(devs[0], (Disk *)devs[0]->private);
513	i = DITEM_SUCCESS;
514    }
515    else {
516	menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
517	if (!menu) {
518	    msgConfirm("No devices suitable for installation found!\n\n"
519		       "Please verify that your disk controller (and attached drives)\n"
520		       "were detected properly.  This can be done by pressing the\n"
521		       "[Scroll Lock] key and using the Arrow keys to move back to\n"
522		       "the boot messages.  Press [Scroll Lock] again to return.");
523	    i = DITEM_FAILURE;
524	}
525	else {
526	    i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
527	    free(menu);
528	}
529	i = i | DITEM_RESTORE;
530    }
531    return i;
532}
533
534int
535diskPartitionWrite(dialogMenuItem *self)
536{
537    Device **devs;
538    char *cp;
539    int i;
540
541    if ((cp = variable_get(DISK_PARTITIONED)) && strcmp(cp, "yes"))
542	return DITEM_SUCCESS;
543    else if (!cp) {
544	msgConfirm("You must partition the disk(s) before this option can be used.");
545	return DITEM_FAILURE;
546    }
547
548    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
549    if (!devs) {
550	msgConfirm("Unable to find any disks to write to??");
551	return DITEM_FAILURE;
552    }
553    if (isDebug())
554	msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
555
556    for (i = 0; devs[i]; i++) {
557	Chunk *c1;
558	Disk *d = (Disk *)devs[i]->private;
559
560	if (!devs[i]->enabled)
561	    continue;
562
563	Set_Boot_Blocks(d, boot1, boot2);
564	msgNotify("Writing partition information to drive %s", d->name);
565	if (!Fake && Write_Disk(d)) {
566	    msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
567	    return DITEM_FAILURE;
568	}
569	/* Now scan for bad blocks, if necessary */
570	for (c1 = d->chunks->part; c1; c1 = c1->next) {
571	    if (c1->flags & CHUNK_BAD144) {
572		int ret;
573
574		msgNotify("Running bad block scan on slice %s", c1->name);
575		if (!Fake) {
576		    ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
577		    if (ret)
578			msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
579		    ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
580		    if (ret)
581			msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
582		}
583	    }
584	}
585    }
586    /* Now it's not "yes", but "written" */
587    variable_set2(DISK_PARTITIONED, "written");
588    return DITEM_SUCCESS;
589}
590