xref: /openbsd-src/sbin/pdisk/partition_map.c (revision ddc1a6c2c1755a24d9236ec889a3c01e9f865ea5)
1*ddc1a6c2Skrw /*	$OpenBSD: partition_map.c,v 1.99 2019/07/31 00:14:10 krw Exp $	*/
2fb04e59aSjasper 
386ec409aSkrw /*
486ec409aSkrw  * partition_map.c - partition map routines
586ec409aSkrw  *
686ec409aSkrw  * Written by Eryk Vershen
786ec409aSkrw  */
8dce63815Sdrahn 
9dce63815Sdrahn /*
10dce63815Sdrahn  * Copyright 1996,1997,1998 by Apple Computer, Inc.
11dce63815Sdrahn  *              All Rights Reserved
12dce63815Sdrahn  *
13dce63815Sdrahn  * Permission to use, copy, modify, and distribute this software and
14dce63815Sdrahn  * its documentation for any purpose and without fee is hereby granted,
15dce63815Sdrahn  * provided that the above copyright notice appears in all copies and
16dce63815Sdrahn  * that both the copyright notice and this permission notice appear in
17dce63815Sdrahn  * supporting documentation.
18dce63815Sdrahn  *
19dce63815Sdrahn  * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
20dce63815Sdrahn  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21dce63815Sdrahn  * FOR A PARTICULAR PURPOSE.
22dce63815Sdrahn  *
23dce63815Sdrahn  * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
24dce63815Sdrahn  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
25dce63815Sdrahn  * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
26dce63815Sdrahn  * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
27dce63815Sdrahn  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28dce63815Sdrahn  */
29dce63815Sdrahn 
300392b273Skrw #include <sys/queue.h>
314611a116Skrw #include <sys/stdint.h>
32786df0ccSkrw 
33786df0ccSkrw #include <err.h>
34dce63815Sdrahn #include <stdio.h>
35dce63815Sdrahn #include <stdlib.h>
36dce63815Sdrahn #include <string.h>
37dce63815Sdrahn 
38dce63815Sdrahn #include "partition_map.h"
39dce63815Sdrahn #include "io.h"
40cf1921b2Skrw #include "file_media.h"
41dce63815Sdrahn 
42dce63815Sdrahn #define APPLE_HFS_FLAGS_VALUE	0x4000037f
43dce63815Sdrahn 
44dce63815Sdrahn const char     *kFreeType = "Apple_Free";
45dce63815Sdrahn const char     *kMapType = "Apple_partition_map";
461faa4e16Sdrahn const char     *kUnixType = "OpenBSD";
47dce63815Sdrahn const char     *kHFSType = "Apple_HFS";
48dce63815Sdrahn 
49f4c16b6bSkrw void		combine_entry(struct entry *);
50d9d66606Skrw struct entry   *create_entry(struct partition_map *, long, const char *,
51d9d66606Skrw     const char *, uint32_t, uint32_t);
52f4c16b6bSkrw void		delete_entry(struct entry *);
53f4c16b6bSkrw void		insert_in_base_order(struct entry *);
54f4c16b6bSkrw void		insert_in_disk_order(struct entry *);
55356b92ceSkrw int		read_partition_map(struct partition_map *);
56f4c16b6bSkrw void		remove_driver(struct entry *);
57356b92ceSkrw void		renumber_disk_addresses(struct partition_map *);
58dce63815Sdrahn 
59356b92ceSkrw struct partition_map *
open_partition_map(int fd,char * name,uint64_t mediasz,uint32_t sectorsz)60edde6c52Skrw open_partition_map(int fd, char *name, uint64_t mediasz, uint32_t sectorsz)
61dce63815Sdrahn {
62356b92ceSkrw 	struct partition_map *map;
639bcf66d3Skrw 	int ok;
64dce63815Sdrahn 
65356b92ceSkrw 	map = malloc(sizeof(struct partition_map));
6684028592Skrw 	if (map == NULL)
6784028592Skrw 		errx(1, "No memory to open partition map");
6836bbc7f2Skrw 
6936bbc7f2Skrw 	map->fd = fd;
70dce63815Sdrahn 	map->name = name;
7136bbc7f2Skrw 
72dce63815Sdrahn 	map->changed = 0;
730392b273Skrw 	LIST_INIT(&map->disk_order);
74ef60065bSkrw 	LIST_INIT(&map->base_order);
7536bbc7f2Skrw 	map->blocks_in_map = 0;
7636bbc7f2Skrw 	map->maximum_in_map = -1;
774611a116Skrw 
784611a116Skrw 	if (mediasz > UINT32_MAX)
794611a116Skrw 		map->media_size = UINT32_MAX;
804611a116Skrw 	else
8122aa3c6fSkrw 		map->media_size = mediasz;
82dce63815Sdrahn 
836f012227Skrw 	if (read_block0(map->fd, map) == 0) {
8468d0f91bSkrw 		warnx("Can't read block 0 from '%s'", name);
8536bbc7f2Skrw 		free_partition_map(map);
86dce63815Sdrahn 		return NULL;
87dce63815Sdrahn 	}
886f012227Skrw 	if (map->sbSig == BLOCK0_SIGNATURE &&
896f012227Skrw 	    map->sbBlkSize == sectorsz &&
90*ddc1a6c2Skrw 	    map->sbBlkCount <= mediasz) {
9199776eb0Skrw 		if (read_partition_map(map) == 0)
92dce63815Sdrahn 			return map;
93fd500837Skrw 	} else {
946f012227Skrw 		if (map->sbSig != BLOCK0_SIGNATURE)
95fd500837Skrw 			warnx("Block 0 signature: Expected 0x%04x, "
96fd500837Skrw 			    "got 0x%04x", BLOCK0_SIGNATURE,
976f012227Skrw 			    map->sbSig);
986f012227Skrw 		else if (map->sbBlkSize != sectorsz)
99fd500837Skrw 			warnx("Block 0 sbBlkSize (%u) != sector size (%u)",
1006f012227Skrw 			    map->sbBlkSize, sectorsz);
101*ddc1a6c2Skrw 		else if (map->sbBlkCount > mediasz)
102*ddc1a6c2Skrw 			warnx("Block 0 sbBlkCount (%u) > media size (%llu)",
1036f012227Skrw 			    map->sbBlkCount,
104fd500837Skrw 			    (unsigned long long)mediasz);
105fd500837Skrw 	}
10636bbc7f2Skrw 
10736bbc7f2Skrw 	if (!lflag) {
10836bbc7f2Skrw 		my_ungetch('\n');
10936bbc7f2Skrw 		printf("No valid partition map found on '%s'.\n", name);
1109bcf66d3Skrw 		ok = get_okay("Create default map? [n/y]: ", 0);
1119bcf66d3Skrw 		flush_to_newline(0);
1129bcf66d3Skrw 		if (ok == 1) {
11336bbc7f2Skrw 			free_partition_map(map);
114edde6c52Skrw 			map = create_partition_map(fd, name, mediasz, sectorsz);
11536bbc7f2Skrw 			if (map)
116106ae2cbSkrw 				return map;
117dce63815Sdrahn 		}
11836bbc7f2Skrw 	}
11936bbc7f2Skrw 
12036bbc7f2Skrw 	free_partition_map(map);
121dce63815Sdrahn 	return NULL;
122dce63815Sdrahn }
123dce63815Sdrahn 
124dce63815Sdrahn 
125dce63815Sdrahn void
free_partition_map(struct partition_map * map)126356b92ceSkrw free_partition_map(struct partition_map *map)
127dce63815Sdrahn {
128f4c16b6bSkrw 	struct entry *entry;
129dce63815Sdrahn 
1300645e669Skrw 	if (map == NULL)
1310645e669Skrw 		return;
1320645e669Skrw 
1330392b273Skrw 	while (!LIST_EMPTY(&map->disk_order)) {
1340392b273Skrw 		entry = LIST_FIRST(&map->disk_order);
1350392b273Skrw 		LIST_REMOVE(entry, disk_entry);
136dce63815Sdrahn 		free(entry);
137dce63815Sdrahn 	}
1380645e669Skrw 
139dce63815Sdrahn 	free(map);
140dce63815Sdrahn }
141dce63815Sdrahn 
142dce63815Sdrahn int
read_partition_map(struct partition_map * map)143356b92ceSkrw read_partition_map(struct partition_map *map)
144dce63815Sdrahn {
145f4c16b6bSkrw 	struct entry *cur, *nextcur;
146f8fa35e5Skrw 	struct entry *entry;
14799776eb0Skrw 	int ix;
148fd500837Skrw 	uint32_t limit, base, next, nextbase;
149dce63815Sdrahn 
1509225bef5Skrw 	limit = 1; /* There has to be at least one, which has the real limit. */
15199776eb0Skrw 	for (ix = 1; ix <= limit; ix++) {
152f8fa35e5Skrw 		entry = malloc(sizeof(struct entry));
153f8fa35e5Skrw 		if (entry == NULL)
15484028592Skrw 			errx(1, "No memory for partition entry");
155f8fa35e5Skrw 		if (read_dpme(map->fd, ix, entry) == 0) {
15668d0f91bSkrw 			warnx("Can't read block %u from '%s'", ix, map->name);
157f8fa35e5Skrw 			free(entry);
15899776eb0Skrw 			return 1;
15999776eb0Skrw 		}
160f8fa35e5Skrw 		if (entry->dpme_signature != DPME_SIGNATURE) {
16199776eb0Skrw 			warnx("Invalid signature on block %d. Expected %x, "
16299776eb0Skrw 			    "got %x", ix, DPME_SIGNATURE,
163f8fa35e5Skrw 			    entry->dpme_signature);
164f8fa35e5Skrw 			free(entry);
16599776eb0Skrw 			return 1;
16699776eb0Skrw 		}
1679225bef5Skrw 		if (ix == 1) {
1689225bef5Skrw 			if (entry->dpme_map_entries > entry->dpme_pblocks) {
1699225bef5Skrw 				warnx("Map entry count (%u) > # of physical "
1709225bef5Skrw 				    "blocks (%u)", entry->dpme_map_entries,
1719225bef5Skrw 				    entry->dpme_pblocks);
1729225bef5Skrw 				free(entry);
1739225bef5Skrw 				return 1;
1749225bef5Skrw 			}
1759225bef5Skrw 			if (entry->dpme_map_entries == 0) {
1769225bef5Skrw 				warnx("Map entry count ==  0. Must be > 0");
1779225bef5Skrw 				free(entry);
1789225bef5Skrw 				return 1;
1799225bef5Skrw 			}
1809225bef5Skrw 			map->maximum_in_map = entry->dpme_pblocks;
181f8fa35e5Skrw 			limit = entry->dpme_map_entries;
1829225bef5Skrw 		}
183f8fa35e5Skrw 		if (limit != entry->dpme_map_entries) {
18499776eb0Skrw 			warnx("Invalid entry count on block %d. "
18599776eb0Skrw 			    "Expected %d, got %d", ix, limit,
186f8fa35e5Skrw 			    entry->dpme_map_entries);
187f8fa35e5Skrw 			free(entry);
18899776eb0Skrw 			return 1;
18999776eb0Skrw 		}
190f8fa35e5Skrw 		if (entry->dpme_lblock_start >= entry->dpme_pblocks) {
191fd500837Skrw 			warnx("\tlogical start (%u) >= block count"
192f8fa35e5Skrw 			    "count (%u).", entry->dpme_lblock_start,
193f8fa35e5Skrw 			    entry->dpme_pblocks);
194f8fa35e5Skrw 			free(entry);
195fd500837Skrw 			return 1;
196fd500837Skrw 		}
197f8fa35e5Skrw 		if (entry->dpme_lblocks > entry->dpme_pblocks -
198f8fa35e5Skrw 			entry->dpme_lblock_start) {
199fd500837Skrw 			warnx("\tlogical blocks (%u) > available blocks (%u).",
200f8fa35e5Skrw 			    entry->dpme_lblocks,
201f8fa35e5Skrw 			    entry->dpme_pblocks - entry->dpme_lblock_start);
202f8fa35e5Skrw 			free(entry);
203fd500837Skrw 			return 1;
204fd500837Skrw 		}
2059225bef5Skrw 		entry->the_map = map;
2069225bef5Skrw 		entry->disk_address = ix;
2079225bef5Skrw 		insert_in_disk_order(entry);
2089225bef5Skrw 		insert_in_base_order(entry);
2099225bef5Skrw 		map->blocks_in_map++;
210dce63815Sdrahn 	}
211fd500837Skrw 
212fd500837Skrw 	/* Traverse base_order looking for
213fd500837Skrw 	 *
214fd500837Skrw 	 * 1) Overlapping partitions
215fd500837Skrw 	 * 2) Unmapped space
216fd500837Skrw 	 */
217ef60065bSkrw 	LIST_FOREACH(cur, &map->base_order, base_entry) {
218f8fa35e5Skrw 		base = cur->dpme_pblock_start;
219f8fa35e5Skrw 		next = base + cur->dpme_pblocks;
220e4c0374dSkrw 		if (base >= map->media_size ||
221e4c0374dSkrw 		    next < base ||
222e4c0374dSkrw 		    next > map->media_size) {
223e4c0374dSkrw 			warnx("Partition extends past end of disk: %u -> %u",
224e4c0374dSkrw 			    base, next);
225e4c0374dSkrw 		}
226ef60065bSkrw 		nextcur = LIST_NEXT(cur, base_entry);
227ef60065bSkrw 		if (nextcur)
228f8fa35e5Skrw 			nextbase = nextcur->dpme_pblock_start;
229fd500837Skrw 		else
230fd500837Skrw 			nextbase = map->media_size;
231fd500837Skrw 		if (next != nextbase)
232fd500837Skrw 			warnx("Unmapped pblocks: %u -> %u", next, nextbase);
233fd500837Skrw 		if (next > nextbase)
234fd500837Skrw 			warnx("Partition %ld overlaps next partition",
235fd500837Skrw 			    cur->disk_address);
236fd500837Skrw 	}
237fd500837Skrw 
238dce63815Sdrahn 	return 0;
239dce63815Sdrahn }
240dce63815Sdrahn 
241dce63815Sdrahn 
242dce63815Sdrahn void
write_partition_map(struct partition_map * map)243356b92ceSkrw write_partition_map(struct partition_map *map)
244dce63815Sdrahn {
245f4c16b6bSkrw 	struct entry *entry;
246fa0674aeSkrw 	int result;
247dce63815Sdrahn 
2486f012227Skrw 	result = write_block0(map->fd, map);
249fa0674aeSkrw 	if (result == 0)
25068d0f91bSkrw 		warn("Unable to write block zero");
251fa0674aeSkrw 
2520392b273Skrw 	LIST_FOREACH(entry, &map->disk_order, disk_entry) {
253f8fa35e5Skrw 		result = write_dpme(map->fd, entry->disk_address, entry);
254fa0674aeSkrw 		if (result == 0)
255fa0674aeSkrw 			warn("Unable to write block %ld", entry->disk_address);
256dce63815Sdrahn 	}
257dce63815Sdrahn }
258dce63815Sdrahn 
259dce63815Sdrahn 
260356b92ceSkrw struct partition_map *
create_partition_map(int fd,char * name,u_int64_t mediasz,uint32_t sectorsz)261edde6c52Skrw create_partition_map(int fd, char *name, u_int64_t mediasz, uint32_t sectorsz)
262dce63815Sdrahn {
263356b92ceSkrw 	struct partition_map *map;
264f8fa35e5Skrw 	struct entry *entry;
265dce63815Sdrahn 
266356b92ceSkrw 	map = malloc(sizeof(struct partition_map));
26784028592Skrw 	if (map == NULL)
26884028592Skrw 		errx(1, "No memory to create partition map");
26984028592Skrw 
270dce63815Sdrahn 	map->name = name;
27136bbc7f2Skrw 	map->fd = fd;
272dce63815Sdrahn 	map->changed = 1;
2730392b273Skrw 	LIST_INIT(&map->disk_order);
274ef60065bSkrw 	LIST_INIT(&map->base_order);
275dce63815Sdrahn 
276dce63815Sdrahn 	map->blocks_in_map = 0;
277dce63815Sdrahn 	map->maximum_in_map = -1;
2787e2ef4a3Skrw 	map->media_size = mediasz;
279dce63815Sdrahn 
2806f012227Skrw 	map->sbSig = BLOCK0_SIGNATURE;
281ead46a34Skrw 	map->sbBlkSize = sectorsz;
2826f012227Skrw 	map->sbBlkCount = map->media_size;
283dce63815Sdrahn 
284d9d66606Skrw 	entry = create_entry(map, 1, "", kFreeType, 1, mediasz - 1);
285f8fa35e5Skrw 	if (entry == NULL)
286d9d66606Skrw 		errx(1, "No memory for new dpme");
28784028592Skrw 
2886f012227Skrw 	add_partition_to_map("Apple", kMapType, 1,
2896f012227Skrw 	    (map->media_size <= 128 ? 2 : 63), map);
29036bbc7f2Skrw 
29184028592Skrw 	return map;
292dce63815Sdrahn }
293dce63815Sdrahn 
294dce63815Sdrahn 
295dce63815Sdrahn int
add_partition_to_map(const char * name,const char * dptype,uint32_t base,uint32_t length,struct partition_map * map)2966aea8f79Skrw add_partition_to_map(const char *name, const char *dptype, uint32_t base,
297356b92ceSkrw     uint32_t length, struct partition_map *map)
298dce63815Sdrahn {
299f4c16b6bSkrw 	struct entry *cur;
300045080e6Skrw 	int limit, new_entries;
301045080e6Skrw 	uint32_t old_base, old_length, old_address;
302045080e6Skrw 	uint32_t new_base, new_length;
303dce63815Sdrahn 
304045080e6Skrw 	if (map->maximum_in_map < 0)
305045080e6Skrw 		limit = map->media_size;
306045080e6Skrw 	else
307045080e6Skrw 		limit = map->maximum_in_map;
308045080e6Skrw 
309045080e6Skrw 	/* find a block of free space that starts includes base and length */
310ef60065bSkrw 	LIST_FOREACH(cur, &map->base_order, base_entry) {
311f8fa35e5Skrw 		if (strncasecmp(cur->dpme_type, kFreeType, DPISTRLEN))
312045080e6Skrw 		    continue;
313f8fa35e5Skrw 		if (cur->dpme_pblock_start <= base &&
314ddf6313dSkrw 		    (base + length) <=
315f8fa35e5Skrw 		    (cur->dpme_pblock_start + cur->dpme_pblocks))
316dce63815Sdrahn 			break;
317dce63815Sdrahn 	}
318045080e6Skrw 	if (cur == NULL) {
319dce63815Sdrahn 		printf("requested base and length is not "
320dce63815Sdrahn 		       "within an existing free partition\n");
321dce63815Sdrahn 		return 0;
322dce63815Sdrahn 	}
323f8fa35e5Skrw 	old_base = cur->dpme_pblock_start;
324f8fa35e5Skrw 	old_length = cur->dpme_pblocks;
325045080e6Skrw 	old_address = cur->disk_address;
326045080e6Skrw 
327045080e6Skrw 	/* Check that there is enough room in the map for the new entries! */
328045080e6Skrw 	if (base == old_base && length == old_length)
329045080e6Skrw 		new_entries = 0;
330045080e6Skrw 	else if (base == old_base)
331045080e6Skrw 		new_entries = 1;
332045080e6Skrw 	else if (base - old_base < old_length - length)
333045080e6Skrw 		new_entries = 2;
334a8b53b4cSkrw 	else
335045080e6Skrw 		new_entries = 1;
336045080e6Skrw 	if (map->blocks_in_map + new_entries > limit) {
337dce63815Sdrahn 		printf("the map is not big enough\n");
338dce63815Sdrahn 		return 0;
339dce63815Sdrahn 	}
340045080e6Skrw 
341045080e6Skrw 	/*
342045080e6Skrw 	 * Delete old free entry from map and add back 1 to 3 new entries.
343045080e6Skrw 	 *
344045080e6Skrw 	 * 1) Empty space from base+len to old end.
345045080e6Skrw 	 * 2) New entry from specified base for length.
346045080e6Skrw 	 * 3) Empty space from old base to new base.
347045080e6Skrw 	 *
348045080e6Skrw 	 *  All with the same disk address, so they must be added in that
349045080e6Skrw 	 *  order!
350045080e6Skrw 	 */
351045080e6Skrw 	delete_entry(cur);
352045080e6Skrw 
353045080e6Skrw 	new_base = base + length;
354045080e6Skrw 	new_length = (old_base + old_length) - new_base;
355045080e6Skrw 	if (new_length > 0) {
356045080e6Skrw 		/* New free space entry *after* new partition. */
357d9d66606Skrw 		cur = create_entry(map, old_address, "", kFreeType, new_base,
358d9d66606Skrw 		    new_length);
359f8fa35e5Skrw 		if (cur == NULL)
360045080e6Skrw 			errx(1, "No memory for new dpme");
361045080e6Skrw 	}
362045080e6Skrw 
363d9d66606Skrw 	cur = create_entry(map, old_address, name, dptype, base, length);
364f8fa35e5Skrw 	if (cur == NULL)
365f8fa35e5Skrw 		errx(1, "No memory for new entry");
366a8b53b4cSkrw 
367045080e6Skrw 	new_length = base - old_base;
368045080e6Skrw 	if (new_length > 0) {
369045080e6Skrw 		/* New free space entry *before* new partition. */
370d9d66606Skrw 		cur = create_entry(map, old_address, "", kFreeType, old_base,
371d9d66606Skrw 		    new_length);
372f8fa35e5Skrw 		if (cur == NULL)
373f8fa35e5Skrw 			errx(1, "No memory for new entry");
374dce63815Sdrahn 	}
375045080e6Skrw 
376dce63815Sdrahn 	renumber_disk_addresses(map);
377dce63815Sdrahn 	map->changed = 1;
378dce63815Sdrahn 	return 1;
379dce63815Sdrahn }
380dce63815Sdrahn 
381dce63815Sdrahn 
382f8fa35e5Skrw struct entry*
create_entry(struct partition_map * map,long ix,const char * name,const char * dptype,uint32_t base,uint32_t length)383d9d66606Skrw create_entry(struct partition_map *map, long ix, const char *name,
384d9d66606Skrw     const char *dptype, uint32_t base, uint32_t length)
385dce63815Sdrahn {
386f8fa35e5Skrw 	struct entry *entry;
387dce63815Sdrahn 
388f8fa35e5Skrw 	entry = calloc(1, sizeof(struct entry));
389f8fa35e5Skrw 	if (entry == NULL)
390f8fa35e5Skrw 		errx(1, "No memory for new entry");
39184028592Skrw 
392f8fa35e5Skrw 	entry->dpme_signature = DPME_SIGNATURE;
393f8fa35e5Skrw 	entry->dpme_map_entries = 1;
394f8fa35e5Skrw 	entry->dpme_pblock_start = base;
395f8fa35e5Skrw 	entry->dpme_pblocks = length;
396f8fa35e5Skrw 	strlcpy(entry->dpme_name, name, sizeof(entry->dpme_name));
397f8fa35e5Skrw 	strlcpy(entry->dpme_type, dptype, sizeof(entry->dpme_type));
3982b90374cSkrw 	if (strncasecmp(dptype, kFreeType, DPISTRLEN)) {
3992b90374cSkrw 		/* Only non-kFreeType entries get lblock info != 0. */
400f8fa35e5Skrw 		entry->dpme_lblocks = entry->dpme_pblocks;
4012b90374cSkrw 	}
402f8fa35e5Skrw 	dpme_init_flags(entry);
40384028592Skrw 
404d9d66606Skrw 	entry->disk_address = ix;
405d9d66606Skrw 	entry->the_map = map;
406d9d66606Skrw 
407d9d66606Skrw 	insert_in_disk_order(entry);
408d9d66606Skrw 	insert_in_base_order(entry);
409d9d66606Skrw 
410d9d66606Skrw 	map->blocks_in_map++;
411d9d66606Skrw 	if (map->maximum_in_map < 0) {
412d9d66606Skrw 		if (strncasecmp(entry->dpme_type, kMapType, DPISTRLEN) == 0)
413d9d66606Skrw 			map->maximum_in_map = entry->dpme_pblocks;
414d9d66606Skrw 	}
415d9d66606Skrw 
416f8fa35e5Skrw 	return entry;
417b42d9302Smartin }
418b42d9302Smartin 
419b42d9302Smartin void
dpme_init_flags(struct entry * entry)420f8fa35e5Skrw dpme_init_flags(struct entry *entry)
421b42d9302Smartin {
422f8fa35e5Skrw 	if (strncasecmp(entry->dpme_type, kFreeType, DPISTRLEN) == 0)
423f8fa35e5Skrw 		entry->dpme_flags = 0;
424f8fa35e5Skrw 	else if (strncasecmp(entry->dpme_type, kMapType, DPISTRLEN) == 0)
425f8fa35e5Skrw 		entry->dpme_flags = DPME_VALID | DPME_ALLOCATED;
426f8fa35e5Skrw 	else if (strncasecmp(entry->dpme_type, kHFSType, DPISTRLEN) == 0)
427f8fa35e5Skrw 		entry->dpme_flags = APPLE_HFS_FLAGS_VALUE;
428a8b53b4cSkrw 	else
429f8fa35e5Skrw 		entry->dpme_flags = DPME_VALID | DPME_ALLOCATED |
43015055065Skrw 		    DPME_READABLE | DPME_WRITABLE;
431dce63815Sdrahn }
432dce63815Sdrahn 
433dce63815Sdrahn void
renumber_disk_addresses(struct partition_map * map)434356b92ceSkrw renumber_disk_addresses(struct partition_map *map)
435dce63815Sdrahn {
436f4c16b6bSkrw 	struct entry *cur;
437b42d9302Smartin 	long ix;
438dce63815Sdrahn 
43986ec409aSkrw 	/* reset disk addresses */
440b42d9302Smartin 	ix = 1;
4410392b273Skrw 	LIST_FOREACH(cur, &map->disk_order, disk_entry) {
442b42d9302Smartin 		cur->disk_address = ix++;
443f8fa35e5Skrw 		cur->dpme_map_entries = map->blocks_in_map;
444dce63815Sdrahn 	}
445dce63815Sdrahn }
446dce63815Sdrahn 
447dce63815Sdrahn void
delete_partition_from_map(struct entry * entry)448f4c16b6bSkrw delete_partition_from_map(struct entry *entry)
449dce63815Sdrahn {
45083c5af48Skrw 	struct partition_map *map;
45183c5af48Skrw 	uint32_t base, length, address;
45283c5af48Skrw 
453f8fa35e5Skrw 	if (strncasecmp(entry->dpme_type, kMapType, DPISTRLEN) == 0) {
454dce63815Sdrahn 		printf("Can't delete entry for the map itself\n");
455dce63815Sdrahn 		return;
456dce63815Sdrahn 	}
457f8fa35e5Skrw 	if (strncasecmp(entry->dpme_type, kFreeType, DPISTRLEN) == 0) {
458c9315992Skrw 		printf("Can't delete entry for free space\n");
459c9315992Skrw 		return;
460c9315992Skrw 	}
461575b2bc1Skrw 	if (contains_driver(entry)) {
462dce63815Sdrahn 		printf("This program can't install drivers\n");
463ddf6313dSkrw 		if (get_okay("are you sure you want to delete this driver? "
464ddf6313dSkrw 		    "[n/y]: ", 0) != 1) {
465dce63815Sdrahn 			return;
466dce63815Sdrahn 		}
46786ec409aSkrw 		remove_driver(entry);	/* update block0 if necessary */
468b42d9302Smartin 	}
469c9315992Skrw 
47083c5af48Skrw 	map = entry->the_map;
47183c5af48Skrw 	base = entry->dpme_pblock_start;
47283c5af48Skrw 	length = entry->dpme_pblocks;
47383c5af48Skrw 	address = entry->disk_address;
474c9315992Skrw 
47583c5af48Skrw 	delete_entry(entry);
47683c5af48Skrw 	entry = create_entry(map, address, "" , kFreeType, base, length);
477dce63815Sdrahn 	combine_entry(entry);
478c9315992Skrw 	renumber_disk_addresses(entry->the_map);
479c9315992Skrw 	entry->the_map->changed = 1;
480dce63815Sdrahn }
481dce63815Sdrahn 
482dce63815Sdrahn 
483dce63815Sdrahn int
contains_driver(struct entry * entry)484f4c16b6bSkrw contains_driver(struct entry *entry)
485dce63815Sdrahn {
486356b92ceSkrw 	struct partition_map *map;
48770060700Skrw 	struct ddmap *m;
488c579b49fSkrw 	int i;
489024cef13Skrw 	uint32_t start;
490dce63815Sdrahn 
491dce63815Sdrahn 	map = entry->the_map;
4926f012227Skrw 	m = map->sbDDMap;
4936f012227Skrw 	for (i = 0; i < map->sbDrvrCount; i++) {
49437e225b6Skrw 		start = m[i].ddBlock;
495f8fa35e5Skrw 		if (entry->dpme_pblock_start <= start &&
4960645e669Skrw 		    (start + m[i].ddSize) <= (entry->dpme_pblock_start +
497f8fa35e5Skrw 		    entry->dpme_pblocks))
498dce63815Sdrahn 			return 1;
499dce63815Sdrahn 	}
5000645e669Skrw 
501dce63815Sdrahn 	return 0;
502dce63815Sdrahn }
503dce63815Sdrahn 
504dce63815Sdrahn 
505dce63815Sdrahn void
combine_entry(struct entry * entry)506f4c16b6bSkrw combine_entry(struct entry *entry)
507dce63815Sdrahn {
508f4c16b6bSkrw 	struct entry *p;
509024cef13Skrw 	uint32_t end;
510dce63815Sdrahn 
511a8b53b4cSkrw 	if (entry == NULL ||
512f8fa35e5Skrw 	    strncasecmp(entry->dpme_type, kFreeType, DPISTRLEN) != 0)
513dce63815Sdrahn 		return;
514a8b53b4cSkrw 
515ef60065bSkrw 	p = LIST_NEXT(entry, base_entry);
516ef60065bSkrw 	if (p != NULL) {
517f8fa35e5Skrw 		if (strncasecmp(p->dpme_type, kFreeType, DPISTRLEN) !=
5188887dc3dSkrw 		    0) {
51986ec409aSkrw 			/* next is not free */
520f8fa35e5Skrw 		} else if (entry->dpme_pblock_start +
521f8fa35e5Skrw 		    entry->dpme_pblocks != p->dpme_pblock_start) {
52286ec409aSkrw 			/* next is not contiguous (XXX this is bad) */
523dce63815Sdrahn 			printf("next entry is not contiguous\n");
52486ec409aSkrw 			/* start is already minimum */
52586ec409aSkrw 			/* new end is maximum of two ends */
526f8fa35e5Skrw 			end = p->dpme_pblock_start + p->dpme_pblocks;
527f8fa35e5Skrw 			if (end > entry->dpme_pblock_start +
528f8fa35e5Skrw 			    entry->dpme_pblocks) {
529f8fa35e5Skrw 				entry->dpme_pblocks = end -
530f8fa35e5Skrw 				    entry->dpme_pblock_start;
531dce63815Sdrahn 			}
532dce63815Sdrahn 			delete_entry(p);
533dce63815Sdrahn 		} else {
534f8fa35e5Skrw 			entry->dpme_pblocks += p->dpme_pblocks;
535dce63815Sdrahn 			delete_entry(p);
536dce63815Sdrahn 		}
537dce63815Sdrahn 	}
538ef60065bSkrw 
539ef60065bSkrw 	LIST_FOREACH(p, &entry->the_map->base_order, base_entry) {
540ef60065bSkrw 		if (LIST_NEXT(p, base_entry) == entry)
541ef60065bSkrw 			break;
542ef60065bSkrw 	}
543ef60065bSkrw 	if (p != NULL) {
544f8fa35e5Skrw 		if (strncasecmp(p->dpme_type, kFreeType, DPISTRLEN) != 0) {
54586ec409aSkrw 			/* previous is not free */
546f8fa35e5Skrw 		} else if (p->dpme_pblock_start + p->dpme_pblocks !=
547f8fa35e5Skrw 		    entry->dpme_pblock_start) {
54886ec409aSkrw 			/* previous is not contiguous (XXX this is bad) */
549dce63815Sdrahn 			printf("previous entry is not contiguous\n");
55086ec409aSkrw 			/* new end is maximum of two ends */
551f8fa35e5Skrw 			end = p->dpme_pblock_start + p->dpme_pblocks;
552f8fa35e5Skrw 			if (end < entry->dpme_pblock_start +
553f8fa35e5Skrw 			    entry->dpme_pblocks) {
554f8fa35e5Skrw 				end = entry->dpme_pblock_start +
555f8fa35e5Skrw 				    entry->dpme_pblocks;
556dce63815Sdrahn 			}
5572b90374cSkrw 			entry->dpme_pblocks = end - p->dpme_pblock_start;
5582b90374cSkrw 			entry->dpme_pblock_start = p->dpme_pblock_start;
559dce63815Sdrahn 			delete_entry(p);
560dce63815Sdrahn 		} else {
5612b90374cSkrw 			entry->dpme_pblock_start = p->dpme_pblock_start;
562f8fa35e5Skrw 			entry->dpme_pblocks += p->dpme_pblocks;
563dce63815Sdrahn 			delete_entry(p);
564dce63815Sdrahn 		}
565dce63815Sdrahn 	}
566dce63815Sdrahn }
567dce63815Sdrahn 
568dce63815Sdrahn 
569dce63815Sdrahn void
delete_entry(struct entry * entry)570f4c16b6bSkrw delete_entry(struct entry *entry)
571dce63815Sdrahn {
572356b92ceSkrw 	struct partition_map *map;
573dce63815Sdrahn 
574dce63815Sdrahn 	map = entry->the_map;
575dce63815Sdrahn 	map->blocks_in_map--;
576dce63815Sdrahn 
5770392b273Skrw 	LIST_REMOVE(entry, disk_entry);
578ef60065bSkrw 	LIST_REMOVE(entry, base_entry);
579a8b53b4cSkrw 
580dce63815Sdrahn 	free(entry);
581dce63815Sdrahn }
582dce63815Sdrahn 
583dce63815Sdrahn 
584f4c16b6bSkrw struct entry *
find_entry_by_disk_address(long ix,struct partition_map * map)585356b92ceSkrw find_entry_by_disk_address(long ix, struct partition_map *map)
586dce63815Sdrahn {
587f4c16b6bSkrw 	struct entry *cur;
588dce63815Sdrahn 
5890392b273Skrw 	LIST_FOREACH(cur, &map->disk_order, disk_entry) {
590a8b53b4cSkrw 		if (cur->disk_address == ix)
591dce63815Sdrahn 			break;
592dce63815Sdrahn 	}
593dce63815Sdrahn 	return cur;
594dce63815Sdrahn }
595dce63815Sdrahn 
596dce63815Sdrahn 
597f4c16b6bSkrw struct entry *
find_entry_by_type(const char * type_name,struct partition_map * map)598356b92ceSkrw find_entry_by_type(const char *type_name, struct partition_map *map)
599dce63815Sdrahn {
600f4c16b6bSkrw 	struct entry *cur;
601dce63815Sdrahn 
602ef60065bSkrw 	LIST_FOREACH(cur, &map->base_order, base_entry) {
6030645e669Skrw 		if (strncasecmp(cur->dpme_type, type_name, DPISTRLEN) == 0)
604dce63815Sdrahn 			break;
605dce63815Sdrahn 	}
606dce63815Sdrahn 	return cur;
607dce63815Sdrahn }
608dce63815Sdrahn 
609f4c16b6bSkrw struct entry *
find_entry_by_base(uint32_t base,struct partition_map * map)610356b92ceSkrw find_entry_by_base(uint32_t base, struct partition_map *map)
611b42d9302Smartin {
612f4c16b6bSkrw 	struct entry *cur;
613b42d9302Smartin 
614ef60065bSkrw 	LIST_FOREACH(cur, &map->base_order, base_entry) {
615f8fa35e5Skrw 		if (cur->dpme_pblock_start == base)
616b42d9302Smartin 			break;
617b42d9302Smartin 	}
618b42d9302Smartin 	return cur;
619b42d9302Smartin }
620b42d9302Smartin 
621dce63815Sdrahn 
622dce63815Sdrahn void
move_entry_in_map(long index1,long index2,struct partition_map * map)623356b92ceSkrw move_entry_in_map(long index1, long index2, struct partition_map *map)
624dce63815Sdrahn {
625f4c16b6bSkrw 	struct entry *p1, *p2;
626dce63815Sdrahn 
627a14e71f0Skrw 	if (index1 == index2)
628a14e71f0Skrw 		return;
629a14e71f0Skrw 
630a14e71f0Skrw 	if (index1 == 1 || index2 == 1) {
631a14e71f0Skrw 		printf("Partition #1 cannot be moved\n");
632a14e71f0Skrw 		return;
633a14e71f0Skrw 	}
634a14e71f0Skrw 	p1 = find_entry_by_disk_address(index1, map);
635a14e71f0Skrw 	if (p1 == NULL) {
636a14e71f0Skrw 		printf("Partition #%ld not found\n", index1);
637a14e71f0Skrw 		return;
638a14e71f0Skrw 	}
639a14e71f0Skrw 	p2 = find_entry_by_disk_address(index2, map);
640a14e71f0Skrw 	if (p2 == NULL) {
641a14e71f0Skrw 		printf("Partition #%ld not found\n", index2);
642a14e71f0Skrw 		return;
643a14e71f0Skrw 	}
644a14e71f0Skrw 
6450392b273Skrw 	LIST_REMOVE(p1, disk_entry);
6460392b273Skrw 	LIST_REMOVE(p2, disk_entry);
647a14e71f0Skrw 
648a14e71f0Skrw 	p1->disk_address = index2;
649a14e71f0Skrw 	p2->disk_address = index1;
650a14e71f0Skrw 
651a14e71f0Skrw 	insert_in_disk_order(p1);
652a14e71f0Skrw 	insert_in_disk_order(p2);
653a14e71f0Skrw 
654dce63815Sdrahn 	renumber_disk_addresses(map);
655dce63815Sdrahn 	map->changed = 1;
656dce63815Sdrahn }
657dce63815Sdrahn 
658dce63815Sdrahn 
659dce63815Sdrahn void
insert_in_disk_order(struct entry * entry)660f4c16b6bSkrw insert_in_disk_order(struct entry *entry)
661dce63815Sdrahn {
662356b92ceSkrw 	struct partition_map *map;
663f4c16b6bSkrw 	struct entry *cur;
664dce63815Sdrahn 
66586ec409aSkrw 	/* find position in disk list & insert */
666dce63815Sdrahn 	map = entry->the_map;
6670392b273Skrw 	if (LIST_EMPTY(&map->disk_order)) {
6680392b273Skrw 		LIST_INSERT_HEAD(&map->disk_order, entry, disk_entry);
6690392b273Skrw 		return;
670dce63815Sdrahn 	}
6710392b273Skrw 
6720392b273Skrw 	LIST_FOREACH(cur, &map->disk_order, disk_entry) {
6730392b273Skrw 		if (cur->disk_address >= entry->disk_address) {
6740392b273Skrw 			LIST_INSERT_BEFORE(cur, entry, disk_entry);
6750392b273Skrw 			return;
6760392b273Skrw 		}
6770392b273Skrw 		if (LIST_NEXT(cur, disk_entry) == NULL) {
6780392b273Skrw 			LIST_INSERT_AFTER(cur, entry, disk_entry);
6790392b273Skrw 			return;
680dce63815Sdrahn 		}
681dce63815Sdrahn 	}
682dce63815Sdrahn }
683dce63815Sdrahn 
684dce63815Sdrahn 
685dce63815Sdrahn void
insert_in_base_order(struct entry * entry)686f4c16b6bSkrw insert_in_base_order(struct entry *entry)
687dce63815Sdrahn {
688356b92ceSkrw 	struct partition_map *map;
689f4c16b6bSkrw 	struct entry *cur;
690ef60065bSkrw 	uint32_t start;
691dce63815Sdrahn 
69286ec409aSkrw 	/* find position in base list & insert */
693dce63815Sdrahn 	map = entry->the_map;
694ef60065bSkrw 	if (LIST_EMPTY(&map->base_order)) {
695ef60065bSkrw 		LIST_INSERT_HEAD(&map->base_order, entry, base_entry);
696ef60065bSkrw 		return;
697dce63815Sdrahn 	}
698ef60065bSkrw 
699f8fa35e5Skrw 	start = entry->dpme_pblock_start;
700ef60065bSkrw 	LIST_FOREACH(cur, &map->base_order, base_entry) {
701f8fa35e5Skrw 		if (start <= cur->dpme_pblock_start) {
702ef60065bSkrw 			LIST_INSERT_BEFORE(cur, entry, base_entry);
703ef60065bSkrw 			return;
704ef60065bSkrw 		}
705ef60065bSkrw 		if (LIST_NEXT(cur, base_entry) == NULL) {
706ef60065bSkrw 			LIST_INSERT_AFTER(cur, entry, base_entry);
707ef60065bSkrw 			return;
708dce63815Sdrahn 		}
709dce63815Sdrahn 	}
710dce63815Sdrahn }
711dce63815Sdrahn 
712dce63815Sdrahn 
713dce63815Sdrahn void
resize_map(long new_size,struct partition_map * map)714356b92ceSkrw resize_map(long new_size, struct partition_map *map)
715dce63815Sdrahn {
716f4c16b6bSkrw 	struct entry *entry;
717f4c16b6bSkrw 	struct entry *next;
718dce63815Sdrahn 	int incr;
719dce63815Sdrahn 
720dce63815Sdrahn 	entry = find_entry_by_type(kMapType, map);
721dce63815Sdrahn 
722dce63815Sdrahn 	if (entry == NULL) {
723dce63815Sdrahn 		printf("Couldn't find entry for map!\n");
724dce63815Sdrahn 		return;
725dce63815Sdrahn 	}
726f8fa35e5Skrw 	if (new_size == entry->dpme_pblocks)
727dce63815Sdrahn 		return;
728a8b53b4cSkrw 
729ef60065bSkrw 	next = LIST_NEXT(entry, base_entry);
730ef60065bSkrw 
731f8fa35e5Skrw 	if (new_size < entry->dpme_pblocks) {
732ef60065bSkrw 		/* make it smaller */
7336dc45c87Skrw 		if (next == NULL ||
7340645e669Skrw 		    strncasecmp(next->dpme_type, kFreeType, DPISTRLEN) != 0)
735dce63815Sdrahn 			incr = 1;
736a8b53b4cSkrw 		else
737dce63815Sdrahn 			incr = 0;
738dce63815Sdrahn 		if (new_size < map->blocks_in_map + incr) {
739dce63815Sdrahn 			printf("New size would be too small\n");
740dce63815Sdrahn 			return;
741dce63815Sdrahn 		}
742dce63815Sdrahn 		goto doit;
743dce63815Sdrahn 	}
74486ec409aSkrw 	/* make it larger */
7456dc45c87Skrw 	if (next == NULL ||
746f8fa35e5Skrw 	    strncasecmp(next->dpme_type, kFreeType, DPISTRLEN) != 0) {
747dce63815Sdrahn 		printf("No free space to expand into\n");
748dce63815Sdrahn 		return;
749dce63815Sdrahn 	}
750f8fa35e5Skrw 	if (entry->dpme_pblock_start + entry->dpme_pblocks
751f8fa35e5Skrw 	    != next->dpme_pblock_start) {
752dce63815Sdrahn 		printf("No contiguous free space to expand into\n");
753dce63815Sdrahn 		return;
754dce63815Sdrahn 	}
755f8fa35e5Skrw 	if (new_size > entry->dpme_pblocks + next->dpme_pblocks) {
756dce63815Sdrahn 		printf("No enough free space\n");
757dce63815Sdrahn 		return;
758dce63815Sdrahn 	}
759dce63815Sdrahn doit:
760f8fa35e5Skrw 	entry->dpme_type[0] = 0;
761dce63815Sdrahn 	delete_partition_from_map(entry);
762dce63815Sdrahn 	add_partition_to_map("Apple", kMapType, 1, new_size, map);
763dce63815Sdrahn 	map->maximum_in_map = new_size;
764dce63815Sdrahn }
765dce63815Sdrahn 
766dce63815Sdrahn 
767dce63815Sdrahn void
remove_driver(struct entry * entry)768f4c16b6bSkrw remove_driver(struct entry *entry)
769dce63815Sdrahn {
7706f012227Skrw 	struct partition_map *map;
77170060700Skrw 	struct ddmap *m;
772c579b49fSkrw 	int i, j;
773024cef13Skrw 	uint32_t start;
774dce63815Sdrahn 
775dce63815Sdrahn 	/*
776dce63815Sdrahn 	 * compute the factor to convert the block numbers in block0
777dce63815Sdrahn 	 * into partition map block numbers.
778dce63815Sdrahn 	 */
7796f012227Skrw 	map = entry->the_map;
7806f012227Skrw 	m = map->sbDDMap;
7816f012227Skrw 	for (i = 0; i < map->sbDrvrCount; i++) {
78237e225b6Skrw 		start = m[i].ddBlock;
783ddf6313dSkrw 		/*
784ddf6313dSkrw 		 * zap the driver if it is wholly contained in the
785ddf6313dSkrw 		 * partition
786ddf6313dSkrw 		 */
7870645e669Skrw 		if (entry->dpme_pblock_start <= start && (start +
7880645e669Skrw 		    m[i].ddSize) <= (entry->dpme_pblock_start +
7890645e669Skrw 		    entry->dpme_pblocks)) {
790ddf6313dSkrw 			/*
7910645e669Skrw 			 * Delete this driver by copying down later ones and
7920645e669Skrw 			 * zapping the last one.
793ddf6313dSkrw 			 */
7940645e669Skrw 			for (j = i + 1; j < map->sbDrvrCount; j++, i++) {
79537e225b6Skrw 				m[i].ddBlock = m[i].ddBlock;
796dce63815Sdrahn 				m[i].ddSize = m[j].ddSize;
797dce63815Sdrahn 				m[i].ddType = m[j].ddType;
798dce63815Sdrahn 			}
79937e225b6Skrw 			m[i].ddBlock = 0;
800dce63815Sdrahn 			m[i].ddSize = 0;
801dce63815Sdrahn 			m[i].ddType = 0;
8026f012227Skrw 			map->sbDrvrCount -= 1;
803ddf6313dSkrw 			return;	/* XXX if we continue we will delete
804ddf6313dSkrw 				 * other drivers? */
805dce63815Sdrahn 		}
806dce63815Sdrahn 	}
807dce63815Sdrahn }
808