1/*	$OpenBSD: pdisk.c,v 1.87 2016/05/28 22:26:13 tb Exp $	*/
2
3/*
4 * pdisk - an editor for Apple format partition tables
5 *
6 * Written by Eryk Vershen
7 *
8 * Still under development (as of 15 January 1998)
9 */
10
11/*
12 * Copyright 1996,1997,1998 by Apple Computer, Inc.
13 *              All Rights Reserved
14 *
15 * Permission to use, copy, modify, and distribute this software and
16 * its documentation for any purpose and without fee is hereby granted,
17 * provided that the above copyright notice appears in all copies and
18 * that both the copyright notice and this permission notice appear in
19 * supporting documentation.
20 *
21 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
22 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE.
24 *
25 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
26 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
27 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
28 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
29 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 */
31
32#include <sys/param.h>		/* DEV_BSIZE */
33#include <sys/dkio.h>
34#include <sys/disklabel.h>
35#include <sys/ioctl.h>
36#include <sys/queue.h>
37#include <sys/stat.h>
38
39#include <err.h>
40#include <fcntl.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45#include <util.h>
46
47#include "partition_map.h"
48#include "io.h"
49#include "dump.h"
50
51int	lflag;	/* list the device */
52int	rflag;	/* open device read Only */
53
54static int	first_get = 1;
55
56void	do_dump_map(struct partition_map *, int);
57void	do_change_map_size(struct partition_map *);
58void	do_create_partition(struct partition_map *, int);
59void	do_delete_partition(struct partition_map *);
60void	do_display_entry(struct partition_map *);
61void	do_rename_partition(struct partition_map *);
62void	do_change_type(struct partition_map *);
63void	do_reorder(struct partition_map *);
64void	do_write_partition_map(struct partition_map *);
65void	edit(struct partition_map **);
66int	get_base_argument(long *, struct partition_map *);
67int	get_size_argument(long *, struct partition_map *);
68
69__dead static void usage(void);
70
71int
72main(int argc, char **argv)
73{
74	struct disklabel dl;
75	struct stat st;
76	struct partition_map *map;
77	int c, fd, oflags;
78
79	oflags = O_RDWR;
80	while ((c = getopt(argc, argv, "lr")) != -1) {
81		switch (c) {
82		case 'l':
83			lflag = 1;
84			oflags = O_RDONLY;
85			break;
86		case 'r':
87			rflag = 1;
88			oflags = O_RDONLY;
89			break;
90		default:
91			usage();
92			break;
93		}
94	}
95
96	argc -= optind;
97	argv += optind;
98
99	if (argc != 1)
100		usage();
101
102	fd = opendev(*argv, oflags, OPENDEV_PART, NULL);
103	if (fd == -1)
104		err(1, "can't open file '%s'", *argv);
105
106	if (fstat(fd, &st) == -1)
107		err(1, "can't fstat %s", *argv);
108	if (!S_ISCHR(st.st_mode))
109		errx(1, "%s is not a character device", *argv);
110
111	if (ioctl(fd, DIOCGPDINFO, &dl) == -1)
112		err(1, "can't get disklabel for %s", *argv);
113	if (dl.d_secsize != DEV_BSIZE)
114		errx(1, "disk sector size (%d) != 512\n", dl.d_secsize);
115
116	if (pledge("stdio", NULL) == -1)
117		err(1, "pledge");
118
119	map = open_partition_map(fd, *argv, DL_GETDSIZE(&dl), dl.d_secsize);
120	if (map != NULL) {
121		if (lflag)
122			dump_partition_map(map);
123		else
124			edit(&map);
125	}
126
127	free_partition_map(map);
128	close(fd);
129
130	return 0;
131}
132
133/*
134 * Edit the file
135 */
136void
137edit(struct partition_map **mapp)
138{
139	struct partition_map *map = *mapp;
140	struct partition_map *oldmap;
141	int command;
142
143	printf("Edit %s -\n", map->name);
144
145	while (get_command("Command (? for help): ", first_get, &command)) {
146		first_get = 0;
147
148		switch (command) {
149		case '?':
150			printf("Notes:\n"
151			    "  Base and length fields are blocks, which "
152			    "vary in size between media.\n"
153			    "  The base field can be <nth>p; i.e. the "
154			    "base of the nth partition.\n"
155			    "  The length field can be a length followed "
156			    "by k, m, g or t to indicate\n"
157			    "    kilo, mega, giga, or tera bytes.\n"
158			    "  The length field can also be <nth>p; i.e. "
159			    "the length of the nth partition.\n"
160			    "  The name of a partition is descriptive "
161			    "text.\n\n");
162
163			/* fall through */
164		case 'h':
165			printf("Commands are:\n"
166			    "  ?    verbose command help\n"
167			    "  C    create a partition of a specified type\n"
168			    "  c    create an OpenBSD partition\n"
169			    "  d    delete a partition\n"
170			    "  f    full display of a partition\n"
171			    "  h    command help\n"
172			    "  i    (re)initialize the partition map\n"
173			    "  n    (re)name a partition\n"
174			    "  P    show the partition map's data structures\n"
175			    "  p    print the partition map\n"
176			    "  q    quit editing\n"
177			    "  r    reorder (swap) disk positions of two "
178			        "entries in the partition map\n"
179			    "  s    change the size of the partition map\n"
180			    "  t    change the type of a partition\n"
181			    "  w    write the partition map to disk\n");
182			break;
183		case 'P':
184			do_dump_map(map, 1);
185			break;
186		case 'p':
187			do_dump_map(map, 0);
188			break;
189		case 'q':
190			if (map->changed) {
191				if (get_okay("Discard changes? [n/y]: ", 0) !=
192				    1) {
193					break;
194				}
195			}
196			flush_to_newline(1);
197			return;
198		case 'i':
199			if (get_okay("Discard current map? [n/y]: ", 0) == 1) {
200				oldmap = map;
201				map = create_partition_map(oldmap->fd,
202				    oldmap->name, oldmap->media_size,
203				    oldmap->sbBlkSize);
204				if (map == NULL)
205					break;
206				*mapp = map;
207				free_partition_map(oldmap);
208			}
209			break;
210		case 'C':
211			do_create_partition(map, 1);
212			break;
213		case 'c':
214			do_create_partition(map, 0);
215			break;
216		case 'n':
217			do_rename_partition(map);
218			break;
219		case 'd':
220			do_delete_partition(map);
221			break;
222		case 'r':
223			do_reorder(map);
224			break;
225		case 's':
226			do_change_map_size(map);
227			break;
228		case 't':
229			do_change_type(map);
230			break;
231		case 'w':
232			do_write_partition_map(map);
233			break;
234		case 'f':
235			do_display_entry(map);
236			break;
237		default:
238			bad_input("No such command (%c)", command);
239			break;
240		}
241	}
242}
243
244void
245do_create_partition(struct partition_map *map, int get_type)
246{
247	long base, length;
248	char *name = NULL;
249	char *type = NULL;
250
251	if (get_base_argument(&base, map) == 0)
252		return;
253	if (get_size_argument(&length, map) == 0)
254		return;
255
256	name = get_dpistr_argument("Name of partition: ");
257	if (name == NULL) {
258		bad_input("Bad name");
259		goto out;
260	}
261
262	if (get_type == 0)
263		type = strdup(kUnixType);
264	else
265		type = get_dpistr_argument("Type of partition: ");
266	if (type == NULL) {
267		bad_input("Bad type");
268		goto out;
269	}
270
271	if (strncasecmp(type, kFreeType, DPISTRLEN) == 0) {
272		bad_input("Can't create a partition with the Free type");
273		goto out;
274	}
275	if (strncasecmp(type, kMapType, DPISTRLEN) == 0) {
276		bad_input("Can't create a partition with the Map type");
277		goto out;
278	}
279
280	add_partition_to_map(name, type, base, length, map);
281
282out:
283	free(type);
284	free(name);
285
286	return;
287}
288
289int
290get_base_argument(long *number, struct partition_map *map)
291{
292	struct entry *entry;
293	int result = 0;
294
295	if (get_number_argument("First block: ", number) == 0) {
296		bad_input("Bad block number");
297	} else {
298		result = 1;
299		if (get_partition_modifier()) {
300			entry = find_entry_by_disk_address(*number, map);
301			if (entry == NULL) {
302				bad_input("Bad partition number");
303				result = 0;
304			} else {
305				*number = entry->dpme_pblock_start;
306			}
307		}
308	}
309	return result;
310}
311
312
313int
314get_size_argument(long *number, struct partition_map *map)
315{
316	struct entry *entry;
317	unsigned long multiple;
318	int result = 0;
319
320	if (get_number_argument("Length in blocks: ", number) == 0) {
321		bad_input("Bad length");
322	} else {
323		multiple = get_multiplier(map->sbBlkSize);
324		if (multiple == 0) {
325			bad_input("Bad multiplier");
326		} else if (multiple != 1) {
327			*number *= multiple;
328			result = 1;
329		} else if (get_partition_modifier()) {
330			entry = find_entry_by_disk_address(*number, map);
331			if (entry == NULL) {
332				bad_input("Bad partition number");
333			} else {
334				*number = entry->dpme_pblocks;
335				result = 1;
336			}
337		} else {
338			result = 1;
339		}
340	}
341	return result;
342}
343
344
345void
346do_rename_partition(struct partition_map *map)
347{
348	struct entry *entry;
349	char *name;
350	long ix;
351
352	if (get_number_argument("Partition number: ", &ix) == 0) {
353		bad_input("Bad partition number");
354		return;
355	}
356	entry = find_entry_by_disk_address(ix, map);
357	if (entry == NULL) {
358		printf("No such partition\n");
359		return;
360	}
361
362	printf("Existing partition name ``%s''.\n", entry->dpme_name);
363	name = get_dpistr_argument("New name of partition: ");
364	if (name == NULL) {
365		bad_input("Bad name");
366		return;
367	}
368
369	/*
370	 * Since dpme_name is supposed to be NUL-filled, make sure
371	 * current contents are zapped before copying in new name!
372	 */
373	memset(entry->dpme_name, 0, sizeof(entry->dpme_name));
374	strlcpy(entry->dpme_name, name, sizeof(entry->dpme_name));
375	map->changed = 1;
376
377	free(name);
378	return;
379}
380
381void
382do_change_type(struct partition_map *map)
383{
384	struct entry *entry;
385	char *type;
386	long ix;
387
388	if (get_number_argument("Partition number: ", &ix) == 0) {
389		bad_input("Bad partition number");
390		return;
391	}
392	entry = find_entry_by_disk_address(ix, map);
393	if (entry == NULL) {
394		printf("No such partition\n");
395		return;
396	}
397
398	printf("Existing partition type ``%s''.\n", entry->dpme_type);
399	type = get_dpistr_argument("New type of partition: ");
400	if (type == NULL) {
401		bad_input("Bad type");
402		return;
403	}
404
405        /*
406	 * Since dpme_type is supposed to be NUL-filled, make sure
407         * current contents are zapped before copying in new type!
408	 */
409	memset(entry->dpme_type, 0, sizeof(entry->dpme_type));
410	strncpy(entry->dpme_type, type, sizeof(entry->dpme_type));
411	map->changed = 1;
412
413	free(type);
414	return;
415}
416
417
418void
419do_delete_partition(struct partition_map *map)
420{
421	struct entry *cur;
422	long ix;
423
424	if (get_number_argument("Partition number: ", &ix) == 0) {
425		bad_input("Bad partition number");
426		return;
427	}
428
429	cur = find_entry_by_disk_address(ix, map);
430	if (cur == NULL)
431		printf("No such partition\n");
432	else
433		delete_partition_from_map(cur);
434}
435
436
437void
438do_reorder(struct partition_map *map)
439{
440	long ix, old_index;
441
442	if (get_number_argument("Partition number: ", &old_index) == 0) {
443		bad_input("Bad partition number");
444		return;
445	}
446	if (get_number_argument("New number: ", &ix) == 0) {
447		bad_input("Bad partition number");
448		return;
449	}
450	move_entry_in_map(old_index, ix, map);
451}
452
453
454void
455do_write_partition_map(struct partition_map *map)
456{
457	if (map->changed == 0) {
458		bad_input("The map has not been changed.");
459		return;
460	}
461	if (rflag) {
462		bad_input("The map is not writable.");
463		return;
464	}
465	printf("Writing the map destroys what was there before. ");
466	if (get_okay("Is that okay? [n/y]: ", 0) != 1) {
467		return;
468	}
469	write_partition_map(map);
470
471	map->changed = 0;
472}
473
474
475void
476do_change_map_size(struct partition_map *map)
477{
478	long size;
479
480	if (get_number_argument("New size: ", &size) == 0) {
481		bad_input("Bad size");
482		return;
483	}
484	resize_map(size, map);
485}
486
487
488void
489do_display_entry(struct partition_map *map)
490{
491	long number;
492
493	if (get_number_argument("Partition number: ", &number) == 0) {
494		bad_input("Bad partition number");
495		return;
496	}
497	if (number == 0)
498		full_dump_block_zero(map);
499	else
500		full_dump_partition_entry(map, number);
501}
502
503void
504do_dump_map(struct partition_map *map, int verbose)
505{
506	if (verbose)
507		show_data_structures(map);
508	else
509		dump_partition_map(map);
510}
511
512__dead static void
513usage(void)
514{
515	extern char *__progname;
516
517	fprintf(stderr, "usage: %s [-lr] disk\n", __progname);
518
519	exit(1);
520}
521