/* $NetBSD: fdisk.c,v 1.61 2003/04/30 10:29:52 dsl 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 #ifndef lint __RCSID("$NetBSD: fdisk.c,v 1.61 2003/04/30 10:29:52 dsl Exp $"); #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__x86_64__) #include #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 const char *disk = _PATH_DEFDISK; struct disklabel disklabel; /* disk parameters */ int cylinders, sectors, heads, cylindersectors, disksectors; struct mbr_sector mboot; #if defined(__i386__) || defined(__x86_64__) #define OPTIONS "0123BSafilus:b:c:" #else #define OPTIONS "0123Safilus: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; const 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"}, {0xa8, "Apple UFS"}, {0xa9, "NetBSD"}, {0xab, "Apple Boot"}, {0xaf, "Apple HFS"}, {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"}, }; #define KNOWN_SYSIDS (sizeof(part_types)/sizeof(part_types[0])) void usage(void); void print_s0(int); void print_part(int); void print_mbr_partition(struct mbr_partition *, off_t, off_t, int); int read_boot(const char *, void *, size_t); void init_sector0(int, int); void intuit_translated_geometry(void); void get_geometry(void); void get_diskname(const char *, char *, size_t); int try_heads(quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t, quad_t); int try_sectors(quad_t, quad_t, quad_t, quad_t, quad_t); void change_part(int, int, int, int); void print_params(void); void change_active(int); void get_params_to_use(void); void dos(int, unsigned char *, unsigned char *, unsigned char *); int open_disk(int); int read_disk(off_t, void *); int write_disk(off_t, void *); int get_params(void); int read_s0(off_t, struct mbr_sector *); int write_s0(void); int yesno(const char *); void decimal(const char *, int *); int type_match(const void *, const void *); const char *get_type(int); int get_mapping(int, int *, int *, int *, unsigned long *); #if defined(__i386__) || defined(__x86_64__) void configure_bootsel(void); #endif static unsigned short getshort(void *); static void putshort(void *p, unsigned short); static unsigned long getlong(void *); static void putlong(void *, unsigned long); int main(int, char *[]); int main(int argc, char *argv[]) { int ch, part, mib[2]; size_t len; char *root_device; int csysid, cstart, csize; /* For the b_flag. */ mib[0] = CTL_KERN; mib[1] = KERN_ROOT_DEVICE; if (sysctl(mib, 2, NULL, &len, NULL, 0) != -1 && (root_device = malloc(len)) != NULL && sysctl(mib, 2, root_device, &len, NULL, 0) != -1) disk = root_device; 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; #if defined(__i386__) || defined(__x86_64__) 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 'l': for (len = 0; len < KNOWN_SYSIDS; len++) printf("%03d %s\n", part_types[len].type, part_types[len].name); return 0; case 'u': u_flag = 1; break; case 's': s_flag = 1; if (sscanf(optarg, "%d/%d/%d", &csysid, &cstart, &csize) != 3) errx(1, "Bad argument to the -s flag."); break; case 'b': b_flag = 1; if (sscanf(optarg, "%d/%d/%d", &b_cyl, &b_head, &b_sec) != 3) errx(1, "Bad argument to the -b flag."); 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) { warnx("-s flag requires a partition selected."); usage(); } if (argc > 0) disk = argv[0]; if (open_disk(B_flag || a_flag || i_flag || u_flag) < 0) exit(1); if (read_s0(0, &mboot)) init_sector0(sectors > 63 ? 63 : sectors, 1); #if defined(__i386__) || defined(__x86_64__) 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); #if defined(__i386__) || defined(__x86_64__) 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) { (void)fprintf(stderr, "Usage: %s [-aiufBS] [-0|-1|-2|-3] " "[-b cylinders/heads/sectors]\n" "\t%s [-s id/start/size] [-c bootcode] [device]\n", getprogname(), getprogname()); exit(1); } void print_s0(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 unsigned short getshort(void *p) { unsigned char *cp = p; return cp[0] | (cp[1] << 8); } static void putshort(void *p, unsigned short l) { unsigned char *cp = p; *cp++ = l; *cp++ = l >> 8; } static unsigned long getlong(void *p) { unsigned char *cp = p; return cp[0] | (cp[1] << 8) | (cp[2] << 16) | (cp[3] << 24); } static void putlong(void *p, unsigned long l) { unsigned char *cp = p; *cp++ = l; *cp++ = l >> 8; *cp++ = l >> 16; *cp++ = l >> 24; } void print_part(int part) { struct mbr_partition *partp; int empty; partp = &mboot.mbr_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; } print_mbr_partition(partp, 0, 0, 0); } void print_mbr_partition(struct mbr_partition *partp, off_t offset, off_t exoffset, int indent) { int empty; off_t start; empty = (partp->mbrp_typ == 0); if (MBR_IS_EXTENDED(partp->mbrp_typ)) start = (off_t)getlong(&partp->mbrp_start) + exoffset; else start = (off_t)getlong(&partp->mbrp_start) + offset; if (empty) { printf("\n"); return; } printf("sysid %d (%s)\n", partp->mbrp_typ, get_type(partp->mbrp_typ)); printf("%*s start %lld, size %ld (%ld MB), flag 0x%x\n", indent, "", (long long)start, getlong(&partp->mbrp_size), getlong(&partp->mbrp_size) * 512 / (1024 * 1024), partp->mbrp_flag); printf("%*s beg: cylinder %4d, head %3d, sector %2d\n", indent, "", MBR_PCYL(partp->mbrp_scyl, partp->mbrp_ssect), partp->mbrp_shd, MBR_PSECT(partp->mbrp_ssect)); printf("%*s end: cylinder %4d, head %3d, sector %2d\n", indent, "", MBR_PCYL(partp->mbrp_ecyl, partp->mbrp_esect), partp->mbrp_ehd, MBR_PSECT(partp->mbrp_esect)); if (MBR_IS_EXTENDED(partp->mbrp_typ)) { struct mbr_sector eboot; int part; printf("%*s Extended partition table:\n", indent, ""); if (read_s0(start, &eboot) == -1) return; indent += 8; if (exoffset == 0) exoffset = start; for (part = 0; part < NMBRPART; part++) { printf("%*s%d: ", indent, "", part); print_mbr_partition(&eboot.mbr_parts[part], start, exoffset, indent); } } } int read_boot(const char *name, void *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 > (off_t)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(int start, int dopart) { int i; #ifdef DEFAULT_BOOTCODE if (!bootsize) bootsize = read_boot(DEFAULT_BOOTDIR "/" DEFAULT_BOOTCODE, bootcode, sizeof bootcode); #endif memcpy(mboot.mbr_bootinst, bootcode, sizeof(mboot.mbr_bootinst)); putshort(&mboot.mbr_signature, MBR_MAGIC); if (dopart) for (i = 0; i < 4; i++) memset(&mboot.mbr_parts[i], 0, sizeof(mboot.mbr_parts[i])); } #if defined(__i386__) || defined(__x86_64__) void get_diskname(const char *fullname, char *diskname, size_t size) { const char *p, *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(void) { 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(void) { struct mbr_bootsel *mbs = (struct mbr_bootsel *)&mboot.mbr_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.mbr_parts[i].mbrp_typ != 0) { if (firstpart == -1) firstpart = i; nused++; } } if (nused == 0) { warnx("No used partitions found. Partition the disk first."); return; } if (mbs->mbrb_magic != MBR_MAGIC) { if (!yesno("Bootselector not yet installed. Install it now?")) { warnx("Bootselector not installed."); return; } bootsize = read_boot(DEFAULT_BOOTDIR "/" DEFAULT_BOOTSELCODE, bootcode, sizeof bootcode); memcpy(mboot.mbr_bootinst, bootcode, sizeof(mboot.mbr_bootinst)); bootsel_modified = 1; mbs->mbrb_flags |= BFL_SELACTIVE; } else { if (mbs->mbrb_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->mbrb_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->mbrb_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->mbrb_nametab[i][0] != 0) printf("%d: %s\n", i, &mbs->mbrb_nametab[i][0]); else printf("%d: \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.mbr_parts[item].mbrp_typ == 0) { printf("The partition entry is unused\n"); item = -1; continue; } printf("Enter descriptions (max. 8 characters): "); rewind(stdin); if (!fgets(desc, PARTNAMESIZE + 1, stdin)) errx(1, "EOF"); fpurge(stdin); p = strchr(desc, '\n'); if (p != NULL) *p = 0; strcpy(&mbs->mbrb_nametab[item][0], desc); entry_changed = bootsel_modified = 1; if (item < NMBRPART -1) item++; else item = -1; } if (entry_changed) printf("Boot selection menu option names are now:\n"); firstpart = -1; for (i = 0; i < NMBRPART; i++) { if (mbs->mbrb_nametab[i][0] != 0) { firstpart = i; if (entry_changed) printf("%d: %s\n", i, &mbs->mbrb_nametab[i][0]); } else { if (entry_changed) printf("%d: \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->mbrb_flags & BFL_SELACTIVE)) { printf("The bootselector is not yet active.\n"); if (yesno("Activate it now?")) mbs->mbrb_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->mbrb_timeo) / 18200); do { decimal("Timeout value", &timo); } while (timo < 0 || timo > 3600); mbs->mbrb_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->mbrb_nametab[i][0] != 0) printf("%d: %s\n", i, &mbs->mbrb_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->mbrb_defkey == SCAN_ENTER) item = 10; else item = mbs->mbrb_defkey - SCAN_F1; if (item < 0 || item > 10 || (item < NMBRPART && mbs->mbrb_nametab[item][0] == 0)) item = 10; do { decimal("Default boot option", &item); } while (item < 0 || item > 10 || (item < NMBRPART && mbs->mbrb_nametab[item][0] == 0)); if (item == 10) mbs->mbrb_defkey = SCAN_ENTER; else mbs->mbrb_defkey = SCAN_F1 + item; done: for (i = 0; i < NMBRPART; i++) { if (mboot.mbr_parts[i].mbrp_typ != 0 && mboot.mbr_parts[i].mbrp_start >= (unsigned)(dos_cylinders * dos_heads * dos_sectors)) { mbs->mbrb_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(void) { int xcylinders = -1, xheads = -1, xsectors = -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) { xheads = num / denom; break; } } if (xheads != -1) break; } if (xheads == -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 * xheads + h1; if (denom != 0 && num % denom == 0) { xsectors = num / denom; break; } } if (xsectors == -1) return; /* Estimate the number of cylinders. */ xcylinders = disklabel.d_secperunit / xheads / xsectors; /* 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 (xsectors * (c1 * xheads + h1) + s1 != a1) return; if (c1 >= xcylinders) xcylinders = c1 + 1; } /* Everything checks out. Reset the geometry to use for further * calculations. */ dos_cylinders = xcylinders; dos_heads = xheads; dos_sectors = xsectors; dos_cylindersectors = xheads * xsectors; } /* 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(int i, int *cylinder, int *head, int *sector, unsigned long *absolute) { struct mbr_partition *part = &mboot.mbr_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(int part, int csysid, int cstart, int csize) { struct mbr_partition *partp; partp = &mboot.mbr_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(void) { 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("Disk: %s\n", disk); 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(int which) { struct mbr_partition *partp; int part; int active = 4; partp = &mboot.mbr_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(void) { 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(int sector, unsigned char *cylinderp, unsigned char *headp, unsigned char *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(int cyl) { if (cyl >= MAXCYL) warnx("partition start beyond BIOS limit"); } #endif int fd = -1; int open_disk(int update) { static char namebuf[MAXPATHLEN + 1]; fd = opendisk(disk, update ? O_RDWR : O_RDONLY, namebuf, sizeof(namebuf), 0); if (fd < 0) { if (errno == ENODEV) warnx("%s is not a character device", namebuf); else warn("%s", namebuf); return (-1); } disk = namebuf; if (get_params() == -1) { close(fd); return (-1); } return (0); } int read_disk(off_t sector, void *buf) { if (fd == -1) errx(1, "read_disk(); fd == -1"); if (lseek(fd, sector * 512, 0) == -1) return (-1); return (read(fd, buf, 512)); } int write_disk(off_t sector, void *buf) { if (fd == -1) errx(1, "write_disk(); fd == -1"); if (lseek(fd, sector * 512, 0) == -1) return (-1); return (write(fd, buf, 512)); } int get_params(void) { 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(off_t offset, struct mbr_sector *boot) { if (read_disk(offset, boot->mbr_bootinst) == -1) { warn("can't read %s partition table", offset ? "extended" : "fdisk"); return (-1); } if (getshort(&boot->mbr_signature) != MBR_MAGIC) { warnx("invalid %s partition table found", offset ? "extended" : "fdisk"); return (-1); } return (0); } int write_s0(void) { 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.mbr_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(const char *str) { int ch, first; printf("%s [n] ", str); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (ch == EOF) errx(1, "EOF"); return (first == 'y' || first == 'Y'); } void decimal(const 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); if (!fgets(lbuf, LBUF, stdin)) errx(1, "EOF"); 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(const void *key, const void *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); } const char * get_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); }