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