xref: /openbsd-src/sbin/fdisk/cmd.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: cmd.c,v 1.135 2021/07/21 20:26:30 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 <signal.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <uuid.h>
29 
30 #include "part.h"
31 #include "disk.h"
32 #include "misc.h"
33 #include "mbr.h"
34 #include "gpt.h"
35 #include "user.h"
36 #include "cmd.h"
37 
38 int		 gedit(const int);
39 int		 edit(const int, struct mbr *);
40 int		 gsetpid(const int);
41 int		 setpid(const int, struct mbr *);
42 int		 parsepn(const char *);
43 
44 int		 ask_num(const char *, int, int, int);
45 int		 ask_pid(const int, struct uuid *);
46 char		*ask_string(const char *, const char *);
47 
48 extern const unsigned char	manpage[];
49 extern const int		manpage_sz;
50 
51 int
52 Xreinit(char *args, struct mbr *mbr)
53 {
54 	struct dos_mbr		dos_mbr;
55 	int			dogpt;
56 
57 	dogpt = 0;
58 
59 	if (strncasecmp(args, "gpt", 3) == 0)
60 		dogpt = 1;
61 	else if (strncasecmp(args, "mbr", 3) == 0)
62 		dogpt = 0;
63 	else if (strlen(args) > 0) {
64 		printf("Unrecognized modifier '%s'\n", args);
65 		return CMD_CONT;
66 	}
67 
68 	MBR_make(&initial_mbr, &dos_mbr);
69 	MBR_parse(&dos_mbr, mbr->mbr_lba_self, mbr->mbr_lba_firstembr, mbr);
70 
71 	if (dogpt) {
72 		GPT_init(GHANDGP);
73 		GPT_print("s", TERSE);
74 	} else {
75 		MBR_init(mbr);
76 		MBR_print(mbr, "s");
77 	}
78 
79 	printf("Use 'write' to update disk.\n");
80 
81 	return CMD_DIRTY;
82 }
83 
84 int
85 Xdisk(char *args, struct mbr *mbr)
86 {
87 	int			maxcyl  = 1024;
88 	int			maxhead = 256;
89 	int			maxsec  = 63;
90 
91 	/* Print out disk info */
92 	DISK_printgeometry(args);
93 
94 #if defined (__powerpc__) || defined (__mips__)
95 	maxcyl  = 9999999;
96 	maxhead = 9999999;
97 	maxsec  = 9999999;
98 #endif
99 
100 	if (ask_yn("Change disk geometry?")) {
101 		disk.dk_cylinders = ask_num("BIOS Cylinders",
102 		    disk.dk_cylinders, 1, maxcyl);
103 		disk.dk_heads = ask_num("BIOS Heads",
104 		    disk.dk_heads, 1, maxhead);
105 		disk.dk_sectors = ask_num("BIOS Sectors",
106 		    disk.dk_sectors, 1, maxsec);
107 
108 		disk.dk_size = disk.dk_cylinders * disk.dk_heads * disk.dk_sectors;
109 	}
110 
111 	return CMD_CONT;
112 }
113 
114 int
115 Xswap(char *args, struct mbr *mbr)
116 {
117 	char			*from, *to;
118 	int			 pf, pt;
119 	struct prt		 pp;
120 	struct gpt_partition	 gg;
121 
122 	to = args;
123 	from = strsep(&to, " \t");
124 
125 	pt = parsepn(to);
126 	if (pt == -1)
127 		return CMD_CONT;
128 
129 	pf = parsepn(from);
130 	if (pf == -1)
131 		return CMD_CONT;
132 
133 	if (pt == pf) {
134 		printf("%d same partition as %d, doing nothing.\n", pt, pf);
135 		return CMD_CONT;
136 	}
137 
138 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
139 		gg = gp[pt];
140 		gp[pt] = gp[pf];
141 		gp[pf] = gg;
142 	} else {
143 		pp = mbr->mbr_prt[pt];
144 		mbr->mbr_prt[pt] = mbr->mbr_prt[pf];
145 		mbr->mbr_prt[pf] = pp;
146 	}
147 
148 	return CMD_DIRTY;
149 }
150 
151 int
152 gedit(const int pn)
153 {
154 	struct uuid		 oldtype;
155 	struct gpt_partition	*gg;
156 	char			*name;
157 	uint16_t		*utf;
158 	int			 i;
159 
160 	gg = &gp[pn];
161 	oldtype = gg->gp_type;
162 
163 	if (gsetpid(pn))
164 		return -1;
165 
166 	if (uuid_is_nil(&gg->gp_type, NULL)) {
167 		if (uuid_is_nil(&oldtype, NULL) == 0) {
168 			memset(gg, 0, sizeof(struct gpt_partition));
169 			printf("Partition %d is disabled.\n", pn);
170 		}
171 		return 0;
172 	}
173 
174 	if (GPT_get_lba_start(pn) == -1 ||
175 	    GPT_get_lba_end(pn) == -1) {
176 		return -1;
177 	}
178 
179 	name = ask_string("Partition name", utf16le_to_string(gg->gp_name));
180 	if (strlen(name) >= GPTPARTNAMESIZE) {
181 		printf("partition name must be < %d characters\n",
182 		    GPTPARTNAMESIZE);
183 		return -1;
184 	}
185 	/*
186 	 * N.B.: simple memcpy() could copy trash from static buf! This
187 	 * would create false positives for the partition having changed.
188 	 */
189 	utf = string_to_utf16le(name);
190 	for (i = 0; i < GPTPARTNAMESIZE; i++) {
191 		gg->gp_name[i] = utf[i];
192 		if (utf[i] == 0)
193 			break;
194 	}
195 	return 0;
196 }
197 
198 int
199 parsepn(const char *pnstr)
200 {
201 	const char		*errstr;
202 	int			 maxpn, pn;
203 
204 	if (pnstr == NULL) {
205 		printf("no partition number\n");
206 		return -1;
207 	}
208 
209 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
210 		maxpn = letoh32(gh.gh_part_num) - 1;
211 	else
212 		maxpn = NDOSPART - 1;
213 
214 	pn = strtonum(pnstr, 0, maxpn, &errstr);
215 	if (errstr) {
216 		printf("partition number is %s: %s\n", errstr, pnstr);
217 		return -1;
218 	}
219 
220 	return pn;
221 }
222 
223 int
224 edit(const int pn, struct mbr *mbr)
225 {
226 	struct prt		*pp;
227 	unsigned char		 oldid;
228 
229 	pp = &mbr->mbr_prt[pn];
230 	oldid = pp->prt_id;
231 
232 	if (setpid(pn, mbr))
233 		return -1;
234 
235 	if (pp->prt_id == DOSPTYP_UNUSED) {
236 		if (oldid != DOSPTYP_UNUSED) {
237 			memset(pp, 0, sizeof(*pp));
238 			printf("Partition %d is disabled.\n", pn);
239 		}
240 		return 0;
241 	}
242 
243 	if (ask_yn("Do you wish to edit in CHS mode?")) {
244 		pp->prt_scyl = ask_num("BIOS Starting cylinder", pp->prt_scyl,  0,
245 		    disk.dk_cylinders - 1);
246 		pp->prt_shead = ask_num("BIOS Starting head",    pp->prt_shead, 0,
247 		    disk.dk_heads - 1);
248 		pp->prt_ssect = ask_num("BIOS Starting sector",  pp->prt_ssect, 1,
249 		    disk.dk_sectors);
250 
251 		pp->prt_ecyl = ask_num("BIOS Ending cylinder",   pp->prt_ecyl,
252 		    pp->prt_scyl, disk.dk_cylinders - 1);
253 		pp->prt_ehead = ask_num("BIOS Ending head",      pp->prt_ehead,
254 		    (pp->prt_scyl == pp->prt_ecyl) ? pp->prt_shead : 0, disk.dk_heads - 1);
255 		pp->prt_esect = ask_num("BIOS Ending sector",    pp->prt_esect,
256 		    (pp->prt_scyl == pp->prt_ecyl && pp->prt_shead == pp->prt_ehead) ? pp->prt_ssect
257 		    : 1, disk.dk_sectors);
258 
259 		/* Fix up off/size values */
260 		PRT_fix_BN(pp, pn);
261 		/* Fix up CHS values for LBA */
262 		PRT_fix_CHS(pp);
263 	} else {
264 		pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0, disk.dk_size - 1);
265 		pp->prt_ns = getuint64("Partition size",   pp->prt_ns, 1,
266 		    disk.dk_size - pp->prt_bs);
267 
268 		/* Fix up CHS values */
269 		PRT_fix_CHS(pp);
270 	}
271 
272 	return 0;
273 }
274 
275 int
276 Xedit(char *args, struct mbr *mbr)
277 {
278 	struct gpt_partition	 oldgg;
279 	struct prt		 oldprt;
280 	struct gpt_partition	*gg;
281 	int			 pn;
282 
283 	pn = parsepn(args);
284 	if (pn == -1)
285 		return CMD_CONT;
286 
287 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
288 		oldgg = gp[pn];
289 		if (gedit(pn))
290 			gp[pn] = oldgg;
291 		else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg)))
292 			return CMD_DIRTY;
293 	} else {
294 		oldprt = mbr->mbr_prt[pn];
295 		if (edit(pn, mbr))
296 			mbr->mbr_prt[pn] = oldprt;
297 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
298 			return CMD_DIRTY;
299 	}
300 
301 	return CMD_CONT;
302 }
303 
304 int
305 gsetpid(const int pn)
306 {
307 	struct uuid		 gp_type, gp_guid;
308 	struct gpt_partition	*gg;
309 	int			 num, status;
310 
311 	gg = &gp[pn];
312 
313 	GPT_print_parthdr(TERSE);
314 	GPT_print_part(pn, "s", TERSE);
315 
316 	uuid_dec_le(&gg->gp_type, &gp_type);
317 	if (PRT_protected_guid(&gp_type)) {
318 		printf("can't edit partition type %s\n",
319 		    PRT_uuid_to_typename(&gp_type));
320 		return -1;
321 	}
322 
323 	/* Ask for partition type or GUID. */
324 	num = ask_pid(PRT_uuid_to_type(&gp_type), &gp_type);
325 	if (num <= 0xff)
326 		gp_type = *(PRT_type_to_uuid(num));
327 	if (PRT_protected_guid(&gp_type)) {
328 		printf("can't change partition type to %s\n",
329 		    PRT_uuid_to_typename(&gp_type));
330 		return -1;
331 	}
332 
333 	uuid_dec_le(&gg->gp_guid, &gp_guid);
334 	if (uuid_is_nil(&gp_guid, NULL)) {
335 		uuid_create(&gp_guid, &status);
336 		if (status != uuid_s_ok) {
337 			printf("could not create guid for partition\n");
338 			return -1;
339 		}
340 	}
341 
342 	uuid_enc_le(&gg->gp_type, &gp_type);
343 	uuid_enc_le(&gg->gp_guid, &gp_guid);
344 
345 	return 0;
346 }
347 
348 int
349 setpid(const int pn, struct mbr *mbr)
350 {
351 	struct prt		*pp;
352 
353 	pp = &mbr->mbr_prt[pn];
354 
355 	PRT_print(0, NULL, NULL);
356 	PRT_print(pn, pp, NULL);
357 
358 	pp->prt_id = ask_pid(pp->prt_id, NULL);
359 
360 	return 0;
361 }
362 
363 int
364 Xsetpid(char *args, struct mbr *mbr)
365 {
366 	struct gpt_partition	oldgg;
367 	struct prt		oldprt;
368 	int			pn;
369 
370 	pn = parsepn(args);
371 	if (pn == -1)
372 		return CMD_CONT;
373 
374 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
375 		oldgg = gp[pn];
376 		if (gsetpid(pn))
377 			gp[pn] = oldgg;
378 		else if (memcmp(&gp[pn], &oldgg, sizeof(oldgg)))
379 			return CMD_DIRTY;
380 	} else {
381 		oldprt = mbr->mbr_prt[pn];
382 		if (setpid(pn, mbr))
383 			mbr->mbr_prt[pn] = oldprt;
384 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
385 			return CMD_DIRTY;
386 	}
387 
388 	return CMD_CONT;
389 }
390 
391 int
392 Xselect(char *args, struct mbr *mbr)
393 {
394 	static uint64_t		lba_firstembr = 0;
395 	uint64_t		lba_self;
396 	int			pn;
397 
398 	pn = parsepn(args);
399 	if (pn == -1)
400 		return CMD_CONT;
401 
402 	lba_self = mbr->mbr_prt[pn].prt_bs;
403 
404 	/* Sanity checks */
405 	if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) &&
406 	    (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) {
407 		printf("Partition %d is not an extended partition.\n", pn);
408 		return CMD_CONT;
409 	}
410 
411 	if (lba_firstembr == 0)
412 		lba_firstembr = lba_self;
413 
414 	if (lba_self == 0) {
415 		printf("Loop to MBR (sector 0)! Not selected.\n");
416 		return CMD_CONT;
417 	} else {
418 		printf("Selected extended partition %d\n", pn);
419 		printf("New EMBR at offset %llu.\n", lba_self);
420 	}
421 
422 	/* Recursion is beautiful! */
423 	USER_edit(lba_self, lba_firstembr);
424 
425 	return CMD_CONT;
426 }
427 
428 int
429 Xprint(char *args, struct mbr *mbr)
430 {
431 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
432 		GPT_print(args, VERBOSE);
433 	else
434 		MBR_print(mbr, args);
435 
436 	return CMD_CONT;
437 }
438 
439 int
440 Xwrite(char *args, struct mbr *mbr)
441 {
442 	int			i, n;
443 
444 	for (i = 0, n = 0; i < NDOSPART; i++)
445 		if (mbr->mbr_prt[i].prt_id == 0xA6)
446 			n++;
447 	if (n >= 2) {
448 		warnx("MBR contains more than one OpenBSD partition!");
449 		if (!ask_yn("Write MBR anyway?"))
450 			return CMD_CONT;
451 	}
452 
453 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
454 		printf("Writing GPT.\n");
455 		if (GPT_write() == -1) {
456 			warn("error writing GPT");
457 			return CMD_CONT;
458 		}
459 	} else {
460 		printf("Writing MBR at offset %lld.\n", (long long)mbr->mbr_lba_self);
461 		if (MBR_write(mbr) == -1) {
462 			warn("error writing MBR");
463 			return CMD_CONT;
464 		}
465 		GPT_zap_headers();
466 	}
467 
468 	return CMD_CLEAN;
469 }
470 
471 int
472 Xquit(char *args, struct mbr *mbr)
473 {
474 	return CMD_SAVE;
475 }
476 
477 int
478 Xabort(char *args, struct mbr *mbr)
479 {
480 	exit(0);
481 }
482 
483 int
484 Xexit(char *args, struct mbr *mbr)
485 {
486 	return CMD_EXIT;
487 }
488 
489 int
490 Xhelp(char *args, struct mbr *mbr)
491 {
492 	char			 help[80];
493 	char			*mbrstr;
494 	int			 i;
495 
496 	for (i = 0; cmd_table[i].cmd_name != NULL; i++) {
497 		strlcpy(help, cmd_table[i].cmd_help, sizeof(help));
498 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
499 			if (cmd_table[i].cmd_gpt == 0)
500 				continue;
501 			mbrstr = strstr(help, "MBR");
502 			if (mbrstr)
503 				memcpy(mbrstr, "GPT", 3);
504 		}
505 		printf("\t%s\t\t%s\n", cmd_table[i].cmd_name, help);
506 	}
507 
508 	return CMD_CONT;
509 }
510 
511 int
512 Xupdate(char *args, struct mbr *mbr)
513 {
514 	memcpy(mbr->mbr_code, initial_mbr.mbr_code, sizeof(mbr->mbr_code));
515 	mbr->mbr_signature = DOSMBR_SIGNATURE;
516 	printf("Machine code updated.\n");
517 	return CMD_DIRTY;
518 }
519 
520 int
521 Xflag(char *args, struct mbr *mbr)
522 {
523 	const char		*errstr;
524 	char			*part, *flag;
525 	long long		 val = -1;
526 	int			 i, pn;
527 
528 	flag = args;
529 	part = strsep(&flag, " \t");
530 
531 	pn = parsepn(part);
532 	if (pn == -1)
533 		return CMD_CONT;
534 
535 	if (flag != NULL) {
536 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
537 			val = strtonum(flag, 0, INT64_MAX, &errstr);
538 		else
539 			val = strtonum(flag, 0, 0xff, &errstr);
540 		if (errstr) {
541 			printf("flag value is %s: %s.\n", errstr, flag);
542 			return CMD_CONT;
543 		}
544 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
545 			gp[pn].gp_attrs = htole64(val);
546 		else
547 			mbr->mbr_prt[pn].prt_flag = val;
548 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
549 	} else {
550 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
551 			for (i = 0; i < NGPTPARTITIONS; i++) {
552 				if (i == pn)
553 					gp[i].gp_attrs = htole64(GPTDOSACTIVE);
554 				else
555 					gp[i].gp_attrs = htole64(0);
556 			}
557 		} else {
558 			for (i = 0; i < NDOSPART; i++) {
559 				if (i == pn)
560 					mbr->mbr_prt[i].prt_flag = DOSACTIVE;
561 				else
562 					mbr->mbr_prt[i].prt_flag = 0x00;
563 			}
564 		}
565 		printf("Partition %d marked active.\n", pn);
566 	}
567 
568 	return CMD_DIRTY;
569 }
570 
571 int
572 Xmanual(char *args, struct mbr *mbr)
573 {
574 	char			*pager = "/usr/bin/less";
575 	char			*p;
576 	FILE			*f;
577 	sig_t			 opipe;
578 
579 	opipe = signal(SIGPIPE, SIG_IGN);
580 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
581 		pager = p;
582 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
583 		f = popen(p, "w");
584 		if (f) {
585 			fwrite(manpage, manpage_sz, 1, f);
586 			pclose(f);
587 		}
588 		free(p);
589 	}
590 
591 	signal(SIGPIPE, opipe);
592 
593 	return CMD_CONT;
594 }
595 
596 int
597 ask_num(const char *str, int dflt, int low, int high)
598 {
599 	char			 lbuf[100];
600 	const char		*errstr;
601 	int			 num;
602 
603 	if (dflt < low)
604 		dflt = low;
605 	else if (dflt > high)
606 		dflt = high;
607 
608 	do {
609 		printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
610 
611 		if (string_from_line(lbuf, sizeof(lbuf)))
612 			errx(1, "eof");
613 
614 		if (lbuf[0] == '\0') {
615 			num = dflt;
616 			errstr = NULL;
617 		} else {
618 			num = (int)strtonum(lbuf, low, high, &errstr);
619 			if (errstr)
620 				printf("%s is %s: %s.\n", str, errstr, lbuf);
621 		}
622 	} while (errstr);
623 
624 	return num;
625 }
626 
627 int
628 ask_pid(const int dflt, struct uuid *guid)
629 {
630 	char			lbuf[100], *cp;
631 	int			num = -1, status;
632 
633 	do {
634 		printf("Partition id ('0' to disable) [01 - FF]: [%X] ", dflt);
635 		printf("(? for help) ");
636 
637 		if (string_from_line(lbuf, sizeof(lbuf)))
638 			errx(1, "eof");
639 
640 		if (lbuf[0] == '?') {
641 			PRT_printall();
642 			continue;
643 		}
644 
645 		if (guid && strlen(lbuf) == UUID_STR_LEN) {
646 			uuid_from_string(lbuf, guid, &status);
647 			if (status == uuid_s_ok)
648 				return 0x100;
649 		}
650 
651 		/* Convert */
652 		cp = lbuf;
653 		num = strtol(lbuf, &cp, 16);
654 
655 		/* Make sure only number present */
656 		if (cp == lbuf)
657 			num = dflt;
658 		if (*cp != '\0') {
659 			printf("'%s' is not a valid number.\n", lbuf);
660 			num = -1;
661 		} else if (num == 0) {
662 			break;
663 		} else if (num < 0 || num > 0xff) {
664 			printf("'%x' is out of range.\n", num);
665 		}
666 	} while (num < 0 || num > 0xff);
667 
668 	return num;
669 }
670 
671 char *
672 ask_string(const char *prompt, const char *oval)
673 {
674 	static char		buf[UUID_STR_LEN + 1];
675 
676 	buf[0] = '\0';
677 	printf("%s: [%s] ", prompt, oval ? oval : "");
678 	if (string_from_line(buf, sizeof(buf)))
679 		errx(1, "eof");
680 
681 	if (buf[0] == '\0' && oval)
682 		strlcpy(buf, oval, sizeof(buf));
683 
684 	return buf;
685 }
686