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