xref: /openbsd/sbin/fdisk/cmd.c (revision 01a159e4)
1 /*	$OpenBSD: cmd.c,v 1.180 2024/03/01 17:48:03 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/disklabel.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <uuid.h>
30 
31 #include "part.h"
32 #include "disk.h"
33 #include "misc.h"
34 #include "mbr.h"
35 #include "gpt.h"
36 #include "user.h"
37 #include "cmd.h"
38 
39 int		 gedit(const int);
40 int		 edit(const int, struct mbr *);
41 int		 gsetpid(const int);
42 int		 setpid(const int, struct mbr *);
43 int		 parsepn(const char *);
44 int		 parseflag(const char *, uint64_t *);
45 
46 int		 ask_num(const char *, int, int, int);
47 int		 ask_pid(const int);
48 const struct uuid *ask_uuid(const struct uuid *);
49 
50 extern const unsigned char	manpage[];
51 extern const int		manpage_sz;
52 
53 int
Xreinit(const char * args,struct mbr * mbr)54 Xreinit(const char *args, struct mbr *mbr)
55 {
56 	int			dogpt;
57 
58 	dogpt = 0;
59 
60 	if (strcasecmp(args, "gpt") == 0)
61 		dogpt = 1;
62 	else if (strcasecmp(args, "mbr") == 0)
63 		dogpt = 0;
64 	else if (strlen(args) > 0) {
65 		printf("Unrecognized modifier '%s'\n", args);
66 		return CMD_CONT;
67 	}
68 
69 	if (dogpt) {
70 		GPT_init(GHANDGP);
71 		GPT_print("s", TERSE);
72 	} else {
73 		MBR_init(mbr);
74 		MBR_print(mbr, "s");
75 	}
76 
77 	printf("Use 'write' to update disk.\n");
78 
79 	return CMD_DIRTY;
80 }
81 
82 int
Xswap(const char * args,struct mbr * mbr)83 Xswap(const char *args, struct mbr *mbr)
84 {
85 	char			 lbuf[LINEBUFSZ];
86 	char			*from, *to;
87 	int			 pf, pt;
88 	struct prt		 pp;
89 	struct gpt_partition	 gg;
90 
91 	if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
92 		printf("argument string too long\n");
93 		return CMD_CONT;
94 	}
95 
96 	to = lbuf;
97 	from = strsep(&to, WHITESPACE);
98 
99 	pt = parsepn(to);
100 	if (pt == -1)
101 		return CMD_CONT;
102 
103 	pf = parsepn(from);
104 	if (pf == -1)
105 		return CMD_CONT;
106 
107 	if (pt == pf) {
108 		printf("%d same partition as %d, doing nothing.\n", pt, pf);
109 		return CMD_CONT;
110 	}
111 
112 	if (gh.gh_sig == GPTSIGNATURE) {
113 		gg = gp[pt];
114 		gp[pt] = gp[pf];
115 		gp[pf] = gg;
116 	} else {
117 		pp = mbr->mbr_prt[pt];
118 		mbr->mbr_prt[pt] = mbr->mbr_prt[pf];
119 		mbr->mbr_prt[pf] = pp;
120 	}
121 
122 	return CMD_DIRTY;
123 }
124 
125 int
gedit(const int pn)126 gedit(const int pn)
127 {
128 	struct uuid		 oldtype;
129 
130 	oldtype = gp[pn].gp_type;
131 
132 	if (gsetpid(pn))
133 		return -1;
134 
135 	if (uuid_is_nil(&gp[pn].gp_type, NULL)) {
136 		if (uuid_is_nil(&oldtype, NULL) == 0) {
137 			memset(&gp[pn], 0, sizeof(gp[pn]));
138 			printf("Partition %d is disabled.\n", pn);
139 		}
140 		return 0;
141 	}
142 
143 	if (GPT_get_lba_start(pn) == -1 ||
144 	    GPT_get_lba_end(pn) == -1 ||
145 	    GPT_get_name(pn) == -1) {
146 		return -1;
147 	}
148 
149 	return 0;
150 }
151 
152 int
parsepn(const char * pnstr)153 parsepn(const char *pnstr)
154 {
155 	const char		*errstr;
156 	int			 maxpn, pn;
157 
158 	if (pnstr == NULL) {
159 		printf("no partition number\n");
160 		return -1;
161 	}
162 
163 	if (gh.gh_sig == GPTSIGNATURE)
164 		maxpn = gh.gh_part_num - 1;
165 	else
166 		maxpn = NDOSPART - 1;
167 
168 	pn = strtonum(pnstr, 0, maxpn, &errstr);
169 	if (errstr) {
170 		printf("partition number is %s: %s\n", errstr, pnstr);
171 		return -1;
172 	}
173 
174 	return pn;
175 }
176 
177 int
parseflag(const char * flagstr,uint64_t * flagvalue)178 parseflag(const char *flagstr, uint64_t *flagvalue)
179 {
180 	const char		*errstr;
181 	char			*ep;
182 	uint64_t		 val;
183 
184 	flagstr += strspn(flagstr, WHITESPACE);
185 	if (flagstr[0] == '0' && (flagstr[1] == 'x' || flagstr[1] == 'X')) {
186 		errno = 0;
187 		val = strtoull(flagstr, &ep, 16);
188 		if (errno || ep == flagstr || *ep != '\0' ||
189 		    (gh.gh_sig != GPTSIGNATURE && val > 0xff)) {
190 			printf("flag value is invalid: %s\n", flagstr);
191 			return -1;
192 		}
193 		goto done;
194 	}
195 
196 	if (gh.gh_sig == GPTSIGNATURE)
197 		val = strtonum(flagstr, 0, INT64_MAX, &errstr);
198 	else
199 		val = strtonum(flagstr, 0, 0xff, &errstr);
200 	if (errstr) {
201 		printf("flag value is %s: %s\n", errstr, flagstr);
202 		return -1;
203 	}
204 
205  done:
206 	*flagvalue = val;
207 	return 0;
208 }
209 
210 int
edit(const int pn,struct mbr * mbr)211 edit(const int pn, struct mbr *mbr)
212 {
213 	struct chs		 start, end;
214 	struct prt		*pp;
215 	uint64_t		 track;
216 	unsigned char		 oldid;
217 
218 	pp = &mbr->mbr_prt[pn];
219 	oldid = pp->prt_id;
220 
221 	if (setpid(pn, mbr))
222 		return -1;
223 
224 	if (pp->prt_id == DOSPTYP_UNUSED) {
225 		if (oldid != DOSPTYP_UNUSED) {
226 			memset(pp, 0, sizeof(*pp));
227 			printf("Partition %d is disabled.\n", pn);
228 		}
229 		return 0;
230 	}
231 
232 	if (ask_yn("Do you wish to edit in CHS mode?")) {
233 		PRT_lba_to_chs(pp, &start, &end);
234 		start.chs_cyl = ask_num("BIOS Starting cylinder", start.chs_cyl,
235 		    0, disk.dk_cylinders - 1);
236 		start.chs_head = ask_num("BIOS Starting head", start.chs_head,
237 		    0, disk.dk_heads - 1);
238 		start.chs_sect = ask_num("BIOS Starting sector", start.chs_sect,
239 		    1, disk.dk_sectors);
240 
241 		end.chs_cyl = ask_num("BIOS Ending cylinder", end.chs_cyl,
242 		    start.chs_cyl, disk.dk_cylinders - 1);
243 		end.chs_head = ask_num("BIOS Ending head", end.chs_head,
244 		    (start.chs_cyl == end.chs_cyl) ? start.chs_head : 0,
245 		    disk.dk_heads - 1);
246 		end.chs_sect = ask_num("BIOS Ending sector", end.chs_sect,
247 		    (start.chs_cyl == end.chs_cyl && start.chs_head ==
248 		    end.chs_head) ? start.chs_sect : 1, disk.dk_sectors);
249 
250 		/* The ATA/ATAPI spec says LBA = (C × HPC + H) × SPT + (S − 1) */
251 		track = start.chs_cyl * disk.dk_heads + start.chs_head;
252 		pp->prt_bs = track * disk.dk_sectors + (start.chs_sect - 1);
253 		track = end.chs_cyl * disk.dk_heads + end.chs_head;
254 		pp->prt_ns = track * disk.dk_sectors + (end.chs_sect - 1) -
255 		    pp->prt_bs + 1;
256 	} else {
257 		pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0,
258 		    disk.dk_size - 1);
259 		pp->prt_ns = getuint64("Partition size",   pp->prt_ns, 1,
260 		    disk.dk_size - pp->prt_bs);
261 	}
262 
263 	return 0;
264 }
265 
266 int
Xedit(const char * args,struct mbr * mbr)267 Xedit(const char *args, struct mbr *mbr)
268 {
269 	struct gpt_partition	 oldgg;
270 	struct prt		 oldprt;
271 	int			 pn;
272 
273 	pn = parsepn(args);
274 	if (pn == -1)
275 		return CMD_CONT;
276 
277 	if (gh.gh_sig == GPTSIGNATURE) {
278 		oldgg = gp[pn];
279 		if (gedit(pn))
280 			gp[pn] = oldgg;
281 		else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
282 			return CMD_DIRTY;
283 	} else {
284 		oldprt = mbr->mbr_prt[pn];
285 		if (edit(pn, mbr))
286 			mbr->mbr_prt[pn] = oldprt;
287 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
288 			return CMD_DIRTY;
289 	}
290 
291 	return CMD_CONT;
292 }
293 
294 int
gsetpid(const int pn)295 gsetpid(const int pn)
296 {
297 	int32_t			 is_nil;
298 	uint32_t		 status;
299 
300 	GPT_print_parthdr(TERSE);
301 	GPT_print_part(pn, "s", TERSE);
302 
303 	if (PRT_protected_uuid(&gp[pn].gp_type)) {
304 		printf("can't edit partition type %s\n",
305 		    PRT_uuid_to_desc(&gp[pn].gp_type));
306 		return -1;
307 	}
308 
309 	is_nil = uuid_is_nil(&gp[pn].gp_type, NULL);
310 	gp[pn].gp_type = *ask_uuid(&gp[pn].gp_type);
311 	if (PRT_protected_uuid(&gp[pn].gp_type) && is_nil == 0) {
312 		printf("can't change partition type to %s\n",
313 		    PRT_uuid_to_desc(&gp[pn].gp_type));
314 		return -1;
315 	}
316 
317 	if (uuid_is_nil(&gp[pn].gp_guid, NULL)) {
318 		uuid_create(&gp[pn].gp_guid, &status);
319 		if (status != uuid_s_ok) {
320 			printf("could not create guid for partition\n");
321 			return -1;
322 		}
323 	}
324 
325 	return 0;
326 }
327 
328 int
setpid(const int pn,struct mbr * mbr)329 setpid(const int pn, struct mbr *mbr)
330 {
331 	struct prt		*pp;
332 
333 	pp = &mbr->mbr_prt[pn];
334 
335 	PRT_print_parthdr();
336 	PRT_print_part(pn, pp, "s");
337 
338 	pp->prt_id = ask_pid(pp->prt_id);
339 
340 	return 0;
341 }
342 
343 int
Xsetpid(const char * args,struct mbr * mbr)344 Xsetpid(const char *args, struct mbr *mbr)
345 {
346 	struct gpt_partition	oldgg;
347 	struct prt		oldprt;
348 	int			pn;
349 
350 	pn = parsepn(args);
351 	if (pn == -1)
352 		return CMD_CONT;
353 
354 	if (gh.gh_sig == GPTSIGNATURE) {
355 		oldgg = gp[pn];
356 		if (gsetpid(pn))
357 			gp[pn] = oldgg;
358 		else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
359 			return CMD_DIRTY;
360 	} else {
361 		oldprt = mbr->mbr_prt[pn];
362 		if (setpid(pn, mbr))
363 			mbr->mbr_prt[pn] = oldprt;
364 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
365 			return CMD_DIRTY;
366 	}
367 
368 	return CMD_CONT;
369 }
370 
371 int
Xselect(const char * args,struct mbr * mbr)372 Xselect(const char *args, struct mbr *mbr)
373 {
374 	static uint64_t		lba_firstembr = 0;
375 	uint64_t		lba_self;
376 	int			pn;
377 
378 	pn = parsepn(args);
379 	if (pn == -1)
380 		return CMD_CONT;
381 
382 	lba_self = mbr->mbr_prt[pn].prt_bs;
383 
384 	if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) &&
385 	    (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) {
386 		printf("Partition %d is not an extended partition.\n", pn);
387 		return CMD_CONT;
388 	}
389 
390 	if (lba_firstembr == 0)
391 		lba_firstembr = lba_self;
392 
393 	if (lba_self == 0) {
394 		printf("Loop to MBR (sector 0)! Not selected.\n");
395 		return CMD_CONT;
396 	} else {
397 		printf("Selected extended partition %d\n", pn);
398 		printf("New EMBR at offset %llu.\n", lba_self);
399 	}
400 
401 	USER_edit(lba_self, lba_firstembr);
402 
403 	return CMD_CONT;
404 }
405 
406 int
Xprint(const char * args,struct mbr * mbr)407 Xprint(const char *args, struct mbr *mbr)
408 {
409 	if (gh.gh_sig == GPTSIGNATURE)
410 		GPT_print(args, VERBOSE);
411 	else if (MBR_valid_prt(mbr))
412 		MBR_print(mbr, args);
413 	else {
414 		DISK_printgeometry("s");
415 		printf("Offset: %d\tSignature: 0x%X.\tNo MBR or GPT.\n",
416 		    DOSBBSECTOR, (int)mbr->mbr_signature);
417 	}
418 
419 	return CMD_CONT;
420 }
421 
422 int
Xwrite(const char * args,struct mbr * mbr)423 Xwrite(const char *args, struct mbr *mbr)
424 {
425 	unsigned int		i, n;
426 
427 	for (i = 0, n = 0; i < nitems(mbr->mbr_prt); i++)
428 		if (mbr->mbr_prt[i].prt_id == DOSPTYP_OPENBSD)
429 			n++;
430 	if (n > 1) {
431 		warnx("MBR contains more than one OpenBSD partition!");
432 		if (ask_yn("Write MBR anyway?") == 0)
433 			return CMD_CONT;
434 	}
435 
436 	if (gh.gh_sig == GPTSIGNATURE) {
437 		printf("Writing GPT.\n");
438 		if (GPT_write() == -1) {
439 			warnx("error writing GPT");
440 			return CMD_CONT;
441 		}
442 	} else {
443 		printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self);
444 		if (MBR_write(mbr) == -1) {
445 			warnx("error writing MBR");
446 			return CMD_CONT;
447 		}
448 		GPT_zap_headers();
449 	}
450 
451 	return CMD_CLEAN;
452 }
453 
454 int
Xquit(const char * args,struct mbr * mbr)455 Xquit(const char *args, struct mbr *mbr)
456 {
457 	return CMD_QUIT;
458 }
459 
460 int
Xabort(const char * args,struct mbr * mbr)461 Xabort(const char *args, struct mbr *mbr)
462 {
463 	exit(0);
464 }
465 
466 int
Xexit(const char * args,struct mbr * mbr)467 Xexit(const char *args, struct mbr *mbr)
468 {
469 	return CMD_EXIT;
470 }
471 
472 int
Xhelp(const char * args,struct mbr * mbr)473 Xhelp(const char *args, struct mbr *mbr)
474 {
475 	USER_help(mbr);
476 
477 	return CMD_CONT;
478 }
479 
480 int
Xupdate(const char * args,struct mbr * mbr)481 Xupdate(const char *args, struct mbr *mbr)
482 {
483 	memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
484 	mbr->mbr_signature = DOSMBR_SIGNATURE;
485 	printf("Machine code updated.\n");
486 	return CMD_DIRTY;
487 }
488 
489 int
Xflag(const char * args,struct mbr * mbr)490 Xflag(const char *args, struct mbr *mbr)
491 {
492 	char			 lbuf[LINEBUFSZ];
493 	char			*part, *flag;
494 	uint64_t		 val;
495 	int			 i, pn;
496 
497 	if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
498 		printf("argument string too long\n");
499 		return CMD_CONT;
500 	}
501 
502 	flag = lbuf;
503 	part = strsep(&flag, WHITESPACE);
504 
505 	pn = parsepn(part);
506 	if (pn == -1)
507 		return CMD_CONT;
508 
509 	if (flag != NULL) {
510 		if (parseflag(flag, &val) == -1)
511 			return CMD_CONT;
512 		if (gh.gh_sig == GPTSIGNATURE)
513 			gp[pn].gp_attrs = val;
514 		else
515 			mbr->mbr_prt[pn].prt_flag = val;
516 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
517 	} else {
518 		if (gh.gh_sig == GPTSIGNATURE) {
519 			for (i = 0; i < gh.gh_part_num; i++) {
520 				if (i == pn)
521 					gp[i].gp_attrs = GPTPARTATTR_BOOTABLE;
522 				else
523 					gp[i].gp_attrs &= ~GPTPARTATTR_BOOTABLE;
524 			}
525 		} else {
526 			for (i = 0; i < nitems(mbr->mbr_prt); i++) {
527 				if (i == pn)
528 					mbr->mbr_prt[i].prt_flag = DOSACTIVE;
529 				else
530 					mbr->mbr_prt[i].prt_flag = 0x00;
531 			}
532 		}
533 		printf("Partition %d marked active.\n", pn);
534 	}
535 
536 	return CMD_DIRTY;
537 }
538 
539 int
Xmanual(const char * args,struct mbr * mbr)540 Xmanual(const char *args, struct mbr *mbr)
541 {
542 	char			*pager = "/usr/bin/less";
543 	char			*p;
544 	FILE			*f;
545 	sig_t			 opipe;
546 
547 	opipe = signal(SIGPIPE, SIG_IGN);
548 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
549 		pager = p;
550 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
551 		f = popen(p, "w");
552 		if (f) {
553 			fwrite(manpage, manpage_sz, 1, f);
554 			pclose(f);
555 		}
556 		free(p);
557 	}
558 
559 	signal(SIGPIPE, opipe);
560 
561 	return CMD_CONT;
562 }
563 
564 int
ask_num(const char * str,int dflt,int low,int high)565 ask_num(const char *str, int dflt, int low, int high)
566 {
567 	char			 lbuf[LINEBUFSZ];
568 	const char		*errstr;
569 	int			 num;
570 
571 	if (dflt < low)
572 		dflt = low;
573 	else if (dflt > high)
574 		dflt = high;
575 
576 	do {
577 		printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
578 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
579 
580 		if (lbuf[0] == '\0') {
581 			num = dflt;
582 			errstr = NULL;
583 		} else {
584 			num = (int)strtonum(lbuf, low, high, &errstr);
585 			if (errstr)
586 				printf("%s is %s: %s.\n", str, errstr, lbuf);
587 		}
588 	} while (errstr);
589 
590 	return num;
591 }
592 
593 int
ask_pid(const int dflt)594 ask_pid(const int dflt)
595 {
596 	char			lbuf[LINEBUFSZ];
597 	int			num;
598 
599 	for (;;) {
600 		printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt);
601 		printf("(? for help) ");
602 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
603 
604 		if (strlen(lbuf) == 0)
605 			return dflt;
606 		if (strcmp(lbuf, "?") == 0) {
607 			PRT_print_mbrmenu(lbuf, sizeof(lbuf));
608 			if (strlen(lbuf) == 0)
609 				continue;
610 		}
611 
612 		num = hex_octet(lbuf);
613 		if (num != -1)
614 			return num;
615 
616 		printf("'%s' is not a valid partition id.\n", lbuf);
617 	}
618 }
619 
620 const struct uuid *
ask_uuid(const struct uuid * olduuid)621 ask_uuid(const struct uuid *olduuid)
622 {
623 	char			 lbuf[LINEBUFSZ];
624 	static struct uuid	 uuid;
625 	const char		*guid;
626 	char			*dflt;
627 	uint32_t		 status;
628 
629 	dflt = PRT_uuid_to_menudflt(olduuid);
630 	if (dflt == NULL) {
631 		if (asprintf(&dflt, "00") == -1) {
632 			warn("asprintf()");
633 			goto done;
634 		}
635 	}
636 
637 	for (;;) {
638 		printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ",
639 		    dflt);
640 		printf("(? for help) ");
641 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
642 
643 		if (strcmp(lbuf, "?") == 0) {
644 			PRT_print_gptmenu(lbuf, sizeof(lbuf));
645 			if (strlen(lbuf) == 0)
646 				continue;
647 		} else if (strlen(lbuf) == 0) {
648 			uuid = *olduuid;
649 			goto done;
650 		}
651 
652 		guid = PRT_menuid_to_guid(hex_octet(lbuf));
653 		if (guid == NULL)
654 			guid = lbuf;
655 
656 		uuid_from_string(guid, &uuid, &status);
657 		if (status == uuid_s_ok)
658 			goto done;
659 
660 		printf("'%s' has no associated UUID\n", lbuf);
661 	}
662 
663  done:
664 	free(dflt);
665 	return &uuid;
666 }
667