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