src/sbin/fdisk/mbr.c

218 lines
5.7 KiB
C

/* $OpenBSD: mbr.c,v 1.124 2023/05/17 12:59:37 krw Exp $ */
/*
* Copyright (c) 1997 Tobias Weingartner
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/param.h> /* DEV_BSIZE */
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "part.h"
#include "disk.h"
#include "misc.h"
#include "mbr.h"
#include "gpt.h"
struct dos_mbr default_dmbr;
void mbr_to_dos_mbr(const struct mbr *, struct dos_mbr *);
void dos_mbr_to_mbr(const struct dos_mbr *, const uint64_t,
const uint64_t, struct mbr *);
void
MBR_init(struct mbr *mbr)
{
struct dos_partition dp;
struct prt bootprt, obsdprt;
daddr_t daddr;
memset(&gmbr, 0, sizeof(gmbr));
memset(&gh, 0, sizeof(gh));
memset(&gp, 0, sizeof(gp));
if (mbr->mbr_lba_self != 0) {
/* Extended MBR - save lba's, set sig, zap everything else. */
memset(mbr->mbr_code, 0, sizeof(mbr->mbr_code));
memset(mbr->mbr_prt, 0, sizeof(mbr->mbr_prt));
mbr->mbr_signature = DOSMBR_SIGNATURE;
return;
}
memset(&obsdprt, 0, sizeof(obsdprt));
memset(&bootprt, 0, sizeof(bootprt));
if (disk.dk_bootprt.prt_ns > 0) {
bootprt = disk.dk_bootprt;
} else {
memcpy(&dp, &default_dmbr.dmbr_parts[0], sizeof(dp));
PRT_dp_to_prt(&dp, 0, 0, &bootprt);
}
if (bootprt.prt_ns > 0) {
/* Start OpenBSD partition immediately after bootprt. */
obsdprt.prt_bs = bootprt.prt_bs + bootprt.prt_ns;
} else if (disk.dk_heads > 1 || disk.dk_cylinders > 1) {
/*
* Start OpenBSD partition on power of 2 block number
* after the first track.
*/
daddr = 1;
while (daddr < DL_SECTOBLK(&dl, disk.dk_sectors))
daddr *= 2;
obsdprt.prt_bs = DL_BLKTOSEC(&dl, daddr);
} else {
/* Start OpenBSD partition immediately after MBR. */
obsdprt.prt_bs = 1;
}
if (obsdprt.prt_bs >= disk.dk_size) {
memset(&obsdprt, 0, sizeof(obsdprt));
} else {
obsdprt.prt_ns = disk.dk_size - obsdprt.prt_bs;
obsdprt.prt_id = DOSPTYP_OPENBSD;
if (bootprt.prt_flag != DOSACTIVE)
obsdprt.prt_flag = DOSACTIVE;
}
memset(mbr, 0, sizeof(*mbr));
memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
mbr->mbr_prt[0] = bootprt;
mbr->mbr_prt[3] = obsdprt;
mbr->mbr_signature = DOSMBR_SIGNATURE;
}
void
dos_mbr_to_mbr(const struct dos_mbr *dmbr, const uint64_t lba_self,
const uint64_t lba_firstembr, struct mbr *mbr)
{
struct dos_partition dos_parts[NDOSPART];
uint8_t *p;
unsigned int i;
p = (uint8_t *)dmbr;
mbr->mbr_dmbrzeros = 0;
for (i = 0; i < sizeof(struct dos_mbr) && *p == 0; i++, p++)
mbr->mbr_dmbrzeros++;
memcpy(mbr->mbr_code, dmbr->dmbr_boot, sizeof(mbr->mbr_code));
mbr->mbr_lba_self = lba_self;
mbr->mbr_lba_firstembr = lba_firstembr;
mbr->mbr_signature = letoh16(dmbr->dmbr_sign);
memcpy(dos_parts, dmbr->dmbr_parts, sizeof(dos_parts));
for (i = 0; i < nitems(mbr->mbr_prt); i++) {
memset(&mbr->mbr_prt[i], 0, sizeof(mbr->mbr_prt[i]));
if (i < nitems(dmbr->dmbr_parts))
PRT_dp_to_prt(&dos_parts[i], lba_self, lba_firstembr,
&mbr->mbr_prt[i]);
}
}
void
mbr_to_dos_mbr(const struct mbr *mbr, struct dos_mbr *dos_mbr)
{
struct dos_partition dos_partition;
unsigned int i;
memcpy(dos_mbr->dmbr_boot, mbr->mbr_code, sizeof(dos_mbr->dmbr_boot));
dos_mbr->dmbr_sign = htole16(DOSMBR_SIGNATURE);
for (i = 0; i < nitems(dos_mbr->dmbr_parts); i++) {
memset(&dos_partition, 0, sizeof(dos_partition));
if (i < nitems(mbr->mbr_prt)) {
PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
mbr->mbr_lba_firstembr, &dos_partition);
}
memcpy(&dos_mbr->dmbr_parts[i], &dos_partition,
sizeof(dos_mbr->dmbr_parts[i]));
}
}
void
MBR_print(const struct mbr *mbr, const char *units)
{
unsigned int i;
DISK_printgeometry("s");
printf("Offset: %llu\t", mbr->mbr_lba_self);
printf("Signature: 0x%X\n", (int)mbr->mbr_signature);
PRT_print_parthdr();
for (i = 0; i < nitems(mbr->mbr_prt); i++)
PRT_print_part(i, &mbr->mbr_prt[i], units);
}
int
MBR_read(const uint64_t lba_self, const uint64_t lba_firstembr, struct mbr *mbr)
{
struct dos_mbr dos_mbr;
if (DISK_readbytes(&dos_mbr, lba_self, sizeof(dos_mbr)))
return -1;
dos_mbr_to_mbr(&dos_mbr, lba_self, lba_firstembr, mbr);
return 0;
}
int
MBR_write(const struct mbr *mbr)
{
struct dos_mbr dos_mbr;
mbr_to_dos_mbr(mbr, &dos_mbr);
if (DISK_writebytes(&dos_mbr, mbr->mbr_lba_self, sizeof(dos_mbr)))
return -1;
/* Refresh in-kernel disklabel from the updated disk information. */
if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
warn("DIOCRLDINFO");
return 0;
}
int
MBR_valid_prt(const struct mbr *mbr)
{
uint64_t bs, ns;
unsigned int i, nprt;
unsigned char id;
if (mbr->mbr_dmbrzeros == sizeof(struct dos_mbr))
return 1; /* All zeros struct dos_mbr is editable. */
nprt = 0;
for (i = 0; i < nitems(mbr->mbr_prt); i++) {
bs = mbr->mbr_prt[i].prt_bs;
ns = mbr->mbr_prt[i].prt_ns;
id = mbr->mbr_prt[i].prt_id;
if ((bs == 0 && ns == 0 && id == 0) ||
(bs < DL_GETDSIZE(&dl) && ns > 0 && ns <= DL_GETDSIZE(&dl)))
nprt++;
}
return nprt > 0 && mbr->mbr_signature == DOSMBR_SIGNATURE;
}