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