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