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
main(int argc,char ** argv)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
edit(struct partition_map ** mapp)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
do_create_partition(struct partition_map * map,int get_type)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
get_base_argument(long * number,struct partition_map * map)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
get_size_argument(long * number,struct partition_map * map)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
do_rename_partition(struct partition_map * map)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
do_change_type(struct partition_map * map)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
do_delete_partition(struct partition_map * map)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
do_reorder(struct partition_map * map)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
do_write_partition_map(struct partition_map * map)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
do_change_map_size(struct partition_map * map)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
do_display_entry(struct partition_map * map)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
do_dump_map(struct partition_map * map,int verbose)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
usage(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