xref: /openbsd-src/sbin/pdisk/dump.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
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 int strnlen(char *s, int n);
128 
129 
130 //
131 // Routines
132 //
133 int
134 dump(char *name)
135 {
136     partition_map_header *map;
137     int junk;
138 
139     map = open_partition_map(name, &junk, 0);
140     if (map == NULL) {
141 	//error(-1, "No partition map in '%s'", name);
142 	return 0;
143     }
144 
145     dump_partition_map(map, 1);
146 
147     close_partition_map(map);
148 
149     return 1;
150 }
151 
152 
153 void
154 dump_block_zero(partition_map_header *map)
155 {
156     Block0 *p;
157     DDMap *m;
158     int i;
159     double value;
160     int prefix;
161     long t;
162 
163     p = map->misc;
164     if (p->sbSig != BLOCK0_SIGNATURE) {
165 	return;
166     }
167 
168     value = ((double)p->sbBlkCount) * p->sbBlkSize;
169     adjust_value_and_compute_prefix(&value, &prefix);
170     printf("\nDevice block size=%u, Number of Blocks=%lu (%1.1f%c)\n",
171 	    p->sbBlkSize, p->sbBlkCount, value, prefix);
172 
173     printf("DeviceType=0x%x, DeviceId=0x%x\n",
174 	    p->sbDevType, p->sbDevId);
175     if (p->sbDrvrCount > 0) {
176 	printf("Drivers-\n");
177 	m = (DDMap *) p->sbMap;
178 	for (i = 0; i < p->sbDrvrCount; i++) {
179 	    printf("%u: %3u @ %lu, ", i+1,
180 		    m[i].ddSize, get_align_long(&m[i].ddBlock));
181 	    if (map->logical_block != p->sbBlkSize) {
182 		t = (m[i].ddSize * p->sbBlkSize) / map->logical_block;
183 		printf("(%lu@", t);
184 		t = (get_align_long(&m[i].ddBlock) * p->sbBlkSize)
185 			/ map->logical_block;
186 		printf("%lu)  ", t);
187 	    }
188 	    printf("type=0x%x\n", m[i].ddType);
189 	}
190     }
191     printf("\n");
192 }
193 
194 
195 void
196 dump_partition_map(partition_map_header *map, int disk_order)
197 {
198     partition_map * entry;
199     int max_type_length;
200     int max_name_length;
201     int digits;
202 
203     if (map == NULL) {
204 	bad_input("No partition map exists");
205 	return;
206     }
207     printf("\nPartition map (with %d byte blocks) on '%s'\n",
208 	map->logical_block, map->name);
209 
210     digits = number_of_digits(get_max_base_or_length(map));
211     if (digits < 6) {
212 	digits = 6;
213     }
214     if (aflag) {
215 	max_type_length = 4;
216     } else {
217 	max_type_length = get_max_type_string_length(map);
218 	if (max_type_length < 4) {
219 	    max_type_length = 4;
220 	}
221     }
222     max_name_length = get_max_name_string_length(map);
223     if (max_name_length < 6) {
224 	max_name_length = 6;
225     }
226     printf(" #: %*s %-*s %*s   %-*s ( size )\n",
227 	    max_type_length, "type",
228 	    max_name_length, "name",
229 	    digits, "length", digits, "base");
230 
231     if (disk_order) {
232 	for (entry = map->disk_order; entry != NULL;
233 		entry = entry->next_on_disk) {
234 
235 	    dump_partition_entry(entry, max_type_length, max_name_length, digits);
236 	}
237     } else {
238 	for (entry = map->base_order; entry != NULL;
239 		entry = entry->next_by_base) {
240 
241 	    dump_partition_entry(entry, max_type_length, max_name_length, digits);
242 	}
243     }
244     dump_block_zero(map);
245 }
246 
247 
248 void
249 dump_partition_entry(partition_map *entry, int type_length, int name_length, int digits)
250 {
251     partition_map_header *map;
252     int j;
253     DPME *p;
254     const char *s;
255     u32 size;
256     double bytes;
257     int driver;
258     // int kind;
259     char *buf;
260 #if 1
261     BZB *bp;
262 #endif
263 
264     map = entry->the_map;
265     p = entry->data;
266     driver = entry->contains_driver? '*': ' ';
267     if (aflag) {
268 	s = "????";
269 	for (j = 0; plist[j].abbr != 0; j++) {
270 	    if (strcmp(p->dpme_type, plist[j].full) == 0) {
271 		s = plist[j].abbr;
272 		break;
273 	    }
274 	}
275 	printf("%2ld: %.4s", entry->disk_address, s);
276     } else {
277 	printf("%2ld: %*.32s", entry->disk_address, type_length, p->dpme_type);
278     }
279 
280     buf = (char *) malloc(name_length+1);
281     if (entry->HFS_name == NULL || fflag == 0) {
282 	strncpy(buf, p->dpme_name, name_length);
283 	buf[name_length] = 0;
284     } else {
285 	snprintf(buf, name_length + 1, "\"%s\"", entry->HFS_name);
286     }
287     printf("%c%-*.32s ", driver, name_length, buf);
288     free(buf);
289     /*
290     switch (entry->HFS_kind) {
291     case kHFS_std:	kind = 'h'; break;
292     case kHFS_embed:	kind = 'e'; break;
293     case kHFS_plus:	kind = '+'; break;
294     default:
295     case kHFS_not:	kind = ' '; break;
296     }
297     printf("%c ", kind);
298     */
299 
300     if (pflag) {
301 	printf("%*lu ", digits, p->dpme_pblocks);
302 	size = p->dpme_pblocks;
303     } else if (p->dpme_lblocks + p->dpme_lblock_start != p->dpme_pblocks) {
304 	printf("%*lu+", digits, p->dpme_lblocks);
305 	size = p->dpme_lblocks;
306     } else if (p->dpme_lblock_start != 0) {
307 	printf("%*lu ", digits, p->dpme_lblocks);
308 	size = p->dpme_lblocks;
309     } else {
310 	printf("%*lu ", digits, p->dpme_pblocks);
311 	size = p->dpme_pblocks;
312     }
313     if (pflag || p->dpme_lblock_start == 0) {
314 	printf("@ %-*lu", digits, p->dpme_pblock_start);
315     } else {
316 	printf("@~%-*lu", digits, p->dpme_pblock_start + p->dpme_lblock_start);
317     }
318 
319     bytes = ((double)size) * map->logical_block;
320     adjust_value_and_compute_prefix(&bytes, &j);
321     if (j != ' ' && j != 'K') {
322 	printf(" (%#5.1f%c)", bytes, j);
323     }
324 
325 #if 1
326     // Old A/UX fields that no one pays attention to anymore.
327     bp = (BZB *) (p->dpme_bzb);
328     j = -1;
329     if (bp->bzb_magic == BZBMAGIC) {
330 	switch (bp->bzb_type) {
331 	case FSTEFS:
332 	    s = "EFS";
333 	    break;
334 	case FSTSFS:
335 	    s = "SFS";
336 	    j = 1;
337 	    break;
338 	case FST:
339 	default:
340 	    if (bzb_root_get(bp) != 0) {
341 		if (bzb_usr_get(bp) != 0) {
342 		    s = "RUFS";
343 		} else {
344 		    s = "RFS";
345 		}
346 		j = 0;
347 	    } else if (bzb_usr_get(bp) != 0) {
348 		s = "UFS";
349 		j = 2;
350 	    } else {
351 		s = "FS";
352 	    }
353 	    break;
354 	}
355 	if (bzb_slice_get(bp) != 0) {
356 	    printf(" s%1ld %4s", bzb_slice_get(bp)-1, s);
357 	} else if (j >= 0) {
358 	    printf(" S%1d %4s", j, s);
359 	} else {
360 	    printf("    %4s", s);
361 	}
362 	if (bzb_crit_get(bp) != 0) {
363 	    printf(" K%1d", bp->bzb_cluster);
364 	} else if (j < 0) {
365 	    printf("   ");
366 	} else {
367 	    printf(" k%1d", bp->bzb_cluster);
368 	}
369 	if (bp->bzb_mount_point[0] != 0) {
370 	    printf("  %.64s", bp->bzb_mount_point);
371 	}
372     }
373 #endif
374     printf("\n");
375 }
376 
377 
378 void
379 show_data_structures(partition_map_header *map)
380 {
381     Block0 *zp;
382     DDMap *m;
383     int i;
384     int j;
385     partition_map * entry;
386     DPME *p;
387     BZB *bp;
388     const char *s;
389 
390     if (map == NULL) {
391 	printf("No partition map exists\n");
392 	return;
393     }
394     printf("Header:\n");
395     printf("map %d blocks out of %d,  media %lu blocks (%d byte blocks)\n",
396 	    map->blocks_in_map, map->maximum_in_map,
397 	    map->media_size, map->logical_block);
398     printf("Map is%s writable", (map->writable)?kStringEmpty:kStringNot);
399     printf(", but%s changed", (map->changed)?kStringEmpty:kStringNot);
400     printf(" and has%s been written\n", (map->written)?kStringEmpty:kStringNot);
401     printf("\n");
402 
403     if (map->misc == NULL) {
404 	printf("No block zero\n");
405     } else {
406 	zp = map->misc;
407 
408 	printf("Block0:\n");
409 	printf("signature 0x%x", zp->sbSig);
410 	if (zp->sbSig == BLOCK0_SIGNATURE) {
411 	    printf("\n");
412 	} else {
413 	    printf(" should be 0x%x\n", BLOCK0_SIGNATURE);
414 	}
415 	printf("Block size=%u, Number of Blocks=%lu\n",
416 		zp->sbBlkSize, zp->sbBlkCount);
417 	printf("DeviceType=0x%x, DeviceId=0x%x, sbData=0x%lx\n",
418 		zp->sbDevType, zp->sbDevId, zp->sbData);
419 	if (zp->sbDrvrCount == 0) {
420 	    printf("No drivers\n");
421 	} else {
422 	    printf("%u driver%s-\n", zp->sbDrvrCount,
423 		    (zp->sbDrvrCount>1)?"s":kStringEmpty);
424 	    m = (DDMap *) zp->sbMap;
425 	    for (i = 0; i < zp->sbDrvrCount; i++) {
426             printf("%u: @ %lu for %u, type=0x%x\n", i+1,
427 		   get_align_long(&m[i].ddBlock),
428 		   m[i].ddSize, m[i].ddType);
429 	    }
430 	}
431     }
432     printf("\n");
433 
434 /*
435 u32     dpme_boot_args[32]      ;
436 u32     dpme_reserved_3[62]     ;
437 */
438     printf(" #:                 type  length   base    "
439 	    "flags        (logical)\n");
440     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
441 	p = entry->data;
442 	printf("%2ld: %20.32s ",
443 		entry->disk_address, p->dpme_type);
444 	printf("%7lu @ %-7lu ", p->dpme_pblocks, p->dpme_pblock_start);
445 	printf("%c%c%c%c%c%c%c%c%c%c%c%c ",
446 		(dpme_valid_get(p))?'V':'.',
447 		(dpme_allocated_get(p))?'A':'.',
448 		(dpme_in_use_get(p))?'I':'.',
449 		(dpme_bootable_get(p))?'B':'.',
450 		(dpme_readable_get(p))?'R':'.',
451 		(dpme_writable_get(p))?'W':'.',
452 		(dpme_os_pic_code_get(p))?'P':'.',
453 		(dpme_os_specific_2_get(p))?'2':'.',
454 		(dpme_chainable_get(p))?'C':'.',
455 		(dpme_diskdriver_get(p))?'D':'.',
456 		(bitfield_get(p->dpme_flags, 30, 1))?'M':'.',
457 		(bitfield_get(p->dpme_flags, 31, 1))?'X':'.');
458 	if (p->dpme_lblock_start != 0 || p->dpme_pblocks != p->dpme_lblocks) {
459 	    printf("(%lu @ %lu)", p->dpme_lblocks, p->dpme_lblock_start);
460 	}
461 	printf("\n");
462     }
463     printf("\n");
464     printf(" #:  booter   bytes      load_address      "
465 	    "goto_address checksum processor\n");
466     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
467 	p = entry->data;
468 	printf("%2ld: ", entry->disk_address);
469 	printf("%7lu ", p->dpme_boot_block);
470 	printf("%7lu ", p->dpme_boot_bytes);
471 	printf("%8lx ", (u32)p->dpme_load_addr);
472 	printf("%8lx ", (u32)p->dpme_load_addr_2);
473 	printf("%8lx ", (u32)p->dpme_goto_addr);
474 	printf("%8lx ", (u32)p->dpme_goto_addr_2);
475 	printf("%8lx ", p->dpme_checksum);
476 	printf("%.32s", p->dpme_process_id);
477 	printf("\n");
478     }
479     printf("\n");
480 /*
481 xx: cccc RU *dd s...
482 */
483     printf(" #: type RU *slice mount_point (A/UX only fields)\n");
484     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
485 	p = entry->data;
486 	printf("%2ld: ", entry->disk_address);
487 
488 	bp = (BZB *) (p->dpme_bzb);
489 	j = -1;
490 	if (bp->bzb_magic == BZBMAGIC) {
491 	    switch (bp->bzb_type) {
492 	    case FSTEFS:
493 		s = "esch";
494 		break;
495 	    case FSTSFS:
496 		s = "swap";
497 		j = 1;
498 		break;
499 	    case FST:
500 	    default:
501 		s = "fsys";
502 		if (bzb_root_get(bp) != 0) {
503 		    j = 0;
504 		} else if (bzb_usr_get(bp) != 0) {
505 		    j = 2;
506 		}
507 		break;
508 	    }
509 	    printf("%4s ", s);
510 	    printf("%c%c ",
511 		    (bzb_root_get(bp))?'R':' ',
512 		    (bzb_usr_get(bp))?'U':' ');
513 	    if (bzb_slice_get(bp) != 0) {
514 		printf("  %2ld", bzb_slice_get(bp)-1);
515 	    } else if (j >= 0) {
516 		printf(" *%2d", j);
517 	    } else {
518 		printf("    ");
519 	    }
520 	    if (bp->bzb_mount_point[0] != 0) {
521 		printf(" %.64s", bp->bzb_mount_point);
522 	    }
523 	}
524 	printf("\n");
525     }
526 }
527 
528 
529 void
530 full_dump_partition_entry(partition_map_header *map, int ix)
531 {
532     partition_map * cur;
533     DPME *p;
534     int i;
535     u32 t;
536 
537     cur = find_entry_by_disk_address(ix, map);
538     if (cur == NULL) {
539 	printf("No such partition\n");
540 	return;
541     }
542 
543     p = cur->data;
544     printf("             signature: 0x%x\n", p->dpme_signature);
545     printf("             reserved1: 0x%x\n", p->dpme_reserved_1);
546     printf(" number of map entries: %ld\n", p->dpme_map_entries);
547     printf("        physical start: %10lu  length: %10lu\n", p->dpme_pblock_start, p->dpme_pblocks);
548     printf("         logical start: %10lu  length: %10lu\n", p->dpme_lblock_start, p->dpme_lblocks);
549 
550     printf("                 flags: 0x%lx\n", (u32)p->dpme_flags);
551     printf("                        ");
552     if (dpme_valid_get(p)) printf("valid ");
553     if (dpme_allocated_get(p)) printf("alloc ");
554     if (dpme_in_use_get(p)) printf("in-use ");
555     if (dpme_bootable_get(p)) printf("boot ");
556     if (dpme_readable_get(p)) printf("read ");
557     if (dpme_writable_get(p)) printf("write ");
558     if (dpme_os_pic_code_get(p)) printf("pic ");
559     t = p->dpme_flags >> 7;
560     for (i = 7; i <= 31; i++) {
561     	if (t & 0x1) {
562     	    printf("%d ", i);
563     	}
564     	t = t >> 1;
565     }
566     printf("\n");
567 
568     printf("                  name: '%.32s'\n", p->dpme_name);
569     printf("                  type: '%.32s'\n", p->dpme_type);
570 
571     printf("      boot start block: %10lu\n", p->dpme_boot_block);
572     printf("boot length (in bytes): %10lu\n", p->dpme_boot_bytes);
573     printf("          load address: 0x%08lx  0x%08lx\n",
574 		(u32)p->dpme_load_addr, (u32)p->dpme_load_addr_2);
575     printf("         start address: 0x%08lx  0x%08lx\n",
576 		(u32)p->dpme_goto_addr, (u32)p->dpme_goto_addr_2);
577     printf("              checksum: 0x%08lx\n", p->dpme_checksum);
578     printf("             processor: '%.32s'\n", p->dpme_process_id);
579     printf("boot args field -");
580     dump_block((unsigned char *)p->dpme_boot_args, 32*4);
581     printf("dpme_reserved_3 -");
582     dump_block((unsigned char *)p->dpme_reserved_3, 62*4);
583 }
584 
585 
586 void
587 dump_block(unsigned char *addr, int len)
588 {
589     int i;
590     int j;
591     int limit1;
592     int limit;
593 #define LINE_LEN 16
594 #define UNIT_LEN  4
595 #define OTHER_LEN  8
596 
597     for (i = 0; i < len; i = limit) {
598     	limit1 = i + LINE_LEN;
599     	if (limit1 > len) {
600     	    limit = len;
601     	} else {
602     	    limit = limit1;
603     	}
604 	printf("\n%03x: ", i);
605     	for (j = i; j < limit1; j++) {
606 	    if (j % UNIT_LEN == 0) {
607 		printf(" ");
608 	    }
609 	    if (j < limit) {
610 		printf("%02x", addr[j]);
611 	    } else {
612 		printf("  ");
613 	    }
614     	}
615 	printf(" ");
616     	for (j = i; j < limit; j++) {
617 	    if (j % OTHER_LEN == 0) {
618 		printf(" ");
619 	    }
620     	    if (addr[j] < ' ') {
621     	    	printf(".");
622     	    } else {
623     	    	printf("%c", addr[j]);
624     	    }
625     	}
626     }
627     printf("\n");
628 }
629 
630 void
631 full_dump_block_zero(partition_map_header *map)
632 {
633     Block0 *zp;
634     DDMap *m;
635     int i;
636 
637     if (map == NULL) {
638 	printf("No partition map exists\n");
639 	return;
640     }
641 
642     if (map->misc == NULL) {
643 	printf("No block zero\n");
644 	return;
645     }
646     zp = map->misc;
647 
648     printf("             signature: 0x%x\n", zp->sbSig);
649     printf("       size of a block: %d\n", zp->sbBlkSize);
650     printf("      number of blocks: %ld\n", zp->sbBlkCount);
651     printf("           device type: 0x%x\n", zp->sbDevType);
652     printf("             device id: 0x%x\n", zp->sbDevId);
653     printf("                  data: 0x%lx\n", zp->sbData);
654     printf("          driver count: %d\n", zp->sbDrvrCount);
655     m = (DDMap *) zp->sbMap;
656     for (i = 0; &m[i].ddType < &zp->sbMap[247]; i++) {
657     	if (m[i].ddBlock == 0 && m[i].ddSize == 0 && m[i].ddType == 0) {
658     	    break;
659     	}
660 	printf("      driver %3u block: %ld\n", i+1, m[i].ddBlock);
661 	printf("        size in blocks: %d\n", m[i].ddSize);
662 	printf("           driver type: 0x%x\n", m[i].ddType);
663     }
664     printf("remainder of block -");
665     dump_block((unsigned char *)&m[i].ddBlock, (&zp->sbMap[247]-((unsigned short *)&m[i].ddBlock))*2);
666 }
667 
668 
669 void
670 display_patches(partition_map *entry)
671 {
672     long long offset;
673     MEDIA m;
674     static unsigned char *patch_block;
675     PatchListPtr p;
676     PatchDescriptorPtr q;
677     unsigned char *next;
678     unsigned char *s;
679     int i;
680 
681     offset = entry->data->dpme_pblock_start;
682     m = entry->the_map->m;
683     offset = ((long long) entry->data->dpme_pblock_start) * entry->the_map->logical_block;
684     if (patch_block == NULL) {
685 	patch_block = (unsigned char *) malloc(PBLOCK_SIZE);
686 	if (patch_block == NULL) {
687 	    error(errno, "can't allocate memory for patch block buffer");
688 	    return;
689 	}
690     }
691     if (read_media(m, (long long)offset, PBLOCK_SIZE, (char *)patch_block) == 0) {
692 	error(errno, "Can't read patch block");
693 	return;
694     }
695     p = (PatchListPtr) patch_block;
696     if (p->numPatchBlocks != 1) {
697 	i = p->numPatchBlocks;
698 	free(patch_block);
699 	patch_block = (unsigned char *) malloc(PBLOCK_SIZE*i);
700 	if (patch_block == NULL) {
701 	    error(errno, "can't allocate memory for patch blocks buffer");
702 	    return;
703 	}
704 	s = patch_block + PBLOCK_SIZE*i;
705 	while (i > 0) {
706 	    s -= PBLOCK_SIZE;
707 	    i -= 1;
708 	    if (read_media(m, offset+i, PBLOCK_SIZE, (char *)s) == 0) {
709 		error(errno, "Can't read patch block %d", i);
710 		return;
711 	    }
712 	}
713 	p = (PatchListPtr) patch_block;
714     }
715     printf("Patch list (%d entries)\n", p->numPatches);
716     q = p->thePatch;
717     for (i = 0; i < p->numPatches; i++) {
718 	printf("%2d signature: '%.4s'\n", i+1, (char *)&q->patchSig);
719 	printf("     version: %d.%d\n", q->majorVers, q->minorVers);
720 	printf("       flags: 0x%lx\n", q->flags);
721 	printf("      offset: %ld\n", q->patchOffset);
722 	printf("        size: %ld\n", q->patchSize);
723 	printf("         CRC: 0x%lx\n", q->patchCRC);
724 	printf("        name: '%.*s'\n", q->patchName[0], &q->patchName[1]);
725 	printf("      vendor: '%.*s'\n", q->patchVendor[0], &q->patchVendor[1]);
726 	next = ((unsigned char *)q) + q->patchDescriptorLen;
727 	s = &q->patchVendor[q->patchVendor[0]+1];
728 	if (next > s) {
729 	    printf("remainder of entry -");
730 	    dump_block(s, next-s);
731 	}
732 	q = (PatchDescriptorPtr)next;
733     }
734 }
735 
736 int
737 strnlen(char *s, int n)
738 {
739     int i;
740 
741     for (i = 0; i < n; i++) {
742 	if (*s == 0) {
743 	    break;
744 	}
745 	s++;
746     }
747     return i;
748 }
749 
750 int
751 get_max_type_string_length(partition_map_header *map)
752 {
753     partition_map * entry;
754     int max;
755     int length;
756 
757     if (map == NULL) {
758 	return 0;
759     }
760 
761     max = 0;
762 
763     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
764 	length = strnlen(entry->data->dpme_type, DPISTRLEN);
765 	if (length > max) {
766 	    max = length;
767 	}
768     }
769 
770     return max;
771 }
772 
773 int
774 get_max_name_string_length(partition_map_header *map)
775 {
776     partition_map * entry;
777     int max;
778     int length;
779 
780     if (map == NULL) {
781 	return 0;
782     }
783 
784     max = 0;
785 
786     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
787 	length = strnlen(entry->data->dpme_name, DPISTRLEN);
788 	if (length > max) {
789 	    max = length;
790 	}
791 
792 	if (fflag) {
793 		if (entry->HFS_name == NULL) {
794 		    length = 0;
795 		} else {
796 		    length = strlen(entry->HFS_name) + 2;
797 		}
798 		if (length > max) {
799 		    max = length;
800 		}
801 	}
802     }
803 
804     return max;
805 }
806 
807 int
808 get_max_base_or_length(partition_map_header *map)
809 {
810     partition_map * entry;
811     int max;
812 
813     if (map == NULL) {
814 	return 0;
815     }
816 
817     max = 0;
818 
819     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
820 	if (entry->data->dpme_pblock_start > max) {
821 	    max = entry->data->dpme_pblock_start;
822 	}
823 	if (entry->data->dpme_pblocks > max) {
824 	    max = entry->data->dpme_pblocks;
825 	}
826 	if (entry->data->dpme_lblock_start > max) {
827 	    max = entry->data->dpme_lblock_start;
828 	}
829 	if (entry->data->dpme_lblocks > max) {
830 	    max = entry->data->dpme_lblocks;
831 	}
832     }
833 
834     return max;
835 }
836 
837 void
838 adjust_value_and_compute_prefix(double *value, int *prefix)
839 {
840     double bytes;
841     int multiplier;
842 
843     bytes = *value;
844     if (bytes < 1024.0) {
845 	multiplier = ' ';
846     } else {
847 	bytes = bytes / 1024.0;
848 	if (bytes < 1024.0) {
849 	    multiplier = 'K';
850 	} else {
851 	    bytes = bytes / 1024.0;
852 	    if (bytes < 1024.0) {
853 		multiplier = 'M';
854 	    } else {
855 		bytes = bytes / 1024.0;
856 		if (bytes < 1024.0) {
857 		    multiplier = 'G';
858 		} else {
859 		    bytes = bytes / 1024.0;
860 		    multiplier = 'T';
861 		}
862 	    }
863 	}
864     }
865     *value = bytes;
866     *prefix = multiplier;
867 }
868