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