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