src/sbin/fdisk/cmd.c

667 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* $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;
}