xref: /openbsd-src/sbin/pdisk/pdisk.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 //
2 // pdisk - an editor for Apple format partition tables
3 //
4 // Written by Eryk Vershen
5 //
6 // Still under development (as of 15 January 1998)
7 //
8 
9 /*
10  * Copyright 1996,1997,1998 by Apple Computer, Inc.
11  *              All Rights Reserved
12  *
13  * Permission to use, copy, modify, and distribute this software and
14  * its documentation for any purpose and without fee is hereby granted,
15  * provided that the above copyright notice appears in all copies and
16  * that both the copyright notice and this permission notice appear in
17  * supporting documentation.
18  *
19  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
20  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE.
22  *
23  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
24  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
25  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
26  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
27  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28  */
29 
30 // for printf()
31 #include <stdio.h>
32 
33 // for malloc() & free()
34 #include <stdlib.h>
35 #include <unistd.h>
36 
37 // for strncpy() & strlen()
38 #include <string.h>
39 // for O_RDONLY
40 #include <fcntl.h>
41 // for errno
42 #include <errno.h>
43 
44 #include "pdisk.h"
45 #include "io.h"
46 #include "partition_map.h"
47 #include "pathname.h"
48 #include "hfs_misc.h"
49 #include "errors.h"
50 #include "dump.h"
51 #include "validate.h"
52 #include "version.h"
53 #include "util.h"
54 
55 
56 //
57 // Defines
58 //
59 #define ARGV_CHUNK 5
60 #define CFLAG_DEFAULT	0
61 #define DFLAG_DEFAULT	0
62 #define HFLAG_DEFAULT	0
63 #define INTERACT_DEFAULT	0
64 #define LFLAG_DEFAULT	0
65 #define RFLAG_DEFAULT	0
66 #define VFLAG_DEFAULT	0
67 
68 
69 //
70 // Types
71 //
72 
73 
74 //
75 // Global Constants
76 //
77 enum getopt_values {
78     kLongOption = 0,
79     kBadOption = '?',
80     kOptionArg = 1000,
81     kListOption = 1001,
82     kLogicalOption = 1002
83 };
84 
85 
86 //
87 // Global Variables
88 //
89 int lflag = LFLAG_DEFAULT;	/* list the device */
90 char *lfile;	/* list */
91 int vflag = VFLAG_DEFAULT;	/* show version */
92 int hflag = HFLAG_DEFAULT;	/* show help */
93 int dflag = DFLAG_DEFAULT;	/* turn on debugging commands and printout */
94 int rflag = RFLAG_DEFAULT;	/* open device read Only */
95 int interactive = INTERACT_DEFAULT;
96 int cflag = CFLAG_DEFAULT;	/* compute device size */
97 
98 static int first_get = 1;
99 
100 
101 //
102 // Forward declarations
103 //
104 void do_change_map_size(partition_map_header *map);
105 #ifdef __m68k__
106 void do_update_dpme(partition_map *entry);
107 #endif
108 void do_create_partition(partition_map_header *map, int get_type);
109 void do_delete_partition(partition_map_header *map);
110 void do_display_block(partition_map_header *map, char *alt_name);
111 void do_display_entry(partition_map_header *map);
112 void do_examine_patch_partition(partition_map_header *map);
113 int do_expert(partition_map_header *map, char *name);
114 void do_rename_partition(partition_map_header *map);
115 void do_change_type(partition_map_header *map);
116 void do_reorder(partition_map_header *map);
117 void do_write_partition_map(partition_map_header *map);
118 void edit(char *name, int ask_logical_size);
119 int get_base_argument(long *number, partition_map_header *map);
120 int get_command_line(int *argc, char ***argv);
121 int get_size_argument(long *number, partition_map_header *map);
122 int get_options(int argc, char **argv);
123 void interact(void);
124 void print_edit_notes(void);
125 void print_expert_notes(void);
126 
127 
128 //
129 // Routines
130 //
131 int
132 main(int argc, char **argv)
133 {
134     int name_index;
135     char *versionstr;
136 
137     init_program_name(argv);
138 
139     if (sizeof(DPME) != PBLOCK_SIZE) {
140 	fatal(-1, "Size of partition map entry (%d) "
141 		"is not equal to block size (%d)\n",
142 		sizeof(DPME), PBLOCK_SIZE);
143     }
144     if (sizeof(Block0) != PBLOCK_SIZE) {
145 	fatal(-1, "Size of block zero structure (%d) "
146 		"is not equal to block size (%d)\n",
147 		sizeof(Block0), PBLOCK_SIZE);
148     }
149     versionstr = (char *)get_version_string();
150     if (versionstr) {
151 	if (strcmp(VERSION, versionstr) != 0) {
152 		fatal(-1, "Version string static form (%s) does not match dynamic form (%s)\n",
153 		    VERSION, versionstr);
154 	}
155 	free(versionstr);
156     }
157 
158     name_index = get_options(argc, argv);
159 
160     if (vflag) {
161 	printf("version " VERSION " (" RELEASE_DATE ")\n");
162     }
163     if (hflag) {
164  	do_help();
165     } else if (interactive) {
166 	interact();
167     } else if (lflag) {
168 	if (lfile != NULL) {
169 	    dump(lfile);
170 	} else if (name_index < argc) {
171 	    while (name_index < argc) {
172 		dump(argv[name_index++]);
173 	    }
174 	} else {
175 	    do_help();
176 	}
177     } else if (name_index < argc) {
178 	while (name_index < argc) {
179 	    edit(argv[name_index++], 0);
180 	}
181     } else if (!vflag) {
182  	do_help();
183     }
184     return 0;
185 }
186 
187 
188 void
189 interact()
190 {
191     char *name;
192     int command;
193     int ask_logical_size;
194 
195     while (get_command("Top level command (? for help): ", first_get, &command)) {
196 	first_get = 0;
197 	ask_logical_size = 0;
198 
199 	switch (command) {
200 	case '?':
201 	    // fall through
202 	case 'H':
203 	case 'h':
204 	    printf("Commands are:\n");
205 	    printf("  h    print help\n");
206 	    printf("  v    print the version number and release date\n");
207 	    printf("  l    list device's map\n");
208 	    printf("  e    edit device's map\n");
209 	    printf("  E    (edit map with specified block size)\n");
210 	    printf("  r    toggle readonly flag\n");
211 	    printf("  f    toggle show filesystem name flag\n");
212 	    if (dflag) {
213 		printf("  a    toggle abbreviate flag\n");
214 		printf("  p    toggle physical flag\n");
215 		printf("  c    toggle compute size flag\n");
216 		printf("  d    toggle debug flag\n");
217 		printf("  x    examine block n of device\n");
218 	    }
219 	    printf("  q    quit the program\n");
220 	    break;
221 	case 'Q':
222 	case 'q':
223 	    return;
224 	    break;
225 	case 'V':
226 	case 'v':
227 	    printf("version " VERSION " (" RELEASE_DATE ")\n");
228 	    break;
229 	case 'l':
230 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
231 		bad_input("Bad name");
232 		break;
233 	    }
234 	    dump(name);
235 	    free(name);
236 	    break;
237 	case 'E':
238 	    ask_logical_size = 1;
239 	case 'e':
240 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
241 		bad_input("Bad name");
242 		break;
243 	    }
244 	    edit(name, ask_logical_size);
245 	    free(name);
246 	    break;
247 	case 'R':
248 	case 'r':
249 	    if (rflag) {
250 		rflag = 0;
251 	    } else {
252 		rflag = 1;
253 	    }
254 	    printf("Now in %s mode.\n", (rflag)?"readonly":"read/write");
255 	    break;
256 	case 'F':
257 	case 'f':
258 	    if (fflag) {
259 		fflag = 0;
260 	    } else {
261 		fflag = 1;
262 	    }
263 	    printf("Now in show %s name mode.\n", (fflag)?"filesystem":"partition");
264 	    break;
265 	case 'A':
266 	case 'a':
267 	    if (dflag) {
268 		if (aflag) {
269 		    aflag = 0;
270 		} else {
271 		    aflag = 1;
272 		}
273 		printf("Now in %s mode.\n", (aflag)?"abbreviate":"full type");
274 	    } else {
275 	    	goto do_error;
276 	    }
277 	    break;
278 	case 'P':
279 	case 'p':
280 	    if (dflag) {
281 		if (pflag) {
282 		    pflag = 0;
283 		} else {
284 		    pflag = 1;
285 		}
286 		printf("Now in %s mode.\n", (pflag)?"physical":"logical");
287 	    } else {
288 	    	goto do_error;
289 	    }
290 	    break;
291 	case 'D':
292 	case 'd':
293 	    if (dflag) {
294 		dflag = 0;
295 	    } else {
296 		dflag = 1;
297 	    }
298 	    printf("Now in %s mode.\n", (dflag)?"debug":"normal");
299 	    break;
300 	case 'C':
301 	case 'c':
302 	    if (dflag) {
303 		if (cflag) {
304 		    cflag = 0;
305 		} else {
306 		    cflag = 1;
307 		}
308 		printf("Now in %s device size mode.\n", (cflag)?"always compute":"use existing");
309 	    } else {
310 	    	goto do_error;
311 	    }
312 	    break;
313 	case 'X':
314 	case 'x':
315 	    if (dflag) {
316 		do_display_block(0, 0);
317 	    } else {
318 	    	goto do_error;
319 	    }
320 	    break;
321 	default:
322 	do_error:
323 	    bad_input("No such command (%c)", command);
324 	    break;
325 	}
326     }
327 }
328 
329 
330 int
331 get_options(int argc, char **argv)
332 {
333     int c;
334     extern int optind;
335     extern char *optarg;
336     int flag = 0;
337 
338     lflag = LFLAG_DEFAULT;
339     lfile = NULL;
340     vflag = VFLAG_DEFAULT;
341     hflag = HFLAG_DEFAULT;
342     dflag = DFLAG_DEFAULT;
343     rflag = RFLAG_DEFAULT;
344     aflag = AFLAG_DEFAULT;
345     pflag = PFLAG_DEFAULT;
346     interactive = INTERACT_DEFAULT;
347     cflag = CFLAG_DEFAULT;
348 
349     optind = 1; // reset option scanner logic
350     while ((c = getopt(argc, argv, "hlvdric")) != -1) {
351 	switch (c) {
352 	case 'h':
353 	    hflag = (HFLAG_DEFAULT)?0:1;
354 	    break;
355 	case 'l':
356 	    lflag = (LFLAG_DEFAULT)?0:1;
357 	    break;
358 	case 'v':
359 	    vflag = (VFLAG_DEFAULT)?0:1;
360 	    break;
361 	case 'd':
362 	    dflag = (DFLAG_DEFAULT)?0:1;
363 	    break;
364 	case 'c':
365 	    cflag = (CFLAG_DEFAULT)?0:1;
366 	    break;
367 	case 'r':
368 	    rflag = (RFLAG_DEFAULT)?0:1;
369 	    break;
370 	case 'i':
371 	    interactive = (INTERACT_DEFAULT)?0:1;
372 	    break;
373 	case 'a':
374 	    aflag = (AFLAG_DEFAULT)?0:1;
375 	    break;
376 	case kLogicalOption:
377 	    pflag = (PFLAG_DEFAULT)?0:1;
378 	    break;
379 	default:
380 	    flag = 1;
381 	    break;
382 	}
383     }
384     if (flag) {
385 	usage("bad arguments");
386     }
387     return optind;
388 }
389 
390 
391 void
392 print_edit_notes()
393 {
394     printf("Notes:\n");
395     printf("  Base and length fields are blocks, which vary in size between media.\n");
396     printf("  The base field can be <nth>p; i.e. use the base of the nth partition.\n");
397     printf("  The length field can be a length followed by k, m, g or t to indicate\n");
398     printf("  kilo, mega, giga, or tera bytes; also the length can be <nth>p; i.e. use\n");
399     printf("  the length of the nth partition.\n");
400     printf("  The name of a partition is descriptive text.\n");
401     printf("\n");
402 }
403 
404 
405 //
406 // Edit the file
407 //
408 void
409 edit(char *name, int ask_logical_size)
410 {
411     partition_map_header *map;
412     int command;
413     int order;
414     int get_type;
415     int valid_file;
416 
417     map = open_partition_map(name, &valid_file, ask_logical_size);
418     if (!valid_file) {
419     	return;
420     }
421 
422     printf("Edit %s -\n", name);
423 
424     while (get_command("Command (? for help): ", first_get, &command)) {
425 	first_get = 0;
426 	order = 1;
427 	get_type = 0;
428 
429 	switch (command) {
430 	case '?':
431 	    print_edit_notes();
432 	    // fall through
433 	case 'H':
434 	case 'h':
435 	    printf("Commands are:\n");
436 	    printf("  C    (create with type also specified)\n");
437 	    printf("  c    create new partition (standard OpenBSD root)\n");
438 	    printf("  d    delete a partition\n");
439 	    printf("  h    help\n");
440 	    printf("  i    initialize partition map\n");
441 	    printf("  n    (re)name a partition\n");
442 	    printf("  P    (print ordered by base address)\n");
443 	    printf("  p    print the partition table\n");
444 	    printf("  q    quit editing\n");
445 	    printf("  r    reorder partition entry in map\n");
446 	    printf("  s    change size of partition map\n");
447 	    printf("  t    change a partition's type\n");
448 	    if (!rflag) {
449 		printf("  w    write the partition table\n");
450 	    }
451 	    if (dflag) {
452 		printf("  x    extra extensions for experts\n");
453 	    }
454 	    break;
455 	case 'P':
456 	    order = 0;
457 	    // fall through
458 	case 'p':
459 	    dump_partition_map(map, order);
460 	    break;
461 	case 'Q':
462 	case 'q':
463 	    if (map && map->changed) {
464 		if (get_okay("Discard changes? [n/y]: ", 0) != 1) {
465 		    break;
466 		}
467 	    }
468 	    flush_to_newline(1);
469 	    goto finis;
470 	    break;
471 	case 'I':
472 	case 'i':
473 	    map = init_partition_map(name, map);
474 	    break;
475 	case 'C':
476 	    get_type = 1;
477 	    // fall through
478 	case 'c':
479 	    do_create_partition(map, get_type);
480 	    break;
481 	case 'N':
482 	case 'n':
483 	    do_rename_partition(map);
484 	    break;
485 	case 'D':
486 	case 'd':
487 	    do_delete_partition(map);
488 	    break;
489 	case 'R':
490 	case 'r':
491 	    do_reorder(map);
492 	    break;
493 	case 'S':
494 	case 's':
495 	    do_change_map_size(map);
496 	    break;
497 	case 'T':
498 	case 't':
499 	    do_change_type(map);
500 	    break;
501 	case 'X':
502 	case 'x':
503 	    if (!dflag) {
504 		goto do_error;
505 	    } else if (do_expert(map, name)) {
506 		flush_to_newline(1);
507 		goto finis;
508 	    }
509 	    break;
510 	case 'W':
511 	case 'w':
512 	    if (!rflag) {
513 		do_write_partition_map(map);
514 	    } else {
515 	    	goto do_error;
516 	    }
517 	    break;
518 	default:
519 	do_error:
520 	    bad_input("No such command (%c)", command);
521 	    break;
522 	}
523     }
524 finis:
525 
526     close_partition_map(map);
527 }
528 
529 #ifdef __m68k__
530 void
531 do_update_dpme(partition_map *entry)
532 {
533     int slice = 0;
534     if (!entry) return;
535     dpme_init_flags(entry->data);
536     entry->HFS_name = get_HFS_name(entry, &entry->HFS_kind);
537     if (istrncmp(entry->data->dpme_type, kUnixType, DPISTRLEN) == 0) {
538 	printf("Available partition slices for %s:\n",entry->data->dpme_type);
539 	printf("  a   root partition\n");
540 	printf("  b   swap partition\n");
541 	printf("  c   do not set any bzb bits\n");
542 	printf("  g   user partition\n");
543 	printf("Other lettered values will create user partitions\n");
544 	get_command("Select a slice for default bzb values: ",0,&slice);
545     }
546     bzb_init_slice((BZB *)entry->data->dpme_bzb,slice);
547     entry->the_map->changed = 1;
548 }
549 #endif
550 
551 void
552 do_create_partition(partition_map_header *map, int get_type)
553 {
554     long base;
555     long length;
556     char *name = 0;
557     char *type_name = 0;
558 
559     if (map == NULL) {
560 	bad_input("No partition map exists");
561 	return;
562     }
563     if (!rflag && map->writable == 0) {
564 	printf("The map is not writable.\n");
565     }
566 // XXX add help feature (i.e. '?' in any argument routine prints help string)
567     if (get_base_argument(&base, map) == 0) {
568 	return;
569     }
570     if (get_size_argument(&length, map) == 0) {
571 	return;
572     }
573 
574     if (get_string_argument("Name of partition: ", &name, 1) == 0) {
575 	bad_input("Bad name");
576 	return;
577     }
578     if (get_type == 0) {
579 	add_partition_to_map(name, kUnixType, base, length, map);
580     } else if (get_string_argument("Type of partition: ", &type_name, 1) == 0) {
581 	bad_input("Bad type");
582 	goto xit1;
583     } else {
584 	if (istrncmp(type_name, kFreeType, DPISTRLEN) == 0) {
585 	    bad_input("Can't create a partition with the Free type");
586 	    goto xit2;
587 	}
588 	if (istrncmp(type_name, kMapType, DPISTRLEN) == 0) {
589 	    bad_input("Can't create a partition with the Map type");
590 	    goto xit2;
591 	}
592 	add_partition_to_map(name, type_name, base, length, map);
593     }
594 #ifdef __m68k__
595     do_update_dpme(find_entry_by_base(base,map));
596 #endif
597 xit2:
598     if (type_name)
599         free(type_name);
600 xit1:
601     if (name)
602         free(name);
603     return;
604 }
605 
606 
607 int
608 get_base_argument(long *number, partition_map_header *map)
609 {
610     partition_map * entry;
611     int result = 0;
612 
613     if (get_number_argument("First block: ", number, kDefault) == 0) {
614 	bad_input("Bad block number");
615     } else {
616 	result = 1;
617 	if (get_partition_modifier()) {
618 	    entry = find_entry_by_disk_address(*number, map);
619 	    if (entry == NULL) {
620 		bad_input("Bad partition number");
621 		result = 0;
622 	    } else {
623 		*number = entry->data->dpme_pblock_start;
624 	    }
625 	}
626     }
627     return result;
628 }
629 
630 
631 int
632 get_size_argument(long *number, partition_map_header *map)
633 {
634     partition_map * entry;
635     int result = 0;
636     unsigned long multiple;
637 
638     if (get_number_argument("Length in blocks: ", number, kDefault) == 0) {
639 	bad_input("Bad length");
640     } else {
641 	multiple = get_multiplier(map->logical_block);
642 	if (multiple == 0) {
643 	    bad_input("Bad multiplier");
644 	} else if (multiple != 1) {
645 	    *number *= multiple;
646 	    result = 1;
647 	} else if (get_partition_modifier()) {
648 	    entry = find_entry_by_disk_address(*number, map);
649 	    if (entry == NULL) {
650 		bad_input("Bad partition number");
651 	    } else {
652 		*number = entry->data->dpme_pblocks;
653 		result = 1;
654 	    }
655 	} else {
656 	    result = 1;
657 	}
658     }
659     return result;
660 }
661 
662 
663 void
664 do_rename_partition(partition_map_header *map)
665 {
666     partition_map * entry;
667     long ix;
668     char *name;
669 
670     if (map == NULL) {
671 	bad_input("No partition map exists");
672 	return;
673     }
674     if (!rflag && map->writable == 0) {
675 	printf("The map is not writable.\n");
676     }
677     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
678 	bad_input("Bad partition number");
679 	return;
680     }
681     if (get_string_argument("New name of partition: ", &name, 1) == 0) {
682 	bad_input("Bad name");
683 	return;
684     }
685 
686 	// find partition and change it
687     entry = find_entry_by_disk_address(ix, map);
688     if (entry == NULL) {
689 	printf("No such partition\n");
690     } else {
691 	// stuff name into partition map entry data
692 	strncpy(entry->data->dpme_name, name, DPISTRLEN);
693 	map->changed = 1;
694     }
695     free(name);
696     return;
697 }
698 
699 void
700 do_change_type(partition_map_header *map)
701 {
702     partition_map * entry;
703     long ix;
704     char *type = NULL;
705 
706     if (map == NULL) {
707 	bad_input("No partition map exists");
708 	return;
709     }
710 
711     if (!rflag && map->writable == 0) {
712 	printf("The map is not writable.\n");
713     }
714 
715     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
716 	bad_input("Bad partition number");
717 	return;
718     }
719 
720     entry = find_entry_by_disk_address(ix, map);
721 
722     if (entry == NULL ) {
723         printf("No such partition\n");
724 	goto out;
725     }
726 
727     printf("Existing partition type ``%s''.\n", entry->data->dpme_type);
728     if (get_string_argument("New type of partition: ", &type, 1) == 0) {
729 	bad_input("Bad type");
730 	goto out;
731     }
732 
733     strncpy(entry->data->dpme_type, type, DPISTRLEN);
734 #ifdef __m68k__
735     do_update_dpme(entry);
736 #endif
737     map->changed = 1;
738 
739 out:
740     if (type)
741         free(type);
742     return;
743 }
744 
745 
746 void
747 do_delete_partition(partition_map_header *map)
748 {
749     partition_map * cur;
750     long ix;
751 
752     if (map == NULL) {
753 	bad_input("No partition map exists");
754 	return;
755     }
756     if (!rflag && map->writable == 0) {
757 	printf("The map is not writable.\n");
758     }
759     if (get_number_argument("Partition number: ", &ix, kDefault) == 0) {
760 	bad_input("Bad partition number");
761 	return;
762     }
763 
764 	// find partition and delete it
765     cur = find_entry_by_disk_address(ix, map);
766     if (cur == NULL) {
767 	printf("No such partition\n");
768     } else {
769 	delete_partition_from_map(cur);
770     }
771 }
772 
773 
774 void
775 do_reorder(partition_map_header *map)
776 {
777     long old_index;
778     long ix;
779 
780     if (map == NULL) {
781 	bad_input("No partition map exists");
782 	return;
783     }
784     if (!rflag && map->writable == 0) {
785 	printf("The map is not writable.\n");
786     }
787     if (get_number_argument("Partition number: ", &old_index, kDefault) == 0) {
788 	bad_input("Bad partition number");
789 	return;
790     }
791     if (get_number_argument("New number: ", &ix, kDefault) == 0) {
792 	bad_input("Bad partition number");
793 	return;
794     }
795 
796     move_entry_in_map(old_index, ix, map);
797 }
798 
799 
800 void
801 do_write_partition_map(partition_map_header *map)
802 {
803     if (map == NULL) {
804 	bad_input("No partition map exists");
805 	return;
806     }
807     if (map->changed == 0 && map->written == 0) {
808 	bad_input("The map has not been changed.");
809 	return;
810     }
811     if (map->writable == 0) {
812 	bad_input("The map is not writable.");
813 	return;
814     }
815     printf("Writing the map destroys what was there before. ");
816     if (get_okay("Is that okay? [n/y]: ", 0) != 1) {
817 	return;
818     }
819 
820     write_partition_map(map);
821 
822     map->changed = 0;
823     map->written = 1;
824 
825     // exit(0);
826 }
827 
828 
829 void
830 print_expert_notes()
831 {
832     printf("Notes:\n");
833     printf("  The expert commands are for low level and experimental features.\n");
834     printf("  These commands are available only when debug mode is on.\n");
835     printf("\n");
836 }
837 
838 
839 int
840 do_expert(partition_map_header *map, char *name)
841 {
842     int command;
843     int quit = 0;
844 
845     while (get_command("Expert command (? for help): ", first_get, &command)) {
846 	first_get = 0;
847 
848 	switch (command) {
849 	case '?':
850 	    print_expert_notes();
851 	    // fall through
852 	case 'H':
853 	case 'h':
854 	    printf("Commands are:\n");
855 	    printf("  h    print help\n");
856 	    printf("  d    dump block n\n");
857 	    printf("  p    print the partition table\n");
858 	    if (dflag) {
859 		printf("  P    (show data structures  - debugging)\n");
860 	    }
861 	    printf("  f    full display of nth entry\n");
862 	    printf("  v    validate map\n");
863 	    printf("  e    examine patch partition\n");
864 	    printf("  q    return to main edit menu\n");
865 	    printf("  Q    quit editing\n");
866 	    break;
867 	case 'q':
868 	    flush_to_newline(1);
869 	    goto finis;
870 	    break;
871 	case 'Q':
872 	    if (map->changed) {
873 		if (get_okay("Discard changes? [n/y]: ", 0) != 1) {
874 		    break;
875 		}
876 	    }
877 	    quit = 1;
878 	    goto finis;
879 	    break;
880 	case 'P':
881 	    if (dflag) {
882 		show_data_structures(map);
883 		break;
884 	    }
885 	    // fall through
886 	case 'p':
887 	    dump_partition_map(map, 1);
888 	    break;
889 	case 'D':
890 	case 'd':
891 	    do_display_block(map, name);
892 	    break;
893 	case 'F':
894 	case 'f':
895 	    do_display_entry(map);
896 	    break;
897 	case 'V':
898 	case 'v':
899 	    validate_map(map);
900 	    break;
901 	case 'E':
902 	case 'e':
903 	    do_examine_patch_partition(map);
904 	    break;
905 	default:
906 	    bad_input("No such command (%c)", command);
907 	    break;
908 	}
909     }
910 finis:
911     return quit;
912 }
913 
914 void
915 do_change_map_size(partition_map_header *map)
916 {
917     long size;
918 
919     if (map == NULL) {
920 	bad_input("No partition map exists");
921 	return;
922     }
923     if (!rflag && map->writable == 0) {
924 	printf("The map is not writable.\n");
925     }
926     if (get_number_argument("New size: ", &size, kDefault) == 0) {
927 	bad_input("Bad size");
928 	return;
929     }
930     resize_map(size, map);
931 }
932 
933 
934 void
935 do_display_block(partition_map_header *map, char *alt_name)
936 {
937     MEDIA m;
938     long number;
939     char *name;
940     static unsigned char *display_block;
941     static int display_g;
942     int g;
943     static long next_number = -1;
944 
945     if (map != NULL) {
946     	name = 0;
947 	m = map->m;
948 	g = map->logical_block;
949     } else {
950 	if (alt_name == 0) {
951 	    if (get_string_argument("Name of device: ", &name, 1) == 0) {
952 		bad_input("Bad name");
953 		return;
954 	    }
955 	} else {
956 	    if ((name = strdup(alt_name)) == NULL) {
957 		error(errno, "strdup failed");
958 		return;
959 	    }
960 	}
961 	m = open_pathname_as_media(name, O_RDONLY);
962 	if (m == 0) {
963 	    error(errno, "can't open file '%s'", name);
964 	    free(name);
965 	    return;
966 	}
967 	g = media_granularity(m);
968 	if (g < PBLOCK_SIZE) {
969 	    g = PBLOCK_SIZE;
970 	}
971     }
972     if (get_number_argument("Block number: ", &number, next_number) == 0) {
973 	bad_input("Bad block number");
974 	goto xit;
975     }
976     if (display_block == NULL || display_g < g) {
977     	if (display_block != NULL) {
978     	    free(display_block);
979     	    display_g = 0;
980 	}
981 	display_block = (unsigned char *) malloc(g);
982 	if (display_block == NULL) {
983 	    error(errno, "can't allocate memory for display block buffer");
984 	    goto xit;
985 	}
986 	display_g = g;
987     }
988     if (read_media(m, ((long long)number) * g, g, (char *)display_block) != 0) {
989 	printf("block %ld -", number);
990 	dump_block((unsigned char*) display_block, g);
991 	next_number = number + 1;
992     }
993 
994 xit:
995     if (name) {
996 	close_media(m);
997 	free(name);
998     }
999     return;
1000 }
1001 
1002 
1003 void
1004 do_display_entry(partition_map_header *map)
1005 {
1006     long number;
1007 
1008     if (map == NULL) {
1009 	bad_input("No partition map exists");
1010 	return;
1011     }
1012     if (get_number_argument("Partition number: ", &number, kDefault) == 0) {
1013 	bad_input("Bad partition number");
1014 	return;
1015     }
1016     if (number == 0) {
1017     	full_dump_block_zero(map);
1018     } else {
1019 	full_dump_partition_entry(map, number);
1020     }
1021 }
1022 
1023 
1024 void
1025 do_examine_patch_partition(partition_map_header *map)
1026 {
1027     partition_map * entry;
1028 
1029     if (map == NULL) {
1030 	bad_input("No partition map exists");
1031 	return;
1032     }
1033     entry = find_entry_by_type(kPatchType, map);
1034     if (entry == NULL) {
1035 	printf("No patch partition\n");
1036     } else {
1037 	display_patches(entry);
1038     }
1039 }
1040