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