xref: /openbsd/sbin/fdisk/cmd.c (revision 14c04d21)
1 /*	$OpenBSD: cmd.c,v 1.179 2023/11/18 15:42:09 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
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
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
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
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
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
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
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
295 gsetpid(const int pn)
296 {
297 	uint32_t		 status;
298 
299 	GPT_print_parthdr(TERSE);
300 	GPT_print_part(pn, "s", TERSE);
301 
302 	if (PRT_protected_uuid(&gp[pn].gp_type)) {
303 		printf("can't edit partition type %s\n",
304 		    PRT_uuid_to_desc(&gp[pn].gp_type));
305 		return -1;
306 	}
307 
308 	gp[pn].gp_type = *ask_uuid(&gp[pn].gp_type);
309 	if (PRT_protected_uuid(&gp[pn].gp_type)) {
310 		printf("can't change partition type to %s\n",
311 		    PRT_uuid_to_desc(&gp[pn].gp_type));
312 		return -1;
313 	}
314 
315 	if (uuid_is_nil(&gp[pn].gp_guid, NULL)) {
316 		uuid_create(&gp[pn].gp_guid, &status);
317 		if (status != uuid_s_ok) {
318 			printf("could not create guid for partition\n");
319 			return -1;
320 		}
321 	}
322 
323 	return 0;
324 }
325 
326 int
327 setpid(const int pn, struct mbr *mbr)
328 {
329 	struct prt		*pp;
330 
331 	pp = &mbr->mbr_prt[pn];
332 
333 	PRT_print_parthdr();
334 	PRT_print_part(pn, pp, "s");
335 
336 	pp->prt_id = ask_pid(pp->prt_id);
337 
338 	return 0;
339 }
340 
341 int
342 Xsetpid(const char *args, struct mbr *mbr)
343 {
344 	struct gpt_partition	oldgg;
345 	struct prt		oldprt;
346 	int			pn;
347 
348 	pn = parsepn(args);
349 	if (pn == -1)
350 		return CMD_CONT;
351 
352 	if (gh.gh_sig == GPTSIGNATURE) {
353 		oldgg = gp[pn];
354 		if (gsetpid(pn))
355 			gp[pn] = oldgg;
356 		else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
357 			return CMD_DIRTY;
358 	} else {
359 		oldprt = mbr->mbr_prt[pn];
360 		if (setpid(pn, mbr))
361 			mbr->mbr_prt[pn] = oldprt;
362 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
363 			return CMD_DIRTY;
364 	}
365 
366 	return CMD_CONT;
367 }
368 
369 int
370 Xselect(const char *args, struct mbr *mbr)
371 {
372 	static uint64_t		lba_firstembr = 0;
373 	uint64_t		lba_self;
374 	int			pn;
375 
376 	pn = parsepn(args);
377 	if (pn == -1)
378 		return CMD_CONT;
379 
380 	lba_self = mbr->mbr_prt[pn].prt_bs;
381 
382 	if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) &&
383 	    (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) {
384 		printf("Partition %d is not an extended partition.\n", pn);
385 		return CMD_CONT;
386 	}
387 
388 	if (lba_firstembr == 0)
389 		lba_firstembr = lba_self;
390 
391 	if (lba_self == 0) {
392 		printf("Loop to MBR (sector 0)! Not selected.\n");
393 		return CMD_CONT;
394 	} else {
395 		printf("Selected extended partition %d\n", pn);
396 		printf("New EMBR at offset %llu.\n", lba_self);
397 	}
398 
399 	USER_edit(lba_self, lba_firstembr);
400 
401 	return CMD_CONT;
402 }
403 
404 int
405 Xprint(const char *args, struct mbr *mbr)
406 {
407 	if (gh.gh_sig == GPTSIGNATURE)
408 		GPT_print(args, VERBOSE);
409 	else if (MBR_valid_prt(mbr))
410 		MBR_print(mbr, args);
411 	else {
412 		DISK_printgeometry("s");
413 		printf("Offset: %d\tSignature: 0x%X.\tNo MBR or GPT.\n",
414 		    DOSBBSECTOR, (int)mbr->mbr_signature);
415 	}
416 
417 	return CMD_CONT;
418 }
419 
420 int
421 Xwrite(const char *args, struct mbr *mbr)
422 {
423 	unsigned int		i, n;
424 
425 	for (i = 0, n = 0; i < nitems(mbr->mbr_prt); i++)
426 		if (mbr->mbr_prt[i].prt_id == DOSPTYP_OPENBSD)
427 			n++;
428 	if (n > 1) {
429 		warnx("MBR contains more than one OpenBSD partition!");
430 		if (ask_yn("Write MBR anyway?") == 0)
431 			return CMD_CONT;
432 	}
433 
434 	if (gh.gh_sig == GPTSIGNATURE) {
435 		printf("Writing GPT.\n");
436 		if (GPT_write() == -1) {
437 			warnx("error writing GPT");
438 			return CMD_CONT;
439 		}
440 	} else {
441 		printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self);
442 		if (MBR_write(mbr) == -1) {
443 			warnx("error writing MBR");
444 			return CMD_CONT;
445 		}
446 		GPT_zap_headers();
447 	}
448 
449 	return CMD_CLEAN;
450 }
451 
452 int
453 Xquit(const char *args, struct mbr *mbr)
454 {
455 	return CMD_QUIT;
456 }
457 
458 int
459 Xabort(const char *args, struct mbr *mbr)
460 {
461 	exit(0);
462 }
463 
464 int
465 Xexit(const char *args, struct mbr *mbr)
466 {
467 	return CMD_EXIT;
468 }
469 
470 int
471 Xhelp(const char *args, struct mbr *mbr)
472 {
473 	USER_help(mbr);
474 
475 	return CMD_CONT;
476 }
477 
478 int
479 Xupdate(const char *args, struct mbr *mbr)
480 {
481 	memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
482 	mbr->mbr_signature = DOSMBR_SIGNATURE;
483 	printf("Machine code updated.\n");
484 	return CMD_DIRTY;
485 }
486 
487 int
488 Xflag(const char *args, struct mbr *mbr)
489 {
490 	char			 lbuf[LINEBUFSZ];
491 	char			*part, *flag;
492 	uint64_t		 val;
493 	int			 i, pn;
494 
495 	if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
496 		printf("argument string too long\n");
497 		return CMD_CONT;
498 	}
499 
500 	flag = lbuf;
501 	part = strsep(&flag, WHITESPACE);
502 
503 	pn = parsepn(part);
504 	if (pn == -1)
505 		return CMD_CONT;
506 
507 	if (flag != NULL) {
508 		if (parseflag(flag, &val) == -1)
509 			return CMD_CONT;
510 		if (gh.gh_sig == GPTSIGNATURE)
511 			gp[pn].gp_attrs = val;
512 		else
513 			mbr->mbr_prt[pn].prt_flag = val;
514 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
515 	} else {
516 		if (gh.gh_sig == GPTSIGNATURE) {
517 			for (i = 0; i < gh.gh_part_num; i++) {
518 				if (i == pn)
519 					gp[i].gp_attrs = GPTPARTATTR_BOOTABLE;
520 				else
521 					gp[i].gp_attrs &= ~GPTPARTATTR_BOOTABLE;
522 			}
523 		} else {
524 			for (i = 0; i < nitems(mbr->mbr_prt); i++) {
525 				if (i == pn)
526 					mbr->mbr_prt[i].prt_flag = DOSACTIVE;
527 				else
528 					mbr->mbr_prt[i].prt_flag = 0x00;
529 			}
530 		}
531 		printf("Partition %d marked active.\n", pn);
532 	}
533 
534 	return CMD_DIRTY;
535 }
536 
537 int
538 Xmanual(const char *args, struct mbr *mbr)
539 {
540 	char			*pager = "/usr/bin/less";
541 	char			*p;
542 	FILE			*f;
543 	sig_t			 opipe;
544 
545 	opipe = signal(SIGPIPE, SIG_IGN);
546 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
547 		pager = p;
548 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
549 		f = popen(p, "w");
550 		if (f) {
551 			fwrite(manpage, manpage_sz, 1, f);
552 			pclose(f);
553 		}
554 		free(p);
555 	}
556 
557 	signal(SIGPIPE, opipe);
558 
559 	return CMD_CONT;
560 }
561 
562 int
563 ask_num(const char *str, int dflt, int low, int high)
564 {
565 	char			 lbuf[LINEBUFSZ];
566 	const char		*errstr;
567 	int			 num;
568 
569 	if (dflt < low)
570 		dflt = low;
571 	else if (dflt > high)
572 		dflt = high;
573 
574 	do {
575 		printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
576 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
577 
578 		if (lbuf[0] == '\0') {
579 			num = dflt;
580 			errstr = NULL;
581 		} else {
582 			num = (int)strtonum(lbuf, low, high, &errstr);
583 			if (errstr)
584 				printf("%s is %s: %s.\n", str, errstr, lbuf);
585 		}
586 	} while (errstr);
587 
588 	return num;
589 }
590 
591 int
592 ask_pid(const int dflt)
593 {
594 	char			lbuf[LINEBUFSZ];
595 	int			num;
596 
597 	for (;;) {
598 		printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt);
599 		printf("(? for help) ");
600 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
601 
602 		if (strlen(lbuf) == 0)
603 			return dflt;
604 		if (strcmp(lbuf, "?") == 0) {
605 			PRT_print_mbrmenu(lbuf, sizeof(lbuf));
606 			if (strlen(lbuf) == 0)
607 				continue;
608 		}
609 
610 		num = hex_octet(lbuf);
611 		if (num != -1)
612 			return num;
613 
614 		printf("'%s' is not a valid partition id.\n", lbuf);
615 	}
616 }
617 
618 const struct uuid *
619 ask_uuid(const struct uuid *olduuid)
620 {
621 	char			 lbuf[LINEBUFSZ];
622 	static struct uuid	 uuid;
623 	const char		*guid;
624 	char			*dflt;
625 	uint32_t		 status;
626 
627 	dflt = PRT_uuid_to_menudflt(olduuid);
628 	if (dflt == NULL) {
629 		if (asprintf(&dflt, "00") == -1) {
630 			warn("asprintf()");
631 			goto done;
632 		}
633 	}
634 
635 	for (;;) {
636 		printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ",
637 		    dflt);
638 		printf("(? for help) ");
639 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
640 
641 		if (strcmp(lbuf, "?") == 0) {
642 			PRT_print_gptmenu(lbuf, sizeof(lbuf));
643 			if (strlen(lbuf) == 0)
644 				continue;
645 		} else if (strlen(lbuf) == 0) {
646 			uuid = *olduuid;
647 			goto done;
648 		}
649 
650 		guid = PRT_menuid_to_guid(hex_octet(lbuf));
651 		if (guid == NULL)
652 			guid = lbuf;
653 
654 		uuid_from_string(guid, &uuid, &status);
655 		if (status == uuid_s_ok)
656 			goto done;
657 
658 		printf("'%s' has no associated UUID\n", lbuf);
659 	}
660 
661  done:
662 	free(dflt);
663 	return &uuid;
664 }
665