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