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