xref: /openbsd-src/sbin/pdisk/dump.c (revision 898184e3e61f9129feb5978fad5a8c6865f00b92)
1 //
2 // dump.c - dumping partition maps
3 //
4 // Written by Eryk Vershen
5 //
6 
7 /*
8  * Copyright 1996,1997,1998 by Apple Computer, Inc.
9  *              All Rights Reserved
10  *
11  * Permission to use, copy, modify, and distribute this software and
12  * its documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appears in all copies and
14  * that both the copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
18  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE.
20  *
21  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
22  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
24  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
25  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26  */
27 
28 // for *printf()
29 #include <stdio.h>
30 
31 // for malloc() & free()
32 #include <stdlib.h>
33 
34 // for strcmp()
35 #include <string.h>
36 // for O_RDONLY
37 #include <fcntl.h>
38 // for errno
39 #include <errno.h>
40 
41 #include "dump.h"
42 #include "pathname.h"
43 #include "io.h"
44 #include "errors.h"
45 
46 
47 //
48 // Defines
49 //
50 #if DPISTRLEN != 32
51 #error Change in strlen in partition entries! Fix constants
52 #endif
53 
54 #define get_align_long(x)	(*(x))
55 
56 
57 //
58 // Types
59 //
60 typedef struct names {
61     const char *abbr;
62     const char *full;
63 } NAMES;
64 
65 typedef unsigned long OSType;
66 
67 typedef struct PatchDescriptor {
68     OSType		patchSig;
69     unsigned short	majorVers;
70     unsigned short	minorVers;
71     unsigned long	flags;
72     unsigned long	patchOffset;
73     unsigned long	patchSize;
74     unsigned long	patchCRC;
75     unsigned long	patchDescriptorLen;
76     unsigned char	patchName[33];
77     unsigned char	patchVendor[1];
78 } PatchDescriptor;
79 typedef PatchDescriptor * PatchDescriptorPtr;
80 
81 typedef struct PatchList {
82     unsigned short numPatchBlocks;	// number of disk blocks to hold the patch list
83     unsigned short numPatches;		// number of patches in list
84     PatchDescriptor thePatch[1];
85 } PatchList;
86 typedef PatchList *PatchListPtr;
87 
88 
89 //
90 // Global Constants
91 //
92 NAMES plist[] = {
93     {"Drvr", "Apple_Driver"},
94     {"Drv4", "Apple_Driver43"},
95     {"Free", "Apple_Free"},
96     {"Patc", "Apple_Patches"},
97     {" HFS", "Apple_HFS"},
98     {" MFS", "Apple_MFS"},
99     {"PDOS", "Apple_PRODOS"},
100     {"junk", "Apple_Scratch"},
101     {"unix", "Apple_UNIX_SVR2"},
102     {" map", "Apple_partition_map"},
103     {0,	0},
104 };
105 
106 const char * kStringEmpty	= "";
107 const char * kStringNot		= " not";
108 
109 
110 //
111 // Global Variables
112 //
113 int aflag = AFLAG_DEFAULT;	/* abbreviate partition types */
114 int pflag = PFLAG_DEFAULT;	/* show physical limits of partition */
115 int fflag = FFLAG_DEFAULT;	/* show HFS volume names */
116 
117 
118 //
119 // Forward declarations
120 //
121 void adjust_value_and_compute_prefix(double *value, int *prefix);
122 void dump_block_zero(partition_map_header *map);
123 void dump_partition_entry(partition_map *entry, int type_length, int name_length, int digits);
124 int get_max_base_or_length(partition_map_header *map);
125 int get_max_name_string_length(partition_map_header *map);
126 int get_max_type_string_length(partition_map_header *map);
127 
128 
129 //
130 // Routines
131 //
132 int
133 dump(char *name)
134 {
135     partition_map_header *map;
136     int junk;
137 
138     map = open_partition_map(name, &junk, 0);
139     if (map == NULL) {
140 	//error(-1, "No partition map in '%s'", name);
141 	return 0;
142     }
143 
144     dump_partition_map(map, 1);
145 
146     close_partition_map(map);
147 
148     return 1;
149 }
150 
151 
152 void
153 dump_block_zero(partition_map_header *map)
154 {
155     Block0 *p;
156     DDMap *m;
157     int i;
158     double value;
159     int prefix;
160     long t;
161 
162     p = map->misc;
163     if (p->sbSig != BLOCK0_SIGNATURE) {
164 	return;
165     }
166 
167     value = ((double)p->sbBlkCount) * p->sbBlkSize;
168     adjust_value_and_compute_prefix(&value, &prefix);
169     printf("\nDevice block size=%u, Number of Blocks=%lu (%1.1f%c)\n",
170 	    p->sbBlkSize, p->sbBlkCount, value, prefix);
171 
172     printf("DeviceType=0x%x, DeviceId=0x%x\n",
173 	    p->sbDevType, p->sbDevId);
174     if (p->sbDrvrCount > 0) {
175 	printf("Drivers-\n");
176 	m = (DDMap *) p->sbMap;
177 	for (i = 0; i < p->sbDrvrCount; i++) {
178 	    printf("%u: %3u @ %lu, ", i+1,
179 		    m[i].ddSize, get_align_long(&m[i].ddBlock));
180 	    if (map->logical_block != p->sbBlkSize) {
181 		t = (m[i].ddSize * p->sbBlkSize) / map->logical_block;
182 		printf("(%lu@", t);
183 		t = (get_align_long(&m[i].ddBlock) * p->sbBlkSize)
184 			/ map->logical_block;
185 		printf("%lu)  ", t);
186 	    }
187 	    printf("type=0x%x\n", m[i].ddType);
188 	}
189     }
190     printf("\n");
191 }
192 
193 
194 void
195 dump_partition_map(partition_map_header *map, int disk_order)
196 {
197     partition_map * entry;
198     int max_type_length;
199     int max_name_length;
200     int digits;
201 
202     if (map == NULL) {
203 	bad_input("No partition map exists");
204 	return;
205     }
206     printf("\nPartition map (with %d byte blocks) on '%s'\n",
207 	map->logical_block, map->name);
208 
209     digits = number_of_digits(get_max_base_or_length(map));
210     if (digits < 6) {
211 	digits = 6;
212     }
213     if (aflag) {
214 	max_type_length = 4;
215     } else {
216 	max_type_length = get_max_type_string_length(map);
217 	if (max_type_length < 4) {
218 	    max_type_length = 4;
219 	}
220     }
221     max_name_length = get_max_name_string_length(map);
222     if (max_name_length < 6) {
223 	max_name_length = 6;
224     }
225     printf(" #: %*s %-*s %*s   %-*s ( size )\n",
226 	    max_type_length, "type",
227 	    max_name_length, "name",
228 	    digits, "length", digits, "base");
229 
230     if (disk_order) {
231 	for (entry = map->disk_order; entry != NULL;
232 		entry = entry->next_on_disk) {
233 
234 	    dump_partition_entry(entry, max_type_length, max_name_length, digits);
235 	}
236     } else {
237 	for (entry = map->base_order; entry != NULL;
238 		entry = entry->next_by_base) {
239 
240 	    dump_partition_entry(entry, max_type_length, max_name_length, digits);
241 	}
242     }
243     dump_block_zero(map);
244 }
245 
246 
247 void
248 dump_partition_entry(partition_map *entry, int type_length, int name_length, int digits)
249 {
250     partition_map_header *map;
251     int j;
252     DPME *p;
253     const char *s;
254     u32 size;
255     double bytes;
256     int driver;
257     // int kind;
258     char *buf;
259 #if 1
260     BZB *bp;
261 #endif
262 
263     map = entry->the_map;
264     p = entry->data;
265     driver = entry->contains_driver? '*': ' ';
266     if (aflag) {
267 	s = "????";
268 	for (j = 0; plist[j].abbr != 0; j++) {
269 	    if (strcmp(p->dpme_type, plist[j].full) == 0) {
270 		s = plist[j].abbr;
271 		break;
272 	    }
273 	}
274 	printf("%2ld: %.4s", entry->disk_address, s);
275     } else {
276 	printf("%2ld: %*.32s", entry->disk_address, type_length, p->dpme_type);
277     }
278 
279     buf = (char *) malloc(name_length+1);
280     if (entry->HFS_name == NULL || fflag == 0) {
281 	strncpy(buf, p->dpme_name, name_length);
282 	buf[name_length] = 0;
283     } else {
284 	snprintf(buf, name_length + 1, "\"%s\"", entry->HFS_name);
285     }
286     printf("%c%-*.32s ", driver, name_length, buf);
287     free(buf);
288     /*
289     switch (entry->HFS_kind) {
290     case kHFS_std:	kind = 'h'; break;
291     case kHFS_embed:	kind = 'e'; break;
292     case kHFS_plus:	kind = '+'; break;
293     default:
294     case kHFS_not:	kind = ' '; break;
295     }
296     printf("%c ", kind);
297     */
298 
299     if (pflag) {
300 	printf("%*lu ", digits, p->dpme_pblocks);
301 	size = p->dpme_pblocks;
302     } else if (p->dpme_lblocks + p->dpme_lblock_start != p->dpme_pblocks) {
303 	printf("%*lu+", digits, p->dpme_lblocks);
304 	size = p->dpme_lblocks;
305     } else if (p->dpme_lblock_start != 0) {
306 	printf("%*lu ", digits, p->dpme_lblocks);
307 	size = p->dpme_lblocks;
308     } else {
309 	printf("%*lu ", digits, p->dpme_pblocks);
310 	size = p->dpme_pblocks;
311     }
312     if (pflag || p->dpme_lblock_start == 0) {
313 	printf("@ %-*lu", digits, p->dpme_pblock_start);
314     } else {
315 	printf("@~%-*lu", digits, p->dpme_pblock_start + p->dpme_lblock_start);
316     }
317 
318     bytes = ((double)size) * map->logical_block;
319     adjust_value_and_compute_prefix(&bytes, &j);
320     if (j != ' ' && j != 'K') {
321 	printf(" (%#5.1f%c)", bytes, j);
322     }
323 
324 #if 1
325     // Old A/UX fields that no one pays attention to anymore.
326     bp = (BZB *) (p->dpme_bzb);
327     j = -1;
328     if (bp->bzb_magic == BZBMAGIC) {
329 	switch (bp->bzb_type) {
330 	case FSTEFS:
331 	    s = "EFS";
332 	    break;
333 	case FSTSFS:
334 	    s = "SFS";
335 	    j = 1;
336 	    break;
337 	case FST:
338 	default:
339 	    if (bzb_root_get(bp) != 0) {
340 		if (bzb_usr_get(bp) != 0) {
341 		    s = "RUFS";
342 		} else {
343 		    s = "RFS";
344 		}
345 		j = 0;
346 	    } else if (bzb_usr_get(bp) != 0) {
347 		s = "UFS";
348 		j = 2;
349 	    } else {
350 		s = "FS";
351 	    }
352 	    break;
353 	}
354 	if (bzb_slice_get(bp) != 0) {
355 	    printf(" s%1ld %4s", bzb_slice_get(bp)-1, s);
356 	} else if (j >= 0) {
357 	    printf(" S%1d %4s", j, s);
358 	} else {
359 	    printf("    %4s", s);
360 	}
361 	if (bzb_crit_get(bp) != 0) {
362 	    printf(" K%1d", bp->bzb_cluster);
363 	} else if (j < 0) {
364 	    printf("   ");
365 	} else {
366 	    printf(" k%1d", bp->bzb_cluster);
367 	}
368 	if (bp->bzb_mount_point[0] != 0) {
369 	    printf("  %.64s", bp->bzb_mount_point);
370 	}
371     }
372 #endif
373     printf("\n");
374 }
375 
376 
377 void
378 show_data_structures(partition_map_header *map)
379 {
380     Block0 *zp;
381     DDMap *m;
382     int i;
383     int j;
384     partition_map * entry;
385     DPME *p;
386     BZB *bp;
387     const char *s;
388 
389     if (map == NULL) {
390 	printf("No partition map exists\n");
391 	return;
392     }
393     printf("Header:\n");
394     printf("map %d blocks out of %d,  media %lu blocks (%d byte blocks)\n",
395 	    map->blocks_in_map, map->maximum_in_map,
396 	    map->media_size, map->logical_block);
397     printf("Map is%s writable", (map->writable)?kStringEmpty:kStringNot);
398     printf(", but%s changed", (map->changed)?kStringEmpty:kStringNot);
399     printf(" and has%s been written\n", (map->written)?kStringEmpty:kStringNot);
400     printf("\n");
401 
402     if (map->misc == NULL) {
403 	printf("No block zero\n");
404     } else {
405 	zp = map->misc;
406 
407 	printf("Block0:\n");
408 	printf("signature 0x%x", zp->sbSig);
409 	if (zp->sbSig == BLOCK0_SIGNATURE) {
410 	    printf("\n");
411 	} else {
412 	    printf(" should be 0x%x\n", BLOCK0_SIGNATURE);
413 	}
414 	printf("Block size=%u, Number of Blocks=%lu\n",
415 		zp->sbBlkSize, zp->sbBlkCount);
416 	printf("DeviceType=0x%x, DeviceId=0x%x, sbData=0x%lx\n",
417 		zp->sbDevType, zp->sbDevId, zp->sbData);
418 	if (zp->sbDrvrCount == 0) {
419 	    printf("No drivers\n");
420 	} else {
421 	    printf("%u driver%s-\n", zp->sbDrvrCount,
422 		    (zp->sbDrvrCount>1)?"s":kStringEmpty);
423 	    m = (DDMap *) zp->sbMap;
424 	    for (i = 0; i < zp->sbDrvrCount; i++) {
425             printf("%u: @ %lu for %u, type=0x%x\n", i+1,
426 		   get_align_long(&m[i].ddBlock),
427 		   m[i].ddSize, m[i].ddType);
428 	    }
429 	}
430     }
431     printf("\n");
432 
433 /*
434 u32     dpme_boot_args[32]      ;
435 u32     dpme_reserved_3[62]     ;
436 */
437     printf(" #:                 type  length   base    "
438 	    "flags        (logical)\n");
439     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
440 	p = entry->data;
441 	printf("%2ld: %20.32s ",
442 		entry->disk_address, p->dpme_type);
443 	printf("%7lu @ %-7lu ", p->dpme_pblocks, p->dpme_pblock_start);
444 	printf("%c%c%c%c%c%c%c%c%c%c%c%c ",
445 		(dpme_valid_get(p))?'V':'.',
446 		(dpme_allocated_get(p))?'A':'.',
447 		(dpme_in_use_get(p))?'I':'.',
448 		(dpme_bootable_get(p))?'B':'.',
449 		(dpme_readable_get(p))?'R':'.',
450 		(dpme_writable_get(p))?'W':'.',
451 		(dpme_os_pic_code_get(p))?'P':'.',
452 		(dpme_os_specific_2_get(p))?'2':'.',
453 		(dpme_chainable_get(p))?'C':'.',
454 		(dpme_diskdriver_get(p))?'D':'.',
455 		(bitfield_get(p->dpme_flags, 30, 1))?'M':'.',
456 		(bitfield_get(p->dpme_flags, 31, 1))?'X':'.');
457 	if (p->dpme_lblock_start != 0 || p->dpme_pblocks != p->dpme_lblocks) {
458 	    printf("(%lu @ %lu)", p->dpme_lblocks, p->dpme_lblock_start);
459 	}
460 	printf("\n");
461     }
462     printf("\n");
463     printf(" #:  booter   bytes      load_address      "
464 	    "goto_address checksum processor\n");
465     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
466 	p = entry->data;
467 	printf("%2ld: ", entry->disk_address);
468 	printf("%7lu ", p->dpme_boot_block);
469 	printf("%7lu ", p->dpme_boot_bytes);
470 	printf("%8lx ", (u32)p->dpme_load_addr);
471 	printf("%8lx ", (u32)p->dpme_load_addr_2);
472 	printf("%8lx ", (u32)p->dpme_goto_addr);
473 	printf("%8lx ", (u32)p->dpme_goto_addr_2);
474 	printf("%8lx ", p->dpme_checksum);
475 	printf("%.32s", p->dpme_process_id);
476 	printf("\n");
477     }
478     printf("\n");
479 /*
480 xx: cccc RU *dd s...
481 */
482     printf(" #: type RU *slice mount_point (A/UX only fields)\n");
483     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
484 	p = entry->data;
485 	printf("%2ld: ", entry->disk_address);
486 
487 	bp = (BZB *) (p->dpme_bzb);
488 	j = -1;
489 	if (bp->bzb_magic == BZBMAGIC) {
490 	    switch (bp->bzb_type) {
491 	    case FSTEFS:
492 		s = "esch";
493 		break;
494 	    case FSTSFS:
495 		s = "swap";
496 		j = 1;
497 		break;
498 	    case FST:
499 	    default:
500 		s = "fsys";
501 		if (bzb_root_get(bp) != 0) {
502 		    j = 0;
503 		} else if (bzb_usr_get(bp) != 0) {
504 		    j = 2;
505 		}
506 		break;
507 	    }
508 	    printf("%4s ", s);
509 	    printf("%c%c ",
510 		    (bzb_root_get(bp))?'R':' ',
511 		    (bzb_usr_get(bp))?'U':' ');
512 	    if (bzb_slice_get(bp) != 0) {
513 		printf("  %2ld", bzb_slice_get(bp)-1);
514 	    } else if (j >= 0) {
515 		printf(" *%2d", j);
516 	    } else {
517 		printf("    ");
518 	    }
519 	    if (bp->bzb_mount_point[0] != 0) {
520 		printf(" %.64s", bp->bzb_mount_point);
521 	    }
522 	}
523 	printf("\n");
524     }
525 }
526 
527 
528 void
529 full_dump_partition_entry(partition_map_header *map, int ix)
530 {
531     partition_map * cur;
532     DPME *p;
533     int i;
534     u32 t;
535 
536     cur = find_entry_by_disk_address(ix, map);
537     if (cur == NULL) {
538 	printf("No such partition\n");
539 	return;
540     }
541 
542     p = cur->data;
543     printf("             signature: 0x%x\n", p->dpme_signature);
544     printf("             reserved1: 0x%x\n", p->dpme_reserved_1);
545     printf(" number of map entries: %ld\n", p->dpme_map_entries);
546     printf("        physical start: %10lu  length: %10lu\n", p->dpme_pblock_start, p->dpme_pblocks);
547     printf("         logical start: %10lu  length: %10lu\n", p->dpme_lblock_start, p->dpme_lblocks);
548 
549     printf("                 flags: 0x%lx\n", (u32)p->dpme_flags);
550     printf("                        ");
551     if (dpme_valid_get(p)) printf("valid ");
552     if (dpme_allocated_get(p)) printf("alloc ");
553     if (dpme_in_use_get(p)) printf("in-use ");
554     if (dpme_bootable_get(p)) printf("boot ");
555     if (dpme_readable_get(p)) printf("read ");
556     if (dpme_writable_get(p)) printf("write ");
557     if (dpme_os_pic_code_get(p)) printf("pic ");
558     t = p->dpme_flags >> 7;
559     for (i = 7; i <= 31; i++) {
560     	if (t & 0x1) {
561     	    printf("%d ", i);
562     	}
563     	t = t >> 1;
564     }
565     printf("\n");
566 
567     printf("                  name: '%.32s'\n", p->dpme_name);
568     printf("                  type: '%.32s'\n", p->dpme_type);
569 
570     printf("      boot start block: %10lu\n", p->dpme_boot_block);
571     printf("boot length (in bytes): %10lu\n", p->dpme_boot_bytes);
572     printf("          load address: 0x%08lx  0x%08lx\n",
573 		(u32)p->dpme_load_addr, (u32)p->dpme_load_addr_2);
574     printf("         start address: 0x%08lx  0x%08lx\n",
575 		(u32)p->dpme_goto_addr, (u32)p->dpme_goto_addr_2);
576     printf("              checksum: 0x%08lx\n", p->dpme_checksum);
577     printf("             processor: '%.32s'\n", p->dpme_process_id);
578     printf("boot args field -");
579     dump_block((unsigned char *)p->dpme_boot_args, 32*4);
580     printf("dpme_reserved_3 -");
581     dump_block((unsigned char *)p->dpme_reserved_3, 62*4);
582 }
583 
584 
585 void
586 dump_block(unsigned char *addr, int len)
587 {
588     int i;
589     int j;
590     int limit1;
591     int limit;
592 #define LINE_LEN 16
593 #define UNIT_LEN  4
594 #define OTHER_LEN  8
595 
596     for (i = 0; i < len; i = limit) {
597     	limit1 = i + LINE_LEN;
598     	if (limit1 > len) {
599     	    limit = len;
600     	} else {
601     	    limit = limit1;
602     	}
603 	printf("\n%03x: ", i);
604     	for (j = i; j < limit1; j++) {
605 	    if (j % UNIT_LEN == 0) {
606 		printf(" ");
607 	    }
608 	    if (j < limit) {
609 		printf("%02x", addr[j]);
610 	    } else {
611 		printf("  ");
612 	    }
613     	}
614 	printf(" ");
615     	for (j = i; j < limit; j++) {
616 	    if (j % OTHER_LEN == 0) {
617 		printf(" ");
618 	    }
619     	    if (addr[j] < ' ') {
620     	    	printf(".");
621     	    } else {
622     	    	printf("%c", addr[j]);
623     	    }
624     	}
625     }
626     printf("\n");
627 }
628 
629 void
630 full_dump_block_zero(partition_map_header *map)
631 {
632     Block0 *zp;
633     DDMap *m;
634     int i;
635 
636     if (map == NULL) {
637 	printf("No partition map exists\n");
638 	return;
639     }
640 
641     if (map->misc == NULL) {
642 	printf("No block zero\n");
643 	return;
644     }
645     zp = map->misc;
646 
647     printf("             signature: 0x%x\n", zp->sbSig);
648     printf("       size of a block: %d\n", zp->sbBlkSize);
649     printf("      number of blocks: %ld\n", zp->sbBlkCount);
650     printf("           device type: 0x%x\n", zp->sbDevType);
651     printf("             device id: 0x%x\n", zp->sbDevId);
652     printf("                  data: 0x%lx\n", zp->sbData);
653     printf("          driver count: %d\n", zp->sbDrvrCount);
654     m = (DDMap *) zp->sbMap;
655     for (i = 0; &m[i].ddType < &zp->sbMap[247]; i++) {
656     	if (m[i].ddBlock == 0 && m[i].ddSize == 0 && m[i].ddType == 0) {
657     	    break;
658     	}
659 	printf("      driver %3u block: %ld\n", i+1, m[i].ddBlock);
660 	printf("        size in blocks: %d\n", m[i].ddSize);
661 	printf("           driver type: 0x%x\n", m[i].ddType);
662     }
663     printf("remainder of block -");
664     dump_block((unsigned char *)&m[i].ddBlock, (&zp->sbMap[247]-((unsigned short *)&m[i].ddBlock))*2);
665 }
666 
667 
668 void
669 display_patches(partition_map *entry)
670 {
671     long long offset;
672     MEDIA m;
673     static unsigned char *patch_block;
674     PatchListPtr p;
675     PatchDescriptorPtr q;
676     unsigned char *next;
677     unsigned char *s;
678     int i;
679 
680     offset = entry->data->dpme_pblock_start;
681     m = entry->the_map->m;
682     offset = ((long long) entry->data->dpme_pblock_start) * entry->the_map->logical_block;
683     if (patch_block == NULL) {
684 	patch_block = (unsigned char *) malloc(PBLOCK_SIZE);
685 	if (patch_block == NULL) {
686 	    error(errno, "can't allocate memory for patch block buffer");
687 	    return;
688 	}
689     }
690     if (read_media(m, (long long)offset, PBLOCK_SIZE, (char *)patch_block) == 0) {
691 	error(errno, "Can't read patch block");
692 	return;
693     }
694     p = (PatchListPtr) patch_block;
695     if (p->numPatchBlocks != 1) {
696 	i = p->numPatchBlocks;
697 	free(patch_block);
698 	patch_block = (unsigned char *) malloc(PBLOCK_SIZE*i);
699 	if (patch_block == NULL) {
700 	    error(errno, "can't allocate memory for patch blocks buffer");
701 	    return;
702 	}
703 	s = patch_block + PBLOCK_SIZE*i;
704 	while (i > 0) {
705 	    s -= PBLOCK_SIZE;
706 	    i -= 1;
707 	    if (read_media(m, offset+i, PBLOCK_SIZE, (char *)s) == 0) {
708 		error(errno, "Can't read patch block %d", i);
709 		return;
710 	    }
711 	}
712 	p = (PatchListPtr) patch_block;
713     }
714     printf("Patch list (%d entries)\n", p->numPatches);
715     q = p->thePatch;
716     for (i = 0; i < p->numPatches; i++) {
717 	printf("%2d signature: '%.4s'\n", i+1, (char *)&q->patchSig);
718 	printf("     version: %d.%d\n", q->majorVers, q->minorVers);
719 	printf("       flags: 0x%lx\n", q->flags);
720 	printf("      offset: %ld\n", q->patchOffset);
721 	printf("        size: %ld\n", q->patchSize);
722 	printf("         CRC: 0x%lx\n", q->patchCRC);
723 	printf("        name: '%.*s'\n", q->patchName[0], &q->patchName[1]);
724 	printf("      vendor: '%.*s'\n", q->patchVendor[0], &q->patchVendor[1]);
725 	next = ((unsigned char *)q) + q->patchDescriptorLen;
726 	s = &q->patchVendor[q->patchVendor[0]+1];
727 	if (next > s) {
728 	    printf("remainder of entry -");
729 	    dump_block(s, next-s);
730 	}
731 	q = (PatchDescriptorPtr)next;
732     }
733 }
734 
735 int
736 get_max_type_string_length(partition_map_header *map)
737 {
738     partition_map * entry;
739     int max;
740     int length;
741 
742     if (map == NULL) {
743 	return 0;
744     }
745 
746     max = 0;
747 
748     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
749 	length = strnlen(entry->data->dpme_type, DPISTRLEN);
750 	if (length > max) {
751 	    max = length;
752 	}
753     }
754 
755     return max;
756 }
757 
758 int
759 get_max_name_string_length(partition_map_header *map)
760 {
761     partition_map * entry;
762     int max;
763     int length;
764 
765     if (map == NULL) {
766 	return 0;
767     }
768 
769     max = 0;
770 
771     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
772 	length = strnlen(entry->data->dpme_name, DPISTRLEN);
773 	if (length > max) {
774 	    max = length;
775 	}
776 
777 	if (fflag) {
778 		if (entry->HFS_name == NULL) {
779 		    length = 0;
780 		} else {
781 		    length = strlen(entry->HFS_name) + 2;
782 		}
783 		if (length > max) {
784 		    max = length;
785 		}
786 	}
787     }
788 
789     return max;
790 }
791 
792 int
793 get_max_base_or_length(partition_map_header *map)
794 {
795     partition_map * entry;
796     int max;
797 
798     if (map == NULL) {
799 	return 0;
800     }
801 
802     max = 0;
803 
804     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
805 	if (entry->data->dpme_pblock_start > max) {
806 	    max = entry->data->dpme_pblock_start;
807 	}
808 	if (entry->data->dpme_pblocks > max) {
809 	    max = entry->data->dpme_pblocks;
810 	}
811 	if (entry->data->dpme_lblock_start > max) {
812 	    max = entry->data->dpme_lblock_start;
813 	}
814 	if (entry->data->dpme_lblocks > max) {
815 	    max = entry->data->dpme_lblocks;
816 	}
817     }
818 
819     return max;
820 }
821 
822 void
823 adjust_value_and_compute_prefix(double *value, int *prefix)
824 {
825     double bytes;
826     int multiplier;
827 
828     bytes = *value;
829     if (bytes < 1024.0) {
830 	multiplier = ' ';
831     } else {
832 	bytes = bytes / 1024.0;
833 	if (bytes < 1024.0) {
834 	    multiplier = 'K';
835 	} else {
836 	    bytes = bytes / 1024.0;
837 	    if (bytes < 1024.0) {
838 		multiplier = 'M';
839 	    } else {
840 		bytes = bytes / 1024.0;
841 		if (bytes < 1024.0) {
842 		    multiplier = 'G';
843 		} else {
844 		    bytes = bytes / 1024.0;
845 		    multiplier = 'T';
846 		}
847 	    }
848 	}
849     }
850     *value = bytes;
851     *prefix = multiplier;
852 }
853