xref: /openbsd/sbin/fdisk/cmd.c (revision 85135147)
1 /*	$OpenBSD: cmd.c,v 1.177 2023/11/10 15:41:11 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 	if (flagstr[0] == '0' && (flagstr[1] == 'x' || flagstr[1] == 'X')) {
185 		errno = 0;
186 		val = strtoull(flagstr, &ep, 16);
187 		if (errno || ep == flagstr || *ep != '\0' ||
188 		    (gh.gh_sig != GPTSIGNATURE && val > 0xff)) {
189 			printf("flag value is invalid: %s\n", flagstr);
190 			return -1;
191 		}
192 		goto done;
193 	}
194 
195 	if (gh.gh_sig == GPTSIGNATURE)
196 		val = strtonum(flagstr, 0, INT64_MAX, &errstr);
197 	else
198 		val = strtonum(flagstr, 0, 0xff, &errstr);
199 	if (errstr) {
200 		printf("flag value is %s: %s\n", errstr, flagstr);
201 		return -1;
202 	}
203 
204  done:
205 	*flagvalue = val;
206 	return 0;
207 }
208 
209 int
210 edit(const int pn, struct mbr *mbr)
211 {
212 	struct chs		 start, end;
213 	struct prt		*pp;
214 	uint64_t		 track;
215 	unsigned char		 oldid;
216 
217 	pp = &mbr->mbr_prt[pn];
218 	oldid = pp->prt_id;
219 
220 	if (setpid(pn, mbr))
221 		return -1;
222 
223 	if (pp->prt_id == DOSPTYP_UNUSED) {
224 		if (oldid != DOSPTYP_UNUSED) {
225 			memset(pp, 0, sizeof(*pp));
226 			printf("Partition %d is disabled.\n", pn);
227 		}
228 		return 0;
229 	}
230 
231 	if (ask_yn("Do you wish to edit in CHS mode?")) {
232 		PRT_lba_to_chs(pp, &start, &end);
233 		start.chs_cyl = ask_num("BIOS Starting cylinder", start.chs_cyl,
234 		    0, disk.dk_cylinders - 1);
235 		start.chs_head = ask_num("BIOS Starting head", start.chs_head,
236 		    0, disk.dk_heads - 1);
237 		start.chs_sect = ask_num("BIOS Starting sector", start.chs_sect,
238 		    1, disk.dk_sectors);
239 
240 		end.chs_cyl = ask_num("BIOS Ending cylinder", end.chs_cyl,
241 		    start.chs_cyl, disk.dk_cylinders - 1);
242 		end.chs_head = ask_num("BIOS Ending head", end.chs_head,
243 		    (start.chs_cyl == end.chs_cyl) ? start.chs_head : 0,
244 		    disk.dk_heads - 1);
245 		end.chs_sect = ask_num("BIOS Ending sector", end.chs_sect,
246 		    (start.chs_cyl == end.chs_cyl && start.chs_head ==
247 		    end.chs_head) ? start.chs_sect : 1, disk.dk_sectors);
248 
249 		/* The ATA/ATAPI spec says LBA = (C × HPC + H) × SPT + (S − 1) */
250 		track = start.chs_cyl * disk.dk_heads + start.chs_head;
251 		pp->prt_bs = track * disk.dk_sectors + (start.chs_sect - 1);
252 		track = end.chs_cyl * disk.dk_heads + end.chs_head;
253 		pp->prt_ns = track * disk.dk_sectors + (end.chs_sect - 1) -
254 		    pp->prt_bs + 1;
255 	} else {
256 		pp->prt_bs = getuint64("Partition offset", pp->prt_bs, 0,
257 		    disk.dk_size - 1);
258 		pp->prt_ns = getuint64("Partition size",   pp->prt_ns, 1,
259 		    disk.dk_size - pp->prt_bs);
260 	}
261 
262 	return 0;
263 }
264 
265 int
266 Xedit(const char *args, struct mbr *mbr)
267 {
268 	struct gpt_partition	 oldgg;
269 	struct prt		 oldprt;
270 	int			 pn;
271 
272 	pn = parsepn(args);
273 	if (pn == -1)
274 		return CMD_CONT;
275 
276 	if (gh.gh_sig == GPTSIGNATURE) {
277 		oldgg = gp[pn];
278 		if (gedit(pn))
279 			gp[pn] = oldgg;
280 		else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
281 			return CMD_DIRTY;
282 	} else {
283 		oldprt = mbr->mbr_prt[pn];
284 		if (edit(pn, mbr))
285 			mbr->mbr_prt[pn] = oldprt;
286 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
287 			return CMD_DIRTY;
288 	}
289 
290 	return CMD_CONT;
291 }
292 
293 int
294 gsetpid(const int pn)
295 {
296 	uint32_t		 status;
297 
298 	GPT_print_parthdr(TERSE);
299 	GPT_print_part(pn, "s", TERSE);
300 
301 	if (PRT_protected_uuid(&gp[pn].gp_type)) {
302 		printf("can't edit partition type %s\n",
303 		    PRT_uuid_to_desc(&gp[pn].gp_type));
304 		return -1;
305 	}
306 
307 	gp[pn].gp_type = *ask_uuid(&gp[pn].gp_type);
308 	if (PRT_protected_uuid(&gp[pn].gp_type)) {
309 		printf("can't change partition type to %s\n",
310 		    PRT_uuid_to_desc(&gp[pn].gp_type));
311 		return -1;
312 	}
313 
314 	if (uuid_is_nil(&gp[pn].gp_guid, NULL)) {
315 		uuid_create(&gp[pn].gp_guid, &status);
316 		if (status != uuid_s_ok) {
317 			printf("could not create guid for partition\n");
318 			return -1;
319 		}
320 	}
321 
322 	return 0;
323 }
324 
325 int
326 setpid(const int pn, struct mbr *mbr)
327 {
328 	struct prt		*pp;
329 
330 	pp = &mbr->mbr_prt[pn];
331 
332 	PRT_print_parthdr();
333 	PRT_print_part(pn, pp, "s");
334 
335 	pp->prt_id = ask_pid(pp->prt_id);
336 
337 	return 0;
338 }
339 
340 int
341 Xsetpid(const char *args, struct mbr *mbr)
342 {
343 	struct gpt_partition	oldgg;
344 	struct prt		oldprt;
345 	int			pn;
346 
347 	pn = parsepn(args);
348 	if (pn == -1)
349 		return CMD_CONT;
350 
351 	if (gh.gh_sig == GPTSIGNATURE) {
352 		oldgg = gp[pn];
353 		if (gsetpid(pn))
354 			gp[pn] = oldgg;
355 		else if (memcmp(&gp[pn], &oldgg, sizeof(gp[pn])))
356 			return CMD_DIRTY;
357 	} else {
358 		oldprt = mbr->mbr_prt[pn];
359 		if (setpid(pn, mbr))
360 			mbr->mbr_prt[pn] = oldprt;
361 		else if (memcmp(&mbr->mbr_prt[pn], &oldprt, sizeof(oldprt)))
362 			return CMD_DIRTY;
363 	}
364 
365 	return CMD_CONT;
366 }
367 
368 int
369 Xselect(const char *args, struct mbr *mbr)
370 {
371 	static uint64_t		lba_firstembr = 0;
372 	uint64_t		lba_self;
373 	int			pn;
374 
375 	pn = parsepn(args);
376 	if (pn == -1)
377 		return CMD_CONT;
378 
379 	lba_self = mbr->mbr_prt[pn].prt_bs;
380 
381 	if ((mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTEND) &&
382 	    (mbr->mbr_prt[pn].prt_id != DOSPTYP_EXTENDL)) {
383 		printf("Partition %d is not an extended partition.\n", pn);
384 		return CMD_CONT;
385 	}
386 
387 	if (lba_firstembr == 0)
388 		lba_firstembr = lba_self;
389 
390 	if (lba_self == 0) {
391 		printf("Loop to MBR (sector 0)! Not selected.\n");
392 		return CMD_CONT;
393 	} else {
394 		printf("Selected extended partition %d\n", pn);
395 		printf("New EMBR at offset %llu.\n", lba_self);
396 	}
397 
398 	USER_edit(lba_self, lba_firstembr);
399 
400 	return CMD_CONT;
401 }
402 
403 int
404 Xprint(const char *args, struct mbr *mbr)
405 {
406 	if (gh.gh_sig == GPTSIGNATURE)
407 		GPT_print(args, VERBOSE);
408 	else if (MBR_valid_prt(mbr))
409 		MBR_print(mbr, args);
410 	else {
411 		DISK_printgeometry("s");
412 		printf("Offset: %d\tSignature: 0x%X.\tNo MBR or GPT.\n",
413 		    DOSBBSECTOR, (int)mbr->mbr_signature);
414 	}
415 
416 	return CMD_CONT;
417 }
418 
419 int
420 Xwrite(const char *args, struct mbr *mbr)
421 {
422 	unsigned int		i, n;
423 
424 	for (i = 0, n = 0; i < nitems(mbr->mbr_prt); i++)
425 		if (mbr->mbr_prt[i].prt_id == DOSPTYP_OPENBSD)
426 			n++;
427 	if (n > 1) {
428 		warnx("MBR contains more than one OpenBSD partition!");
429 		if (ask_yn("Write MBR anyway?") == 0)
430 			return CMD_CONT;
431 	}
432 
433 	if (gh.gh_sig == GPTSIGNATURE) {
434 		printf("Writing GPT.\n");
435 		if (GPT_write() == -1) {
436 			warnx("error writing GPT");
437 			return CMD_CONT;
438 		}
439 	} else {
440 		printf("Writing MBR at offset %llu.\n", mbr->mbr_lba_self);
441 		if (MBR_write(mbr) == -1) {
442 			warnx("error writing MBR");
443 			return CMD_CONT;
444 		}
445 		GPT_zap_headers();
446 	}
447 
448 	return CMD_CLEAN;
449 }
450 
451 int
452 Xquit(const char *args, struct mbr *mbr)
453 {
454 	return CMD_QUIT;
455 }
456 
457 int
458 Xabort(const char *args, struct mbr *mbr)
459 {
460 	exit(0);
461 }
462 
463 int
464 Xexit(const char *args, struct mbr *mbr)
465 {
466 	return CMD_EXIT;
467 }
468 
469 int
470 Xhelp(const char *args, struct mbr *mbr)
471 {
472 	USER_help(mbr);
473 
474 	return CMD_CONT;
475 }
476 
477 int
478 Xupdate(const char *args, struct mbr *mbr)
479 {
480 	memcpy(mbr->mbr_code, default_dmbr.dmbr_boot, sizeof(mbr->mbr_code));
481 	mbr->mbr_signature = DOSMBR_SIGNATURE;
482 	printf("Machine code updated.\n");
483 	return CMD_DIRTY;
484 }
485 
486 int
487 Xflag(const char *args, struct mbr *mbr)
488 {
489 	char			 lbuf[LINEBUFSZ];
490 	char			*part, *flag;
491 	uint64_t		 val;
492 	int			 i, pn;
493 
494 	if (strlcpy(lbuf, args, sizeof(lbuf)) >= sizeof(lbuf)) {
495 		printf("argument string too long\n");
496 		return CMD_CONT;
497 	}
498 
499 	flag = lbuf;
500 	part = strsep(&flag, WHITESPACE);
501 
502 	pn = parsepn(part);
503 	if (pn == -1)
504 		return CMD_CONT;
505 
506 	if (flag != NULL) {
507 		if (parseflag(flag, &val) == -1)
508 			return CMD_CONT;
509 		if (gh.gh_sig == GPTSIGNATURE)
510 			gp[pn].gp_attrs = val;
511 		else
512 			mbr->mbr_prt[pn].prt_flag = val;
513 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
514 	} else {
515 		if (gh.gh_sig == GPTSIGNATURE) {
516 			for (i = 0; i < gh.gh_part_num; i++) {
517 				if (i == pn)
518 					gp[i].gp_attrs = GPTPARTATTR_BOOTABLE;
519 				else
520 					gp[i].gp_attrs = 0;
521 			}
522 		} else {
523 			for (i = 0; i < nitems(mbr->mbr_prt); i++) {
524 				if (i == pn)
525 					mbr->mbr_prt[i].prt_flag = DOSACTIVE;
526 				else
527 					mbr->mbr_prt[i].prt_flag = 0x00;
528 			}
529 		}
530 		printf("Partition %d marked active.\n", pn);
531 	}
532 
533 	return CMD_DIRTY;
534 }
535 
536 int
537 Xmanual(const char *args, struct mbr *mbr)
538 {
539 	char			*pager = "/usr/bin/less";
540 	char			*p;
541 	FILE			*f;
542 	sig_t			 opipe;
543 
544 	opipe = signal(SIGPIPE, SIG_IGN);
545 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
546 		pager = p;
547 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
548 		f = popen(p, "w");
549 		if (f) {
550 			fwrite(manpage, manpage_sz, 1, f);
551 			pclose(f);
552 		}
553 		free(p);
554 	}
555 
556 	signal(SIGPIPE, opipe);
557 
558 	return CMD_CONT;
559 }
560 
561 int
562 ask_num(const char *str, int dflt, int low, int high)
563 {
564 	char			 lbuf[LINEBUFSZ];
565 	const char		*errstr;
566 	int			 num;
567 
568 	if (dflt < low)
569 		dflt = low;
570 	else if (dflt > high)
571 		dflt = high;
572 
573 	do {
574 		printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
575 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
576 
577 		if (lbuf[0] == '\0') {
578 			num = dflt;
579 			errstr = NULL;
580 		} else {
581 			num = (int)strtonum(lbuf, low, high, &errstr);
582 			if (errstr)
583 				printf("%s is %s: %s.\n", str, errstr, lbuf);
584 		}
585 	} while (errstr);
586 
587 	return num;
588 }
589 
590 int
591 ask_pid(const int dflt)
592 {
593 	char			lbuf[LINEBUFSZ];
594 	int			num;
595 
596 	for (;;) {
597 		printf("Partition id ('0' to disable) [01 - FF]: [%02X] ", dflt);
598 		printf("(? for help) ");
599 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
600 
601 		if (strlen(lbuf) == 0)
602 			return dflt;
603 		if (strcmp(lbuf, "?") == 0) {
604 			PRT_print_mbrmenu(lbuf, sizeof(lbuf));
605 			if (strlen(lbuf) == 0)
606 				continue;
607 		}
608 
609 		num = hex_octet(lbuf);
610 		if (num != -1)
611 			return num;
612 
613 		printf("'%s' is not a valid partition id.\n", lbuf);
614 	}
615 }
616 
617 const struct uuid *
618 ask_uuid(const struct uuid *olduuid)
619 {
620 	char			 lbuf[LINEBUFSZ];
621 	static struct uuid	 uuid;
622 	const char		*guid;
623 	char			*dflt;
624 	uint32_t		 status;
625 
626 	dflt = PRT_uuid_to_menudflt(olduuid);
627 	if (dflt == NULL) {
628 		if (asprintf(&dflt, "00") == -1) {
629 			warn("asprintf()");
630 			goto done;
631 		}
632 	}
633 
634 	for (;;) {
635 		printf("Partition id ('0' to disable) [01 - FF, <uuid>]: [%s] ",
636 		    dflt);
637 		printf("(? for help) ");
638 		string_from_line(lbuf, sizeof(lbuf), TRIMMED);
639 
640 		if (strcmp(lbuf, "?") == 0) {
641 			PRT_print_gptmenu(lbuf, sizeof(lbuf));
642 			if (strlen(lbuf) == 0)
643 				continue;
644 		} else if (strlen(lbuf) == 0) {
645 			uuid = *olduuid;
646 			goto done;
647 		}
648 
649 		guid = PRT_menuid_to_guid(hex_octet(lbuf));
650 		if (guid == NULL)
651 			guid = lbuf;
652 
653 		uuid_from_string(guid, &uuid, &status);
654 		if (status == uuid_s_ok)
655 			goto done;
656 
657 		printf("'%s' has no associated UUID\n", lbuf);
658 	}
659 
660  done:
661 	free(dflt);
662 	return &uuid;
663 }
664