1904 lines
48 KiB
C
1904 lines
48 KiB
C
/* $NetBSD: mbr.c,v 1.5 2015/05/10 10:14:02 martin Exp $ */
|
|
|
|
/*
|
|
* Copyright 1997 Piermont Information Systems Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Written by Philip A. Nelson for Piermont Information Systems Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of Piermont Information Systems Inc. may not be used to endorse
|
|
* or promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Following applies to the geometry guessing code
|
|
*/
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/* mbr.c -- DOS Master Boot Record editing code */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <util.h>
|
|
#include "defs.h"
|
|
#include "mbr.h"
|
|
#include "md.h"
|
|
#include "msg_defs.h"
|
|
#include "menu_defs.h"
|
|
#include "endian.h"
|
|
|
|
#define NO_BOOTMENU (-0x100)
|
|
|
|
#define MAXCYL 1023 /* Possibly 1024 */
|
|
#define MAXHEAD 255 /* Possibly 256 */
|
|
#define MAXSECTOR 63
|
|
|
|
struct part_id {
|
|
int id;
|
|
const char *name;
|
|
} part_ids[] = {
|
|
{0, "unused"},
|
|
{MBR_PTYPE_NETBSD, "NetBSD"},
|
|
{MBR_PTYPE_EXT_LBA, "Extended partition, LBA"},
|
|
{MBR_PTYPE_386BSD, "FreeBSD/386BSD"},
|
|
{MBR_PTYPE_OPENBSD, "OpenBSD"},
|
|
{MBR_PTYPE_LNXEXT2, "Linux native"},
|
|
{MBR_PTYPE_LNXSWAP, "Linux swap"},
|
|
{MBR_PTYPE_FAT12, "DOS FAT12"},
|
|
{MBR_PTYPE_FAT16S, "DOS FAT16, <32M"},
|
|
{MBR_PTYPE_FAT16B, "DOS FAT16, >32M"},
|
|
{MBR_PTYPE_FAT16L, "Windows FAT16, LBA"},
|
|
{MBR_PTYPE_FAT32, "Windows FAT32"},
|
|
{MBR_PTYPE_FAT32L, "Windows FAT32, LBA"},
|
|
{MBR_PTYPE_NTFSVOL, "NTFS volume set"},
|
|
{MBR_PTYPE_NTFS, "NTFS"},
|
|
{MBR_PTYPE_PREP, "PReP Boot"},
|
|
#ifdef MBR_PTYPE_SOLARIS
|
|
{MBR_PTYPE_SOLARIS, "Solaris"},
|
|
#endif
|
|
{-1, "Unknown"},
|
|
};
|
|
|
|
static int get_mapping(struct mbr_partition *, int, int *, int *, int *,
|
|
daddr_t *);
|
|
static void convert_mbr_chs(int, int, int, uint8_t *, uint8_t *,
|
|
uint8_t *, uint32_t);
|
|
static void get_ptn_alignment(struct mbr_partition *);
|
|
|
|
static unsigned int ptn_alignment;
|
|
static unsigned int ptn_0_offset;
|
|
|
|
/*
|
|
* Notes on the extended partition editor.
|
|
*
|
|
* The extended partition structure is actually a singly linked list.
|
|
* Each of the 'mbr' sectors can only contain 2 items, the first describes
|
|
* a user partition (relative to that mbr sector), the second describes
|
|
* the following partition (relative to the start of the extended partition).
|
|
*
|
|
* The 'start' sector for the user partition is always the size of one
|
|
* track - very often 63. The extended partitions themselves should
|
|
* always start on a cylinder boundary using the BIOS geometry - often
|
|
* 16065 sectors per cylinder.
|
|
*
|
|
* The disk is also always described in increasing sector order.
|
|
*
|
|
* During editing we keep the mbr sectors accurate (it might have been
|
|
* easier to use absolute sector numbers though), and keep a copy of the
|
|
* entire sector - to preserve any information any other OS has tried
|
|
* to squirrel away in the (apparently) unused space.
|
|
*
|
|
* For simplicity we add entries for unused space. These should not
|
|
* get written to the disk.
|
|
*
|
|
* Typical disk (with some small numbers):
|
|
*
|
|
* 0 -> a 63 37 dos
|
|
* b 100 1000 extended LBA (type 15)
|
|
*
|
|
* 100 -> a 63 37 user
|
|
* b 100 200 extended partiton (type 5)
|
|
*
|
|
* 200 -> a 63 37 user
|
|
* b 200 300 extended partiton (type 5)
|
|
*
|
|
* 300 -> a 63 37 user
|
|
* b 0 0 0 (end of chain)
|
|
*
|
|
* If there is a gap, the 'b' partition will start beyond the area
|
|
* described by the 'a' partition.
|
|
*
|
|
* While writing this comment, I can't remember what happens is there
|
|
* is space at the start of the extended partition.
|
|
*/
|
|
|
|
#ifndef debug_extended
|
|
#define dump_mbr(mbr, msg)
|
|
#else
|
|
void
|
|
dump_mbr(mbr_info_t *mbr, const char *msg)
|
|
{
|
|
int i;
|
|
|
|
fprintf(stderr, "%s: bsec %d\n", msg, bsec);
|
|
do {
|
|
fprintf(stderr, "%9p: %9d %9p %6.6s:",
|
|
mbr, mbr->sector, mbr->extended,
|
|
mbr->prev_ext, mbr->last_mounted);
|
|
for (i = 0; i < 4; i++)
|
|
fprintf(stderr, " %*d %9d %9d %9d,\n",
|
|
i ? 41 : 3,
|
|
mbr->mbr.mbr_parts[i].mbrp_type,
|
|
mbr->mbr.mbr_parts[i].mbrp_start,
|
|
mbr->mbr.mbr_parts[i].mbrp_size,
|
|
mbr->mbr.mbr_parts[i].mbrp_start +
|
|
mbr->mbr.mbr_parts[i].mbrp_size);
|
|
} while ((mbr = mbr->extended));
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* To be used only on ports which cannot provide any bios geometry
|
|
*/
|
|
int
|
|
set_bios_geom_with_mbr_guess(void)
|
|
{
|
|
int cyl, head;
|
|
daddr_t sec;
|
|
|
|
read_mbr(pm->diskdev, &mbr);
|
|
msg_display(MSG_nobiosgeom, pm->dlcyl, pm->dlhead, pm->dlsec);
|
|
if (guess_biosgeom_from_mbr(&mbr, &cyl, &head, &sec) >= 0)
|
|
msg_display_add(MSG_biosguess, cyl, head, sec);
|
|
set_bios_geom(cyl, head, sec);
|
|
return edit_mbr(&mbr);
|
|
}
|
|
|
|
/*
|
|
* get C/H/S geometry from user via menu interface and
|
|
* store in globals.
|
|
*/
|
|
void
|
|
set_bios_geom(int cyl, int head, int sec)
|
|
{
|
|
char res[80];
|
|
|
|
msg_display_add(MSG_setbiosgeom);
|
|
|
|
do {
|
|
snprintf(res, 80, "%d", sec);
|
|
msg_prompt_add(MSG_sectors, res, res, 80);
|
|
bsec = atoi(res);
|
|
} while (bsec <= 0 || bsec > 63);
|
|
|
|
do {
|
|
snprintf(res, 80, "%d", head);
|
|
msg_prompt_add(MSG_heads, res, res, 80);
|
|
bhead = atoi(res);
|
|
} while (bhead <= 0 || bhead > 256);
|
|
|
|
bcyl = pm->dlsize / bsec / bhead;
|
|
if (pm->dlsize != bcyl * bsec * bhead)
|
|
bcyl++;
|
|
}
|
|
|
|
#ifdef notdef
|
|
void
|
|
disp_cur_geom(void)
|
|
{
|
|
|
|
msg_display_add(MSG_realgeom, pm->dlcyl, pm->dlhead, pm->dlsec);
|
|
msg_display_add(MSG_biosgeom, bcyl, bhead, bsec);
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Then, the partition stuff...
|
|
*/
|
|
|
|
/*
|
|
* If we change the mbr partitioning, the we must remove any references
|
|
* in the netbsd disklabel to the part we changed.
|
|
*/
|
|
static void
|
|
remove_old_partitions(uint start, int64_t size)
|
|
{
|
|
partinfo *p;
|
|
uint end;
|
|
|
|
if (size > 0) {
|
|
end = start + size;
|
|
} else {
|
|
end = start;
|
|
start = end - size;
|
|
}
|
|
|
|
if (end == 0)
|
|
return;
|
|
|
|
for (p = pm->oldlabel; p < pm->oldlabel + nelem(pm->oldlabel); p++) {
|
|
if (p->pi_offset >= end || p->pi_offset + p->pi_size <= start)
|
|
continue;
|
|
memset(p, 0, sizeof *p);
|
|
}
|
|
}
|
|
|
|
static int
|
|
find_mbr_space(struct mbr_sector *mbrs, uint *start, uint *size, uint from, int ignore)
|
|
{
|
|
uint sz;
|
|
int i;
|
|
uint s, e;
|
|
|
|
check_again:
|
|
sz = pm->dlsize - from;
|
|
for (i = 0; i < MBR_PART_COUNT; i++) {
|
|
if (i == ignore)
|
|
continue;
|
|
s = mbrs->mbr_parts[i].mbrp_start;
|
|
e = s + mbrs->mbr_parts[i].mbrp_size;
|
|
if (s <= from && e > from) {
|
|
from = e;
|
|
goto check_again;
|
|
}
|
|
if (s > from && s - from < sz)
|
|
sz = s - from;
|
|
}
|
|
if (sz == 0)
|
|
return -1;
|
|
if (start != NULL)
|
|
*start = from;
|
|
if (size != NULL)
|
|
*size = sz;
|
|
return 0;
|
|
}
|
|
|
|
static struct mbr_partition *
|
|
get_mbrp(mbr_info_t **mbrip, int opt)
|
|
{
|
|
mbr_info_t *mbri = *mbrip;
|
|
|
|
if (opt >= MBR_PART_COUNT)
|
|
for (opt -= MBR_PART_COUNT - 1; opt; opt--)
|
|
mbri = mbri->extended;
|
|
|
|
*mbrip = mbri;
|
|
return &mbri->mbr.mbr_parts[opt];
|
|
}
|
|
|
|
static int
|
|
err_msg_win(const char *errmsg)
|
|
{
|
|
const char *cont;
|
|
int l, l1;
|
|
|
|
errmsg = msg_string(errmsg);
|
|
cont = msg_string(MSG_Hit_enter_to_continue);
|
|
|
|
l = strlen(errmsg);
|
|
l1 = strlen(cont);
|
|
if (l < l1)
|
|
l = l1;
|
|
|
|
msg_prompt_win("%s.\n%s", -1, 18, l + 5, 4,
|
|
NULL, NULL, 1, errmsg, cont);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_mbr_type(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
mbr_info_t *ext;
|
|
struct mbr_partition *mbrp;
|
|
char *cp;
|
|
int opt = mbri->opt;
|
|
int type;
|
|
u_int start, sz;
|
|
int i;
|
|
char numbuf[5];
|
|
|
|
dump_mbr(ombri, "set type");
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
|
|
type = m->cursel;
|
|
if (type == 0)
|
|
return 1;
|
|
type = part_ids[type - 1].id;
|
|
while (type == -1) {
|
|
snprintf(numbuf, sizeof numbuf, "%u", mbrp->mbrp_type);
|
|
msg_prompt_win(MSG_get_ptn_id, -1, 18, 0, 0,
|
|
numbuf, numbuf, sizeof numbuf);
|
|
type = strtoul(numbuf, &cp, 0);
|
|
if (*cp != 0)
|
|
type = -1;
|
|
}
|
|
|
|
if (type == mbrp->mbrp_type)
|
|
/* type not changed... */
|
|
return 1;
|
|
|
|
mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
|
|
|
|
if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
|
|
/* deleting extended partition.... */
|
|
if (mbri->sector || mbri->extended->extended)
|
|
/* We should have stopped this happening... */
|
|
return err_msg_win("can't delete extended");
|
|
free(mbri->extended);
|
|
mbri->extended = NULL;
|
|
}
|
|
|
|
if (type == 0) {
|
|
/* Deleting partition */
|
|
mbrp->mbrp_type = 0;
|
|
/* Remove references to this space from the NetBSD label */
|
|
remove_old_partitions(mbri->sector + mbrp->mbrp_start,
|
|
mbrp->mbrp_size);
|
|
#ifdef BOOTSEL
|
|
if (ombri->bootsec == mbri->sector + mbrp->mbrp_start)
|
|
ombri->bootsec = 0;
|
|
|
|
memset(mbri->mbrb.mbrbs_nametab[opt], 0,
|
|
sizeof mbri->mbrb.mbrbs_nametab[opt]);
|
|
#endif
|
|
if (mbri->sector == 0) {
|
|
/* A main partition */
|
|
memset(mbrp, 0, sizeof *mbrp);
|
|
return 1;
|
|
}
|
|
|
|
/* Merge with previous and next free areas */
|
|
ext = mbri->prev_ext;
|
|
if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
|
|
/* previous was free - back up one entry */
|
|
mbri = ext;
|
|
ombri->opt--;
|
|
}
|
|
while ((ext = mbri->extended)) {
|
|
if (ext->mbr.mbr_parts[0].mbrp_type != 0)
|
|
break;
|
|
sz = ext->mbr.mbr_parts[0].mbrp_start +
|
|
ext->mbr.mbr_parts[0].mbrp_size;
|
|
/* Increase size of our (empty) partition */
|
|
mbri->mbr.mbr_parts[0].mbrp_size += sz;
|
|
/* Make us describe the next partition */
|
|
mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
|
|
/* fix list of extended partitions */
|
|
mbri->extended = ext->extended;
|
|
if (ext->extended != NULL)
|
|
ext->extended->prev_ext = mbri;
|
|
free(ext);
|
|
/* Make previous size cover all our ptn */
|
|
ext = mbri->prev_ext;
|
|
if (ext != NULL)
|
|
ext->mbr.mbr_parts[1].mbrp_size += sz;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (mbrp->mbrp_start == 0) {
|
|
/* Find first chunk of space... */
|
|
/* Must be in the main partition */
|
|
if (mbri->sector != 0)
|
|
/* shouldn't be possible to have null start... */
|
|
return err_msg_win("main-extended mixup");
|
|
if (find_mbr_space(&mbri->mbr, &start, &sz, bsec, -1) != 0)
|
|
/* no space */
|
|
return err_msg_win(MSG_No_free_space);
|
|
mbrp->mbrp_start = start;
|
|
mbrp->mbrp_size = sz;
|
|
/* If there isn't an active partition mark this one active */
|
|
if (!MBR_IS_EXTENDED(type)) {
|
|
for (i = 0; i < MBR_PART_COUNT; i++)
|
|
if (mbri->mbr.mbr_parts[i].mbrp_flag != 0)
|
|
break;
|
|
if (i == MBR_PART_COUNT)
|
|
mbrp->mbrp_flag = MBR_PFLAG_ACTIVE;
|
|
}
|
|
}
|
|
|
|
if (MBR_IS_EXTENDED(type)) {
|
|
if (mbri->sector != 0)
|
|
/* Can't set extended partition in an extended one */
|
|
return err_msg_win(MSG_Only_one_extended_ptn);
|
|
if (mbri->extended)
|
|
/* Can't have two extended partitions */
|
|
return err_msg_win(MSG_Only_one_extended_ptn);
|
|
/* Create new extended partition */
|
|
ext = calloc(1, sizeof *mbri->extended);
|
|
if (!ext)
|
|
return 0;
|
|
mbri->extended = ext;
|
|
ext->sector = mbrp->mbrp_start;
|
|
ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
|
|
ext->mbr.mbr_parts[0].mbrp_size =
|
|
mbrp->mbrp_size - ptn_0_offset;
|
|
}
|
|
mbrp->mbrp_type = type;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
set_type_label(menudesc *m, int opt, void *arg)
|
|
{
|
|
|
|
if (opt == 0) {
|
|
wprintw(m->mw, "%s", msg_string(MSG_Dont_change));
|
|
return;
|
|
}
|
|
if (opt == 1) {
|
|
wprintw(m->mw, "%s", msg_string(MSG_Delete_partition));
|
|
return;
|
|
}
|
|
if (part_ids[opt - 1].id == -1) {
|
|
wprintw(m->mw, "%s", msg_string(MSG_Other_kind));
|
|
return;
|
|
}
|
|
wprintw(m->mw, "%s", part_ids[opt - 1].name);
|
|
}
|
|
|
|
static int
|
|
edit_mbr_type(menudesc *m, void *arg)
|
|
{
|
|
static menu_ent type_opts[1 + nelem(part_ids)];
|
|
static int type_menu = -1;
|
|
unsigned int i;
|
|
|
|
if (type_menu == -1) {
|
|
for (i = 0; i < nelem(type_opts); i++) {
|
|
type_opts[i].opt_menu = OPT_NOMENU;
|
|
type_opts[i].opt_action = set_mbr_type;
|
|
}
|
|
type_menu = new_menu(NULL, type_opts, nelem(type_opts),
|
|
13, 12, 0, 30,
|
|
MC_SUBMENU | MC_SCROLL | MC_NOEXITOPT | MC_NOCLEAR,
|
|
NULL, set_type_label, NULL,
|
|
NULL, NULL);
|
|
}
|
|
|
|
if (type_menu != -1)
|
|
process_menu(type_menu, arg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
edit_mbr_start(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ext;
|
|
struct mbr_partition *mbrp;
|
|
int opt = mbri->opt;
|
|
uint start, sz;
|
|
uint new_r, new, limit, dflt_r;
|
|
int64_t delta;
|
|
const char *errmsg;
|
|
char *cp;
|
|
struct {
|
|
uint start;
|
|
uint start_r;
|
|
uint limit;
|
|
} freespace[MBR_PART_COUNT];
|
|
unsigned int spaces;
|
|
unsigned int i;
|
|
char prompt[MBR_PART_COUNT * 60];
|
|
unsigned int len;
|
|
char numbuf[12];
|
|
|
|
if (opt >= MBR_PART_COUNT)
|
|
/* should not be able to get here... */
|
|
return 1;
|
|
|
|
mbrp = mbri->mbr.mbr_parts + opt;
|
|
/* locate the start of all free areas */
|
|
spaces = 0;
|
|
for (start = bsec, i = 0; i < MBR_PART_COUNT; start += sz, i++) {
|
|
if (find_mbr_space(&mbri->mbr, &start, &sz, start, opt))
|
|
break;
|
|
if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
|
|
/* Only want the area that contains this partition */
|
|
if (mbrp->mbrp_start < start ||
|
|
mbrp->mbrp_start >= start + sz)
|
|
continue;
|
|
i = MBR_PART_COUNT - 1;
|
|
}
|
|
freespace[spaces].start = start;
|
|
freespace[spaces].start_r = start / sizemult;
|
|
freespace[spaces].limit = start + sz;
|
|
if (++spaces >= sizeof freespace)
|
|
/* shouldn't happen... */
|
|
break;
|
|
}
|
|
|
|
/* Add description of start/size to user prompt */
|
|
len = 0;
|
|
for (i = 0; i < spaces; i++) {
|
|
len += snprintf(prompt + len, sizeof prompt - len,
|
|
msg_string(MSG_ptn_starts),
|
|
freespace[i].start_r,
|
|
freespace[i].limit / sizemult, multname,
|
|
freespace[i].limit / sizemult - freespace[i].start_r,
|
|
multname);
|
|
if (len >= sizeof prompt)
|
|
break;
|
|
}
|
|
|
|
/* And loop until the user gives a sensible answer */
|
|
dflt_r = mbrp->mbrp_start / sizemult;
|
|
errmsg = "";
|
|
for (;;) {
|
|
snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
|
|
msg_prompt_win(MSG_get_ptn_start, -1, 18, 60, spaces + 3,
|
|
numbuf, numbuf, sizeof numbuf,
|
|
prompt, msg_string(errmsg), multname);
|
|
new_r = strtoul(numbuf, &cp, 0);
|
|
if (*cp != 0) {
|
|
errmsg = MSG_Invalid_numeric;
|
|
continue;
|
|
}
|
|
if (new_r == dflt_r)
|
|
/* Unchanged */
|
|
return 0;
|
|
/*
|
|
* Check that the start address from the user is inside one
|
|
* of the free areas.
|
|
*/
|
|
new = new_r * sizemult;
|
|
for (i = 0; i < spaces; i++) {
|
|
if (new_r == freespace[i].start_r) {
|
|
new = freespace[i].start;
|
|
break;
|
|
}
|
|
if (new >= freespace[i].start &&
|
|
new < freespace[i].limit)
|
|
break;
|
|
}
|
|
if (i >= spaces) {
|
|
errmsg = MSG_Space_allocated;
|
|
continue;
|
|
}
|
|
limit = freespace[i].limit;
|
|
/*
|
|
* We can only increase the start of an extended partition
|
|
* if the corresponding space inside the partition isn't used.
|
|
*/
|
|
if (new > mbrp->mbrp_start &&
|
|
MBR_IS_EXTENDED(mbrp->mbrp_type) &&
|
|
(mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
|
|
mbri->extended->mbr.mbr_parts[0].mbrp_size <
|
|
new - mbrp->mbrp_start)) {
|
|
errmsg = MSG_Space_allocated;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (new < mbrp->mbrp_start + mbrp->mbrp_size &&
|
|
limit > mbrp->mbrp_start)
|
|
/* Keep end of partition in the same place */
|
|
limit = mbrp->mbrp_start + mbrp->mbrp_size;
|
|
|
|
delta = new - mbrp->mbrp_start;
|
|
if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
|
|
ext = mbri->extended;
|
|
if (ext->mbr.mbr_parts[0].mbrp_type != 0) {
|
|
/* allocate an extended ptn for the free item */
|
|
ext = calloc(1, sizeof *ext);
|
|
if (!ext)
|
|
return 0;
|
|
ext->sector = mbrp->mbrp_start;
|
|
ext->extended = mbri->extended;
|
|
mbri->extended->prev_ext = ext;
|
|
mbri->extended = ext;
|
|
ext->mbr.mbr_parts[0].mbrp_start = bsec;
|
|
ext->mbr.mbr_parts[0].mbrp_size = -bsec;
|
|
ext->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
|
|
ext->mbr.mbr_parts[1].mbrp_start = 0;
|
|
ext->mbr.mbr_parts[1].mbrp_size =
|
|
ext->extended->mbr.mbr_parts[0].mbrp_start +
|
|
ext->extended->mbr.mbr_parts[0].mbrp_size;
|
|
}
|
|
/* adjust size of first free item */
|
|
ext->mbr.mbr_parts[0].mbrp_size -= delta;
|
|
ext->sector += delta;
|
|
/* and the link of all extended partitions */
|
|
do
|
|
if (ext->extended)
|
|
ext->mbr.mbr_parts[1].mbrp_start -= delta;
|
|
while ((ext = ext->extended));
|
|
}
|
|
remove_old_partitions(mbri->sector + mbrp->mbrp_start, delta);
|
|
|
|
/* finally set partition base and size */
|
|
mbrp->mbrp_start = new;
|
|
mbrp->mbrp_size = limit - new;
|
|
mbri->last_mounted[opt] = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
edit_mbr_size(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
mbr_info_t *ext;
|
|
struct mbr_partition *mbrp;
|
|
int opt = mbri->opt;
|
|
uint start, max, max_r, dflt, dflt_r, new;
|
|
uint freespace;
|
|
int delta;
|
|
char numbuf[12];
|
|
char *cp;
|
|
const char *errmsg;
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
dflt = mbrp->mbrp_size;
|
|
if (opt < MBR_PART_COUNT) {
|
|
max = 0;
|
|
find_mbr_space(&mbri->mbr, &start, &max, mbrp->mbrp_start, opt);
|
|
if (start != mbrp->mbrp_start)
|
|
return 0;
|
|
if (dflt == 0)
|
|
dflt = max;
|
|
} else {
|
|
ext = mbri->extended;
|
|
max = dflt;
|
|
/*
|
|
* If the next extended partition describes a free area,
|
|
* then merge it onto this area.
|
|
*/
|
|
if (ext != NULL && ext->mbr.mbr_parts[0].mbrp_type == 0) {
|
|
if (ext->extended)
|
|
ext->extended->prev_ext = mbri;
|
|
mbri->extended = ext->extended;
|
|
if (mbri->prev_ext)
|
|
mbri->prev_ext->mbr.mbr_parts[1].mbrp_size
|
|
+= mbri->mbr.mbr_parts[1].mbrp_size;
|
|
mbrp->mbrp_size += mbri->mbr.mbr_parts[1].mbrp_size;
|
|
max += mbri->mbr.mbr_parts[1].mbrp_size;
|
|
mbri->mbr.mbr_parts[1] = ext->mbr.mbr_parts[1];
|
|
free(ext);
|
|
}
|
|
}
|
|
|
|
start = mbri->sector + mbrp->mbrp_start;
|
|
/* We need to keep both the unrounded and rounded (_r) max and dflt */
|
|
dflt_r = (start + dflt) / sizemult - start / sizemult;
|
|
if (max == dflt)
|
|
max_r = dflt_r;
|
|
else
|
|
max_r = max / sizemult;
|
|
for (errmsg = "";;) {
|
|
snprintf(numbuf, sizeof numbuf, "%d", dflt_r);
|
|
msg_prompt_win(MSG_get_ptn_size, -1, 18, 0, 0,
|
|
numbuf, numbuf, sizeof numbuf,
|
|
msg_string(errmsg), max_r, multname);
|
|
new = strtoul(numbuf, &cp, 0);
|
|
if (*cp != 0) {
|
|
errmsg = MSG_Invalid_numeric;
|
|
continue;
|
|
}
|
|
if (new > max_r) {
|
|
errmsg = MSG_Too_large;
|
|
continue;
|
|
}
|
|
if (new == 0)
|
|
/* Treat zero as a request for the maximum */
|
|
new = max_r;
|
|
if (new == dflt_r)
|
|
/* If unchanged, don't re-round size */
|
|
new = dflt;
|
|
else {
|
|
/* Round end to the partition alignment */
|
|
if (sizemult != 1) {
|
|
new *= sizemult;
|
|
new += rounddown(start, ptn_alignment);
|
|
new = roundup(new, ptn_alignment);
|
|
new -= start;
|
|
while (new <= 0)
|
|
new += ptn_alignment;
|
|
}
|
|
}
|
|
if (new > max)
|
|
/* We rounded the value to above the max */
|
|
new = max;
|
|
|
|
if (new == dflt || opt >= MBR_PART_COUNT
|
|
|| !MBR_IS_EXTENDED(mbrp->mbrp_type))
|
|
break;
|
|
/*
|
|
* We've been asked to change the size of the main extended
|
|
* partition. If this reduces the size, then that space
|
|
* must be unallocated. If it increases the size then
|
|
* we must add a description ofthe new free space.
|
|
*/
|
|
/* Find last extended partition */
|
|
for (ext = mbri->extended; ext->extended; ext = ext->extended)
|
|
continue;
|
|
if ((new < dflt && (ext->mbr.mbr_parts[0].mbrp_type != 0
|
|
|| (mbrp->mbrp_start + new < ext->sector + bsec
|
|
&& mbrp->mbrp_start + new != ext->sector)))
|
|
|| (new > dflt && ext->mbr.mbr_parts[0].mbrp_type != 0
|
|
&& new < dflt + bsec)) {
|
|
errmsg = MSG_Space_allocated;
|
|
continue;
|
|
}
|
|
delta = new - dflt;
|
|
if (ext->mbr.mbr_parts[0].mbrp_type == 0) {
|
|
/* adjust size of last item (free space) */
|
|
if (mbrp->mbrp_start + new == ext->sector) {
|
|
/* kill last extended ptn */
|
|
ext = ext->prev_ext;
|
|
free(ext->extended);
|
|
ext->extended = NULL;
|
|
memset(&ext->mbr.mbr_parts[1], 0,
|
|
sizeof ext->mbr.mbr_parts[1]);
|
|
break;
|
|
}
|
|
ext->mbr.mbr_parts[0].mbrp_size += delta;
|
|
ext = ext->prev_ext;
|
|
if (ext != NULL)
|
|
ext->mbr.mbr_parts[1].mbrp_size += delta;
|
|
break;
|
|
}
|
|
/* Joy of joys, we must allocate another extended ptn */
|
|
mbri = ext;
|
|
ext = calloc(1, sizeof *ext);
|
|
if (!ext)
|
|
return 0;
|
|
mbri->extended = ext;
|
|
ext->prev_ext = mbri;
|
|
ext->mbr.mbr_parts[0].mbrp_start = bsec;
|
|
ext->mbr.mbr_parts[0].mbrp_size = delta - bsec;
|
|
ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
|
|
+ mbri->mbr.mbr_parts[0].mbrp_size;
|
|
mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
|
|
mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
|
|
mbri->mbr.mbr_parts[1].mbrp_size = delta;
|
|
break;
|
|
}
|
|
|
|
if (opt >= MBR_PART_COUNT && max - new <= (uint32_t)bsec)
|
|
/* Round up if not enough space for a header for free area */
|
|
new = max;
|
|
|
|
if (new != mbrp->mbrp_size) {
|
|
/* Kill information about old partition from label */
|
|
mbri->last_mounted[opt < MBR_PART_COUNT ? opt : 0] = NULL;
|
|
remove_old_partitions(mbri->sector + mbrp->mbrp_start +
|
|
mbrp->mbrp_size, (int64_t)new - mbrp->mbrp_size);
|
|
}
|
|
|
|
mbrp->mbrp_size = new;
|
|
if (opt < MBR_PART_COUNT || new == max)
|
|
return 0;
|
|
|
|
/* Add extended partition for the free space */
|
|
ext = calloc(1, sizeof *ext);
|
|
if (!ext) {
|
|
mbrp->mbrp_size = max;
|
|
return 0;
|
|
}
|
|
/* Link into our extended chain */
|
|
ext->extended = mbri->extended;
|
|
mbri->extended = ext;
|
|
ext->prev_ext = mbri;
|
|
if (ext->extended != NULL)
|
|
ext->extended->prev_ext = ext;
|
|
ext->mbr.mbr_parts[1] = mbri->mbr.mbr_parts[1];
|
|
freespace = max - new;
|
|
if (mbri->prev_ext != NULL)
|
|
mbri->prev_ext->mbr.mbr_parts[1].mbrp_size -= freespace;
|
|
|
|
ext->mbr.mbr_parts[0].mbrp_start = bsec;
|
|
ext->mbr.mbr_parts[0].mbrp_size = freespace - bsec;
|
|
|
|
ext->sector = mbri->sector + mbri->mbr.mbr_parts[0].mbrp_start
|
|
+ mbri->mbr.mbr_parts[0].mbrp_size;
|
|
mbri->mbr.mbr_parts[1].mbrp_start = ext->sector - ombri->extended->sector;
|
|
mbri->mbr.mbr_parts[1].mbrp_type = MBR_PTYPE_EXT;
|
|
mbri->mbr.mbr_parts[1].mbrp_size = freespace;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
edit_mbr_active(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
int i;
|
|
uint8_t *fl;
|
|
|
|
if (mbri->opt >= MBR_PART_COUNT)
|
|
/* sanity */
|
|
return 0;
|
|
|
|
/* Invert active flag */
|
|
fl = &mbri->mbr.mbr_parts[mbri->opt].mbrp_flag;
|
|
if (*fl == MBR_PFLAG_ACTIVE) {
|
|
*fl = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* Ensure there is at most one active partition */
|
|
for (i = 0; i < MBR_PART_COUNT; i++)
|
|
mbri->mbr.mbr_parts[i].mbrp_flag = 0;
|
|
*fl = MBR_PFLAG_ACTIVE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
edit_mbr_install(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
struct mbr_partition *mbrp;
|
|
int opt = mbri->opt;
|
|
uint start;
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
|
|
start = mbri->sector + mbrp->mbrp_start;
|
|
/* We just remember the start address of the partition... */
|
|
if (start == ombri->install)
|
|
ombri->install = 0;
|
|
else
|
|
ombri->install = start;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef BOOTSEL
|
|
static int
|
|
edit_mbr_bootmenu(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
struct mbr_partition *mbrp;
|
|
int opt = mbri->opt;
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
|
|
msg_prompt_win(/* XXX translate? */ "bootmenu", -1, 18, 0, 0,
|
|
mbri->mbrb.mbrbs_nametab[opt],
|
|
mbri->mbrb.mbrbs_nametab[opt],
|
|
sizeof mbri->mbrb.mbrbs_nametab[opt]);
|
|
if (mbri->mbrb.mbrbs_nametab[opt][0] == ' ')
|
|
mbri->mbrb.mbrbs_nametab[opt][0] = 0;
|
|
if (mbri->mbrb.mbrbs_nametab[opt][0] == 0
|
|
&& ombri->bootsec == mbri->sector + mbrp->mbrp_start)
|
|
ombri->bootsec = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
edit_mbr_bootdefault(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
struct mbr_partition *mbrp;
|
|
|
|
mbrp = get_mbrp(&mbri, mbri->opt);
|
|
|
|
ombri->bootsec = mbri->sector + mbrp->mbrp_start;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void set_ptn_label(menudesc *m, int line, void *arg);
|
|
static void set_ptn_header(menudesc *m, void *arg);
|
|
|
|
static int
|
|
edit_mbr_entry(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
static int ptn_menu = -1;
|
|
|
|
static menu_ent ptn_opts[] = {
|
|
#define PTN_OPT_TYPE 0
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_type},
|
|
#define PTN_OPT_START 1
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_start},
|
|
#define PTN_OPT_SIZE 2
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_size},
|
|
#define PTN_OPT_END 3
|
|
{NULL, OPT_NOMENU, OPT_IGNORE, NULL}, /* display end */
|
|
#define PTN_OPT_ACTIVE 4
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_active},
|
|
#define PTN_OPT_INSTALL 5
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_install},
|
|
#ifdef BOOTSEL
|
|
#define PTN_OPT_BOOTMENU 6
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_bootmenu},
|
|
#define PTN_OPT_BOOTDEFAULT 7
|
|
{NULL, OPT_NOMENU, 0, edit_mbr_bootdefault},
|
|
#endif
|
|
{MSG_askunits, MENU_sizechoice, OPT_SUB, NULL},
|
|
};
|
|
|
|
if (ptn_menu == -1)
|
|
ptn_menu = new_menu(NULL, ptn_opts, nelem(ptn_opts),
|
|
15, 6, 0, 54,
|
|
MC_SUBMENU | MC_SCROLL | MC_NOCLEAR,
|
|
set_ptn_header, set_ptn_label, NULL,
|
|
NULL, MSG_Partition_OK);
|
|
if (ptn_menu == -1)
|
|
return 1;
|
|
|
|
mbri->opt = m->cursel;
|
|
process_menu(ptn_menu, mbri);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
set_ptn_label(menudesc *m, int line, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
struct mbr_partition *mbrp;
|
|
int opt;
|
|
static const char *yes, *no;
|
|
|
|
if (yes == NULL) {
|
|
yes = msg_string(MSG_Yes);
|
|
no = msg_string(MSG_No);
|
|
}
|
|
|
|
opt = mbri->opt;
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
|
|
switch (line) {
|
|
case PTN_OPT_TYPE:
|
|
wprintw(m->mw, msg_string(MSG_ptn_type),
|
|
get_partname(mbrp->mbrp_type));
|
|
break;
|
|
case PTN_OPT_START:
|
|
wprintw(m->mw, msg_string(MSG_ptn_start),
|
|
(mbri->sector + mbrp->mbrp_start) / sizemult, multname);
|
|
break;
|
|
case PTN_OPT_SIZE:
|
|
wprintw(m->mw, msg_string(MSG_ptn_size),
|
|
(mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
|
|
sizemult -
|
|
(mbri->sector + mbrp->mbrp_start) / sizemult, multname);
|
|
break;
|
|
case PTN_OPT_END:
|
|
wprintw(m->mw, msg_string(MSG_ptn_end),
|
|
(mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size) /
|
|
sizemult, multname);
|
|
break;
|
|
case PTN_OPT_ACTIVE:
|
|
wprintw(m->mw, msg_string(MSG_ptn_active),
|
|
mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? yes : no);
|
|
break;
|
|
case PTN_OPT_INSTALL:
|
|
wprintw(m->mw, msg_string(MSG_ptn_install),
|
|
mbri->sector + mbrp->mbrp_start == ombri->install &&
|
|
mbrp->mbrp_type == MBR_PTYPE_NETBSD ? yes : no);
|
|
break;
|
|
#ifdef BOOTSEL
|
|
case PTN_OPT_BOOTMENU:
|
|
wprintw(m->mw, msg_string(MSG_bootmenu),
|
|
mbri->mbrb.mbrbs_nametab[opt]);
|
|
break;
|
|
case PTN_OPT_BOOTDEFAULT:
|
|
wprintw(m->mw, msg_string(MSG_boot_dflt),
|
|
ombri->bootsec == mbri->sector + mbrp->mbrp_start ? yes
|
|
: no);
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
set_ptn_header(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
struct mbr_partition *mbrp;
|
|
int opt = mbri->opt;
|
|
int typ;
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
typ = mbrp->mbrp_type;
|
|
|
|
#define DISABLE(opt,cond) \
|
|
if (cond) \
|
|
m->opts[opt].opt_flags |= OPT_IGNORE; \
|
|
else \
|
|
m->opts[opt].opt_flags &= ~OPT_IGNORE;
|
|
|
|
/* Can't change type of the extended partition unless it is empty */
|
|
DISABLE(PTN_OPT_TYPE, MBR_IS_EXTENDED(typ) &&
|
|
(mbri->extended->mbr.mbr_parts[0].mbrp_type != 0 ||
|
|
mbri->extended->extended != NULL));
|
|
|
|
/* It is unnecessary to be able to change the base of an extended ptn */
|
|
DISABLE(PTN_OPT_START, mbri->sector || typ == 0);
|
|
|
|
/* or the size of a free area */
|
|
DISABLE(PTN_OPT_SIZE, typ == 0);
|
|
|
|
/* Only 'normal' partitions can be 'Active' */
|
|
DISABLE(PTN_OPT_ACTIVE, mbri->sector != 0 || MBR_IS_EXTENDED(typ) || typ == 0);
|
|
|
|
/* Can only install into NetBSD partition */
|
|
DISABLE(PTN_OPT_INSTALL, typ != MBR_PTYPE_NETBSD);
|
|
|
|
#ifdef BOOTSEL
|
|
/* The extended partition isn't bootable */
|
|
DISABLE(PTN_OPT_BOOTMENU, MBR_IS_EXTENDED(typ) || typ == 0);
|
|
|
|
if (typ == 0)
|
|
mbri->mbrb.mbrbs_nametab[opt][0] = 0;
|
|
|
|
/* Only partitions with bootmenu names can be made the default */
|
|
DISABLE(PTN_OPT_BOOTDEFAULT, mbri->mbrb.mbrbs_nametab[opt][0] == 0);
|
|
#endif
|
|
#undef DISABLE
|
|
}
|
|
|
|
static void
|
|
set_mbr_label(menudesc *m, int opt, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
mbr_info_t *ombri = arg;
|
|
struct mbr_partition *mbrp;
|
|
uint rstart, rend;
|
|
const char *name, *cp, *mounted;
|
|
int len;
|
|
|
|
mbrp = get_mbrp(&mbri, opt);
|
|
if (opt >= MBR_PART_COUNT)
|
|
opt = 0;
|
|
|
|
if (mbrp->mbrp_type == 0 && mbri->sector == 0) {
|
|
len = snprintf(0, 0, msg_string(MSG_part_row_used), 0, 0, 0);
|
|
wprintw(m->mw, "%*s", len, "");
|
|
} else {
|
|
rstart = mbri->sector + mbrp->mbrp_start;
|
|
rend = (rstart + mbrp->mbrp_size) / sizemult;
|
|
rstart = rstart / sizemult;
|
|
wprintw(m->mw, msg_string(MSG_part_row_used),
|
|
rstart, rend - rstart,
|
|
mbrp->mbrp_flag == MBR_PFLAG_ACTIVE ? 'a' : ' ',
|
|
#ifdef BOOTSEL
|
|
ombri->bootsec == mbri->sector + mbrp->mbrp_start ? 'd' :
|
|
#endif
|
|
' ',
|
|
mbri->sector + mbrp->mbrp_start == ombri->install &&
|
|
mbrp->mbrp_type == MBR_PTYPE_NETBSD ? 'I' : ' ');
|
|
}
|
|
name = get_partname(mbrp->mbrp_type);
|
|
mounted = mbri->last_mounted[opt];
|
|
len = strlen(name);
|
|
cp = strchr(name, ',');
|
|
if (cp != NULL)
|
|
len = cp - name;
|
|
if (mounted && *mounted != 0) {
|
|
wprintw(m->mw, " %*s (%s)", len, name, mounted);
|
|
} else
|
|
wprintw(m->mw, " %.*s", len, name);
|
|
#ifdef BOOTSEL
|
|
if (mbri->mbrb.mbrbs_nametab[opt][0] != 0) {
|
|
int x, y;
|
|
|
|
getyx(m->mw, y, x);
|
|
if (x > 52) {
|
|
x = 52;
|
|
wmove(m->mw, y, x);
|
|
}
|
|
wprintw(m->mw, "%*s %s", 53 - x, "",
|
|
mbri->mbrb.mbrbs_nametab[opt]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
set_mbr_header(menudesc *m, void *arg)
|
|
{
|
|
mbr_info_t *mbri = arg;
|
|
static menu_ent *opts;
|
|
static int num_opts;
|
|
mbr_info_t *ext;
|
|
menu_ent *op;
|
|
int i;
|
|
int left;
|
|
|
|
msg_display(MSG_editparttable);
|
|
|
|
msg_table_add(MSG_part_header, (unsigned long)(pm->dlsize/sizemult),
|
|
multname, multname, multname, multname);
|
|
|
|
if (num_opts == 0) {
|
|
num_opts = 6;
|
|
opts = malloc(6 * sizeof *opts);
|
|
if (opts == NULL) {
|
|
m->numopts = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* First four items are the main partitions */
|
|
for (op = opts, i = 0; i < MBR_PART_COUNT; op++, i++) {
|
|
op->opt_name = NULL;
|
|
op->opt_menu = OPT_NOMENU;
|
|
op->opt_flags = OPT_SUB;
|
|
op->opt_action = edit_mbr_entry;
|
|
}
|
|
left = num_opts - MBR_PART_COUNT;
|
|
|
|
/* Followed by the extended partitions */
|
|
for (ext = mbri->extended; ext; left--, op++, ext = ext->extended) {
|
|
if (left <= 1) {
|
|
menu_ent *new = realloc(opts,
|
|
(num_opts + 4) * sizeof *opts);
|
|
if (new == NULL)
|
|
break;
|
|
num_opts += 4;
|
|
left += 4;
|
|
op = new + (op - opts);
|
|
opts = new;
|
|
}
|
|
op->opt_name = NULL;
|
|
op->opt_menu = OPT_NOMENU;
|
|
op->opt_flags = 0;
|
|
op->opt_action = edit_mbr_entry;
|
|
}
|
|
|
|
/* and unit changer */
|
|
op->opt_name = MSG_askunits;
|
|
op->opt_menu = MENU_sizechoice;
|
|
op->opt_flags = OPT_SUB;
|
|
op->opt_action = NULL;
|
|
op++;
|
|
|
|
m->opts = opts;
|
|
m->numopts = op - opts;
|
|
}
|
|
|
|
int
|
|
mbr_use_wholedisk(mbr_info_t *mbri)
|
|
{
|
|
struct mbr_sector *mbrs = &mbri->mbr;
|
|
mbr_info_t *ext;
|
|
struct mbr_partition *part;
|
|
|
|
part = &mbrs->mbr_parts[0];
|
|
/* Set the partition information for full disk usage. */
|
|
while ((ext = mbri->extended)) {
|
|
mbri->extended = ext->extended;
|
|
free(ext);
|
|
}
|
|
memset(part, 0, MBR_PART_COUNT * sizeof *part);
|
|
#ifdef BOOTSEL
|
|
memset(&mbri->mbrb, 0, sizeof mbri->mbrb);
|
|
#endif
|
|
part[0].mbrp_type = MBR_PTYPE_NETBSD;
|
|
part[0].mbrp_size = pm->dlsize - ptn_0_offset;
|
|
part[0].mbrp_start = ptn_0_offset;
|
|
part[0].mbrp_flag = MBR_PFLAG_ACTIVE;
|
|
|
|
pm->ptstart = ptn_0_offset;
|
|
pm->ptsize = pm->dlsize - ptn_0_offset;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Let user change incore Master Boot Record partitions via menu.
|
|
*/
|
|
int
|
|
edit_mbr(mbr_info_t *mbri)
|
|
{
|
|
struct mbr_sector *mbrs = &mbri->mbr;
|
|
mbr_info_t *ext;
|
|
struct mbr_partition *part;
|
|
int i, j;
|
|
int usefull;
|
|
int mbr_menu;
|
|
int activepart;
|
|
int numbsd;
|
|
uint bsdstart, bsdsize;
|
|
uint start;
|
|
|
|
/* Ask full/part */
|
|
|
|
part = &mbrs->mbr_parts[0];
|
|
get_ptn_alignment(part); /* update ptn_alignment */
|
|
if (partman_go)
|
|
usefull = 0;
|
|
else {
|
|
msg_display(MSG_fullpart, pm->diskdev);
|
|
process_menu(MENU_fullpart, &usefull);
|
|
}
|
|
|
|
/* DOS fdisk label checking and value setting. */
|
|
if (usefull) {
|
|
/* Count nonempty, non-BSD partitions. */
|
|
numbsd = 0;
|
|
for (i = 0; i < MBR_PART_COUNT; i++) {
|
|
j = part[i].mbrp_type;
|
|
if (j == 0)
|
|
continue;
|
|
numbsd++;
|
|
if (j != MBR_PTYPE_NETBSD)
|
|
numbsd++;
|
|
}
|
|
|
|
/* Ask if we really want to blow away non-NetBSD stuff */
|
|
if (numbsd > 1) {
|
|
msg_display(MSG_ovrwrite);
|
|
if (!ask_noyes(NULL)) {
|
|
if (logfp)
|
|
(void)fprintf(logfp, "User answered no to destroy other data, aborting.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
return(md_mbr_use_wholedisk(mbri));
|
|
}
|
|
|
|
mbr_menu = new_menu(NULL, NULL, 16, 0, -1, 15, 70,
|
|
MC_NOBOX | MC_ALWAYS_SCROLL | MC_NOCLEAR,
|
|
set_mbr_header, set_mbr_label, NULL,
|
|
NULL, MSG_Partition_table_ok);
|
|
if (mbr_menu == -1)
|
|
return 0;
|
|
|
|
/* Default to MB, and use bios geometry for cylinder size */
|
|
set_sizemultname_meg();
|
|
pm->current_cylsize = bhead * bsec;
|
|
|
|
for (;;) {
|
|
pm->ptstart = 0;
|
|
pm->ptsize = 0;
|
|
process_menu(mbr_menu, mbri);
|
|
|
|
activepart = 0;
|
|
bsdstart = 0;
|
|
bsdsize = 0;
|
|
for (ext = mbri; ext; ext = ext->extended) {
|
|
part = ext->mbr.mbr_parts;
|
|
for (i = 0; i < MBR_PART_COUNT; part++, i++) {
|
|
if (part->mbrp_flag != 0)
|
|
activepart = 1;
|
|
if (part->mbrp_type != MBR_PTYPE_NETBSD)
|
|
continue;
|
|
start = ext->sector + part->mbrp_start;
|
|
if (start == mbri->install) {
|
|
pm->ptstart = mbri->install;
|
|
pm->ptsize = part->mbrp_size;
|
|
}
|
|
if (bsdstart != 0)
|
|
bsdstart = ~0;
|
|
else {
|
|
bsdstart = start;
|
|
bsdsize = part->mbrp_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Install in only netbsd partition if none tagged */
|
|
if (pm->ptstart == 0 && bsdstart != ~0u) {
|
|
pm->ptstart = bsdstart;
|
|
pm->ptsize = bsdsize;
|
|
}
|
|
|
|
if (pm->ptstart == 0) {
|
|
if (bsdstart == 0)
|
|
msg_display(MSG_nobsdpart);
|
|
else
|
|
msg_display(MSG_multbsdpart, 0);
|
|
msg_display_add(MSG_reeditpart, 0);
|
|
if (!ask_yesno(NULL))
|
|
return 0;
|
|
continue;
|
|
}
|
|
|
|
if (activepart == 0) {
|
|
msg_display(MSG_noactivepart);
|
|
if (ask_yesno(NULL))
|
|
continue;
|
|
}
|
|
/* the md_check_mbr function has 3 ret codes to deal with
|
|
* the different possible states. 0, 1, >1
|
|
*/
|
|
j = md_check_mbr(mbri);
|
|
if (j == 0)
|
|
return 0;
|
|
if (j == 1)
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
free_menu(mbr_menu);
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char *
|
|
get_partname(int typ)
|
|
{
|
|
int j;
|
|
static char unknown[32];
|
|
|
|
for (j = 0; part_ids[j].id != -1; j++)
|
|
if (part_ids[j].id == typ)
|
|
return part_ids[j].name;
|
|
|
|
snprintf(unknown, sizeof unknown, "Unknown (%d)", typ);
|
|
return unknown;
|
|
}
|
|
|
|
#ifdef BOOTSEL
|
|
static int
|
|
validate_and_set_names(mbr_info_t *mbri, const struct mbr_bootsel *src,
|
|
uint32_t ext_base)
|
|
{
|
|
size_t i, l;
|
|
const unsigned char *p;
|
|
|
|
/*
|
|
* The 16 bit magic used to detect whether mbr_bootsel is valid
|
|
* or not is pretty week - collisions have been seen in the wild;
|
|
* but maybe it is just foreign tools corruption reminiscents
|
|
* of NetBSD MBRs. Anyway, before accepting a boot menu definition,
|
|
* make sure it is kinda "sane".
|
|
*/
|
|
|
|
for (i = 0; i < MBR_PART_COUNT; i++) {
|
|
/*
|
|
* Make sure the name does not contain controll chars
|
|
* (not using iscntrl due to minimalistic locale support
|
|
* in miniroot environments) and is properly 0-terminated.
|
|
*/
|
|
for (l = 0, p = (const unsigned char *)&src->mbrbs_nametab[i];
|
|
*p != 0; l++, p++) {
|
|
if (l > MBR_BS_PARTNAMESIZE)
|
|
return 0;
|
|
if (*p < ' ') /* hacky 'iscntrl' */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
memcpy(&mbri->mbrb, src, sizeof(*src));
|
|
|
|
if (ext_base == 0)
|
|
return mbri->mbrb.mbrbs_defkey - SCAN_1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
read_mbr(const char *disk, mbr_info_t *mbri)
|
|
{
|
|
struct mbr_partition *mbrp;
|
|
struct mbr_sector *mbrs = &mbri->mbr;
|
|
mbr_info_t *ext = NULL;
|
|
char diskpath[MAXPATHLEN];
|
|
int fd, i;
|
|
uint32_t ext_base = 0, next_ext = 0, ext_size = 0;
|
|
int rval = -1;
|
|
#ifdef BOOTSEL
|
|
mbr_info_t *ombri = mbri;
|
|
int bootkey = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Fake up a likely 'bios sectors per track' for any extended
|
|
* partition headers we might have to produce.
|
|
*/
|
|
if (bsec == 0)
|
|
bsec = pm->dlsec;
|
|
if (bhead == 0)
|
|
bhead = pm->dlhead;
|
|
if (bcyl == 0)
|
|
bhead = pm->dlcyl;
|
|
|
|
ptn_0_offset = bsec;
|
|
/* use 1MB default offset on large disks as fdisk(8) */
|
|
if (pm->dlsize > 2048 * 1024 * 128)
|
|
ptn_0_offset = 2048;
|
|
|
|
memset(mbri, 0, sizeof *mbri);
|
|
|
|
/* Open the disk. */
|
|
fd = opendisk(disk, O_RDONLY, diskpath, sizeof(diskpath), 0);
|
|
if (fd < 0)
|
|
goto bad_mbr;
|
|
|
|
for (;;) {
|
|
if (pread(fd, mbrs, sizeof *mbrs,
|
|
(ext_base + next_ext) * (off_t)MBR_SECSIZE) - sizeof *mbrs != 0)
|
|
break;
|
|
|
|
if (!valid_mbr(mbrs))
|
|
break;
|
|
|
|
mbrp = &mbrs->mbr_parts[0];
|
|
if (ext_base == 0) {
|
|
get_ptn_alignment(mbrp); /* get ptn_0_offset */
|
|
} else {
|
|
/* sanity check extended chain */
|
|
if (MBR_IS_EXTENDED(mbrp[0].mbrp_type))
|
|
break;
|
|
if (mbrp[1].mbrp_type != 0 &&
|
|
!MBR_IS_EXTENDED(mbrp[1].mbrp_type))
|
|
break;
|
|
if (mbrp[2].mbrp_type != 0 || mbrp[3].mbrp_type != 0)
|
|
break;
|
|
/* Looks ok, link into extended chain */
|
|
mbri->extended = ext;
|
|
ext->prev_ext = next_ext != 0 ? mbri : NULL;
|
|
ext->extended = NULL;
|
|
mbri = ext;
|
|
ext = NULL;
|
|
}
|
|
#if BOOTSEL
|
|
if (mbrs->mbr_bootsel_magic == htole16(MBR_MAGIC)) {
|
|
/* old bootsel, grab bootsel info */
|
|
bootkey = validate_and_set_names(mbri,
|
|
(struct mbr_bootsel *)
|
|
((uint8_t *)mbrs + MBR_BS_OLD_OFFSET),
|
|
ext_base);
|
|
} else if (mbrs->mbr_bootsel_magic == htole16(MBR_BS_MAGIC)) {
|
|
/* new location */
|
|
bootkey = validate_and_set_names(mbri,
|
|
&mbrs->mbr_bootsel, ext_base);
|
|
}
|
|
/* Save original flags for mbr code update tests */
|
|
mbri->oflags = mbri->mbrb.mbrbs_flags;
|
|
#endif
|
|
mbri->sector = next_ext + ext_base;
|
|
next_ext = 0;
|
|
rval = 0;
|
|
for (i = 0; i < MBR_PART_COUNT; mbrp++, i++) {
|
|
if (mbrp->mbrp_type == 0) {
|
|
/* type is unused, discard scum */
|
|
memset(mbrp, 0, sizeof *mbrp);
|
|
continue;
|
|
}
|
|
mbrp->mbrp_start = le32toh(mbrp->mbrp_start);
|
|
mbrp->mbrp_size = le32toh(mbrp->mbrp_size);
|
|
if (MBR_IS_EXTENDED(mbrp->mbrp_type)) {
|
|
next_ext = mbrp->mbrp_start;
|
|
if (ext_base == 0)
|
|
ext_size = mbrp->mbrp_size;
|
|
} else {
|
|
mbri->last_mounted[i] = strdup(get_last_mounted(
|
|
fd, mbri->sector + mbrp->mbrp_start, NULL));
|
|
#if BOOTSEL
|
|
if (ombri->install == 0 &&
|
|
strcmp(mbri->last_mounted[i], "/") == 0)
|
|
ombri->install = mbri->sector +
|
|
mbrp->mbrp_start;
|
|
#endif
|
|
}
|
|
#if BOOTSEL
|
|
if (mbri->mbrb.mbrbs_nametab[i][0] != 0
|
|
&& bootkey-- == 0)
|
|
ombri->bootsec = mbri->sector +
|
|
mbrp->mbrp_start;
|
|
#endif
|
|
}
|
|
|
|
if (ext_base != 0) {
|
|
/* Is there a gap before the next partition? */
|
|
unsigned int limit = next_ext;
|
|
unsigned int base;
|
|
if (limit == 0)
|
|
limit = ext_size;
|
|
mbrp -= MBR_PART_COUNT;
|
|
base =mbri->sector + mbrp->mbrp_start + mbrp->mbrp_size;
|
|
if (mbrp->mbrp_type != 0 && ext_base + limit != base) {
|
|
/* Mock up an extry for the space */
|
|
ext = calloc(1, sizeof *ext);
|
|
if (!ext)
|
|
break;
|
|
ext->sector = base;
|
|
ext->mbr.mbr_magic = htole16(MBR_MAGIC);
|
|
ext->mbr.mbr_parts[1] = mbrp[1];
|
|
ext->mbr.mbr_parts[0].mbrp_start = ptn_0_offset;
|
|
ext->mbr.mbr_parts[0].mbrp_size =
|
|
ext_base + limit - base - ptn_0_offset;
|
|
mbrp[1].mbrp_type = MBR_PTYPE_EXT;
|
|
mbrp[1].mbrp_start = base - ext_base;
|
|
mbrp[1].mbrp_size = limit - mbrp[1].mbrp_start;
|
|
mbri->extended = ext;
|
|
ext->prev_ext = mbri;
|
|
ext->extended = NULL;
|
|
mbri = ext;
|
|
ext = NULL;
|
|
}
|
|
}
|
|
|
|
if (next_ext == 0 || ext_base + next_ext <= mbri->sector)
|
|
break;
|
|
if (ext_base == 0) {
|
|
ext_base = next_ext;
|
|
next_ext = 0;
|
|
}
|
|
ext = calloc(sizeof *ext, 1);
|
|
if (!ext)
|
|
break;
|
|
mbrs = &ext->mbr;
|
|
}
|
|
|
|
bad_mbr:
|
|
free(ext);
|
|
if (fd >= 0)
|
|
close(fd);
|
|
if (rval == -1) {
|
|
memset(&mbrs->mbr_parts, 0, sizeof mbrs->mbr_parts);
|
|
mbrs->mbr_magic = htole16(MBR_MAGIC);
|
|
}
|
|
dump_mbr(ombri, "read");
|
|
return rval;
|
|
}
|
|
|
|
int
|
|
write_mbr(const char *disk, mbr_info_t *mbri, int convert)
|
|
{
|
|
char diskpath[MAXPATHLEN];
|
|
int fd, i, ret = 0;
|
|
struct mbr_partition *mbrp;
|
|
u_int32_t pstart, psize;
|
|
#ifdef BOOTSEL
|
|
struct mbr_sector *mbrs;
|
|
#endif
|
|
struct mbr_sector mbrsec;
|
|
mbr_info_t *ext;
|
|
uint sector;
|
|
|
|
/* Open the disk. */
|
|
fd = opendisk(disk, O_WRONLY, diskpath, sizeof(diskpath), 0);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
#ifdef BOOTSEL
|
|
/*
|
|
* If the main boot code (appears to) contain the netbsd bootcode,
|
|
* copy in all the menu strings and set the default keycode
|
|
* to be that for the default partition.
|
|
* Unfortunately we can't rely on the user having actually updated
|
|
* to the new mbr code :-(
|
|
*/
|
|
if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_BS_MAGIC)
|
|
|| mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC)) {
|
|
int8_t key = SCAN_1;
|
|
uint offset = MBR_BS_OFFSET;
|
|
if (mbri->mbr.mbr_bootsel_magic == htole16(MBR_MAGIC))
|
|
offset = MBR_BS_OLD_OFFSET;
|
|
mbri->mbrb.mbrbs_defkey = SCAN_ENTER;
|
|
if (mbri->mbrb.mbrbs_timeo == 0)
|
|
mbri->mbrb.mbrbs_timeo = 182; /* 10 seconds */
|
|
for (ext = mbri; ext != NULL; ext = ext->extended) {
|
|
mbrs = &ext->mbr;
|
|
mbrp = &mbrs->mbr_parts[0];
|
|
/* Ensure marker is set in each sector */
|
|
mbrs->mbr_bootsel_magic = mbri->mbr.mbr_bootsel_magic;
|
|
/* and copy in bootsel parameters */
|
|
*(struct mbr_bootsel *)((uint8_t *)mbrs + offset) =
|
|
ext->mbrb;
|
|
for (i = 0; i < MBR_PART_COUNT; i++) {
|
|
if (ext->mbrb.mbrbs_nametab[i][0] == 0)
|
|
continue;
|
|
if (ext->sector + mbrp->mbrp_start ==
|
|
mbri->bootsec)
|
|
mbri->mbrb.mbrbs_defkey = key;
|
|
key++;
|
|
}
|
|
}
|
|
/* copy main data (again) since we've put the 'key' in */
|
|
*(struct mbr_bootsel *)((uint8_t *)&mbri->mbr + offset) =
|
|
mbri->mbrb;
|
|
}
|
|
#endif
|
|
|
|
for (ext = mbri; ext != NULL; ext = ext->extended) {
|
|
sector = ext->sector;
|
|
mbrsec = ext->mbr; /* copy sector */
|
|
mbrp = &mbrsec.mbr_parts[0];
|
|
|
|
if (sector != 0 && ext->extended != NULL
|
|
&& ext->extended->mbr.mbr_parts[0].mbrp_type == 0) {
|
|
/* We are followed by an empty slot, collapse out */
|
|
ext = ext->extended;
|
|
/* Make us describe the next non-empty partition */
|
|
mbrp[1] = ext->mbr.mbr_parts[1];
|
|
}
|
|
|
|
for (i = 0; i < MBR_PART_COUNT; i++) {
|
|
if (mbrp[i].mbrp_start == 0 && mbrp[i].mbrp_size == 0) {
|
|
mbrp[i].mbrp_scyl = 0;
|
|
mbrp[i].mbrp_shd = 0;
|
|
mbrp[i].mbrp_ssect = 0;
|
|
mbrp[i].mbrp_ecyl = 0;
|
|
mbrp[i].mbrp_ehd = 0;
|
|
mbrp[i].mbrp_esect = 0;
|
|
continue;
|
|
}
|
|
pstart = mbrp[i].mbrp_start;
|
|
psize = mbrp[i].mbrp_size;
|
|
mbrp[i].mbrp_start = htole32(pstart);
|
|
mbrp[i].mbrp_size = htole32(psize);
|
|
if (convert) {
|
|
convert_mbr_chs(bcyl, bhead, bsec,
|
|
&mbrp[i].mbrp_scyl, &mbrp[i].mbrp_shd,
|
|
&mbrp[i].mbrp_ssect, pstart);
|
|
convert_mbr_chs(bcyl, bhead, bsec,
|
|
&mbrp[i].mbrp_ecyl, &mbrp[i].mbrp_ehd,
|
|
&mbrp[i].mbrp_esect, pstart + psize - 1);
|
|
}
|
|
}
|
|
|
|
mbrsec.mbr_magic = htole16(MBR_MAGIC);
|
|
if (pwrite(fd, &mbrsec, sizeof mbrsec,
|
|
sector * (off_t)MBR_SECSIZE) < 0) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
(void)close(fd);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
valid_mbr(struct mbr_sector *mbrs)
|
|
{
|
|
|
|
return (le16toh(mbrs->mbr_magic) == MBR_MAGIC);
|
|
}
|
|
|
|
static void
|
|
convert_mbr_chs(int cyl, int head, int sec,
|
|
uint8_t *cylp, uint8_t *headp, uint8_t *secp,
|
|
uint32_t relsecs)
|
|
{
|
|
unsigned int tcyl, temp, thead, tsec;
|
|
|
|
temp = head * sec;
|
|
tcyl = relsecs / temp;
|
|
relsecs -= tcyl * temp;
|
|
|
|
thead = relsecs / sec;
|
|
tsec = relsecs - thead * sec + 1;
|
|
|
|
if (tcyl > MAXCYL)
|
|
tcyl = MAXCYL;
|
|
|
|
*cylp = MBR_PUT_LSCYL(tcyl);
|
|
*headp = thead;
|
|
*secp = MBR_PUT_MSCYLANDSEC(tcyl, tsec);
|
|
}
|
|
|
|
/*
|
|
* This function is ONLY to be used as a last resort to provide a
|
|
* hint for the user. Ports should provide a more reliable way
|
|
* of getting the BIOS geometry. The i386 code, for example,
|
|
* uses the BIOS geometry as passed on from the bootblocks,
|
|
* and only uses this as a hint to the user when that information
|
|
* is not present, or a match could not be made with a NetBSD
|
|
* device.
|
|
*/
|
|
|
|
int
|
|
guess_biosgeom_from_mbr(mbr_info_t *mbri, int *cyl, int *head, daddr_t *sec)
|
|
{
|
|
struct mbr_sector *mbrs = &mbri->mbr;
|
|
struct mbr_partition *parts = &mbrs->mbr_parts[0];
|
|
int xcylinders, xheads, i, j;
|
|
daddr_t xsectors;
|
|
int c1, h1, s1, c2, h2, s2;
|
|
daddr_t a1, a2;
|
|
uint64_t num, denom;
|
|
|
|
/*
|
|
* The physical parameters may be invalid as bios geometry.
|
|
* If we cannot determine the actual bios geometry, we are
|
|
* better off picking a likely 'faked' geometry than leaving
|
|
* the invalid physical one.
|
|
*/
|
|
|
|
xcylinders = pm->dlcyl;
|
|
xheads = pm->dlhead;
|
|
xsectors = pm->dlsec;
|
|
if (xcylinders > MAXCYL || xheads > MAXHEAD || xsectors > MAXSECTOR) {
|
|
xsectors = MAXSECTOR;
|
|
xheads = MAXHEAD;
|
|
xcylinders = pm->dlsize / (MAXSECTOR * MAXHEAD);
|
|
if (xcylinders > MAXCYL)
|
|
xcylinders = MAXCYL;
|
|
}
|
|
*cyl = xcylinders;
|
|
*head = xheads;
|
|
*sec = xsectors;
|
|
|
|
xheads = -1;
|
|
|
|
/* Try to deduce the number of heads from two different mappings. */
|
|
for (i = 0; i < MBR_PART_COUNT * 2 - 1; i++) {
|
|
if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
|
|
continue;
|
|
a1 -= s1;
|
|
for (j = i + 1; j < MBR_PART_COUNT * 2; j++) {
|
|
if (get_mapping(parts, j, &c2, &h2, &s2, &a2) < 0)
|
|
continue;
|
|
a2 -= s2;
|
|
num = (uint64_t)h1 * a2 - (quad_t)h2 * a1;
|
|
denom = (uint64_t)c2 * a1 - (quad_t)c1 * a2;
|
|
if (num != 0 && denom != 0 && num % denom == 0) {
|
|
xheads = (int)(num / denom);
|
|
xsectors = a1 / (c1 * xheads + h1);
|
|
break;
|
|
}
|
|
}
|
|
if (xheads != -1)
|
|
break;
|
|
}
|
|
|
|
if (xheads == -1)
|
|
return -1;
|
|
|
|
/*
|
|
* Estimate the number of cylinders.
|
|
* XXX relies on get_disks having been called.
|
|
*/
|
|
xcylinders = pm->dlsize / xheads / xsectors;
|
|
if (pm->dlsize != xcylinders * xheads * xsectors)
|
|
xcylinders++;
|
|
|
|
/*
|
|
* 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 < MBR_PART_COUNT * 2; i++) {
|
|
if (get_mapping(parts, i, &c1, &h1, &s1, &a1) < 0)
|
|
continue;
|
|
if (c1 >= MAXCYL - 1)
|
|
/* Ignore anything that is near the CHS limit */
|
|
continue;
|
|
if (xsectors * (c1 * xheads + h1) + s1 != a1)
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Everything checks out. Reset the geometry to use for further
|
|
* calculations.
|
|
*/
|
|
*cyl = MIN(xcylinders, MAXCYL);
|
|
*head = xheads;
|
|
*sec = xsectors;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
get_mapping(struct mbr_partition *parts, int i,
|
|
int *cylinder, int *head, int *sector, daddr_t *absolute)
|
|
{
|
|
struct mbr_partition *apart = &parts[i / 2];
|
|
|
|
if (apart->mbrp_type == 0)
|
|
return -1;
|
|
if (i % 2 == 0) {
|
|
*cylinder = MBR_PCYL(apart->mbrp_scyl, apart->mbrp_ssect);
|
|
*head = apart->mbrp_shd;
|
|
*sector = MBR_PSECT(apart->mbrp_ssect) - 1;
|
|
*absolute = le32toh(apart->mbrp_start);
|
|
} else {
|
|
*cylinder = MBR_PCYL(apart->mbrp_ecyl, apart->mbrp_esect);
|
|
*head = apart->mbrp_ehd;
|
|
*sector = MBR_PSECT(apart->mbrp_esect) - 1;
|
|
*absolute = le32toh(apart->mbrp_start)
|
|
+ le32toh(apart->mbrp_size) - 1;
|
|
}
|
|
/* Sanity check the data against max values */
|
|
if ((((*cylinder * MAXHEAD) + *head) * (uint32_t)MAXSECTOR + *sector) < *absolute)
|
|
/* cannot be a CHS mapping */
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Determine partition boundary alignment as fdisk(8) does.
|
|
*/
|
|
static void
|
|
get_ptn_alignment(struct mbr_partition *mbrp0)
|
|
{
|
|
uint32_t ptn_0_base, ptn_0_limit;
|
|
|
|
/* Default to using 'traditional' cylinder alignment */
|
|
ptn_alignment = bhead * bsec;
|
|
ptn_0_offset = bsec;
|
|
|
|
if (mbrp0->mbrp_type != 0) {
|
|
/* Try to copy offset of first partition */
|
|
ptn_0_base = le32toh(mbrp0->mbrp_start);
|
|
ptn_0_limit = ptn_0_base + le32toh(mbrp0->mbrp_size);
|
|
if (!(ptn_0_limit & 2047)) {
|
|
/* Partition ends on a 1MB boundary, align to 1MB */
|
|
ptn_alignment = 2048;
|
|
if (ptn_0_base <= 2048
|
|
&& !(ptn_0_base & (ptn_0_base - 1))) {
|
|
/* ptn_base is a power of 2, use it */
|
|
ptn_0_offset = ptn_0_base;
|
|
}
|
|
}
|
|
} else {
|
|
/* Use 1MB offset for large (>128GB) disks */
|
|
if (pm->dlsize > 2048 * 1024 * 128) {
|
|
ptn_alignment = 2048;
|
|
ptn_0_offset = 2048;
|
|
}
|
|
}
|
|
}
|