xref: /openbsd/sbin/pdisk/pdisk.c (revision 76d0caae)
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 
51 int	lflag;	/* list the device */
52 int	rflag;	/* open device read Only */
53 
54 static int	first_get = 1;
55 
56 void	do_dump_map(struct partition_map *, int);
57 void	do_change_map_size(struct partition_map *);
58 void	do_create_partition(struct partition_map *, int);
59 void	do_delete_partition(struct partition_map *);
60 void	do_display_entry(struct partition_map *);
61 void	do_rename_partition(struct partition_map *);
62 void	do_change_type(struct partition_map *);
63 void	do_reorder(struct partition_map *);
64 void	do_write_partition_map(struct partition_map *);
65 void	edit(struct partition_map **);
66 int	get_base_argument(long *, struct partition_map *);
67 int	get_size_argument(long *, struct partition_map *);
68 
69 __dead static void usage(void);
70 
71 int
72 main(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  */
136 void
137 edit(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 
244 void
245 do_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 
282 out:
283 	free(type);
284 	free(name);
285 
286 	return;
287 }
288 
289 int
290 get_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 
313 int
314 get_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 
345 void
346 do_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 
381 void
382 do_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 
418 void
419 do_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 
437 void
438 do_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 
454 void
455 do_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 
475 void
476 do_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 
488 void
489 do_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 
503 void
504 do_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
513 usage(void)
514 {
515 	extern char *__progname;
516 
517 	fprintf(stderr, "usage: %s [-lr] disk\n", __progname);
518 
519 	exit(1);
520 }
521