File: [local] / src / sbin / fdisk / cmd.c (download)
Revision 1.180, Fri Mar 1 17:48:03 2024 UTC (3 months ago) by krw
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, HEAD Changes since 1.179: +4 -2 lines
Allow fdisk(8) to add GPT partitions of protected types.
This makes it possible to provision virtual machine images that
need a "BIOS Boot" partition.
Report, original diff and testing by Christian Ludwig. Thanks!
ok miod@
|
/* $OpenBSD: cmd.c,v 1.180 2024/03/01 17:48:03 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/types.h>
#include <sys/disklabel.h>
#include <err.h>
#include <errno.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uuid.h>
#include "part.h"
#include "disk.h"
#include "misc.h"
#include "mbr.h"
#include "gpt.h"
#include "user.h"
#include "cmd.h"
int gedit(const int);
int edit(const int, struct mbr *);
int gsetpid(const int);
int setpid(const int, struct mbr *);
int parsepn(const char *);
int parseflag(const char *, uint64_t *);
int ask_num(const char *, int, int, int);
int ask_pid(const int);
const struct uuid *ask_uuid(const struct uuid *);
extern const unsigned char manpage[];
extern const int manpage_sz;
int
Xreinit(const char *args, struct mbr *mbr)
{
int dogpt;
dogpt = 0;
if (strcasecmp(args, "gpt") == 0)
dogpt = 1;
else if (strcasecmp(args, "mbr") == 0)
dogpt = 0;
else if (strlen(args) > 0) {
printf("Unrecognized modifier '%s'\n", args);
return CMD_CONT;
}
if (dogpt) {
GPT_init(GHANDGP);
GPT_print("s", TERSE);
} else {
MBR_init(mbr);
MBR_print(mbr, "s");
}
printf("Use 'write' to update disk.\n");
return CMD_DIRTY;
}
int
Xswap(const char *args, struct mbr *mbr)
{
char lbuf[LINEBUFSZ];
char *from, *to;
int pf, pt;
struct prt pp;
struct gpt_partition gg;
if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
printf("argument string too long\n");
return CMD_CONT;
}
to = lbuf;
from = strsep(&to, WHITESPACE);
pt = parsepn(to);
if (pt == -1)
return CMD_CONT;
pf = parsepn(from);
if (pf == -1)
return CMD_CONT;
if (pt == pf) {
printf("%d same partition as %d, doing nothing.\n", pt, pf);
return CMD_CONT;
}
if (gh.gh_sig == GPTSIGNATURE) {
gg = gp[pt];
gp[pt] = gp[pf];
gp[pf] = gg;
} else {
pp = mbr->mbr_prt[pt];
mbr->mbr_prt[pt] = mbr->mbr_prt[pf];
mbr->mbr_prt[pf] = pp;
}
return CMD_DIRTY;
}
int
gedit(const int pn)
{
struct uuid oldtype;
oldtype = gp[pn].gp_type;
if (gsetpid(pn))
return -1;
if (uuid_is_nil(&gp[pn].gp_type, NULL)) {
if (uuid_is_nil(&oldtype, NULL) == 0) {
memset(&gp[pn], 0, sizeof(gp[pn]));
printf("Partition %d is disabled.\n", pn);
}
return 0;
}
if (GPT_get_lba_start(pn) == -1 ||
GPT_get_lba_end(pn) == -1 ||
GPT_get_name(pn) == -1) {
return -1;
}
return 0;
}
int
parsepn(const char *pnstr)
{
const char *errstr;
int maxpn, pn;
if (pnstr == NULL) {
printf("no partition number\n");
return -1;
}
if (gh.gh_sig == GPTSIGNATURE)
maxpn = gh.gh_part_num - 1;
else
maxpn = NDOSPART - 1;
pn = strtonum(pnstr, 0, maxpn, &errstr);
if (errstr) {
printf("partition number is %s: %s\n", errstr, pnstr);
return -1;
}
return pn;
}
int
parseflag(const char *flagstr, uint64_t *flagvalue)
{
const char *errstr;
char *ep;
uint64_t val;
flagstr += strspn(flagstr, WHITESPACE);
if (flagstr[0] == '0' && (flagstr[1] == 'x' || flagstr[1] == 'X')) {
errno = 0;
val = strtoull(flagstr, &ep, 16);
if (errno || ep == flagstr || *ep != '\0' ||
(gh.gh_sig != GPTSIGNATURE && val > 0xff)) {
printf("flag value is invalid: %s\n", flagstr);
return -1;
}
goto done;
}
if (gh.gh_sig == GPTSIGNATURE)
val = strtonum(flagstr, 0, INT64_MAX, &errstr);
else
val = strtonum(flagstr, 0, 0xff, &errstr);
if (errstr) {
printf("flag value is %s: %s\n", errstr, flagstr);
return -1;
}
done:
*flagvalue = val;
return 0;
}
int
edit(const int pn, struct mbr *mbr)
{
struct chs start, end;
struct prt *pp;
uint64_t track;
unsigned char oldid;
pp = &mbr->mbr_prt[pn];
oldid = pp->prt_id;
if (setpid(pn, mbr))
return -1;
if (pp->prt_id == DOSPTYP_UNUSED) {
if (oldid != DOSPTYP_UNUSED) {
memset(pp, 0, sizeof(*pp));
printf("Partition %d is disabled.\n", pn);
}
return 0;
}
if (ask_yn("Do you wish to edit in CHS mode?")) {
PRT_lba_to_chs(pp, &start, &end);
start.chs_cyl = ask_num("BIOS Starting cylinder", start.chs_cyl,
0, disk.dk_cylinders - 1);
start.chs_head = ask_num("BIOS Starting head", start.chs_head,
0, disk.dk_heads - 1);
start.chs_sect = ask_num("BIOS Starting sector", start.chs_sect,
1, disk.dk_sectors);
end.chs_cyl = ask_num("BIOS Ending cylinder", end.chs_cyl,
start.chs_cyl, disk.dk_cylinders - 1);
end.chs_head = ask_num("BIOS Ending head", end.chs_head,
(start.chs_cyl == end.chs_cyl) ? start.chs_head : 0,
disk.dk_heads - 1);
end.chs_sect = ask_num("BIOS Ending sector", end.chs_sect,
(start.chs_cyl == end.chs_cyl && start.chs_head ==
end.chs_head) ? start.chs_sect : 1, disk.dk_sectors);
/* The ATA/ATAPI spec says LBA = (C × HPC + H) × SPT + (S − 1) */
track = start.chs_cyl * disk.dk_heads + start.chs_head;
pp->prt_bs = track * disk.dk_sectors + (start.chs_sect - 1);
track = end.chs_cyl * disk.dk_heads + end.chs_head;
pp->prt_ns = track * disk.dk_sectors + (end.chs_sect - 1) -
pp->prt_bs + 1;
} else {
pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0,
disk.dk_size - 1);
pp->prt_ns = getuint64("Partition size", pp->prt_ns, 1,
disk.dk_size - pp->prt_bs);
}
return 0;
}
int
Xedit(const char *args, struct mbr *mbr)
{
struct gpt_partition oldgg;
struct prt oldprt;
int pn;
pn = parsepn(args);
if (pn == -1)
return CMD_CONT;
if (gh.gh_sig == GPTSIGNATURE) {
oldgg = gp[pn];
if (gedit(pn))
gp[pn] = oldgg;
else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
return CMD_DIRTY;
} else {
oldprt = mbr->mbr_prt[pn];
if (edit(pn, mbr))
mbr->mbr_prt[pn] = oldprt;
else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
return CMD_DIRTY;
}
return CMD_CONT;
}
int
gsetpid(const int pn)
{
int32_t is_nil;
uint32_t status;
GPT_print_parthdr(TERSE);
GPT_print_part(pn, "s", TERSE);
if (PRT_protected_uuid(&gp[pn].gp_type)) {
printf("can't edit partition type %s\n",
PRT_uuid_to_desc(&gp[pn].gp_type));
return -1;
}
is_nil = uuid_is_nil(&gp[pn].gp_type, NULL);
gp[pn].gp_type = *ask_uuid(&gp[pn].gp_type);
if (PRT_protected_uuid(&gp[pn].gp_type) && is_nil == 0) {
printf("can't change partition type to %s\n",
PRT_uuid_to_desc(&gp[pn].gp_type));
return -1;
}
if (uuid_is_nil(&gp[pn].gp_guid, NULL)) {
uuid_create(&gp[pn].gp_guid, &status);
if (status != uuid_s_ok) {
printf("could not create guid for partition\n");
return -1;
}
}
return 0;
}
int
setpid(const int pn, struct mbr *mbr)
{
struct prt *pp;
pp = &mbr->mbr_prt[pn];
PRT_print_parthdr();
PRT_print_part(pn, pp, "s");
pp->prt_id = ask_pid(pp->prt_id);
return 0;
}
int
Xsetpid(const char *args, struct mbr *mbr)
{
struct gpt_partition oldgg;
struct prt oldprt;
int pn;
pn = parsepn(args);
if (pn == -1)
return CMD_CONT;
if (gh.gh_sig == GPTSIGNATURE) {
oldgg = gp[pn];
if (gsetpid(pn))
gp[pn] = oldgg;
else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
return CMD_DIRTY;
} else {
oldprt = mbr->mbr_prt[pn];
if (setpid(pn, mbr))
mbr->mbr_prt[pn] = oldprt;
else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
return CMD_DIRTY;
}
return CMD_CONT;
}
int
Xselect(const char *args, struct mbr *mbr)
{
static uint64_t lba_firstembr = 0;
uint64_t lba_self;
int pn;
pn = parsepn(args);
if (pn == -1)
return CMD_CONT;
lba_self = mbr->mbr_prt[pn].prt_bs;
if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) &&
(mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) {
printf("Partition %d is not an extended partition.\n", pn);
return CMD_CONT;
}
if (lba_firstembr == 0)
lba_firstembr = lba_self;
if (lba_self == 0) {
printf("Loop to MBR (sector 0)! Not selected.\n");
return CMD_CONT;
} else {
printf("Selected extended partition %d\n", pn);
printf("New EMBR at offset %llu.\n", lba_self);
}
USER_edit(lba_self, lba_firstembr);
return CMD_CONT;
}
int
Xprint(const char *args, struct mbr *mbr)
{
if (gh.gh_sig == GPTSIGNATURE)
GPT_print(args, VERBOSE);
else if (MBR_valid_prt(mbr))
MBR_print(mbr, args);
else {
DISK_printgeometry("s");
printf("Offset: %d\tSignature: 0x%X.\tNo MBR or GPT.\n",
DOSBBSECTOR, (int)mbr->mbr_signature);
}
return CMD_CONT;
}
int
Xwrite(const char *args, struct mbr *mbr)
{
unsigned int i, n;
for (i = 0, n = 0; i < nitems(mbr->mbr_prt); i++)
if (mbr->mbr_prt[i].prt_id == DOSPTYP_OPENBSD)
n++;
if (n > 1) {
warnx("MBR contains more than one OpenBSD partition!");
if (ask_yn("Write MBR anyway?") == 0)
return CMD_CONT;
}
if (gh.gh_sig == GPTSIGNATURE) {
printf("Writing GPT.\n");
if (GPT_write() == -1) {
warnx("error writing GPT");
return CMD_CONT;
}
} else {
printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self);
if (MBR_write(mbr) == -1) {
warnx("error writing MBR");
return CMD_CONT;
}
GPT_zap_headers();
}
return CMD_CLEAN;
}
int
Xquit(const char *args, struct mbr *mbr)
{
return CMD_QUIT;
}
int
Xabort(const char *args, struct mbr *mbr)
{
exit(0);
}
int
Xexit(const char *args, struct mbr *mbr)
{
return CMD_EXIT;
}
int
Xhelp(const char *args, struct mbr *mbr)
{
USER_help(mbr);
return CMD_CONT;
}
int
Xupdate(const char *args, struct mbr *mbr)
{
memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
mbr->mbr_signature = DOSMBR_SIGNATURE;
printf("Machine code updated.\n");
return CMD_DIRTY;
}
int
Xflag(const char *args, struct mbr *mbr)
{
char lbuf[LINEBUFSZ];
char *part, *flag;
uint64_t val;
int i, pn;
if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
printf("argument string too long\n");
return CMD_CONT;
}
flag = lbuf;
part = strsep(&flag, WHITESPACE);
pn = parsepn(part);
if (pn == -1)
return CMD_CONT;
if (flag != NULL) {
if (parseflag(flag, &val) == -1)
return CMD_CONT;
if (gh.gh_sig == GPTSIGNATURE)
gp[pn].gp_attrs = val;
else
mbr->mbr_prt[pn].prt_flag = val;
printf("Partition %d flag value set to 0x%llx.\n", pn, val);
} else {
if (gh.gh_sig == GPTSIGNATURE) {
for (i = 0; i < gh.gh_part_num; i++) {
if (i == pn)
gp[i].gp_attrs = GPTPARTATTR_BOOTABLE;
else
gp[i].gp_attrs &= ~GPTPARTATTR_BOOTABLE;
}
} else {
for (i = 0; i < nitems(mbr->mbr_prt); i++) {
if (i == pn)
mbr->mbr_prt[i].prt_flag = DOSACTIVE;
else
mbr->mbr_prt[i].prt_flag = 0x00;
}
}
printf("Partition %d marked active.\n", pn);
}
return CMD_DIRTY;
}
int
Xmanual(const char *args, struct mbr *mbr)
{
char *pager = "/usr/bin/less";
char *p;
FILE *f;
sig_t opipe;
opipe = signal(SIGPIPE, SIG_IGN);
if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
pager = p;
if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
f = popen(p, "w");
if (f) {
fwrite(manpage, manpage_sz, 1, f);
pclose(f);
}
free(p);
}
signal(SIGPIPE, opipe);
return CMD_CONT;
}
int
ask_num(const char *str, int dflt, int low, int high)
{
char lbuf[LINEBUFSZ];
const char *errstr;
int num;
if (dflt < low)
dflt = low;
else if (dflt > high)
dflt = high;
do {
printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
string_from_line(lbuf, sizeof(lbuf), TRIMMED);
if (lbuf[0] == '\0') {
num = dflt;
errstr = NULL;
} else {
num = (int)strtonum(lbuf, low, high, &errstr);
if (errstr)
printf("%s is %s: %s.\n", str, errstr, lbuf);
}
} while (errstr);
return num;
}
int
ask_pid(const int dflt)
{
char lbuf[LINEBUFSZ];
int num;
for (;;) {
printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt);
printf("(? for help) ");
string_from_line(lbuf, sizeof(lbuf), TRIMMED);
if (strlen(lbuf) == 0)
return dflt;
if (strcmp(lbuf, "?") == 0) {
PRT_print_mbrmenu(lbuf, sizeof(lbuf));
if (strlen(lbuf) == 0)
continue;
}
num = hex_octet(lbuf);
if (num != -1)
return num;
printf("'%s' is not a valid partition id.\n", lbuf);
}
}
const struct uuid *
ask_uuid(const struct uuid *olduuid)
{
char lbuf[LINEBUFSZ];
static struct uuid uuid;
const char *guid;
char *dflt;
uint32_t status;
dflt = PRT_uuid_to_menudflt(olduuid);
if (dflt == NULL) {
if (asprintf(&dflt, "00") == -1) {
warn("asprintf()");
goto done;
}
}
for (;;) {
printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ",
dflt);
printf("(? for help) ");
string_from_line(lbuf, sizeof(lbuf), TRIMMED);
if (strcmp(lbuf, "?") == 0) {
PRT_print_gptmenu(lbuf, sizeof(lbuf));
if (strlen(lbuf) == 0)
continue;
} else if (strlen(lbuf) == 0) {
uuid = *olduuid;
goto done;
}
guid = PRT_menuid_to_guid(hex_octet(lbuf));
if (guid == NULL)
guid = lbuf;
uuid_from_string(guid, &uuid, &status);
if (status == uuid_s_ok)
goto done;
printf("'%s' has no associated UUID\n", lbuf);
}
done:
free(dflt);
return &uuid;
}