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