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