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