xref: /openbsd/sbin/fdisk/cmd.c (revision 3cab2bb3)
1 /*	$OpenBSD: cmd.c,v 1.98 2017/11/13 21:00:32 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 	u_int64_t bs, ns;
171 	int i, pn;
172 
173 	pn = strtonum(args, 0, NGPTPARTITIONS - 1, &errstr);
174 	if (errstr) {
175 		printf("partition number is %s: %s\n", errstr, args);
176 		return (CMD_CONT);
177 	}
178 	gg = &gp[pn];
179 	oldpart = *gg;
180 
181 	Xgsetpid(args);
182 	if (uuid_is_nil(&gg->gp_type, NULL)) {
183 		if (uuid_is_nil(&oldpart.gp_type, NULL) == 0) {
184 			memset(gg, 0, sizeof(struct gpt_partition));
185 			printf("Partition %d is disabled.\n", pn);
186 		}
187 		goto done;
188 	}
189 
190 	bs = getuint64("Partition offset", letoh64(gg->gp_lba_start),
191 	    letoh64(gh.gh_lba_start), letoh64(gh.gh_lba_end));
192 	ns = getuint64("Partition size", letoh64(gg->gp_lba_end) - bs + 1,
193 	    1, letoh64(gh.gh_lba_end) - bs + 1);
194 
195 	gg->gp_lba_start = htole64(bs);
196 	gg->gp_lba_end = htole64(bs + ns - 1);
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(0);
303 	GPT_print_part(pn, "s", 0);
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 
399 	if (MBR_protective_mbr(mbr) == 0 && letoh64(gh.gh_sig) == GPTSIGNATURE)
400 		GPT_print(args, 1);
401 	else
402 		MBR_print(mbr, args);
403 
404 	return (CMD_CONT);
405 }
406 
407 int
408 Xwrite(char *args, struct mbr *mbr)
409 {
410 	struct dos_mbr dos_mbr;
411 	int i, n;
412 
413 	for (i = 0, n = 0; i < NDOSPART; i++)
414 		if (mbr->part[i].id == 0xA6)
415 			n++;
416 	if (n >= 2) {
417 		warnx("MBR contains more than one OpenBSD partition!");
418 		if (!ask_yn("Write MBR anyway?"))
419 			return (CMD_CONT);
420 	}
421 
422 	MBR_make(mbr, &dos_mbr);
423 
424 	printf("Writing MBR at offset %lld.\n", (long long)mbr->offset);
425 	if (MBR_write(mbr->offset, &dos_mbr) == -1) {
426 		warn("error writing MBR");
427 		return (CMD_CONT);
428 	}
429 
430 	if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
431 		printf("Writing GPT.\n");
432 		if (GPT_write() == -1) {
433 			warn("error writing GPT");
434 			return (CMD_CONT);
435 		}
436 	} else if (reinited) {
437 		/* Make sure GPT doesn't get in the way. */
438 		MBR_zapgpt(&dos_mbr, DL_GETDSIZE(&dl) - 1);
439 	}
440 
441 	/* Refresh in memory copy to reflect what was just written. */
442 	MBR_parse(&dos_mbr, mbr->offset, mbr->reloffset, mbr);
443 
444 	return (CMD_CLEAN);
445 }
446 
447 int
448 Xquit(char *args, struct mbr *mbr)
449 {
450 	return (CMD_SAVE);
451 }
452 
453 int
454 Xabort(char *args, struct mbr *mbr)
455 {
456 	exit(0);
457 }
458 
459 int
460 Xexit(char *args, struct mbr *mbr)
461 {
462 	return (CMD_EXIT);
463 }
464 
465 int
466 Xhelp(char *args, struct mbr *mbr)
467 {
468 	char help[80];
469 	char *mbrstr;
470 	int i;
471 
472 	for (i = 0; cmd_table[i].cmd != NULL; i++) {
473 		strlcpy(help, cmd_table[i].help, sizeof(help));
474 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
475 			if (cmd_table[i].gpt == 0)
476 				continue;
477 			mbrstr = strstr(help, "MBR");
478 			if (mbrstr)
479 				memcpy(mbrstr, "GPT", 3);
480 		}
481 		printf("\t%s\t\t%s\n", cmd_table[i].cmd, help);
482 	}
483 
484 	return (CMD_CONT);
485 }
486 
487 int
488 Xupdate(char *args, struct mbr *mbr)
489 {
490 	/* Update code */
491 	memcpy(mbr->code, initial_mbr.code, sizeof(mbr->code));
492 	mbr->signature = DOSMBR_SIGNATURE;
493 	printf("Machine code updated.\n");
494 	return (CMD_DIRTY);
495 }
496 
497 int
498 Xflag(char *args, struct mbr *mbr)
499 {
500 	const char *errstr;
501 	int i, maxpn, pn = -1;
502 	long long val = -1;
503 	char *part, *flag;
504 
505 	flag = args;
506 	part = strsep(&flag, " \t");
507 
508 	if (letoh64(gh.gh_sig) == GPTSIGNATURE)
509 		maxpn = NGPTPARTITIONS - 1;
510 	else
511 		maxpn = NDOSPART - 1;
512 
513 	pn = strtonum(part, 0, maxpn, &errstr);
514 	if (errstr) {
515 		printf("partition number is %s: %s.\n", errstr, part);
516 		return (CMD_CONT);
517 	}
518 
519 	if (flag != NULL) {
520 		/* Set flag to value provided. */
521 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
522 			val = strtonum(flag, 0, INT64_MAX, &errstr);
523 		else
524 			val = strtonum(flag, 0, 0xff, &errstr);
525 		if (errstr) {
526 			printf("flag value is %s: %s.\n", errstr, flag);
527 			return (CMD_CONT);
528 		}
529 		if (letoh64(gh.gh_sig) == GPTSIGNATURE)
530 			gp[pn].gp_attrs = htole64(val);
531 		else
532 			mbr->part[pn].flag = val;
533 		printf("Partition %d flag value set to 0x%llx.\n", pn, val);
534 	} else {
535 		/* Set active flag */
536 		if (letoh64(gh.gh_sig) == GPTSIGNATURE) {
537 			for (i = 0; i < NGPTPARTITIONS; i++) {
538 				if (i == pn)
539 					gp[i].gp_attrs = htole64(GPTDOSACTIVE);
540 				else
541 					gp[i].gp_attrs = htole64(0);
542 			}
543 		} else {
544 			for (i = 0; i < NDOSPART; i++) {
545 				if (i == pn)
546 					mbr->part[i].flag = DOSACTIVE;
547 				else
548 					mbr->part[i].flag = 0x00;
549 			}
550 		}
551 		printf("Partition %d marked active.\n", pn);
552 	}
553 
554 	return (CMD_DIRTY);
555 }
556 
557 int
558 Xmanual(char *args, struct mbr *mbr)
559 {
560 	char *pager = "/usr/bin/less";
561 	char *p;
562 	sig_t opipe;
563 	extern const unsigned char manpage[];
564 	extern const int manpage_sz;
565 	FILE *f;
566 
567 	opipe = signal(SIGPIPE, SIG_IGN);
568 	if ((p = getenv("PAGER")) != NULL && (*p != '\0'))
569 		pager = p;
570 	if (asprintf(&p, "gunzip -qc|%s", pager) != -1) {
571 		f = popen(p, "w");
572 		if (f) {
573 			fwrite(manpage, manpage_sz, 1, f);
574 			pclose(f);
575 		}
576 		free(p);
577 	}
578 
579 	signal(SIGPIPE, opipe);
580 
581 	return (CMD_CONT);
582 }
583