xref: /openbsd/sbin/pdisk/partition_map.c (revision 78b63d65)
1 //
2 // partition_map.c - partition map routines
3 //
4 // Written by Eryk Vershen (eryk@apple.com)
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(), calloc() & free()
32 #ifndef __linux__
33 #include <stdlib.h>
34 #else
35 #include <malloc.h>
36 #endif
37 
38 // for strncpy() & strcmp()
39 #include <string.h>
40 // for O_RDONLY & O_RDWR
41 #include <fcntl.h>
42 // for errno
43 #include <errno.h>
44 
45 #include "partition_map.h"
46 #include "pathname.h"
47 #include "deblock_media.h"
48 #include "io.h"
49 #include "convert.h"
50 #include "util.h"
51 #include "errors.h"
52 
53 
54 //
55 // Defines
56 //
57 #define APPLE_HFS_FLAGS_VALUE	0x4000037f
58 #define get_align_long(x)	(*(x))
59 #define put_align_long(y, x)	((*(x)) = (y))
60 // #define TEST_COMPUTE
61 
62 
63 //
64 // Types
65 //
66 
67 
68 //
69 // Global Constants
70 //
71 const char * kFreeType	= "Apple_Free";
72 const char * kMapType	= "Apple_partition_map";
73 const char * kUnixType	= "OpenBSD";
74 const char * kHFSType	= "Apple_HFS";
75 const char * kPatchType	= "Apple_Patches";
76 
77 const char * kFreeName	= "Extra";
78 
79 enum add_action {
80     kReplace = 0,
81     kAdd = 1,
82     kSplit = 2
83 };
84 
85 //
86 // Global Variables
87 //
88 extern int cflag;
89 
90 
91 //
92 // Forward declarations
93 //
94 int add_data_to_map(struct dpme *data, long index, partition_map_header *map);
95 int coerce_block0(partition_map_header *map);
96 int contains_driver(partition_map *entry);
97 void combine_entry(partition_map *entry);
98 long compute_device_size(partition_map_header *map, partition_map_header *oldmap);
99 DPME* create_data(const char *name, const char *dptype, u32 base, u32 length);
100 void delete_entry(partition_map *entry);
101 void insert_in_base_order(partition_map *entry);
102 void insert_in_disk_order(partition_map *entry);
103 int read_block(partition_map_header *map, unsigned long num, char *buf);
104 int read_partition_map(partition_map_header *map);
105 void remove_driver(partition_map *entry);
106 void remove_from_disk_order(partition_map *entry);
107 void renumber_disk_addresses(partition_map_header *map);
108 void sync_device_size(partition_map_header *map);
109 int write_block(partition_map_header *map, unsigned long num, char *buf);
110 
111 
112 //
113 // Routines
114 //
115 partition_map_header *
116 open_partition_map(char *name, int *valid_file, int ask_logical_size)
117 {
118     MEDIA m;
119     partition_map_header * map;
120     int writeable;
121     int size;
122 
123     m = open_pathname_as_media(name, (rflag)?O_RDONLY:O_RDWR);
124     if (m == 0) {
125 	m = open_pathname_as_media(name, O_RDONLY);
126 	if (m == 0) {
127 	    error(errno, "can't open file '%s'", name);
128 	    *valid_file = 0;
129 	    return NULL;
130 	} else {
131 	    writeable = 0;
132 	}
133     } else {
134 	writeable = 1;
135     }
136     *valid_file = 1;
137 
138     map = (partition_map_header *) malloc(sizeof(partition_map_header));
139     if (map == NULL) {
140 	error(errno, "can't allocate memory for open partition map");
141 	close_media(m);
142 	return NULL;
143     }
144     map->name = name;
145     map->writeable = (rflag)?0:writeable;
146     map->changed = 0;
147     map->disk_order = NULL;
148     map->base_order = NULL;
149 
150     map->physical_block = media_granularity(m);	/* preflight */
151     m = open_deblock_media(PBLOCK_SIZE, m);
152     map->m = m;
153     map->misc = (Block0 *) malloc(PBLOCK_SIZE);
154     if (map->misc == NULL) {
155 	error(errno, "can't allocate memory for block zero buffer");
156 	close_media(map->m);
157 	free(map);
158 	return NULL;
159     } else if (read_media(map->m, (long long) 0, PBLOCK_SIZE, (char *)map->misc) == 0
160 	    || convert_block0(map->misc, 1)
161 	    || coerce_block0(map)) {
162 	// if I can't read block 0 I might as well give up
163 	error(-1, "Can't read block 0 from '%s'", name);
164 	close_partition_map(map);
165 	return NULL;
166     }
167     map->physical_block = map->misc->sbBlkSize;
168     //printf("physical block size is %d\n", map->physical_block);
169 
170     if (ask_logical_size && interactive) {
171 	size = PBLOCK_SIZE;
172 	printf("A logical block is %d bytes: ", size);
173 	flush_to_newline(0);
174 	get_number_argument("what should be the logical block size? ",
175 		(long *)&size, size);
176 	size = (size / PBLOCK_SIZE) * PBLOCK_SIZE;
177 	if (size < PBLOCK_SIZE) {
178 	    size = PBLOCK_SIZE;
179 	}
180 	map->logical_block = size;
181     } else {
182 	map->logical_block = PBLOCK_SIZE;
183     }
184     if (map->logical_block > MAXIOSIZE) {
185 	map->logical_block = MAXIOSIZE;
186     }
187     if (map->logical_block > map->physical_block) {
188 	map->physical_block = map->logical_block;
189     }
190     map->blocks_in_map = 0;
191     map->maximum_in_map = -1;
192     map->media_size = compute_device_size(map, map);
193     sync_device_size(map);
194 
195     if (read_partition_map(map) < 0) {
196 	// some sort of failure reading the map
197     } else {
198 	// got it!
199 	;
200 	return map;
201     }
202     close_partition_map(map);
203     return NULL;
204 }
205 
206 
207 void
208 close_partition_map(partition_map_header *map)
209 {
210     partition_map * entry;
211     partition_map * next;
212 
213     if (map == NULL) {
214 	return;
215     }
216 
217     free(map->misc);
218 
219     for (entry = map->disk_order; entry != NULL; entry = next) {
220 	next = entry->next_on_disk;
221 	free(entry->data);
222 	free(entry);
223     }
224     close_media(map->m);
225     free(map);
226 }
227 
228 
229 int
230 read_partition_map(partition_map_header *map)
231 {
232     DPME *data;
233     u32 limit;
234     int index;
235     int old_logical;
236     double d;
237 
238 //printf("called read_partition_map\n");
239 //printf("logical = %d, physical = %d\n", map->logical_block, map->physical_block);
240     data = (DPME *) malloc(PBLOCK_SIZE);
241     if (data == NULL) {
242 	error(errno, "can't allocate memory for disk buffers");
243 	return -1;
244     }
245 
246     if (read_block(map, 1, (char *)data) == 0) {
247 	error(-1, "Can't read block 1 from '%s'", map->name);
248 	free(data);
249 	return -1;
250     } else if (convert_dpme(data, 1)
251 	    || data->dpme_signature != DPME_SIGNATURE) {
252 	old_logical = map->logical_block;
253 	map->logical_block = 512;
254 	while (map->logical_block <= map->physical_block) {
255 	    if (read_block(map, 1, (char *)data) == 0) {
256 		error(-1, "Can't read block 1 from '%s'", map->name);
257 		free(data);
258 		return -1;
259 	    } else if (convert_dpme(data, 1) == 0
260 		    && data->dpme_signature == DPME_SIGNATURE) {
261 		d = map->media_size;
262 		map->media_size =  (d * old_logical) / map->logical_block;
263 		break;
264 	    }
265 	    map->logical_block *= 2;
266 	}
267 	if (map->logical_block > map->physical_block) {
268 	    error(-1, "No valid block 1 on '%s'", map->name);
269 	    free(data);
270 	    return -1;
271 	}
272     }
273 //printf("logical = %d, physical = %d\n", map->logical_block, map->physical_block);
274 
275     limit = data->dpme_map_entries;
276     index = 1;
277     while (1) {
278 	if (add_data_to_map(data, index, map) == 0) {
279 	    free(data);
280 	    return -1;
281 	}
282 
283 	if (index >= limit) {
284 	    break;
285 	} else {
286 	    index++;
287 	}
288 
289 	data = (DPME *) malloc(PBLOCK_SIZE);
290 	if (data == NULL) {
291 	    error(errno, "can't allocate memory for disk buffers");
292 	    return -1;
293 	}
294 
295 	if (read_block(map, index, (char *)data) == 0) {
296 	    error(-1, "Can't read block %u from '%s'", index, map->name);
297 	    free(data);
298 	    return -1;
299 	} else if (convert_dpme(data, 1)
300 		|| (data->dpme_signature != DPME_SIGNATURE && dflag == 0)
301 		|| (data->dpme_map_entries != limit && dflag == 0)) {
302 	    error(-1, "Bad data in block %u from '%s'", index, map->name);
303 	    free(data);
304 	    return -1;
305 	}
306     }
307     return 0;
308 }
309 
310 
311 void
312 write_partition_map(partition_map_header *map)
313 {
314     MEDIA m;
315     char *block;
316     partition_map * entry;
317     int i = 0;
318     int result;
319 
320     m = map->m;
321     if (map->misc != NULL) {
322 	convert_block0(map->misc, 0);
323 	result = write_block(map, 0, (char *)map->misc);
324 	convert_block0(map->misc, 1);
325     } else {
326 	block = (char *) calloc(1, PBLOCK_SIZE);
327 	if (block != NULL) {
328 	    result = write_block(map, 0, block);
329 	    free(block);
330 	}
331     }
332     if (result == 0) {
333 	error(errno, "Unable to write block zero");
334     }
335     for (entry = map->disk_order; entry != NULL; entry = entry->next_on_disk) {
336 	convert_dpme(entry->data, 0);
337 	result = write_block(map, entry->disk_address, (char *)entry->data);
338 	convert_dpme(entry->data, 1);
339 	i = entry->disk_address;
340 	if (result == 0) {
341 	    error(errno, "Unable to write block %d", i);
342 	}
343     }
344 	// zap the block after the map (if possible) to get around a bug.
345     if (map->maximum_in_map > 0 &&  i < map->maximum_in_map) {
346 	i += 1;
347 	block = (char *) malloc(PBLOCK_SIZE);
348 	if (block != NULL) {
349 	    if (read_block(map, i, block)) {
350 		block[0] = 0;
351 		write_block(map, i, block);
352 	    }
353 	    free(block);
354 	}
355     }
356     if (interactive)
357 	printf("The partition table has been altered!\n\n");
358 
359     os_reload_media(map->m);
360 }
361 
362 
363 int
364 add_data_to_map(struct dpme *data, long index, partition_map_header *map)
365 {
366     partition_map *entry;
367 
368 //printf("add data %d to map\n", index);
369     entry = (partition_map *) malloc(sizeof(partition_map));
370     if (entry == NULL) {
371 	error(errno, "can't allocate memory for map entries");
372 	return 0;
373     }
374     entry->next_on_disk = NULL;
375     entry->prev_on_disk = NULL;
376     entry->next_by_base = NULL;
377     entry->prev_by_base = NULL;
378     entry->disk_address = index;
379     entry->the_map = map;
380     entry->data = data;
381     entry->contains_driver = contains_driver(entry);
382 
383     insert_in_disk_order(entry);
384     insert_in_base_order(entry);
385 
386     map->blocks_in_map++;
387     if (map->maximum_in_map < 0) {
388 	if (istrncmp(data->dpme_type, kMapType, DPISTRLEN) == 0) {
389 	    map->maximum_in_map = data->dpme_pblocks;
390 	}
391     }
392 
393     return 1;
394 }
395 
396 
397 partition_map_header *
398 init_partition_map(char *name, partition_map_header* oldmap)
399 {
400     partition_map_header *map;
401 
402     if (oldmap != NULL) {
403 	printf("map already exists\n");
404 	if (get_okay("do you want to reinit? [n/y]: ", 0) != 1) {
405 	    return oldmap;
406 	}
407     }
408 
409     map = create_partition_map(name, oldmap);
410     if (map == NULL) {
411 	return oldmap;
412     }
413     close_partition_map(oldmap);
414 
415     add_partition_to_map("Apple", kMapType,
416 	    1, (map->media_size <= 128? 2: 63), map);
417     return map;
418 }
419 
420 
421 partition_map_header *
422 create_partition_map(char *name, partition_map_header *oldmap)
423 {
424     MEDIA m;
425     partition_map_header * map;
426     DPME *data;
427     unsigned long default_number;
428     unsigned long number;
429     int size;
430     unsigned long multiple;
431 
432     m = open_pathname_as_media(name, (rflag)?O_RDONLY:O_RDWR);
433     if (m == 0) {
434 	error(errno, "can't open file '%s' for %sing", name,
435 		(rflag)?"read":"writ");
436 	return NULL;
437     }
438 
439     map = (partition_map_header *) malloc(sizeof(partition_map_header));
440     if (map == NULL) {
441 	error(errno, "can't allocate memory for open partition map");
442 	close_media(m);
443 	return NULL;
444     }
445     map->name = name;
446     map->writeable = (rflag)?0:1;
447     map->changed = 1;
448     map->disk_order = NULL;
449     map->base_order = NULL;
450 
451     if (oldmap != NULL) {
452 	size = oldmap->physical_block;
453     } else {
454 	size = media_granularity(m);
455     }
456     m = open_deblock_media(PBLOCK_SIZE, m);
457     map->m = m;
458     if (interactive) {
459 	printf("A physical block is %d bytes: ", size);
460 	flush_to_newline(0);
461 	get_number_argument("what should be the physical block size? ",
462 		(long *)&size, size);
463 	size = (size / PBLOCK_SIZE) * PBLOCK_SIZE;
464 	if (size < PBLOCK_SIZE) {
465 	    size = PBLOCK_SIZE;
466 	}
467     }
468     if (map->physical_block > MAXIOSIZE) {
469 	map->physical_block = MAXIOSIZE;
470     }
471     map->physical_block = size;
472     // printf("block size is %d\n", map->physical_block);
473 
474     if (oldmap != NULL) {
475 	size = oldmap->logical_block;
476     } else {
477 	size = PBLOCK_SIZE;
478     }
479     if (interactive) {
480 	printf("A logical block is %d bytes: ", size);
481 	flush_to_newline(0);
482 	get_number_argument("what should be the logical block size? ",
483 		(long *)&size, size);
484 	size = (size / PBLOCK_SIZE) * PBLOCK_SIZE;
485 	if (size < PBLOCK_SIZE) {
486 	    size = PBLOCK_SIZE;
487 	}
488     }
489     if (size > map->physical_block) {
490 	size = map->physical_block;
491     }
492     map->logical_block = size;
493 
494     map->blocks_in_map = 0;
495     map->maximum_in_map = -1;
496 
497     number = compute_device_size(map, oldmap);
498     if (interactive) {
499 	printf("size of 'device' is %lu blocks (%d byte blocks): ",
500 		number, map->logical_block);
501 	default_number = number;
502 	flush_to_newline(0);
503 	do {
504 	    if (get_number_argument("what should be the size? ",
505 		    (long *)&number, default_number) == 0) {
506 		printf("Not a number\n");
507 		flush_to_newline(1);
508 		number = 0;
509 	    } else {
510 		multiple = get_multiplier(map->logical_block);
511 		if (multiple == 0) {
512 		    printf("Bad multiplier\n");
513 		    number = 0;
514 		} else if (multiple != 1) {
515 		    if (0xFFFFFFFF/multiple < number) {
516 			printf("Number too large\n");
517 			number = 0;
518 		    } else {
519 			number *= multiple;
520 		    }
521 		}
522 	    }
523 	    default_number = kDefault;
524 	} while (number == 0);
525 
526 	if (number < 4) {
527 	    number = 4;
528 	}
529 	printf("new size of 'device' is %lu blocks (%d byte blocks)\n",
530 		number, map->logical_block);
531     }
532     map->media_size = number;
533 
534     map->misc = (Block0 *) calloc(1, PBLOCK_SIZE);
535     if (map->misc == NULL) {
536 	error(errno, "can't allocate memory for block zero buffer");
537     } else {
538 	// got it!
539 	coerce_block0(map);
540 	sync_device_size(map);
541 
542 	data = (DPME *) calloc(1, PBLOCK_SIZE);
543 	if (data == NULL) {
544 	    error(errno, "can't allocate memory for disk buffers");
545 	} else {
546 	    // set data into entry
547 	    data->dpme_signature = DPME_SIGNATURE;
548 	    data->dpme_map_entries = 1;
549 	    data->dpme_pblock_start = 1;
550 	    data->dpme_pblocks = map->media_size - 1;
551 	    strncpy(data->dpme_name, kFreeName, DPISTRLEN);
552 	    strncpy(data->dpme_type, kFreeType, DPISTRLEN);
553 	    data->dpme_lblock_start = 0;
554 	    data->dpme_lblocks = data->dpme_pblocks;
555 	    dpme_writable_set(data, 1);
556 	    dpme_readable_set(data, 1);
557 	    dpme_bootable_set(data, 0);
558 	    dpme_in_use_set(data, 0);
559 	    dpme_allocated_set(data, 0);
560 	    dpme_valid_set(data, 1);
561 
562 	    if (add_data_to_map(data, 1, map) == 0) {
563 		free(data);
564 	    } else {
565 		return map;
566 	    }
567 	}
568     }
569     close_partition_map(map);
570     return NULL;
571 }
572 
573 
574 int
575 coerce_block0(partition_map_header *map)
576 {
577     Block0 *p;
578 
579     p = map->misc;
580     if (p == NULL) {
581 	return 1;
582     }
583     if (p->sbSig != BLOCK0_SIGNATURE) {
584 	p->sbSig = BLOCK0_SIGNATURE;
585 	if (map->physical_block == 1) {
586 	    p->sbBlkSize = PBLOCK_SIZE;
587 	} else {
588 	    p->sbBlkSize = map->physical_block;
589 	}
590 	p->sbBlkCount = 0;
591 	p->sbDevType = 0;
592 	p->sbDevId = 0;
593 	p->sbData = 0;
594 	p->sbDrvrCount = 0;
595     }
596     return 0;	// we do this simply to make it easier to call this function
597 }
598 
599 
600 int
601 add_partition_to_map(const char *name, const char *dptype, u32 base, u32 length,
602 	partition_map_header *map)
603 {
604     partition_map * cur;
605     DPME *data;
606     enum add_action act;
607     int limit;
608     u32 adjusted_base = 0;
609     u32 adjusted_length = 0;
610     u32 new_base = 0;
611     u32 new_length = 0;
612 
613 	// find a block that starts includes base and length
614     cur = map->base_order;
615     while (cur != NULL) {
616 	if (cur->data->dpme_pblock_start <= base
617 		&& (base + length) <=
618 		    (cur->data->dpme_pblock_start + cur->data->dpme_pblocks)) {
619 	    break;
620 	} else {
621 	    cur = cur->next_by_base;
622 	}
623     }
624 	// if it is not Extra then punt
625     if (cur == NULL
626 	    || istrncmp(cur->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
627 	printf("requested base and length is not "
628 		"within an existing free partition\n");
629 	return 0;
630     }
631 	// figure out what to do and sizes
632     data = cur->data;
633     if (data->dpme_pblock_start == base) {
634 	// replace or add
635 	if (data->dpme_pblocks == length) {
636 	    act = kReplace;
637 	} else {
638 	    act = kAdd;
639 	    adjusted_base = base + length;
640 	    adjusted_length = data->dpme_pblocks - length;
641 	}
642     } else {
643 	// split or add
644 	if (data->dpme_pblock_start + data->dpme_pblocks == base + length) {
645 	    act = kAdd;
646 	    adjusted_base = data->dpme_pblock_start;
647 	    adjusted_length = base - adjusted_base;
648 	} else {
649 	    act = kSplit;
650 	    new_base = data->dpme_pblock_start;
651 	    new_length = base - new_base;
652 	    adjusted_base = base + length;
653 	    adjusted_length = data->dpme_pblocks - (length + new_length);
654 	}
655     }
656 	// if the map will overflow then punt
657     if (map->maximum_in_map < 0) {
658 	limit = map->media_size;
659     } else {
660 	limit = map->maximum_in_map;
661     }
662     if (map->blocks_in_map + act > limit) {
663 	printf("the map is not big enough\n");
664 	return 0;
665     }
666 
667     data = create_data(name, dptype, base, length);
668     if (data == NULL) {
669 	return 0;
670     }
671     if (act == kReplace) {
672 	free(cur->data);
673 	cur->data = data;
674     } else {
675 	    // adjust this block's size
676 	cur->data->dpme_pblock_start = adjusted_base;
677 	cur->data->dpme_pblocks = adjusted_length;
678 	cur->data->dpme_lblocks = adjusted_length;
679 	    // insert new with block address equal to this one
680 	if (add_data_to_map(data, cur->disk_address, map) == 0) {
681 	    free(data);
682 	} else if (act == kSplit) {
683 	    data = create_data(kFreeName, kFreeType, new_base, new_length);
684 	    if (data != NULL) {
685 		    // insert new with block address equal to this one
686 		if (add_data_to_map(data, cur->disk_address, map) == 0) {
687 		    free(data);
688 		}
689 	    }
690 	}
691     }
692 	// renumber disk addresses
693     renumber_disk_addresses(map);
694 	// mark changed
695     map->changed = 1;
696     return 1;
697 }
698 
699 
700 DPME *
701 create_data(const char *name, const char *dptype, u32 base, u32 length)
702 {
703     DPME *data;
704 
705     data = (DPME *) calloc(1, PBLOCK_SIZE);
706     if (data == NULL) {
707 	error(errno, "can't allocate memory for disk buffers");
708     } else {
709 	// set data into entry
710 	data->dpme_signature = DPME_SIGNATURE;
711 	data->dpme_map_entries = 1;
712 	data->dpme_pblock_start = base;
713 	data->dpme_pblocks = length;
714 	strncpy(data->dpme_name, name, DPISTRLEN);
715 	strncpy(data->dpme_type, dptype, DPISTRLEN);
716 	data->dpme_lblock_start = 0;
717 	data->dpme_lblocks = data->dpme_pblocks;
718 	if (strcmp(data->dpme_type, kHFSType) == 0) { /* XXX this is gross, fix it! */
719 	    data->dpme_flags = APPLE_HFS_FLAGS_VALUE;
720 	}
721 	else {
722 	    dpme_writable_set(data, 1);
723 	    dpme_readable_set(data, 1);
724 	    dpme_bootable_set(data, 0);
725 	    dpme_in_use_set(data, 0);
726 	    dpme_allocated_set(data, 1);
727 	    dpme_valid_set(data, 1);
728 	}
729     }
730     return data;
731 }
732 
733 
734 void
735 renumber_disk_addresses(partition_map_header *map)
736 {
737     partition_map * cur;
738     long index;
739 
740 	// reset disk addresses
741     cur = map->disk_order;
742     index = 1;
743     while (cur != NULL) {
744 	cur->disk_address = index++;
745 	cur->data->dpme_map_entries = map->blocks_in_map;
746 	cur = cur->next_on_disk;
747     }
748 }
749 
750 
751 long
752 compute_device_size(partition_map_header *map, partition_map_header *oldmap)
753 {
754 #ifdef TEST_COMPUTE
755     unsigned long length;
756     struct hd_geometry geometry;
757     struct stat info;
758     loff_t pos;
759 #endif
760     char* data;
761     unsigned long l, r, x = 0;
762     long long size;
763     int valid = 0;
764 #ifdef TEST_COMPUTE
765     int fd;
766 
767     fd = map->fd->fd;
768     printf("\n");
769     if (fstat(fd, &info) < 0) {
770 	printf("stat of device failed\n");
771     } else {
772 	printf("stat: mode = 0%o, type=%s\n", info.st_mode,
773 		(S_ISREG(info.st_mode)? "Regular":
774 		(S_ISBLK(info.st_mode)?"Block":"Other")));
775 	printf("size = %d, blocks = %d\n",
776 		info.st_size, info.st_size/map->logical_block);
777     }
778 
779     if (ioctl(fd, BLKGETSIZE, &length) < 0) {
780 	printf("get device size failed\n");
781     } else {
782 	printf("BLKGETSIZE:size in blocks = %u\n", length);
783     }
784 
785     if (ioctl(fd, HDIO_GETGEO, &geometry) < 0) {
786 	printf("get device geometry failed\n");
787     } else {
788 	printf("HDIO_GETGEO: heads=%d, sectors=%d, cylinders=%d, start=%d,  total=%d\n",
789 		geometry.heads, geometry.sectors,
790 		geometry.cylinders, geometry.start,
791 		geometry.heads*geometry.sectors*geometry.cylinders);
792     }
793 
794     if ((pos = llseek(fd, (loff_t)0, SEEK_END)) < 0) {
795 	printf("llseek to end of device failed\n");
796     } else if ((pos = llseek(fd, (loff_t)0, SEEK_CUR)) < 0) {
797 	printf("llseek to end of device failed on second try\n");
798     } else {
799 	printf("llseek: pos = %d, blocks=%d\n", pos, pos/map->logical_block);
800     }
801 #endif
802 
803     if (cflag == 0 && oldmap != NULL && oldmap->misc->sbBlkCount != 0) {
804 	return (oldmap->misc->sbBlkCount
805 		* (oldmap->physical_block / map->logical_block));
806     }
807 
808     size = media_total_size(map->m);
809     if (size != 0) {
810     	return (long)(size / map->logical_block);
811     }
812 
813     // else case
814 
815     data = (char *) malloc(PBLOCK_SIZE);
816     if (data == NULL) {
817 	error(errno, "can't allocate memory for try buffer");
818 	x = 0;
819     } else {
820 	// double till off end
821 	l = 0;
822 	r = 1024;
823 	while (read_block(map, r, data) != 0) {
824 	    l = r;
825 	    if (r <= 1024) {
826 		r = r * 1024;
827 	    } else {
828 		r = r * 2;
829 	    }
830 	    if (r >= 0x80000000) {
831 		r = 0xFFFFFFFE;
832 		break;
833 	    }
834 	}
835 	// binary search for end
836 	while (l <= r) {
837 	    x = (r - l) / 2 + l;
838 	    if ((valid = read_block(map, x, data)) != 0) {
839 		l = x + 1;
840 	    } else {
841 		if (x > 0) {
842 		    r = x - 1;
843 		} else {
844 		    break;
845 		}
846 	    }
847 	}
848 	if (valid != 0) {
849 	    x = x + 1;
850 	}
851 	// printf("size in blocks = %d\n", x);
852 	free(data);
853     }
854 
855     return x;
856 }
857 
858 
859 void
860 sync_device_size(partition_map_header *map)
861 {
862     Block0 *p;
863     unsigned long size;
864     double d;
865 
866     p = map->misc;
867     if (p == NULL) {
868 	return;
869     }
870     d = map->media_size;
871     size = (d * map->logical_block) / map->physical_block;
872     if (p->sbBlkCount != size) {
873 	p->sbBlkCount = size;
874     }
875 }
876 
877 
878 void
879 delete_partition_from_map(partition_map *entry)
880 {
881     partition_map_header *map;
882     DPME *data;
883 
884     if (istrncmp(entry->data->dpme_type, kMapType, DPISTRLEN) == 0) {
885 	printf("Can't delete entry for the map itself\n");
886 	return;
887     }
888     if (entry->contains_driver) {
889 	printf("This program can't install drivers\n");
890 	if (get_okay("are you sure you want to delete this driver? [n/y]: ", 0) != 1) {
891 	    return;
892 	}
893     }
894     data = create_data(kFreeName, kFreeType,
895 	    entry->data->dpme_pblock_start, entry->data->dpme_pblocks);
896     if (data == NULL) {
897 	return;
898     }
899     if (entry->contains_driver) {
900     	remove_driver(entry);	// update block0 if necessary
901     }
902     free(entry->data);
903     entry->data = data;
904     combine_entry(entry);
905     map = entry->the_map;
906     renumber_disk_addresses(map);
907     map->changed = 1;
908 }
909 
910 
911 int
912 contains_driver(partition_map *entry)
913 {
914     partition_map_header *map;
915     Block0 *p;
916     DDMap *m;
917     int i;
918     int f;
919     u32 start;
920 
921     map = entry->the_map;
922     p = map->misc;
923     if (p == NULL) {
924 	return 0;
925     }
926     if (p->sbSig != BLOCK0_SIGNATURE) {
927 	return 0;
928     }
929     if (map->logical_block > p->sbBlkSize) {
930 	return 0;
931     } else {
932 	f = p->sbBlkSize / map->logical_block;
933     }
934     if (p->sbDrvrCount > 0) {
935 	m = (DDMap *) p->sbMap;
936 	for (i = 0; i < p->sbDrvrCount; i++) {
937 	    start = get_align_long(&m[i].ddBlock);
938 	    if (entry->data->dpme_pblock_start <= f*start
939 		    && f*(start + m[i].ddSize)
940 			<= (entry->data->dpme_pblock_start
941 			+ entry->data->dpme_pblocks)) {
942 		return 1;
943 	    }
944 	}
945     }
946     return 0;
947 }
948 
949 
950 void
951 combine_entry(partition_map *entry)
952 {
953     partition_map *p;
954     u32 end;
955 
956     if (entry == NULL
957 	    || istrncmp(entry->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
958 	return;
959     }
960     if (entry->next_by_base != NULL) {
961 	p = entry->next_by_base;
962 	if (istrncmp(p->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
963 	    // next is not free
964 	} else if (entry->data->dpme_pblock_start + entry->data->dpme_pblocks
965 		!= p->data->dpme_pblock_start) {
966 	    // next is not contiguous (XXX this is bad)
967 	    printf("next entry is not contiguous\n");
968 	    // start is already minimum
969 	    // new end is maximum of two ends
970 	    end = p->data->dpme_pblock_start + p->data->dpme_pblocks;
971 	    if (end > entry->data->dpme_pblock_start + entry->data->dpme_pblocks) {
972 	    	entry->data->dpme_pblocks = end - entry->data->dpme_pblock_start;
973 	    }
974 	    entry->data->dpme_lblocks = entry->data->dpme_pblocks;
975 	    delete_entry(p);
976 	} else {
977 	    entry->data->dpme_pblocks += p->data->dpme_pblocks;
978 	    entry->data->dpme_lblocks = entry->data->dpme_pblocks;
979 	    delete_entry(p);
980 	}
981     }
982     if (entry->prev_by_base != NULL) {
983 	p = entry->prev_by_base;
984 	if (istrncmp(p->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
985 	    // previous is not free
986 	} else if (p->data->dpme_pblock_start + p->data->dpme_pblocks
987 		!= entry->data->dpme_pblock_start) {
988 	    // previous is not contiguous (XXX this is bad)
989 	    printf("previous entry is not contiguous\n");
990 	    // new end is maximum of two ends
991 	    end = p->data->dpme_pblock_start + p->data->dpme_pblocks;
992 	    if (end < entry->data->dpme_pblock_start + entry->data->dpme_pblocks) {
993 		end = entry->data->dpme_pblock_start + entry->data->dpme_pblocks;
994 	    }
995 	    entry->data->dpme_pblocks = end - p->data->dpme_pblock_start;
996 	    // new start is previous entry's start
997 	    entry->data->dpme_pblock_start = p->data->dpme_pblock_start;
998 	    entry->data->dpme_lblocks = entry->data->dpme_pblocks;
999 	    delete_entry(p);
1000 	} else {
1001 	    entry->data->dpme_pblock_start = p->data->dpme_pblock_start;
1002 	    entry->data->dpme_pblocks += p->data->dpme_pblocks;
1003 	    entry->data->dpme_lblocks = entry->data->dpme_pblocks;
1004 	    delete_entry(p);
1005 	}
1006     }
1007     entry->contains_driver = contains_driver(entry);
1008 }
1009 
1010 
1011 void
1012 delete_entry(partition_map *entry)
1013 {
1014     partition_map_header *map;
1015     partition_map *p;
1016 
1017     map = entry->the_map;
1018     map->blocks_in_map--;
1019 
1020     remove_from_disk_order(entry);
1021 
1022     p = entry->next_by_base;
1023     if (map->base_order == entry) {
1024 	map->base_order = p;
1025     }
1026     if (p != NULL) {
1027 	p->prev_by_base = entry->prev_by_base;
1028     }
1029     if (entry->prev_by_base != NULL) {
1030 	entry->prev_by_base->next_by_base = p;
1031     }
1032 
1033     free(entry->data);
1034     free(entry);
1035 }
1036 
1037 
1038 partition_map *
1039 find_entry_by_disk_address(long index, partition_map_header *map)
1040 {
1041     partition_map * cur;
1042 
1043     cur = map->disk_order;
1044     while (cur != NULL) {
1045 	if (cur->disk_address == index) {
1046 	    break;
1047 	}
1048 	cur = cur->next_on_disk;
1049     }
1050     return cur;
1051 }
1052 
1053 
1054 partition_map *
1055 find_entry_by_type(const char *type_name, partition_map_header *map)
1056 {
1057     partition_map * cur;
1058 
1059     cur = map->base_order;
1060     while (cur != NULL) {
1061 	if (istrncmp(cur->data->dpme_type, type_name, DPISTRLEN) == 0) {
1062 	    break;
1063 	}
1064 	cur = cur->next_by_base;
1065     }
1066     return cur;
1067 }
1068 
1069 
1070 void
1071 move_entry_in_map(long old_index, long index, partition_map_header *map)
1072 {
1073     partition_map * cur;
1074 
1075     cur = find_entry_by_disk_address(old_index, map);
1076     if (cur == NULL) {
1077 	printf("No such partition\n");
1078     } else {
1079 	remove_from_disk_order(cur);
1080 	cur->disk_address = index;
1081 	insert_in_disk_order(cur);
1082 	renumber_disk_addresses(map);
1083 	map->changed = 1;
1084     }
1085 }
1086 
1087 
1088 void
1089 remove_from_disk_order(partition_map *entry)
1090 {
1091     partition_map_header *map;
1092     partition_map *p;
1093 
1094     map = entry->the_map;
1095     p = entry->next_on_disk;
1096     if (map->disk_order == entry) {
1097 	map->disk_order = p;
1098     }
1099     if (p != NULL) {
1100 	p->prev_on_disk = entry->prev_on_disk;
1101     }
1102     if (entry->prev_on_disk != NULL) {
1103 	entry->prev_on_disk->next_on_disk = p;
1104     }
1105     entry->next_on_disk = NULL;
1106     entry->prev_on_disk = NULL;
1107 }
1108 
1109 
1110 void
1111 insert_in_disk_order(partition_map *entry)
1112 {
1113     partition_map_header *map;
1114     partition_map * cur;
1115 
1116     // find position in disk list & insert
1117     map = entry->the_map;
1118     cur = map->disk_order;
1119     if (cur == NULL || entry->disk_address <= cur->disk_address) {
1120 	map->disk_order = entry;
1121 	entry->next_on_disk = cur;
1122 	if (cur != NULL) {
1123 	    cur->prev_on_disk = entry;
1124 	}
1125 	entry->prev_on_disk = NULL;
1126     } else {
1127 	for (cur = map->disk_order; cur != NULL; cur = cur->next_on_disk) {
1128 	    if (cur->disk_address <= entry->disk_address
1129 		    && (cur->next_on_disk == NULL
1130 		    || entry->disk_address <= cur->next_on_disk->disk_address)) {
1131 		entry->next_on_disk = cur->next_on_disk;
1132 		cur->next_on_disk = entry;
1133 		entry->prev_on_disk = cur;
1134 		if (entry->next_on_disk != NULL) {
1135 		    entry->next_on_disk->prev_on_disk = entry;
1136 		}
1137 		break;
1138 	    }
1139 	}
1140     }
1141 }
1142 
1143 
1144 void
1145 insert_in_base_order(partition_map *entry)
1146 {
1147     partition_map_header *map;
1148     partition_map * cur;
1149 
1150     // find position in base list & insert
1151     map = entry->the_map;
1152     cur = map->base_order;
1153     if (cur == NULL
1154 	    || entry->data->dpme_pblock_start <= cur->data->dpme_pblock_start) {
1155 	map->base_order = entry;
1156 	entry->next_by_base = cur;
1157 	if (cur != NULL) {
1158 	    cur->prev_by_base = entry;
1159 	}
1160 	entry->prev_by_base = NULL;
1161     } else {
1162 	for (cur = map->base_order; cur != NULL; cur = cur->next_by_base) {
1163 	    if (cur->data->dpme_pblock_start <= entry->data->dpme_pblock_start
1164 		    && (cur->next_by_base == NULL
1165 		    || entry->data->dpme_pblock_start
1166 			<= cur->next_by_base->data->dpme_pblock_start)) {
1167 		entry->next_by_base = cur->next_by_base;
1168 		cur->next_by_base = entry;
1169 		entry->prev_by_base = cur;
1170 		if (entry->next_by_base != NULL) {
1171 		    entry->next_by_base->prev_by_base = entry;
1172 		}
1173 		break;
1174 	    }
1175 	}
1176     }
1177 }
1178 
1179 
1180 void
1181 resize_map(long new_size, partition_map_header *map)
1182 {
1183     partition_map * entry;
1184     partition_map * next;
1185     int incr;
1186 
1187     // find map entry
1188     entry = find_entry_by_type(kMapType, map);
1189 
1190     if (entry == NULL) {
1191 	printf("Couldn't find entry for map!\n");
1192 	return;
1193     }
1194     next = entry->next_by_base;
1195 
1196 	// same size
1197     if (new_size == entry->data->dpme_pblocks) {
1198 	// do nothing
1199 	return;
1200     }
1201 
1202 	// make it smaller
1203     if (new_size < entry->data->dpme_pblocks) {
1204 	if (next == NULL
1205 		|| istrncmp(next->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
1206 	    incr = 1;
1207 	} else {
1208 	    incr = 0;
1209 	}
1210 	if (new_size < map->blocks_in_map + incr) {
1211 	    printf("New size would be too small\n");
1212 	    return;
1213 	}
1214 	goto doit;
1215     }
1216 
1217 	// make it larger
1218     if (next == NULL
1219 	    || istrncmp(next->data->dpme_type, kFreeType, DPISTRLEN) != 0) {
1220 	printf("No free space to expand into\n");
1221 	return;
1222     }
1223     if (entry->data->dpme_pblock_start + entry->data->dpme_pblocks
1224 	    != next->data->dpme_pblock_start) {
1225 	printf("No contiguous free space to expand into\n");
1226 	return;
1227     }
1228     if (new_size > entry->data->dpme_pblocks + next->data->dpme_pblocks) {
1229 	printf("No enough free space\n");
1230 	return;
1231     }
1232 doit:
1233     entry->data->dpme_type[0] = 0;
1234     delete_partition_from_map(entry);
1235     add_partition_to_map("Apple", kMapType, 1, new_size, map);
1236     map->maximum_in_map = new_size;
1237 }
1238 
1239 
1240 void
1241 remove_driver(partition_map *entry)
1242 {
1243     partition_map_header *map;
1244     Block0 *p;
1245     DDMap *m;
1246     int i;
1247     int j;
1248     int f;
1249     u32 start;
1250 
1251     map = entry->the_map;
1252     p = map->misc;
1253     if (p == NULL) {
1254 	return;
1255     }
1256     if (p->sbSig != BLOCK0_SIGNATURE) {
1257 	return;
1258     }
1259     if (map->logical_block > p->sbBlkSize) {
1260 	/* this is not supposed to happen, but let's just ignore it. */
1261 	return;
1262     } else {
1263 	/*
1264 	 * compute the factor to convert the block numbers in block0
1265 	 * into partition map block numbers.
1266 	 */
1267 	f = p->sbBlkSize / map->logical_block;
1268     }
1269     if (p->sbDrvrCount > 0) {
1270 	m = (DDMap *) p->sbMap;
1271 	for (i = 0; i < p->sbDrvrCount; i++) {
1272 	    start = get_align_long(&m[i].ddBlock);
1273 
1274 	    /* zap the driver if it is wholly contained in the partition */
1275 	    if (entry->data->dpme_pblock_start <= f*start
1276 		    && f*(start + m[i].ddSize)
1277 			<= (entry->data->dpme_pblock_start
1278 			+ entry->data->dpme_pblocks)) {
1279 		// delete this driver
1280 		// by copying down later ones and zapping the last
1281 		for (j = i+1; j < p->sbDrvrCount; j++, i++) {
1282 		   put_align_long(get_align_long(&m[j].ddBlock), &m[i].ddBlock);
1283 		   m[i].ddSize = m[j].ddSize;
1284 		   m[i].ddType = m[j].ddType;
1285 		}
1286 	        put_align_long(0, &m[i].ddBlock);
1287 		m[i].ddSize = 0;
1288 		m[i].ddType = 0;
1289 		p->sbDrvrCount -= 1;
1290 		return; /* XXX if we continue we will delete other drivers? */
1291 	    }
1292 	}
1293     }
1294 }
1295 
1296 int
1297 read_block(partition_map_header *map, unsigned long num, char *buf)
1298 {
1299 //printf("read block %d\n", num);
1300     return read_media(map->m, ((long long) num) * map->logical_block,
1301     		PBLOCK_SIZE, (void *)buf);
1302 }
1303 
1304 
1305 int
1306 write_block(partition_map_header *map, unsigned long num, char *buf)
1307 {
1308     return write_media(map->m, ((long long) num) * map->logical_block,
1309     		PBLOCK_SIZE, (void *)buf);
1310 }
1311