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