xref: /openbsd-src/sbin/fdisk/cmd.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: cmd.c,v 1.104 2021/05/15 15:59:15 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 "disk.h"
31 #include "misc.h"
32 #include "part.h"
33 #include "mbr.h"
34 #include "gpt.h"
35 #include "user.h"
36 #include "cmd.h"
37 
38 int reinited;
39 
40 /* Some helper functions for GPT handling. */
41 int Xgedit(char *);
42 int Xgsetpid(char *);
43 
44 int
45 Xreinit(char *args, struct mbr *mbr)
46 {
47 	struct dos_mbr dos_mbr;
48 	int efi, dogpt;
49 
50 	efi = MBR_protective_mbr(mbr);
51 
52 	if (strncasecmp(args, "gpt", 3) == 0)
53 		dogpt = 1;
54 	else if (strncasecmp(args, "mbr", 3) == 0)
55 		dogpt = 0;
56 	else if (strlen(args) > 0) {
57 		printf("Unrecognized modifier '%s'\n", args);
58 		return (CMD_CONT);
59 	} else if (efi != -1)
60 		dogpt = 1;
61 	else
62 		dogpt = 0;
63 
64 	MBR_make(&initial_mbr, &dos_mbr);
65 	MBR_parse(&dos_mbr, mbr->offset, mbr->reloffset, mbr);
66 
67 	if (dogpt) {
68 		MBR_init_GPT(mbr);
69 		GPT_init();
70 		GPT_print("s", TERSE);
71 	} else {
72 		memset(&gh, 0, sizeof(gh));
73 		MBR_init(mbr);
74 		MBR_print(mbr, "s");
75 	}
76 	reinited = 1;
77 
78 	printf("Use 'write' to update disk.\n");
79 
80 	return (CMD_DIRTY);
81 }
82 
83 int
84 Xdisk(char *args, struct mbr *mbr)
85 {
86 	int maxcyl  = 1024;
87 	int maxhead = 256;
88 	int maxsec  = 63;
89 
90 	/* Print out disk info */
91 	DISK_printgeometry(args);
92 
93 #if defined (__powerpc__) || defined (__mips__)
94 	maxcyl  = 9999999;
95 	maxhead = 9999999;
96 	maxsec  = 9999999;
97 #endif
98 
99 	/* Ask for new info */
100 	if (ask_yn("Change disk geometry?")) {
101 		disk.cylinders = ask_num("BIOS Cylinders",
102 		    disk.cylinders, 1, maxcyl);
103 		disk.heads = ask_num("BIOS Heads",
104 		    disk.heads, 1, maxhead);
105 		disk.sectors = ask_num("BIOS Sectors",
106 		    disk.sectors, 1, maxsec);
107 
108 		disk.size = disk.cylinders * disk.heads * disk.sectors;
109 	}
110 
111 	return (CMD_CONT);
112 }
113 
114 int
115 Xswap(char *args, struct mbr *mbr)
116 {
117 	const char *errstr;
118 	char *from, *to;
119 	int pf, pt, maxpn;
120 	struct prt pp;
121 	struct gpt_partition gg;
122 
123 	to = args;
124 	from = strsep(&to, " \t");
125 
126 	if (to == NULL) {
127 		printf("partition number is invalid:\n");
128 		return (CMD_CONT);
129 	}
130 
131 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
132 		maxpn = NGPTPARTITIONS - 1;
133 	else
134 		maxpn = NDOSPART - 1;
135 
136 	pf = strtonum(from, 0, maxpn, &errstr);
137 	if (errstr) {
138 		printf("partition number is %s: %s\n", errstr, from);
139 		return (CMD_CONT);
140 	}
141 	pt = strtonum(to, 0, maxpn, &errstr);
142 	if (errstr) {
143 		printf("partition number is %s: %s\n", errstr, to);
144 		return (CMD_CONT);
145 	}
146 
147 	if (pt == pf) {
148 		printf("%d same partition as %d, doing nothing.\n", pt, pf);
149 		return (CMD_CONT);
150 	}
151 
152 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
153 		gg = gp[pt];
154 		gp[pt] = gp[pf];
155 		gp[pf] = gg;
156 	} else {
157 		pp = mbr->part[pt];
158 		mbr->part[pt] = mbr->part[pf];
159 		mbr->part[pf] = pp;
160 	}
161 
162 	return (CMD_DIRTY);
163 }
164 
165 int
166 Xgedit(char *args)
167 {
168 	struct gpt_partition oldpart;
169 	const char *errstr;
170 	struct gpt_partition *gg;
171 	char *name;
172 	uint16_t *utf;
173 	int i, pn;
174 
175 	pn = strtonum(args, 0, NGPTPARTITIONS - 1, &errstr);
176 	if (errstr) {
177 		printf("partition number is %s: %s\n", errstr, args);
178 		return (CMD_CONT);
179 	}
180 	gg = &gp[pn];
181 	oldpart = *gg;
182 
183 	Xgsetpid(args);
184 	if (uuid_is_nil(&gg->gp_type, NULL)) {
185 		if (uuid_is_nil(&oldpart.gp_type, NULL) == 0) {
186 			memset(gg, 0, sizeof(struct gpt_partition));
187 			printf("Partition %d is disabled.\n", pn);
188 		}
189 		goto done;
190 	}
191 
192 	if (GPT_get_lba_start(pn) == -1 ||
193 	    GPT_get_lba_end(pn) == -1) {
194 		*gg = oldpart;
195 		goto done;
196 	}
197 
198 	name = ask_string("Partition name", utf16le_to_string(gg->gp_name));
199 	if (strlen(name) >= GPTPARTNAMESIZE) {
200 		printf("partition name must be < %d characters\n",
201 		    GPTPARTNAMESIZE);
202 		goto done;
203 	}
204 	/*
205 	 * N.B.: simple memcpy() could copy trash from static buf! This
206 	 * would create false positives for the partition having changed.
207 	 */
208 	utf = string_to_utf16le(name);
209 	for (i = 0; i < GPTPARTNAMESIZE; i++) {
210 		gg->gp_name[i] = utf[i];
211 		if (utf[i] == 0)
212 			break;
213 	}
214 
215 done:
216 	if (memcmp(gg, &oldpart, sizeof(*gg)))
217 		return (CMD_DIRTY);
218 	else
219 		return (CMD_CONT);
220 }
221 
222 int
223 Xedit(char *args, struct mbr *mbr)
224 {
225 	struct prt oldpart;
226 	const char *errstr;
227 	struct prt *pp;
228 	int pn;
229 
230 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
231 		return (Xgedit(args));
232 
233 	pn = strtonum(args, 0, 3, &errstr);
234 	if (errstr) {
235 		printf("partition number is %s: %s\n", errstr, args);
236 		return (CMD_CONT);
237 	}
238 	pp = &mbr->part[pn];
239 	oldpart = *pp;
240 
241 	Xsetpid(args, mbr);
242 	if (pp->id == DOSPTYP_UNUSED) {
243 		if (oldpart.id != DOSPTYP_UNUSED) {
244 			memset(pp, 0, sizeof(*pp));
245 			printf("Partition %d is disabled.\n", pn);
246 		}
247 		goto done;
248 	}
249 
250 	if (ask_yn("Do you wish to edit in CHS mode?")) {
251 		pp->scyl = ask_num("BIOS Starting cylinder", pp->scyl,  0,
252 		    disk.cylinders - 1);
253 		pp->shead = ask_num("BIOS Starting head",    pp->shead, 0,
254 		    disk.heads - 1);
255 		pp->ssect = ask_num("BIOS Starting sector",  pp->ssect, 1,
256 		    disk.sectors);
257 
258 		pp->ecyl = ask_num("BIOS Ending cylinder",   pp->ecyl,
259 		    pp->scyl, disk.cylinders - 1);
260 		pp->ehead = ask_num("BIOS Ending head",      pp->ehead,
261 		    (pp->scyl == pp->ecyl) ? pp->shead : 0, disk.heads - 1);
262 		pp->esect = ask_num("BIOS Ending sector",    pp->esect,
263 		    (pp->scyl == pp->ecyl && pp->shead == pp->ehead) ? pp->ssect
264 		    : 1, disk.sectors);
265 
266 		/* Fix up off/size values */
267 		PRT_fix_BN(pp, pn);
268 		/* Fix up CHS values for LBA */
269 		PRT_fix_CHS(pp);
270 	} else {
271 		pp->bs = getuint64("Partition offset", pp->bs, 0, disk.size - 1);
272 		pp->ns = getuint64("Partition size",   pp->ns, 1,
273 		    disk.size - pp->bs);
274 
275 		/* Fix up CHS values */
276 		PRT_fix_CHS(pp);
277 	}
278 
279 done:
280 	if (memcmp(pp, &oldpart, sizeof(*pp)))
281 		return (CMD_DIRTY);
282 	else
283 		return (CMD_CONT);
284 }
285 
286 int
287 Xgsetpid(char *args)
288 {
289 	const char *errstr;
290 	struct uuid guid;
291 	struct gpt_partition *gg;
292 	int pn, num, status;
293 
294 	pn = strtonum(args, 0, NGPTPARTITIONS - 1, &errstr);
295 	if (errstr) {
296 		printf("partition number is %s: %s\n", errstr, args);
297 		return (CMD_CONT);
298 	}
299 	gg = &gp[pn];
300 
301 	/* Print out current table entry */
302 	GPT_print_parthdr(TERSE);
303 	GPT_print_part(pn, "s", TERSE);
304 
305 	/* Ask for partition type or GUID. */
306 	uuid_dec_le(&gg->gp_type, &guid);
307 	num = ask_pid(PRT_uuid_to_type(&guid), &guid);
308 	if (num <= 0xff)
309 		guid = *(PRT_type_to_uuid(num));
310 	uuid_enc_le(&gg->gp_type, &guid);
311 
312 	if (uuid_is_nil(&gg->gp_guid, NULL)) {
313 		uuid_create(&guid, &status);
314 		if (status != uuid_s_ok) {
315 			printf("could not create guid for partition\n");
316 			return (CMD_CONT);
317 		}
318 		uuid_enc_le(&gg->gp_guid, &guid);
319 	}
320 
321 	return (CMD_DIRTY);
322 }
323 
324 int
325 Xsetpid(char *args, struct mbr *mbr)
326 {
327 	const char *errstr;
328 	int pn, num;
329 	struct prt *pp;
330 
331 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
332 		return (Xgsetpid(args));
333 
334 	pn = strtonum(args, 0, 3, &errstr);
335 	if (errstr) {
336 		printf("partition number is %s: %s\n", errstr, args);
337 		return (CMD_CONT);
338 	}
339 	pp = &mbr->part[pn];
340 
341 	/* Print out current table entry */
342 	PRT_print(0, NULL, NULL);
343 	PRT_print(pn, pp, NULL);
344 
345 	/* Ask for MBR partition type */
346 	num = ask_pid(pp->id, NULL);
347 	if (num == pp->id)
348 		return (CMD_CONT);
349 
350 	pp->id = num;
351 
352 	return (CMD_DIRTY);
353 }
354 
355 int
356 Xselect(char *args, struct mbr *mbr)
357 {
358 	const char *errstr;
359 	static off_t firstoff = 0;
360 	off_t off;
361 	int pn;
362 
363 	pn = strtonum(args, 0, 3, &errstr);
364 	if (errstr) {
365 		printf("partition number is %s: %s\n", errstr, args);
366 		return (CMD_CONT);
367 	}
368 
369 	off = mbr->part[pn].bs;
370 
371 	/* Sanity checks */
372 	if ((mbr->part[pn].id != DOSPTYP_EXTEND) &&
373 	    (mbr->part[pn].id != DOSPTYP_EXTENDL)) {
374 		printf("Partition %d is not an extended partition.\n", pn);
375 		return (CMD_CONT);
376 	}
377 
378 	if (firstoff == 0)
379 		firstoff = off;
380 
381 	if (!off) {
382 		printf("Loop to offset 0!  Not selected.\n");
383 		return (CMD_CONT);
384 	} else {
385 		printf("Selected extended partition %d\n", pn);
386 		printf("New MBR at offset %lld.\n", (long long)off);
387 	}
388 
389 	/* Recursion is beautiful! */
390 	USER_edit(off, firstoff);
391 
392 	return (CMD_CONT);
393 }
394 
395 int
396 Xprint(char *args, struct mbr *mbr)
397 {
398 	int efi;
399 
400 	efi = MBR_protective_mbr(mbr);
401 	if (efi != -1 && letoh64(gh.gh_sig) == GPTSIGNATURE)
402 		GPT_print(args, VERBOSE);
403 	else
404 		MBR_print(mbr, args);
405 
406 	return (CMD_CONT);
407 }
408 
409 int
410 Xwrite(char *args, struct mbr *mbr)
411 {
412 	struct dos_mbr dos_mbr;
413 	int efi, i, n;
414 
415 	for (i = 0, n = 0; i < NDOSPART; i++)
416 		if (mbr->part[i].id == 0xA6)
417 			n++;
418 	if (n >= 2) {
419 		warnx("MBR contains more than one OpenBSD partition!");
420 		if (!ask_yn("Write MBR anyway?"))
421 			return (CMD_CONT);
422 	}
423 
424 	MBR_make(mbr, &dos_mbr);
425 
426 	printf("Writing MBR at offset %lld.\n", (long long)mbr->offset);
427 	if (MBR_write(mbr->offset, &dos_mbr) == -1) {
428 		warn("error writing MBR");
429 		return (CMD_CONT);
430 	}
431 
432 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
433 		printf("Writing GPT.\n");
434 		efi = MBR_protective_mbr(mbr);
435 		if (efi == -1 || GPT_write() == -1) {
436 			warn("error writing GPT");
437 			return (CMD_CONT);
438 		}
439 	} else {
440 		/* Ensure any on-disk GPT headers are zeroed. */
441 		MBR_zapgpt(&dos_mbr, DL_GETDSIZE(&dl) - 1);
442 	}
443 
444 	/* Refresh in memory copy to reflect what was just written. */
445 	MBR_parse(&dos_mbr, mbr->offset, mbr->reloffset, mbr);
446 
447 	return (CMD_CLEAN);
448 }
449 
450 int
451 Xquit(char *args, struct mbr *mbr)
452 {
453 	return (CMD_SAVE);
454 }
455 
456 int
457 Xabort(char *args, struct mbr *mbr)
458 {
459 	exit(0);
460 }
461 
462 int
463 Xexit(char *args, struct mbr *mbr)
464 {
465 	return (CMD_EXIT);
466 }
467 
468 int
469 Xhelp(char *args, struct mbr *mbr)
470 {
471 	char help[80];
472 	char *mbrstr;
473 	int i;
474 
475 	for (i = 0; cmd_table[i].cmd != NULL; i++) {
476 		strlcpy(help, cmd_table[i].help, sizeof(help));
477 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
478 			if (cmd_table[i].gpt == 0)
479 				continue;
480 			mbrstr = strstr(help, "MBR");
481 			if (mbrstr)
482 				memcpy(mbrstr, "GPT", 3);
483 		}
484 		printf("\t%s\t\t%s\n", cmd_table[i].cmd, help);
485 	}
486 
487 	return (CMD_CONT);
488 }
489 
490 int
491 Xupdate(char *args, struct mbr *mbr)
492 {
493 	/* Update code */
494 	memcpy(mbr->code, initial_mbr.code, sizeof(mbr->code));
495 	mbr->signature = DOSMBR_SIGNATURE;
496 	printf("Machine code updated.\n");
497 	return (CMD_DIRTY);
498 }
499 
500 int
501 Xflag(char *args, struct mbr *mbr)
502 {
503 	const char *errstr;
504 	int i, maxpn, pn = -1;
505 	long long val = -1;
506 	char *part, *flag;
507 
508 	flag = args;
509 	part = strsep(&flag, " \t");
510 
511 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
512 		maxpn = NGPTPARTITIONS - 1;
513 	else
514 		maxpn = NDOSPART - 1;
515 
516 	pn = strtonum(part, 0, maxpn, &errstr);
517 	if (errstr) {
518 		printf("partition number is %s: %s.\n", errstr, part);
519 		return (CMD_CONT);
520 	}
521 
522 	if (flag != NULL) {
523 		/* Set flag to value provided. */
524 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
525 			val = strtonum(flag, 0, INT64_MAX, &errstr);
526 		else
527 			val = strtonum(flag, 0, 0xff, &errstr);
528 		if (errstr) {
529 			printf("flag value is %s: %s.\n", errstr, flag);
530 			return (CMD_CONT);
531 		}
532 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
533 			gp[pn].gp_attrs = htole64(val);
534 		else
535 			mbr->part[pn].flag = val;
536 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
537 	} else {
538 		/* Set active flag */
539 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
540 			for (i = 0; i < NGPTPARTITIONS; i++) {
541 				if (i == pn)
542 					gp[i].gp_attrs = htole64(GPTDOSACTIVE);
543 				else
544 					gp[i].gp_attrs = htole64(0);
545 			}
546 		} else {
547 			for (i = 0; i < NDOSPART; i++) {
548 				if (i == pn)
549 					mbr->part[i].flag = DOSACTIVE;
550 				else
551 					mbr->part[i].flag = 0x00;
552 			}
553 		}
554 		printf("Partition %d marked active.\n", pn);
555 	}
556 
557 	return (CMD_DIRTY);
558 }
559 
560 int
561 Xmanual(char *args, struct mbr *mbr)
562 {
563 	char *pager = "/usr/bin/less";
564 	char *p;
565 	sig_t opipe;
566 	extern const unsigned char manpage[];
567 	extern const int manpage_sz;
568 	FILE *f;
569 
570 	opipe = signal(SIGPIPE, SIG_IGN);
571 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
572 		pager = p;
573 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
574 		f = popen(p, "w");
575 		if (f) {
576 			fwrite(manpage, manpage_sz, 1, f);
577 			pclose(f);
578 		}
579 		free(p);
580 	}
581 
582 	signal(SIGPIPE, opipe);
583 
584 	return (CMD_CONT);
585 }
586