NetBSD/sbin/fdisk/fdisk.c
thorpej 6d78b8c024 When fetching the disk's hardware geometry, first attempt the
DIOCGDEFLABEL ioctl, then fall back onto the DIOCGDINFO ioctl
if that fails.  This ensures that we will get the actual hardware
geometry info rather than any bogus info that might have been
previously written to the disk's label area.
2000-07-06 17:22:43 +00:00

1415 lines
32 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* $NetBSD: fdisk.c,v 1.42 2000/07/06 17:22:43 thorpej Exp $ */
/*
* Mach Operating System
* Copyright (c) 1992 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: fdisk.c,v 1.42 2000/07/06 17:22:43 thorpej Exp $");
#endif /* not lint */
#include <sys/types.h>
#include <sys/disklabel.h>
#include <sys/disklabel_mbr.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <util.h>
#ifdef __i386__
#include <ctype.h>
#include <machine/cpu.h>
#include <sys/sysctl.h>
#endif
#define LBUF 100
static char lbuf[LBUF];
/*
* 14-Dec-89 Robert Baron (rvb) at Carnegie-Mellon University
* Copyright (c) 1989 Robert. V. Baron
* Created.
*/
#ifndef _PATH_DEFDISK
#define _PATH_DEFDISK "/dev/rwd0d"
#endif
char *disk = _PATH_DEFDISK;
struct disklabel disklabel; /* disk parameters */
int cylinders, sectors, heads, cylindersectors, disksectors;
struct mboot {
u_int8_t padding[2]; /* force the longs to be long alligned */
u_int8_t bootinst[MBR_PARTOFF];
struct mbr_partition parts[NMBRPART];
u_int16_t signature;
};
struct mboot mboot;
#ifdef __i386__
#define PARTNAMESIZE 8 /* From mbr_bootsel.S */
struct mbr_bootsel {
u_int8_t defkey;
u_int8_t flags;
u_int16_t timeo;
char nametab[4][PARTNAMESIZE + 1];
u_int16_t magic;
} __attribute__((packed));
#define BFL_SELACTIVE 0x01
#define BFL_EXTINT13 0x02
#define SCAN_ENTER 0x1c
#define SCAN_F1 0x3b
#define MBR_BOOTSELOFF (MBR_PARTOFF - sizeof (struct mbr_bootsel))
#define DEFAULT_BOOTCODE "/usr/mdec/mbr"
#define DEFAULT_BOOTSELCODE "/usr/mdec/mbr_bootsel"
#define OPTIONS "0123BSafius:b:c:"
#else
#define OPTIONS "0123Safius:b:c:"
#endif
#define ACTIVE 0x80
int dos_cylinders;
int dos_heads;
int dos_sectors;
int dos_cylindersectors;
#define DOSSECT(s,c) (((s) & 0x3f) | (((c) >> 2) & 0xc0))
#define DOSCYL(c) ((c) & 0xff)
#define MAXCYL 1024
int partition = -1;
int a_flag; /* set active partition */
int i_flag; /* init bootcode */
int u_flag; /* update partition data */
int sh_flag; /* Output data as shell defines */
int f_flag; /* force --not interactive */
int s_flag; /* set id,offset,size */
int b_flag; /* Set cyl, heads, secs (as c/h/s) */
int B_flag; /* Edit/install bootselect code */
int b_cyl, b_head, b_sec; /* b_flag values. */
int bootsel_modified;
unsigned char bootcode[8192]; /* maximum size of bootcode */
unsigned char tempcode[8192];
int bootsize; /* actual size of bootcode */
static char reserved[] = "reserved";
struct part_type {
int type;
char *name;
} part_types[] = {
{0x00, "unused"},
{0x01, "Primary DOS with 12 bit FAT"},
{0x02, "XENIX / filesystem"},
{0x03, "XENIX /usr filesystem"},
{0x04, "Primary DOS with 16 bit FAT <32M"},
{0x05, "Extended partition"},
{0x06, "Primary 'big' DOS, 16-bit FAT (> 32MB)"},
{0x07, "OS/2 HPFS or NTFS or QNX2 or Advanced UNIX"},
{0x08, "AIX filesystem or OS/2 (thru v1.3) or DELL multiple drives"
"or Commodore DOS or SplitDrive"},
{0x09, "AIX boot partition or Coherent"},
{0x0A, "OS/2 Boot Manager or Coherent swap or OPUS"},
{0x0b, "Primary DOS with 32 bit FAT"},
{0x0c, "Primary DOS with 32 bit FAT - LBA"},
{0x0d, "Type 7??? - LBA"},
{0x0E, "DOS (16-bit FAT) - LBA"},
{0x0F, "Ext. partition - LBA"},
{0x10, "OPUS"},
{0x11, "OS/2 BM: hidden DOS 12-bit FAT"},
{0x12, "Compaq diagnostics"},
{0x14, "OS/2 BM: hidden DOS 16-bit FAT <32M or Novell DOS 7.0 bug"},
{0x16, "OS/2 BM: hidden DOS 16-bit FAT >=32M"},
{0x17, "OS/2 BM: hidden IFS"},
{0x18, "AST Windows swapfile"},
{0x19, "Willowtech Photon coS"},
{0x1e, "hidden FAT95"},
{0x20, "Willowsoft OFS1"},
{0x21, reserved},
{0x23, reserved},
{0x24, "NEC DOS"},
{0x26, reserved},
{0x31, reserved},
{0x33, reserved},
{0x34, reserved},
{0x36, reserved},
{0x38, "Theos"},
{0x3C, "PartitionMagic recovery"},
{0x40, "VENIX 286 or LynxOS"},
{0x41, "Linux/MINIX (sharing disk with DRDOS) or Personal RISC boot"},
{0x42, "SFS or Linux swap (sharing disk with DRDOS)"},
{0x43, "Linux native (sharing disk with DRDOS)"},
{0x4D, "QNX4.x"},
{0x4E, "QNX4.x 2nd part"},
{0x4F, "QNX4.x 3rd part"},
{0x50, "DM (disk manager)"},
{0x51, "DM6 Aux1 (or Novell)"},
{0x52, "CP/M or Microport SysV/AT"},
{0x53, "DM6 Aux3"},
{0x54, "DM6 DDO"},
{0x55, "EZ-Drive (disk manager)"},
{0x56, "Golden Bow (disk manager)"},
{0x5C, "Priam Edisk (disk manager)"},
{0x61, "SpeedStor"},
{0x63, "GNU HURD or Mach or Sys V/386 (such as ISC UNIX) or MtXinu"},
{0x64, "Novell Netware 2.xx or Speedstore"},
{0x65, "Novell Netware 3.xx"},
{0x66, "Novell 386 Netware"},
{0x67, "Novell"},
{0x68, "Novell"},
{0x69, "Novell"},
{0x70, "DiskSecure Multi-Boot"},
{0x71, reserved},
{0x73, reserved},
{0x74, reserved},
{0x75, "PC/IX"},
{0x76, reserved},
{0x80, "MINIX until 1.4a"},
{0x81, "MINIX since 1.4b, early Linux, Mitac dmgr"},
{0x82, "Linux swap or Prime or Solaris"},
{0x83, "Linux native"},
{0x84, "OS/2 hidden C: drive"},
{0x85, "Linux extended"},
{0x86, "NT FAT volume set"},
{0x87, "NTFS volume set or HPFS mirrored"},
{0x93, "Amoeba filesystem"},
{0x94, "Amoeba bad block table"},
{0x99, "Mylex EISA SCSI"},
{0x9f, "BSDI?"},
{0xA0, "IBM Thinkpad hibernation"},
{0xa1, reserved},
{0xa3, reserved},
{0xa4, reserved},
{0xA5, "FreeBSD or 386BSD or old NetBSD"},
{0xA6, "OpenBSD"},
{0xA7, "NeXTSTEP 486"},
{0xa9, "NetBSD"},
{0xb1, reserved},
{0xb3, reserved},
{0xb4, reserved},
{0xb6, reserved},
{0xB7, "BSDI BSD/386 filesystem"},
{0xB8, "BSDI BSD/386 swap"},
{0xc0, "CTOS"},
{0xC1, "DRDOS/sec (FAT-12)"},
{0xC4, "DRDOS/sec (FAT-16, < 32M)"},
{0xC6, "DRDOS/sec (FAT-16, >= 32M)"},
{0xC7, "Syrinx (Cyrnix?) or HPFS disabled"},
{0xd8, "CP/M 86"},
{0xDB, "CP/M or Concurrent CP/M or Concurrent DOS or CTOS"},
{0xE1, "DOS access or SpeedStor 12-bit FAT extended partition"},
{0xE3, "DOS R/O or SpeedStor or Storage Dimensions"},
{0xE4, "SpeedStor 16-bit FAT extended partition < 1024 cyl."},
{0xe5, reserved},
{0xe6, reserved},
{0xeb, "BeOS"},
{0xF1, "SpeedStor or Storage Dimensions"},
{0xF2, "DOS 3.3+ Secondary"},
{0xf3, reserved},
{0xF4, "SpeedStor large partition or Storage Dimensions"},
{0xf6, reserved},
{0xFE, "SpeedStor >1024 cyl. or LANstep or IBM PS/2 IML"},
{0xFF, "Xenix Bad Block Table"},
};
void usage __P((void));
void print_s0 __P((int));
void print_part __P((int));
int read_boot __P((char *, char *, size_t));
void init_sector0 __P((int, int));
void intuit_translated_geometry __P((void));
void get_geometry __P((void));
void get_diskname __P((char *, char *, size_t));
int try_heads __P((quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t,
quad_t));
int try_sectors __P((quad_t, quad_t, quad_t, quad_t, quad_t));
void change_part __P((int, int, int, int));
void print_params __P((void));
void change_active __P((int));
void get_params_to_use __P((void));
void dos __P((int, unsigned char *, unsigned char *, unsigned char *));
int open_disk __P((int));
int read_disk __P((int, void *));
int write_disk __P((int, void *));
int get_params __P((void));
int read_s0 __P((void));
int write_s0 __P((void));
int yesno __P((char *));
void decimal __P((char *, int *));
int type_match __P((const void *, const void *));
char *get_type __P((int));
int get_mapping __P((int, int *, int *, int *, long *));
#ifdef __i386__
void configure_bootsel __P((void));
#endif
static inline unsigned short getshort __P((void *));
static inline void putshort __P((void *p, unsigned short));
static inline unsigned long getlong __P((void *));
static inline void putlong __P((void *, unsigned long));
int main __P((int, char **));
int
main(argc, argv)
int argc;
char *argv[];
{
int ch;
int part;
int csysid, cstart, csize; /* For the b_flag. */
a_flag = i_flag = u_flag = sh_flag = f_flag = s_flag = b_flag = 0;
csysid = cstart = csize = 0;
while ((ch = getopt(argc, argv, OPTIONS)) != -1)
switch (ch) {
case '0':
partition = 0;
break;
case '1':
partition = 1;
break;
case '2':
partition = 2;
break;
case '3':
partition = 3;
break;
#ifdef __i386__
case 'B':
B_flag = 1;
break;
#endif
case 'S':
sh_flag = 1;
break;
case 'a':
a_flag = 1;
break;
case 'f':
f_flag = 1;
break;
case 'i':
i_flag = 1;
break;
case 'u':
u_flag = 1;
break;
case 's':
s_flag = 1;
if (sscanf (optarg, "%d/%d/%d",
&csysid, &cstart, &csize) != 3) {
(void)fprintf (stderr, "%s: Bad argument "
"to the -s flag.\n",
argv[0]);
exit (1);
}
break;
case 'b':
b_flag = 1;
if (sscanf (optarg, "%d/%d/%d",
&b_cyl, &b_head, &b_sec) != 3) {
(void)fprintf (stderr, "%s: Bad argument "
"to the -b flag.\n",
argv[0]);
exit (1);
}
if (b_cyl > MAXCYL)
b_cyl = MAXCYL;
break;
case 'c':
bootsize = read_boot(optarg, bootcode, sizeof bootcode);
break;
default:
usage();
}
argc -= optind;
argv += optind;
if (sh_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
usage();
if (B_flag && (a_flag || i_flag || u_flag || f_flag || s_flag))
usage();
if (partition == -1 && s_flag) {
(void) fprintf (stderr,
"-s flag requires a partition selected.\n");
usage();
}
if (argc > 0)
disk = argv[0];
if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0)
exit(1);
if (read_s0())
init_sector0(sectors > 63 ? 63 : sectors, 1);
#ifdef __i386__
get_geometry();
#else
intuit_translated_geometry();
#endif
if ((i_flag || u_flag) && (!f_flag || b_flag))
get_params_to_use();
if (i_flag)
init_sector0(dos_sectors > 63 ? 63 : dos_sectors, 0);
/* Do the update stuff! */
if (u_flag) {
if (!f_flag)
printf("Partition table:\n");
if (partition == -1)
for (part = 0; part < NMBRPART; part++)
change_part(part,-1, -1, -1);
else
change_part(partition, csysid, cstart, csize);
} else
if (!i_flag)
print_s0(partition);
if (a_flag)
change_active(partition);
#ifdef __i386__
if (B_flag) {
configure_bootsel();
if (B_flag && bootsel_modified)
write_s0();
}
#endif
if (u_flag || a_flag || i_flag) {
if (!f_flag) {
printf("\nWe haven't written the MBR back to disk "
"yet. This is your last chance.\n");
print_s0(-1);
if (yesno("Should we write new partition table?"))
write_s0();
} else
write_s0();
}
exit(0);
}
void
usage()
{
(void)fprintf(stderr, "usage: fdisk [-aiufBS] [-0|-1|-2|-3] "
"[-b cylinders/heads/sectors]\n"
" [-s id/start/size] [-c bootcode] "
"[device]\n");
exit(1);
}
void
print_s0(which)
int which;
{
int part;
print_params();
if (!sh_flag)
printf("Partition table:\n");
if (which == -1) {
for (part = 0; part < NMBRPART; part++) {
if (!sh_flag)
printf("%d: ", part);
print_part(part);
}
} else
print_part(which);
}
static inline unsigned short
getshort(p)
void *p;
{
unsigned char *cp = p;
return cp[0] | (cp[1] << 8);
}
static inline void
putshort(p, l)
void *p;
unsigned short l;
{
unsigned char *cp = p;
*cp++ = l;
*cp++ = l >> 8;
}
static inline unsigned long
getlong(p)
void *p;
{
unsigned char *cp = p;
return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24);
}
static inline void
putlong(p, l)
void *p;
unsigned long l;
{
unsigned char *cp = p;
*cp++ = l;
*cp++ = l >> 8;
*cp++ = l >> 16;
*cp++ = l >> 24;
}
void
print_part(part)
int part;
{
struct mbr_partition *partp;
int empty;
partp = &mboot.parts[part];
empty = (partp->mbrp_typ == 0);
if (sh_flag) {
if (empty) {
printf("PART%dSIZE=0\n", part);
return;
}
printf("PART%dID=%d\n", part, partp->mbrp_typ);
printf("PART%dSIZE=%ld\n", part, getlong(&partp->mbrp_size));
printf("PART%dSTART=%ld\n", part, getlong(&partp->mbrp_start));
printf("PART%dFLAG=0x%x\n", part, partp->mbrp_flag);
printf("PART%dBCYL=%d\n", part, MBR_PCYL(partp->mbrp_scyl,
partp->mbrp_ssect));
printf("PART%dBHEAD=%d\n", part, partp->mbrp_shd);
printf("PART%dBSEC=%d\n", part, MBR_PSECT(partp->mbrp_ssect));
printf("PART%dECYL=%d\n", part, MBR_PCYL(partp->mbrp_ecyl,
partp->mbrp_esect));
printf("PART%dEHEAD=%d\n", part, partp->mbrp_ehd);
printf("PART%dESEC=%d\n", part, MBR_PSECT(partp->mbrp_esect));
return;
}
/* Not sh_flag. */
if (empty) {
printf("<UNUSED>\n");
return;
}
printf("sysid %d (%s)\n", partp->mbrp_typ, get_type(partp->mbrp_typ));
printf(" start %ld, size %ld (%ld MB), flag 0x%x\n",
getlong(&partp->mbrp_start), getlong(&partp->mbrp_size),
getlong(&partp->mbrp_size) * 512 / (1024 * 1024), partp->mbrp_flag);
printf("\tbeg: cylinder %4d, head %3d, sector %2d\n",
MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect),
partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect));
printf("\tend: cylinder %4d, head %3d, sector %2d\n",
MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect),
partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect));
}
int
read_boot(name, buf, len)
char *name;
char *buf;
size_t len;
{
int bfd, ret;
struct stat st;
if ((bfd = open(name, O_RDONLY)) < 0)
err(1, "%s", name);
if (fstat(bfd, &st) == -1)
err(1, "%s", name);
if (st.st_size > len)
errx(1, "%s: bootcode too large", name);
ret = st.st_size;
if (ret < 0x200)
errx(1, "%s: bootcode too small", name);
if (read(bfd, buf, len) != ret)
err(1, "%s", name);
close(bfd);
/*
* Do some sanity checking here
*/
if (getshort(bootcode + MBR_MAGICOFF) != MBR_MAGIC)
errx(1, "%s: invalid magic", name);
ret = (ret + 0x1ff) / 0x200;
ret *= 0x200;
return ret;
}
void
init_sector0(start, dopart)
int start, dopart;
{
int i;
#ifdef DEFAULT_BOOTCODE
if (!bootsize)
bootsize = read_boot(DEFAULT_BOOTCODE, bootcode,
sizeof bootcode);
#endif
memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst));
putshort(&mboot.signature, MBR_MAGIC);
if (dopart)
for (i=0; i<4; i++)
memset(&mboot.parts[i], 0, sizeof(struct mbr_partition));
}
#ifdef __i386__
void
get_diskname(fullname, diskname, size)
char *fullname;
char *diskname;
size_t size;
{
char *p;
char *p2;
size_t len;
p = strrchr(fullname, '/');
if (p == NULL)
p = fullname;
else
p++;
if (*p == 0) {
strncpy(diskname, fullname, size - 1);
diskname[size - 1] = '\0';
return;
}
if (*p == 'r')
p++;
for (p2 = p; *p2 != 0; p2++)
if (isdigit(*p2))
break;
if (*p2 == 0) {
/* XXX invalid diskname? */
strncpy(diskname, fullname, size - 1);
diskname[size - 1] = '\0';
return;
}
while (isdigit(*p2))
p2++;
len = p2 - p;
if (len > size) {
/* XXX */
strncpy(diskname, fullname, size - 1);
diskname[size - 1] = '\0';
return;
}
strncpy(diskname, p, len);
diskname[len] = 0;
}
void
get_geometry()
{
int mib[2], i;
size_t len;
struct disklist *dl;
struct biosdisk_info *bip;
struct nativedisk_info *nip;
char diskname[8];
mib[0] = CTL_MACHDEP;
mib[1] = CPU_DISKINFO;
if (sysctl(mib, 2, NULL, &len, NULL, 0) < 0) {
intuit_translated_geometry();
return;
}
dl = (struct disklist *) malloc(len);
sysctl(mib, 2, dl, &len, NULL, 0);
get_diskname(disk, diskname, sizeof diskname);
for (i = 0; i < dl->dl_nnativedisks; i++) {
nip = &dl->dl_nativedisks[i];
if (strcmp(diskname, nip->ni_devname))
continue;
/*
* XXX listing possible matches is better. This is ok
* for now because the user has a chance to change
* it later.
*/
if (nip->ni_nmatches != 0) {
bip = &dl->dl_biosdisks[nip->ni_biosmatches[0]];
dos_cylinders = bip->bi_cyl;
dos_heads = bip->bi_head;
dos_sectors = bip->bi_sec;
dos_cylindersectors = bip->bi_head * bip->bi_sec;
return;
}
}
/* Allright, allright, make a stupid guess.. */
intuit_translated_geometry();
}
void
configure_bootsel()
{
struct mbr_bootsel *mbs =
(struct mbr_bootsel *)&mboot.bootinst[MBR_BOOTSELOFF];
int i, nused, firstpart = -1, item;
char desc[PARTNAMESIZE + 2], *p;
int timo, entry_changed = 0;
for (i = nused = 0; i < NMBRPART; ++i) {
if (mboot.parts[i].mbrp_typ != 0) {
if (firstpart == -1)
firstpart = i;
nused++;
}
}
if (nused == 0) {
printf("No used partitions found. Partition the disk first.\n");
return;
}
if (mbs->magic != MBR_MAGIC) {
if (!yesno("Bootselector not yet installed. Install it now?")) {
printf("Bootselector not installed.\n");
return;
}
bootsize = read_boot(DEFAULT_BOOTSELCODE, bootcode,
sizeof bootcode);
memcpy(mboot.bootinst, bootcode, sizeof(mboot.bootinst));
bootsel_modified = 1;
mbs->flags |= BFL_SELACTIVE;
} else {
if (mbs->flags & BFL_SELACTIVE) {
printf("The bootselector is installed and active.\n");
if (!yesno("Do you want to change its settings?")) {
if (yesno("Do you want to deactivate it?")) {
mbs->flags &= ~BFL_SELACTIVE;
bootsel_modified = 1;
goto done;
}
return;
}
} else {
printf("The bootselector is installed but not active.\n");
if (yesno("Do you want to activate it?")) {
mbs->flags |= BFL_SELACTIVE;
bootsel_modified = 1;
}
if (!yesno("Do you want to change its settings?"))
goto done;
}
}
printf("\n\nPartition table:\n");
for (i = 0; i < NMBRPART; i++) {
printf("%d: ", i);
print_part(i);
}
printf("\n\nCurrent boot selection menu option names:\n");
for (i = 0; i < NMBRPART; i++) {
if (mbs->nametab[i][0] != 0)
printf("%d: %s\n", i, &mbs->nametab[i][0]);
else
printf("%d: <UNUSED>\n", i);
}
printf("\n");
item = firstpart;
editentries:
while (1) {
decimal("Change which entry (-1 quits)?", &item);
if (item == -1)
break;
if (item < 0 || item >= NMBRPART) {
printf("Invalid entry number\n");
item = -1;
continue;
}
if (mboot.parts[item].mbrp_typ == 0) {
printf("The partition entry is unused\n");
item = -1;
continue;
}
printf("Enter descriptions (max. 8 characters): ");
rewind(stdin);
fgets(desc, PARTNAMESIZE + 1, stdin);
fpurge(stdin);
p = strchr(desc, '\n');
if (p != NULL)
*p = 0;
strcpy(&mbs->nametab[item][0], desc);
entry_changed = bootsel_modified = 1;
item++;
}
if (entry_changed)
printf("Boot selection menu option names are now:\n");
firstpart = -1;
for (i = 0; i < NMBRPART; i++) {
if (mbs->nametab[i][0] != 0) {
firstpart = i;
if (entry_changed)
printf("%d: %s\n", i, &mbs->nametab[i][0]);
} else {
if (entry_changed)
printf("%d: <UNUSED>\n", i);
}
}
if (entry_changed)
printf("\n");
if (firstpart == -1) {
printf("All menu entries are now inactive.\n");
if (!yesno("Are you sure about this?"))
goto editentries;
} else {
if (!(mbs->flags & BFL_SELACTIVE)) {
printf("The bootselector is not yet active.\n");
if (yesno("Activate it now?"))
mbs->flags |= BFL_SELACTIVE;
}
}
/* bootsel is dirty from here on out. */
bootsel_modified = 1;
/* The timeout value is in ticks, 18.2 Hz. Avoid using floats. */
timo = ((1000 * mbs->timeo) / 18200);
do {
decimal("Timeout value", &timo);
} while (timo < 0 || timo > 3600);
mbs->timeo = (u_int16_t)((timo * 18200) / 1000);
printf("Select the default boot option. Options are:\n\n");
for (i = 0; i < NMBRPART; i++) {
if (mbs->nametab[i][0] != 0)
printf("%d: %s\n", i, &mbs->nametab[i][0]);
}
for (i = 4; i < 10; i++)
printf("%d: Harddisk %d\n", i, i - 4);
printf("10: The first active partition\n");
if (mbs->defkey == SCAN_ENTER)
item = 10;
else
item = mbs->defkey - SCAN_F1;
if (item < 0 || item > 10 || mbs->nametab[item][0] == 0)
item = 10;
do {
decimal("Default boot option", &item);
} while (item < 0 || item > 10 ||
(item <= 3 && mbs->nametab[item][0] == 0));
if (item == 10)
mbs->defkey = SCAN_ENTER;
else
mbs->defkey = SCAN_F1 + item;
done:
for (i = 0; i < NMBRPART; i++) {
if (mboot.parts[i].mbrp_typ != 0 &&
mboot.parts[i].mbrp_start >=
(dos_cylinders * dos_heads * dos_sectors)) {
mbs->flags |= BFL_EXTINT13;
break;
}
}
if (bootsel_modified != 0 && !yesno("Update the bootselector?"))
bootsel_modified = 0;
}
#endif
/* Prerequisite: the disklabel parameters and master boot record must
* have been read (i.e. dos_* and mboot are meaningful).
* Specification: modifies dos_cylinders, dos_heads, dos_sectors, and
* dos_cylindersectors to be consistent with what the
* partition table is using, if we can find a geometry
* which is consistent with all partition table entries.
* We may get the number of cylinders slightly wrong (in
* the conservative direction). The idea is to be able
* to create a NetBSD partition on a disk we don't know
* the translated geometry of.
* This whole routine should be replaced with a kernel interface to get
* the BIOS geometry (which in turn requires modifications to the i386
* boot loader to pass in the BIOS geometry for each disk). */
void
intuit_translated_geometry()
{
int cylinders = -1, heads = -1, sectors = -1, i, j;
int c1, h1, s1, c2, h2, s2;
long a1, a2;
quad_t num, denom;
/* Try to deduce the number of heads from two different mappings. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
continue;
for (j = 0; j < 8; j++) {
if (get_mapping(j, &c2, &h2, &s2, &a2) < 0)
continue;
num = (quad_t)h1*(a2-s2) - (quad_t)h2*(a1-s1);
denom = (quad_t)c2*(a1-s1) - (quad_t)c1*(a2-s2);
if (denom != 0 && num % denom == 0) {
heads = num / denom;
break;
}
}
if (heads != -1)
break;
}
if (heads == -1)
return;
/* Now figure out the number of sectors from a single mapping. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
continue;
num = a1 - s1;
denom = c1 * heads + h1;
if (denom != 0 && num % denom == 0) {
sectors = num / denom;
break;
}
}
if (sectors == -1)
return;
/* Estimate the number of cylinders. */
cylinders = disklabel.d_secperunit / heads / sectors;
/* Now verify consistency with each of the partition table entries.
* Be willing to shove cylinders up a little bit to make things work,
* but translation mismatches are fatal. */
for (i = 0; i < NMBRPART * 2; i++) {
if (get_mapping(i, &c1, &h1, &s1, &a1) < 0)
continue;
if (sectors * (c1 * heads + h1) + s1 != a1)
return;
if (c1 >= cylinders)
cylinders = c1 + 1;
}
/* Everything checks out. Reset the geometry to use for further
* calculations. */
dos_cylinders = cylinders;
dos_heads = heads;
dos_sectors = sectors;
dos_cylindersectors = heads * sectors;
}
/* For the purposes of intuit_translated_geometry(), treat the partition
* table as a list of eight mapping between (cylinder, head, sector)
* triplets and absolute sectors. Get the relevant geometry triplet and
* absolute sectors for a given entry, or return -1 if it isn't present.
* Note: for simplicity, the returned sector is 0-based. */
int
get_mapping(i, cylinder, head, sector, absolute)
int i, *cylinder, *head, *sector;
long *absolute;
{
struct mbr_partition *part = &mboot.parts[i / 2];
if (part->mbrp_typ == 0)
return -1;
if (i % 2 == 0) {
*cylinder = MBR_PCYL(part->mbrp_scyl, part->mbrp_ssect);
*head = part->mbrp_shd;
*sector = MBR_PSECT(part->mbrp_ssect) - 1;
*absolute = getlong(&part->mbrp_start);
} else {
*cylinder = MBR_PCYL(part->mbrp_ecyl, part->mbrp_esect);
*head = part->mbrp_ehd;
*sector = MBR_PSECT(part->mbrp_esect) - 1;
*absolute = getlong(&part->mbrp_start)
+ getlong(&part->mbrp_size) - 1;
}
return 0;
}
void
change_part(part, csysid, cstart, csize)
int part, csysid, cstart, csize;
{
struct mbr_partition *partp;
partp = &mboot.parts[part];
if (s_flag) {
if (csysid == 0 && cstart == 0 && csize == 0)
memset(partp, 0, sizeof *partp);
else {
partp->mbrp_typ = csysid;
#if 0
checkcyl(cstart / dos_cylindersectors);
#endif
putlong(&partp->mbrp_start, cstart);
putlong(&partp->mbrp_size, csize);
dos(getlong(&partp->mbrp_start),
&partp->mbrp_scyl, &partp->mbrp_shd, &partp->mbrp_ssect);
dos(getlong(&partp->mbrp_start)
+ getlong(&partp->mbrp_size) - 1,
&partp->mbrp_ecyl, &partp->mbrp_ehd, &partp->mbrp_esect);
}
if (f_flag)
return;
}
printf("The data for partition %d is:\n", part);
print_part(part);
if (!u_flag || !yesno("Do you want to change it?"))
return;
do {
{
int sysid, start, size;
sysid = partp->mbrp_typ,
start = getlong(&partp->mbrp_start),
size = getlong(&partp->mbrp_size);
decimal("sysid", &sysid);
decimal("start", &start);
decimal("size", &size);
partp->mbrp_typ = sysid;
putlong(&partp->mbrp_start, start);
putlong(&partp->mbrp_size, size);
}
if (yesno("Explicitly specify beg/end address?")) {
int tsector, tcylinder, thead;
tcylinder = MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect);
thead = partp->mbrp_shd;
tsector = MBR_PSECT(partp->mbrp_ssect);
decimal("beginning cylinder", &tcylinder);
#if 0
checkcyl(tcylinder);
#endif
decimal("beginning head", &thead);
decimal("beginning sector", &tsector);
partp->mbrp_scyl = DOSCYL(tcylinder);
partp->mbrp_shd = thead;
partp->mbrp_ssect = DOSSECT(tsector, tcylinder);
tcylinder = MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect);
thead = partp->mbrp_ehd;
tsector = MBR_PSECT(partp->mbrp_esect);
decimal("ending cylinder", &tcylinder);
decimal("ending head", &thead);
decimal("ending sector", &tsector);
partp->mbrp_ecyl = DOSCYL(tcylinder);
partp->mbrp_ehd = thead;
partp->mbrp_esect = DOSSECT(tsector, tcylinder);
} else {
if (partp->mbrp_typ == 0
&& getlong(&partp->mbrp_start) == 0
&& getlong(&partp->mbrp_size) == 0)
memset(partp, 0, sizeof *partp);
else {
#if 0
checkcyl(getlong(&partp->mbrp_start)
/ dos_cylindersectors);
#endif
dos(getlong(&partp->mbrp_start), &partp->mbrp_scyl,
&partp->mbrp_shd, &partp->mbrp_ssect);
dos(getlong(&partp->mbrp_start)
+ getlong(&partp->mbrp_size) - 1,
&partp->mbrp_ecyl, &partp->mbrp_ehd,
&partp->mbrp_esect);
}
}
print_part(part);
} while (!yesno("Is this entry okay?"));
}
void
print_params()
{
if (sh_flag) {
printf ("DLCYL=%d\nDLHEAD=%d\nDLSEC=%d\nDLSIZE=%d\n",
cylinders, heads, sectors, disksectors);
printf ("BCYL=%d\nBHEAD=%d\nBSEC=%d\n",
dos_cylinders, dos_heads, dos_sectors);
return;
}
/* Not sh_flag */
printf("NetBSD disklabel disk geometry:\n");
printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n",
cylinders, heads, sectors, cylindersectors);
printf("BIOS disk geometry:\n");
printf("cylinders: %d heads: %d sectors/track: %d (%d sectors/cylinder)\n\n",
dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors);
}
void
change_active(which)
int which;
{
struct mbr_partition *partp;
int part;
int active = 4;
partp = &mboot.parts[0];
if (a_flag && which != -1)
active = which;
else {
for (part = 0; part < NMBRPART; part++)
if (partp[part].mbrp_flag & ACTIVE)
active = part;
}
if (!f_flag) {
if (yesno("Do you want to change the active partition?")) {
printf ("Choosing 4 will make no partition active.\n");
do {
decimal("active partition", &active);
} while (!yesno("Are you happy with this choice?"));
} else
return;
} else
if (active != 4)
printf ("Making partition %d active.\n", active);
for (part = 0; part < NMBRPART; part++)
partp[part].mbrp_flag &= ~ACTIVE;
if (active < 4)
partp[active].mbrp_flag |= ACTIVE;
}
void
get_params_to_use()
{
if (b_flag) {
dos_cylinders = b_cyl;
dos_heads = b_head;
dos_sectors = b_sec;
dos_cylindersectors = dos_heads * dos_sectors;
return;
}
print_params();
if (yesno("Do you want to change our idea of what BIOS thinks?")) {
do {
decimal("BIOS's idea of #cylinders", &dos_cylinders);
decimal("BIOS's idea of #heads", &dos_heads);
decimal("BIOS's idea of #sectors", &dos_sectors);
dos_cylindersectors = dos_heads * dos_sectors;
print_params();
} while (!yesno("Are you happy with this choice?"));
}
}
/***********************************************\
* Change real numbers into strange dos numbers *
\***********************************************/
void
dos(sector, cylinderp, headp, sectorp)
int sector;
unsigned char *cylinderp, *headp, *sectorp;
{
int cylinder, head;
int biosmaxsec;
biosmaxsec = dos_cylinders * dos_heads * dos_sectors - 1;
if (sector > biosmaxsec)
sector = biosmaxsec;
cylinder = sector / dos_cylindersectors;
sector -= cylinder * dos_cylindersectors;
head = sector / dos_sectors;
sector -= head * dos_sectors;
*cylinderp = DOSCYL(cylinder);
*headp = head;
*sectorp = DOSSECT(sector + 1, cylinder);
}
#if 0
void
checkcyl(cyl)
int cyl;
{
if (cyl >= MAXCYL)
warnx("partition start beyond BIOS limit");
}
#endif
int fd;
int
open_disk(u_flag)
int u_flag;
{
static char namebuf[MAXPATHLEN + 1];
struct stat st;
fd = opendisk(disk, u_flag ? O_RDWR : O_RDONLY, namebuf,
sizeof(namebuf), 0);
if (fd < 0) {
warn("%s", namebuf);
return (-1);
}
disk = namebuf;
if (fstat(fd, &st) == -1) {
close(fd);
warn("%s", disk);
return (-1);
}
if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
close(fd);
warnx("%s is not a character device or regular file", disk);
return (-1);
}
if (get_params() == -1) {
close(fd);
return (-1);
}
return (0);
}
int
read_disk(sector, buf)
int sector;
void *buf;
{
if (lseek(fd, (off_t)(sector * 512), 0) == -1)
return (-1);
return (read(fd, buf, 512));
}
int
write_disk(sector, buf)
int sector;
void *buf;
{
if (lseek(fd, (off_t)(sector * 512), 0) == -1)
return (-1);
return (write(fd, buf, 512));
}
int
get_params()
{
if (ioctl(fd, DIOCGDEFLABEL, &disklabel) == -1) {
warn("DIOCGDEFLABEL");
if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
warn("DIOCGDINFO");
return (-1);
}
}
dos_cylinders = cylinders = disklabel.d_ncylinders;
dos_heads = heads = disklabel.d_ntracks;
dos_sectors = sectors = disklabel.d_nsectors;
dos_cylindersectors = cylindersectors = heads * sectors;
disksectors = disklabel.d_secperunit;
return (0);
}
int
read_s0()
{
if (read_disk(0, mboot.bootinst) == -1) {
warn("can't read fdisk partition table");
return (-1);
}
if (getshort(&mboot.signature) != MBR_MAGIC) {
warnx("invalid fdisk partition table found");
return (-1);
}
return (0);
}
int
write_s0()
{
int flag, i;
/*
* write enable label sector before write (if necessary),
* disable after writing.
* needed if the disklabel protected area also protects
* sector 0. (e.g. empty disk)
*/
flag = 1;
if (ioctl(fd, DIOCWLABEL, &flag) < 0)
warn("DIOCWLABEL");
if (write_disk(0, mboot.bootinst) == -1) {
warn("can't write fdisk partition table");
return -1;
}
for (i = bootsize; (i -= 0x200) > 0;)
if (write_disk(i / 0x200, bootcode + i) == -1) {
warn("can't write bootcode");
return -1;
}
flag = 0;
if (ioctl(fd, DIOCWLABEL, &flag) < 0)
warn("DIOCWLABEL");
return 0;
}
int
yesno(str)
char *str;
{
int ch, first;
printf("%s [n] ", str);
first = ch = getchar();
while (ch != '\n' && ch != EOF)
ch = getchar();
return (first == 'y' || first == 'Y');
}
void
decimal(str, num)
char *str;
int *num;
{
int acc = 0;
char *cp;
for (;; printf("%s is not a valid decimal number.\n", lbuf)) {
printf("%s: [%d] ", str, *num);
fgets(lbuf, LBUF, stdin);
lbuf[strlen(lbuf)-1] = '\0';
cp = lbuf;
cp += strspn(cp, " \t");
if (*cp == '\0')
return;
if (!isdigit(*cp) && *cp != '-')
continue;
acc = strtol(lbuf, &cp, 10);
cp += strspn(cp, " \t");
if (*cp != '\0')
continue;
*num = acc;
return;
}
}
int
type_match(key, item)
const void *key, *item;
{
const int *typep = key;
const struct part_type *ptr = item;
if (*typep < ptr->type)
return (-1);
if (*typep > ptr->type)
return (1);
return (0);
}
char *
get_type(type)
int type;
{
struct part_type *ptr;
ptr = bsearch(&type, part_types,
sizeof(part_types) / sizeof(struct part_type),
sizeof(struct part_type), type_match);
if (ptr == 0)
return ("unknown");
return (ptr->name);
}