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