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