xref: /netbsd-src/sys/fs/hfs/libhfs.c (revision dd6a50589e07b50bea16f69073970e5c76b41bfb)
1*dd6a5058Smrg /*	$NetBSD: libhfs.c,v 1.19 2023/08/11 05:51:34 mrg Exp $	*/
225e99827Sdillo 
325e99827Sdillo /*-
425e99827Sdillo  * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
525e99827Sdillo  * All rights reserved.
625e99827Sdillo  *
725e99827Sdillo  * This code is derived from software contributed to The NetBSD Foundation
841627b2cSdillo  * by Yevgeny Binder, Dieter Baron, and Pelle Johansson.
925e99827Sdillo  *
1025e99827Sdillo  * Redistribution and use in source and binary forms, with or without
1125e99827Sdillo  * modification, are permitted provided that the following conditions
1225e99827Sdillo  * are met:
1325e99827Sdillo  * 1. Redistributions of source code must retain the above copyright
1425e99827Sdillo  *    notice, this list of conditions and the following disclaimer.
1525e99827Sdillo  * 2. Redistributions in binary form must reproduce the above copyright
1625e99827Sdillo  *    notice, this list of conditions and the following disclaimer in the
1725e99827Sdillo  *    documentation and/or other materials provided with the distribution.
1825e99827Sdillo  *
1925e99827Sdillo  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2025e99827Sdillo  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2125e99827Sdillo  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2225e99827Sdillo  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2325e99827Sdillo  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2425e99827Sdillo  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2525e99827Sdillo  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2625e99827Sdillo  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2725e99827Sdillo  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2825e99827Sdillo  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2925e99827Sdillo  * POSSIBILITY OF SUCH DAMAGE.
3025e99827Sdillo  */
3125e99827Sdillo 
3225e99827Sdillo /*
3356c3e412Sdillo  *  All functions and variable types have the prefix "hfs_". All constants
3456c3e412Sdillo  *  have the prefix "HFS_".
3525e99827Sdillo  *
3625e99827Sdillo  *  Naming convention for functions which read/write raw, linear data
3725e99827Sdillo  *	into/from a structured form:
3825e99827Sdillo  *
3956c3e412Sdillo  *  hfs_read/write[d][a]_foo_bar
4025e99827Sdillo  *      [d] - read/write from/to [d]isk instead of a memory buffer
4125e99827Sdillo  *      [a] - [a]llocate output buffer instead of using an existing one
4225e99827Sdillo  *            (not applicable for writing functions)
4325e99827Sdillo  *
4425e99827Sdillo  *  Most functions do not have either of these options, so they will read from
4525e99827Sdillo  *	or write to a memory buffer, which has been previously allocated by the
4625e99827Sdillo  *	caller.
4725e99827Sdillo  */
4825e99827Sdillo 
49ceed9c72Slukem #include <sys/cdefs.h>
50*dd6a5058Smrg __KERNEL_RCSID(0, "$NetBSD: libhfs.c,v 1.19 2023/08/11 05:51:34 mrg Exp $");
51ceed9c72Slukem 
5256c3e412Sdillo #include "libhfs.h"
5325e99827Sdillo 
5425e99827Sdillo /* global private file/folder keys */
5556c3e412Sdillo hfs_catalog_key_t hfs_gMetadataDirectoryKey; /* contains HFS+ inodes */
5656c3e412Sdillo hfs_catalog_key_t hfs_gJournalInfoBlockFileKey;
5756c3e412Sdillo hfs_catalog_key_t hfs_gJournalBufferFileKey;
5856c3e412Sdillo hfs_catalog_key_t* hfs_gPrivateObjectKeys[4] = {
5956c3e412Sdillo 	&hfs_gMetadataDirectoryKey,
6056c3e412Sdillo 	&hfs_gJournalInfoBlockFileKey,
6156c3e412Sdillo 	&hfs_gJournalBufferFileKey,
626a896067Smaxv 	NULL
636a896067Smaxv };
6425e99827Sdillo 
6525e99827Sdillo 
6625e99827Sdillo extern uint16_t be16tohp(void** inout_ptr);
6725e99827Sdillo extern uint32_t be32tohp(void** inout_ptr);
6825e99827Sdillo extern uint64_t be64tohp(void** inout_ptr);
6925e99827Sdillo 
70b1afbb31Smatt hfs_callbacks	hfs_gcb;	/* global callbacks */
71b1afbb31Smatt 
72b1afbb31Smatt /*
73b1afbb31Smatt  * global case folding table
74b1afbb31Smatt  * (lazily initialized; see comments at bottom of hfs_open_volume())
75b1afbb31Smatt  */
76b1afbb31Smatt unichar_t* hfs_gcft;
77b1afbb31Smatt 
78b1afbb31Smatt 
7956c3e412Sdillo int hfslib_create_casefolding_table(void);
8025e99827Sdillo 
8125e99827Sdillo #ifdef DLO_DEBUG
8225e99827Sdillo #include <stdio.h>
8325e99827Sdillo void
dlo_print_key(hfs_catalog_key_t * key)8456c3e412Sdillo dlo_print_key(hfs_catalog_key_t *key)
8525e99827Sdillo {
8625e99827Sdillo 	int i;
8725e99827Sdillo 
8825e99827Sdillo 	printf("%ld:[", (long)key->parent_cnid);
8925e99827Sdillo 	for (i=0; i<key->name.length; i++) {
9025e99827Sdillo 		if (key->name.unicode[i] < 256
9125e99827Sdillo 		    && isprint(key->name.unicode[i]))
9225e99827Sdillo 			putchar(key->name.unicode[i]);
9325e99827Sdillo 		else
9425e99827Sdillo 			printf("<%04x>", key->name.unicode[i]);
9525e99827Sdillo 	}
9625e99827Sdillo 	printf("]");
9725e99827Sdillo }
9825e99827Sdillo #endif
9925e99827Sdillo 
10025e99827Sdillo void
hfslib_init(hfs_callbacks * in_callbacks)10156c3e412Sdillo hfslib_init(hfs_callbacks* in_callbacks)
10225e99827Sdillo {
10325e99827Sdillo 	unichar_t	temp[256];
10425e99827Sdillo 
10525e99827Sdillo 	if (in_callbacks != NULL)
10656c3e412Sdillo 		memcpy(&hfs_gcb, in_callbacks, sizeof(hfs_callbacks));
10725e99827Sdillo 
10856c3e412Sdillo 	hfs_gcft = NULL;
10925e99827Sdillo 
11025e99827Sdillo 	/*
11125e99827Sdillo 	 * Create keys for the HFS+ "private" files so we can reuse them whenever
11225e99827Sdillo 	 * we perform a user-visible operation, such as listing directory contents.
11325e99827Sdillo 	 */
11425e99827Sdillo 
11525e99827Sdillo #define ATOU(str, len) /* quick & dirty ascii-to-unicode conversion */ \
11625e99827Sdillo 	do{ int i; for(i=0; i<len; i++) temp[i]=str[i]; } \
11725e99827Sdillo 	while( /*CONSTCOND*/ 0)
11825e99827Sdillo 
11925e99827Sdillo 	ATOU("\0\0\0\0HFS+ Private Data", 21);
12056c3e412Sdillo 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 21, temp,
12156c3e412Sdillo 		&hfs_gMetadataDirectoryKey);
12225e99827Sdillo 
12325e99827Sdillo 	ATOU(".journal_info_block", 19);
12456c3e412Sdillo 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 19, temp,
12556c3e412Sdillo 		&hfs_gJournalInfoBlockFileKey);
12625e99827Sdillo 
12725e99827Sdillo 	ATOU(".journal", 8);
12856c3e412Sdillo 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 8, temp,
12956c3e412Sdillo 		&hfs_gJournalBufferFileKey);
13025e99827Sdillo 
13125e99827Sdillo #undef ATOU
13225e99827Sdillo }
13325e99827Sdillo 
13425e99827Sdillo void
hfslib_done(void)13556c3e412Sdillo hfslib_done(void)
13625e99827Sdillo {
13756c3e412Sdillo 	hfs_callback_args	cbargs;
13825e99827Sdillo 
13956c3e412Sdillo 	if (hfs_gcft != NULL) {
14056c3e412Sdillo 		hfslib_init_cbargs(&cbargs);
14156c3e412Sdillo 		hfslib_free(hfs_gcft, &cbargs);
14256c3e412Sdillo 		hfs_gcft = NULL;
14325e99827Sdillo 	}
14425e99827Sdillo 
14525e99827Sdillo 	return;
14625e99827Sdillo }
14725e99827Sdillo 
14825e99827Sdillo void
hfslib_init_cbargs(hfs_callback_args * ptr)14956c3e412Sdillo hfslib_init_cbargs(hfs_callback_args* ptr)
15025e99827Sdillo {
15156c3e412Sdillo 	memset(ptr, 0, sizeof(hfs_callback_args));
15225e99827Sdillo }
15325e99827Sdillo 
15425e99827Sdillo #if 0
15525e99827Sdillo #pragma mark -
15625e99827Sdillo #pragma mark High-Level Routines
15725e99827Sdillo #endif
15825e99827Sdillo 
15925e99827Sdillo int
hfslib_open_volume(const char * in_device,int in_readonly,hfs_volume * out_vol,hfs_callback_args * cbargs)16056c3e412Sdillo hfslib_open_volume(
16125e99827Sdillo 	const char* in_device,
16225e99827Sdillo 	int in_readonly,
16356c3e412Sdillo 	hfs_volume* out_vol,
16456c3e412Sdillo 	hfs_callback_args* cbargs)
16525e99827Sdillo {
16656c3e412Sdillo 	hfs_catalog_key_t		rootkey;
16756c3e412Sdillo 	hfs_thread_record_t	rootthread;
16841627b2cSdillo 	hfs_hfs_master_directory_block_t mdb;
169e100c48aSdillo 	uint16_t	node_rec_sizes[1];
170e100c48aSdillo 	void*		node_recs[1];
17125e99827Sdillo 	void*		buffer;
17225e99827Sdillo 	void*		buffer2;	/* used as temporary pointer for realloc() */
17325e99827Sdillo 	int			result;
174f75bcfdeSpooka 	int		isopen = 0;
17525e99827Sdillo 
17625e99827Sdillo 	result = 1;
17725e99827Sdillo 	buffer = NULL;
17825e99827Sdillo 
17925e99827Sdillo 	if (in_device == NULL || out_vol == NULL)
18025e99827Sdillo 		return 1;
18125e99827Sdillo 
18225e99827Sdillo 	out_vol->readonly = in_readonly;
18341627b2cSdillo 	out_vol->offset = 0;
18425e99827Sdillo 
18541627b2cSdillo 	if (hfslib_openvoldevice(out_vol, in_device, cbargs) != 0)
18656c3e412Sdillo 		HFS_LIBERR("could not open device");
187f75bcfdeSpooka 	isopen = 1;
18825e99827Sdillo 
18925e99827Sdillo 	/*
19025e99827Sdillo 	 * Read the volume header.
19125e99827Sdillo 	 */
19241627b2cSdillo 	buffer = hfslib_malloc(max(sizeof(hfs_volume_header_t),
19341627b2cSdillo 		sizeof(hfs_hfs_master_directory_block_t)), cbargs);
19425e99827Sdillo 	if (buffer == NULL)
19556c3e412Sdillo 		HFS_LIBERR("could not allocate volume header");
19641627b2cSdillo 	if (hfslib_readd(out_vol, buffer, max(sizeof(hfs_volume_header_t),
19741627b2cSdillo 	    sizeof(hfs_hfs_master_directory_block_t)),
19856c3e412Sdillo 	    HFS_VOLUME_HEAD_RESERVE_SIZE, cbargs) != 0)
19956c3e412Sdillo 		HFS_LIBERR("could not read volume header");
20041627b2cSdillo 
20141627b2cSdillo 	if (be16toh(*((uint16_t *)buffer)) == HFS_SIG_HFS) {
20241627b2cSdillo 		if (hfslib_read_master_directory_block(buffer, &mdb) == 0)
20341627b2cSdillo 			HFS_LIBERR("could not parse master directory block");
2046a896067Smaxv 		if (mdb.embedded_signature == HFS_SIG_HFSP) {
20541627b2cSdillo 			/* XXX: is 512 always correct? */
20641627b2cSdillo 			out_vol->offset =
20741627b2cSdillo 			    mdb.first_block * 512
20841627b2cSdillo 			    + mdb.embedded_extent.start_block
20941627b2cSdillo 			    * (uint64_t)mdb.block_size;
21041627b2cSdillo 
21141627b2cSdillo 			if (hfslib_readd(out_vol, buffer,
21241627b2cSdillo 			    sizeof(hfs_volume_header_t),
21341627b2cSdillo 			    HFS_VOLUME_HEAD_RESERVE_SIZE, cbargs) != 0)
21441627b2cSdillo 				HFS_LIBERR("could not read volume header");
2156a896067Smaxv 		} else
21641627b2cSdillo 			HFS_LIBERR("Plain HFS volumes not currently supported");
21741627b2cSdillo 	}
21841627b2cSdillo 
21956c3e412Sdillo 	if (hfslib_read_volume_header(buffer, &(out_vol->vh)) == 0)
22056c3e412Sdillo 		HFS_LIBERR("could not parse volume header");
22125e99827Sdillo 
22225e99827Sdillo 	/*
22325e99827Sdillo 	 * Check the volume signature to see if this is a legitimate HFS+ or HFSX
22425e99827Sdillo 	 * volume. If so, set the key comparison function pointers appropriately.
22525e99827Sdillo 	 */
2266a896067Smaxv 	switch(out_vol->vh.signature) {
22756c3e412Sdillo 		case HFS_SIG_HFSP:
22856c3e412Sdillo 			out_vol->keycmp = hfslib_compare_catalog_keys_cf;
22925e99827Sdillo 			break;
23056c3e412Sdillo 		case HFS_SIG_HFSX:
23125e99827Sdillo 			out_vol->keycmp = NULL; /* will be set below */
23225e99827Sdillo 			break;
23325e99827Sdillo 		default:
23485fb4c9dSpooka 			/* HFS_LIBERR("unrecognized volume format"); */
23585fb4c9dSpooka 			goto error;
23685fb4c9dSpooka 			break;
23725e99827Sdillo 	}
23825e99827Sdillo 
23925e99827Sdillo 	/*
24025e99827Sdillo 	 * Read the catalog header.
24125e99827Sdillo 	 */
242e100c48aSdillo 	buffer2 = hfslib_realloc(buffer, 512, cbargs);
24325e99827Sdillo 	if (buffer2 == NULL)
24456c3e412Sdillo 		HFS_LIBERR("could not allocate catalog header node");
24525e99827Sdillo 	buffer = buffer2;
24625e99827Sdillo 
247e100c48aSdillo 	/*
2486a896067Smaxv 	 * We are only interested in the node header, so read the first
2496a896067Smaxv 	 * 512 bytes and construct the node descriptor by hand.
250e100c48aSdillo 	 */
251e100c48aSdillo 	if (hfslib_readd(out_vol, buffer, 512,
2526a896067Smaxv 	    out_vol->vh.catalog_file.extents[0].start_block *
2536a896067Smaxv 	    (uint64_t)out_vol->vh.block_size, cbargs) != 0)
25456c3e412Sdillo 		HFS_LIBERR("could not read catalog header node");
255e100c48aSdillo 	node_recs[0] = (char *)buffer+14;
256e100c48aSdillo 	node_rec_sizes[0] = 120;
257e100c48aSdillo 	if (hfslib_read_header_node(node_recs, node_rec_sizes, 1,
25825e99827Sdillo 	    &out_vol->chr, NULL, NULL) == 0)
25956c3e412Sdillo 		HFS_LIBERR("could not parse catalog header node");
26025e99827Sdillo 
2616a896067Smaxv 	/*
2626a896067Smaxv 	 * If this is an HFSX volume, the catalog header specifies the type of
2636a896067Smaxv 	 * key comparison method (case-folding or binary compare) we should
2646a896067Smaxv 	 * use.
2656a896067Smaxv 	 */
2666a896067Smaxv 	if (out_vol->keycmp == NULL) {
26756c3e412Sdillo 		if (out_vol->chr.keycomp_type == HFS_KEY_CASEFOLD)
26856c3e412Sdillo 			out_vol->keycmp = hfslib_compare_catalog_keys_cf;
26956c3e412Sdillo 		else if (out_vol->chr.keycomp_type == HFS_KEY_BINARY)
27056c3e412Sdillo 			out_vol->keycmp = hfslib_compare_catalog_keys_bc;
27125e99827Sdillo 		else
27256c3e412Sdillo 			HFS_LIBERR("undefined key compare method");
27325e99827Sdillo 	}
27425e99827Sdillo 
275e100c48aSdillo 	out_vol->catkeysizefieldsize
276e100c48aSdillo 	    = (out_vol->chr.attributes & HFS_BIG_KEYS_MASK) ?
277e100c48aSdillo 	    sizeof(uint16_t) : sizeof(uint8_t);
278e100c48aSdillo 
27925e99827Sdillo 	/*
28025e99827Sdillo 	 * Read the extent overflow header.
28125e99827Sdillo 	 */
282e100c48aSdillo 	/*
2836a896067Smaxv 	 * We are only interested in the node header, so read the first
2846a896067Smaxv 	 * 512 bytes and construct the node descriptor by hand.
2856a896067Smaxv 	 * buffer is already 512 bytes long.
286e100c48aSdillo 	 */
287e100c48aSdillo 	if (hfslib_readd(out_vol, buffer, 512,
2886a896067Smaxv 	    out_vol->vh.extents_file.extents[0].start_block *
2896a896067Smaxv 	    (uint64_t)out_vol->vh.block_size, cbargs) != 0)
29056c3e412Sdillo 		HFS_LIBERR("could not read extent header node");
29125e99827Sdillo 
292e100c48aSdillo 	node_recs[0] = (char *)buffer+14;
293e100c48aSdillo 	node_rec_sizes[0] = 120;
294e100c48aSdillo 	if (hfslib_read_header_node(node_recs, node_rec_sizes, 1,
29525e99827Sdillo 	    &out_vol->ehr, NULL, NULL) == 0)
29656c3e412Sdillo 		HFS_LIBERR("could not parse extent header node");
297e100c48aSdillo 	out_vol->extkeysizefieldsize
298e100c48aSdillo 	    = (out_vol->ehr.attributes & HFS_BIG_KEYS_MASK) ?
299e100c48aSdillo 	    sizeof(uint16_t):sizeof(uint8_t);
30025e99827Sdillo 	/*
30125e99827Sdillo 	 * Read the journal info block and journal header (if volume journaled).
30225e99827Sdillo 	 */
3036a896067Smaxv 	if (out_vol->vh.attributes & (1<<HFS_VOL_JOURNALED)) {
30425e99827Sdillo 		/* journal info block */
30556c3e412Sdillo 		buffer2 = hfslib_realloc(buffer, sizeof(hfs_journal_info_t), cbargs);
30625e99827Sdillo 		if (buffer2 == NULL)
30756c3e412Sdillo 			HFS_LIBERR("could not allocate journal info block");
30825e99827Sdillo 		buffer = buffer2;
30925e99827Sdillo 
31056c3e412Sdillo 		if (hfslib_readd(out_vol, buffer, sizeof(hfs_journal_info_t),
31125e99827Sdillo 		    out_vol->vh.journal_info_block * out_vol->vh.block_size,
31225e99827Sdillo 		    cbargs) != 0)
31356c3e412Sdillo 			HFS_LIBERR("could not read journal info block");
31425e99827Sdillo 
31556c3e412Sdillo 		if (hfslib_read_journal_info(buffer, &out_vol->jib) == 0)
31656c3e412Sdillo 			HFS_LIBERR("could not parse journal info block");
31725e99827Sdillo 
31825e99827Sdillo 		/* journal header */
31956c3e412Sdillo 		buffer2 = hfslib_realloc(buffer, sizeof(hfs_journal_header_t), cbargs);
32025e99827Sdillo 		if (buffer2 == NULL)
32156c3e412Sdillo 			HFS_LIBERR("could not allocate journal header");
32225e99827Sdillo 		buffer = buffer2;
32325e99827Sdillo 
32456c3e412Sdillo 		if (hfslib_readd(out_vol, buffer, sizeof(hfs_journal_header_t),
32525e99827Sdillo 		    out_vol->jib.offset, cbargs) != 0)
32656c3e412Sdillo 			HFS_LIBERR("could not read journal header");
32725e99827Sdillo 
32856c3e412Sdillo 		if (hfslib_read_journal_header(buffer, &out_vol->jh) == 0)
32956c3e412Sdillo 			HFS_LIBERR("could not parse journal header");
33025e99827Sdillo 
33125e99827Sdillo 		out_vol->journaled = 1;
3326a896067Smaxv 	} else {
33325e99827Sdillo 		out_vol->journaled = 0;
33425e99827Sdillo 	}
33525e99827Sdillo 
33625e99827Sdillo 	/*
33725e99827Sdillo 	 * If this volume uses case-folding comparison and the folding table hasn't
33856c3e412Sdillo 	 * been created yet, do that here. (We don't do this in hfslib_init()
33925e99827Sdillo 	 * because the table is large and we might never even need to use it.)
34025e99827Sdillo 	 */
34156c3e412Sdillo 	if (out_vol->keycmp == hfslib_compare_catalog_keys_cf && hfs_gcft == NULL)
34256c3e412Sdillo 		result = hfslib_create_casefolding_table();
34325e99827Sdillo 	else
34425e99827Sdillo 		result = 0;
34525e99827Sdillo 
34625e99827Sdillo 	/*
34725e99827Sdillo 	 * Find and store the volume name.
34825e99827Sdillo 	 */
34956c3e412Sdillo 	if (hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 0, NULL, &rootkey) == 0)
35056c3e412Sdillo 		HFS_LIBERR("could not make root search key");
35125e99827Sdillo 
35256c3e412Sdillo 	if (hfslib_find_catalog_record_with_key(out_vol, &rootkey,
35356c3e412Sdillo 	    (hfs_catalog_keyed_record_t*)&rootthread, cbargs)!=0)
35456c3e412Sdillo 		HFS_LIBERR("could not find root parent");
35525e99827Sdillo 
35656c3e412Sdillo 	memcpy(&out_vol->name, &rootthread.name, sizeof(hfs_unistr255_t));
35725e99827Sdillo 
35825e99827Sdillo 	/* FALLTHROUGH */
35925e99827Sdillo error:
360696e7eaeSpooka 	if (result != 0 && isopen)
361f75bcfdeSpooka 		hfslib_close_volume(out_vol, cbargs);
36225e99827Sdillo 	if (buffer != NULL)
36356c3e412Sdillo 		hfslib_free(buffer, cbargs);
36425e99827Sdillo 	return result;
36525e99827Sdillo }
36625e99827Sdillo 
36725e99827Sdillo void
hfslib_close_volume(hfs_volume * in_vol,hfs_callback_args * cbargs)36856c3e412Sdillo hfslib_close_volume(hfs_volume* in_vol, hfs_callback_args* cbargs)
36925e99827Sdillo {
37025e99827Sdillo 	if (in_vol == NULL)
37125e99827Sdillo 		return;
37256c3e412Sdillo 	hfslib_closevoldevice(in_vol, cbargs);
37325e99827Sdillo }
37425e99827Sdillo 
37525e99827Sdillo int
hfslib_path_to_cnid(hfs_volume * in_vol,hfs_cnid_t in_cnid,char ** out_unicode,uint16_t * out_length,hfs_callback_args * cbargs)37656c3e412Sdillo hfslib_path_to_cnid(hfs_volume* in_vol,
37756c3e412Sdillo 	hfs_cnid_t in_cnid,
37825e99827Sdillo 	char** out_unicode,
37925e99827Sdillo 	uint16_t* out_length,
38056c3e412Sdillo 	hfs_callback_args* cbargs)
38125e99827Sdillo {
38256c3e412Sdillo 	hfs_thread_record_t	parent_thread;
38356c3e412Sdillo 	hfs_cnid_t	parent_cnid, child_cnid;
38425e99827Sdillo 	char*		newpath;
38525e99827Sdillo 	char*		path;
38625e99827Sdillo 	int			path_offset = 0;
38725e99827Sdillo 	int			result;
38825e99827Sdillo 	uint16_t*	ptr;	/* dummy var */
38925e99827Sdillo 	uint16_t	uchar;	/* dummy var */
39025e99827Sdillo 	uint16_t	total_path_length;
39125e99827Sdillo 
3926a896067Smaxv 	if (in_vol == NULL || in_cnid == 0 || out_unicode == NULL ||
3936a896067Smaxv 	    out_length == NULL)
39425e99827Sdillo 		return 1;
39525e99827Sdillo 
39625e99827Sdillo 	result = 1;
39725e99827Sdillo 	*out_unicode = NULL;
39825e99827Sdillo 	*out_length = 0;
39925e99827Sdillo 	path = NULL;
40025e99827Sdillo 	total_path_length = 0;
40125e99827Sdillo 
40256c3e412Sdillo 	path = hfslib_malloc(514, cbargs); /* 256 unichars plus a forward slash */
40325e99827Sdillo 	if (path == NULL)
40425e99827Sdillo 		return 1;
40525e99827Sdillo 
40625e99827Sdillo 	child_cnid = in_cnid;
40725e99827Sdillo 	parent_cnid = child_cnid; /* skips loop in case in_cnid is root id */
4086a896067Smaxv 	while (parent_cnid != HFS_CNID_ROOT_FOLDER &&
4096a896067Smaxv 	    parent_cnid != HFS_CNID_ROOT_PARENT)
41025e99827Sdillo 	{
4116a896067Smaxv 		if (child_cnid != in_cnid) {
41256c3e412Sdillo 			newpath = hfslib_realloc(path, 514 + total_path_length*2, cbargs);
41325e99827Sdillo 			if (newpath == NULL)
41425e99827Sdillo 				goto exit;
41525e99827Sdillo 			path = newpath;
41625e99827Sdillo 			memmove(path + 514, path + path_offset, total_path_length*2);
41725e99827Sdillo 		}
41825e99827Sdillo 
41956c3e412Sdillo 		parent_cnid = hfslib_find_parent_thread(in_vol, child_cnid,
42025e99827Sdillo 		    &parent_thread, cbargs);
42125e99827Sdillo 		if (parent_cnid == 0)
42225e99827Sdillo 			goto exit;
42325e99827Sdillo 
42425e99827Sdillo 		path_offset = 512 - parent_thread.name.length*2;
42525e99827Sdillo 
42625e99827Sdillo 		memcpy(path + path_offset, parent_thread.name.unicode,
42725e99827Sdillo 			parent_thread.name.length*2);
42825e99827Sdillo 
42925e99827Sdillo 		/* Add a forward slash. The unicode string was specified in big endian
43025e99827Sdillo 		 * format, so convert to core format if necessary. */
43125e99827Sdillo 		path[512] = 0x00;
43225e99827Sdillo 		path[513] = 0x2F;
43325e99827Sdillo 
43425e99827Sdillo 		ptr = (uint16_t*)path + 256;
43525e99827Sdillo 		uchar = be16tohp((void*)&ptr);
43625e99827Sdillo 		*(ptr-1) = uchar;
43725e99827Sdillo 
43825e99827Sdillo 		total_path_length += parent_thread.name.length + 1;
43925e99827Sdillo 		child_cnid = parent_cnid;
44025e99827Sdillo 	}
44125e99827Sdillo 
44225e99827Sdillo 	/*
44325e99827Sdillo 	 * At this point, 'path' holds a sequence of unicode characters which
44425e99827Sdillo 	 * represent the absolute path to the given cnid. This string is missing
44525e99827Sdillo 	 * a terminating null char and an initial forward slash that represents
44625e99827Sdillo 	 * the root of the filesystem. It most likely also has extra space in
44725e99827Sdillo 	 * the beginning, due to the fact that we reserve 512 bytes for each path
44825e99827Sdillo 	 * component and won't usually use all that space. So, we allocate the
44925e99827Sdillo 	 * final string based on the actual length of the absolute path, plus four
45025e99827Sdillo 	 * additional bytes (two unichars) for the forward slash and the null char.
45125e99827Sdillo 	 */
45225e99827Sdillo 
45356c3e412Sdillo 	*out_unicode = hfslib_malloc((total_path_length+2)*2, cbargs);
45425e99827Sdillo 	if (*out_unicode == NULL)
45525e99827Sdillo 		goto exit;
45625e99827Sdillo 
45725e99827Sdillo 	/* copy only the bytes that are actually used */
45825e99827Sdillo 	memcpy(*out_unicode + 2, path + path_offset, total_path_length*2);
45925e99827Sdillo 
46025e99827Sdillo 	/* insert forward slash at start */
461b6ccfb3aSchristos 	uchar = be16toh(0x2F);
462b6ccfb3aSchristos 	memcpy(*out_unicode, &uchar, sizeof(uchar));
46325e99827Sdillo 
46425e99827Sdillo 	/* insert null char at end */
46525e99827Sdillo 	(*out_unicode)[total_path_length*2+2] = 0x00;
46625e99827Sdillo 	(*out_unicode)[total_path_length*2+3] = 0x00;
46725e99827Sdillo 
46825e99827Sdillo 	*out_length = total_path_length + 1 /* extra for forward slash */ ;
46925e99827Sdillo 
47025e99827Sdillo 	result = 0;
47125e99827Sdillo 
47225e99827Sdillo exit:
47325e99827Sdillo 	if (path != NULL)
47456c3e412Sdillo 		hfslib_free(path, cbargs);
47525e99827Sdillo 	return result;
47625e99827Sdillo }
47725e99827Sdillo 
47856c3e412Sdillo hfs_cnid_t
hfslib_find_parent_thread(hfs_volume * in_vol,hfs_cnid_t in_child,hfs_thread_record_t * out_thread,hfs_callback_args * cbargs)47956c3e412Sdillo hfslib_find_parent_thread(
48056c3e412Sdillo 	hfs_volume* in_vol,
48156c3e412Sdillo 	hfs_cnid_t in_child,
48256c3e412Sdillo 	hfs_thread_record_t* out_thread,
48356c3e412Sdillo 	hfs_callback_args* cbargs)
48425e99827Sdillo {
48556c3e412Sdillo 	hfs_catalog_key_t	childkey;
48625e99827Sdillo 
48725e99827Sdillo 	if (in_vol == NULL || in_child == 0 || out_thread == NULL)
48825e99827Sdillo 		return 0;
48925e99827Sdillo 
49056c3e412Sdillo 	if (hfslib_make_catalog_key(in_child, 0, NULL, &childkey) == 0)
49125e99827Sdillo 		return 0;
49225e99827Sdillo 
49356c3e412Sdillo 	if (hfslib_find_catalog_record_with_key(in_vol, &childkey,
49456c3e412Sdillo 		(hfs_catalog_keyed_record_t*)out_thread, cbargs) != 0)
49525e99827Sdillo 		return 0;
49625e99827Sdillo 
49725e99827Sdillo 	return out_thread->parent_cnid;
49825e99827Sdillo }
49925e99827Sdillo 
50025e99827Sdillo /*
50156c3e412Sdillo  * hfslib_find_catalog_record_with_cnid()
50225e99827Sdillo  *
50356c3e412Sdillo  * Looks up a catalog record by calling hfslib_find_parent_thread() and
50456c3e412Sdillo  * hfslib_find_catalog_record_with_key(). out_key may be NULL; if not, the key
50525e99827Sdillo  * corresponding to this cnid is stuffed in it. Returns 0 on success.
50625e99827Sdillo  */
50725e99827Sdillo int
hfslib_find_catalog_record_with_cnid(hfs_volume * in_vol,hfs_cnid_t in_cnid,hfs_catalog_keyed_record_t * out_rec,hfs_catalog_key_t * out_key,hfs_callback_args * cbargs)50856c3e412Sdillo hfslib_find_catalog_record_with_cnid(
50956c3e412Sdillo 	hfs_volume* in_vol,
51056c3e412Sdillo 	hfs_cnid_t in_cnid,
51156c3e412Sdillo 	hfs_catalog_keyed_record_t* out_rec,
51256c3e412Sdillo 	hfs_catalog_key_t* out_key,
51356c3e412Sdillo 	hfs_callback_args* cbargs)
51425e99827Sdillo {
51556c3e412Sdillo 	hfs_cnid_t					parentcnid;
51656c3e412Sdillo 	hfs_thread_record_t		parentthread;
51756c3e412Sdillo 	hfs_catalog_key_t			key;
51825e99827Sdillo 
51925e99827Sdillo 	if (in_vol == NULL || in_cnid == 0 || out_rec == NULL)
52025e99827Sdillo 		return 0;
52125e99827Sdillo 
52225e99827Sdillo 	parentcnid =
52356c3e412Sdillo 		hfslib_find_parent_thread(in_vol, in_cnid, &parentthread, cbargs);
52425e99827Sdillo 	if (parentcnid == 0)
52556c3e412Sdillo 		HFS_LIBERR("could not find parent thread for cnid %i", in_cnid);
52625e99827Sdillo 
52756c3e412Sdillo 	if (hfslib_make_catalog_key(parentthread.parent_cnid,
52825e99827Sdillo 		parentthread.name.length, parentthread.name.unicode, &key) == 0)
52956c3e412Sdillo 		HFS_LIBERR("could not make catalog search key");
53025e99827Sdillo 
53125e99827Sdillo 	if (out_key != NULL)
53225e99827Sdillo 		memcpy(out_key, &key, sizeof(key));
53325e99827Sdillo 
53456c3e412Sdillo 	return hfslib_find_catalog_record_with_key(in_vol, &key, out_rec, cbargs);
53525e99827Sdillo 
53625e99827Sdillo error:
53725e99827Sdillo 	return 1;
53825e99827Sdillo }
53925e99827Sdillo 
54025e99827Sdillo /* Returns 0 on success, 1 on error, and -1 if record was not found. */
54125e99827Sdillo int
hfslib_find_catalog_record_with_key(hfs_volume * in_vol,hfs_catalog_key_t * in_key,hfs_catalog_keyed_record_t * out_rec,hfs_callback_args * cbargs)54256c3e412Sdillo hfslib_find_catalog_record_with_key(
54356c3e412Sdillo 	hfs_volume* in_vol,
54456c3e412Sdillo 	hfs_catalog_key_t* in_key,
54556c3e412Sdillo 	hfs_catalog_keyed_record_t* out_rec,
54656c3e412Sdillo 	hfs_callback_args* cbargs)
54725e99827Sdillo {
548*dd6a5058Smrg 	hfs_node_descriptor_t			nd = { .num_recs = 0 };
54956c3e412Sdillo 	hfs_extent_descriptor_t*		extents;
55056c3e412Sdillo 	hfs_catalog_keyed_record_t		lastrec;
55156c3e412Sdillo 	hfs_catalog_key_t*	curkey;
55225e99827Sdillo 	void**				recs;
55325e99827Sdillo 	void*				buffer;
55425e99827Sdillo 	uint64_t			bytesread;
55525e99827Sdillo 	uint32_t			curnode;
55625e99827Sdillo 	uint16_t*			recsizes;
55725e99827Sdillo 	uint16_t			numextents;
55825e99827Sdillo 	uint16_t			recnum;
55925e99827Sdillo 	int16_t				leaftype;
56025e99827Sdillo 	int					keycompare;
56125e99827Sdillo 	int					result;
56225e99827Sdillo 
56325e99827Sdillo 	if (in_key == NULL || out_rec == NULL || in_vol == NULL)
56425e99827Sdillo 		return 1;
56525e99827Sdillo 
56625e99827Sdillo 	result = 1;
56725e99827Sdillo 	buffer = NULL;
56825e99827Sdillo 	curkey = NULL;
56925e99827Sdillo 	extents = NULL;
57025e99827Sdillo 	recs = NULL;
57125e99827Sdillo 	recsizes = NULL;
57225e99827Sdillo 
57325e99827Sdillo 	/* The key takes up over half a kb of ram, which is a lot for the BSD
57425e99827Sdillo 	 * kernel stack. So allocate it in the heap instead to play it safe. */
57556c3e412Sdillo 	curkey = hfslib_malloc(sizeof(hfs_catalog_key_t), cbargs);
57625e99827Sdillo 	if (curkey == NULL)
57756c3e412Sdillo 		HFS_LIBERR("could not allocate catalog search key");
57825e99827Sdillo 
57956c3e412Sdillo 	buffer = hfslib_malloc(in_vol->chr.node_size, cbargs);
58025e99827Sdillo 	if (buffer == NULL)
58156c3e412Sdillo 		HFS_LIBERR("could not allocate node buffer");
58225e99827Sdillo 
58356c3e412Sdillo 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_CATALOG,
58456c3e412Sdillo 		HFS_DATAFORK, &extents, cbargs);
58525e99827Sdillo 	if (numextents == 0)
58656c3e412Sdillo 		HFS_LIBERR("could not locate fork extents");
58725e99827Sdillo 
58825e99827Sdillo 	curnode = in_vol->chr.root_node;
58925e99827Sdillo 
59025e99827Sdillo #ifdef DLO_DEBUG
59125e99827Sdillo 	printf("-> key ");
59225e99827Sdillo 	dlo_print_key(in_key);
59325e99827Sdillo 	printf("\n");
59425e99827Sdillo #endif
59525e99827Sdillo 
596547d41caSmaxv 	do {
59725e99827Sdillo #ifdef DLO_DEBUG
59825e99827Sdillo 		printf("--> node %d\n", curnode);
59925e99827Sdillo #endif
60025e99827Sdillo 
60156c3e412Sdillo 		if (hfslib_readd_with_extents(in_vol, buffer,
60225e99827Sdillo 			&bytesread,in_vol->chr.node_size, curnode * in_vol->chr.node_size,
60325e99827Sdillo 			extents, numextents, cbargs) != 0)
60456c3e412Sdillo 			HFS_LIBERR("could not read catalog node #%i", curnode);
60525e99827Sdillo 
60656c3e412Sdillo 		if (hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_CATALOG_FILE,
60725e99827Sdillo 			in_vol, cbargs) == 0)
60856c3e412Sdillo 			HFS_LIBERR("could not parse catalog node #%i", curnode);
60925e99827Sdillo 
61025e99827Sdillo 		for (recnum = 0; recnum < nd.num_recs; recnum++)
61125e99827Sdillo 		{
61225e99827Sdillo 			leaftype = nd.kind;
61356c3e412Sdillo 			if (hfslib_read_catalog_keyed_record(recs[recnum], out_rec,
61425e99827Sdillo 				&leaftype, curkey, in_vol) == 0)
61556c3e412Sdillo 				HFS_LIBERR("could not read catalog record #%i",recnum);
61625e99827Sdillo 
61725e99827Sdillo #ifdef DLO_DEBUG
61825e99827Sdillo 			printf("---> record %d: ", recnum);
61925e99827Sdillo 			dlo_print_key(curkey);
62025e99827Sdillo 			fflush(stdout);
62125e99827Sdillo #endif
62225e99827Sdillo 			keycompare = in_vol->keycmp(in_key, curkey);
62325e99827Sdillo #ifdef DLO_DEBUG
62425e99827Sdillo 			printf(" %c\n",
62525e99827Sdillo 			       keycompare < 0 ? '<'
62625e99827Sdillo 			       : keycompare == 0 ? '=' : '>');
62725e99827Sdillo #endif
62825e99827Sdillo 
629547d41caSmaxv 			if (keycompare < 0) {
63025e99827Sdillo 				/* Check if key is less than *every* record, which should never
63125e99827Sdillo 				 * happen if the volume is consistent and the key legit. */
63225e99827Sdillo 				if (recnum == 0)
63356c3e412Sdillo 					HFS_LIBERR("all records greater than key");
63425e99827Sdillo 
63525e99827Sdillo 				/* Otherwise, we've found the first record that exceeds our key,
63625e99827Sdillo 				 * so retrieve the previous record, which is still less... */
63725e99827Sdillo 				memcpy(out_rec, &lastrec,
63856c3e412Sdillo 					sizeof(hfs_catalog_keyed_record_t));
63925e99827Sdillo 
64025e99827Sdillo 				/* ...unless this is a leaf node, which means we've gone from
64125e99827Sdillo 				 * a key which is smaller than the search key, in the previous
64225e99827Sdillo 				 * loop, to a key which is larger, in this loop, and that
64325e99827Sdillo 				 * implies that our search key does not exist on the volume. */
64456c3e412Sdillo 				if (nd.kind == HFS_LEAFNODE)
64525e99827Sdillo 					result = -1;
64625e99827Sdillo 				break;
647547d41caSmaxv 			} else if (keycompare == 0) {
64825e99827Sdillo 				/* If leaf node, found an exact match. */
64925e99827Sdillo 				result = 0;
65025e99827Sdillo 				break;
651547d41caSmaxv 			} else if (recnum == nd.num_recs-1 && keycompare > 0) {
65225e99827Sdillo 				/* If leaf node, we've reached the last record with no match,
65325e99827Sdillo 				 * which means this key is not present on the volume. */
65425e99827Sdillo 				result = -1;
65525e99827Sdillo 				break;
65625e99827Sdillo 			}
65725e99827Sdillo 
65856c3e412Sdillo 			memcpy(&lastrec, out_rec, sizeof(hfs_catalog_keyed_record_t));
65925e99827Sdillo 		}
66025e99827Sdillo 
66156c3e412Sdillo 		if (nd.kind == HFS_INDEXNODE)
66225e99827Sdillo 			curnode = out_rec->child;
66356c3e412Sdillo 		else if (nd.kind == HFS_LEAFNODE)
66425e99827Sdillo 			break;
66556c3e412Sdillo 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
666547d41caSmaxv 	} while (nd.kind != HFS_LEAFNODE);
66725e99827Sdillo 
66825e99827Sdillo 	/* FALLTHROUGH */
66925e99827Sdillo error:
67025e99827Sdillo 	if (extents != NULL)
67156c3e412Sdillo 		hfslib_free(extents, cbargs);
67256c3e412Sdillo 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
67325e99827Sdillo 	if (curkey != NULL)
67456c3e412Sdillo 		hfslib_free(curkey, cbargs);
67525e99827Sdillo 	if (buffer != NULL)
67656c3e412Sdillo 		hfslib_free(buffer, cbargs);
67725e99827Sdillo 	return result;
67825e99827Sdillo }
67925e99827Sdillo 
68025e99827Sdillo /* returns 0 on success */
68125e99827Sdillo /* XXX Need to look this over and make sure it gracefully handles cases where
68225e99827Sdillo  * XXX the key is not found. */
68325e99827Sdillo int
hfslib_find_extent_record_with_key(hfs_volume * in_vol,hfs_extent_key_t * in_key,hfs_extent_record_t * out_rec,hfs_callback_args * cbargs)68456c3e412Sdillo hfslib_find_extent_record_with_key(hfs_volume* in_vol,
68556c3e412Sdillo 	hfs_extent_key_t* in_key,
68656c3e412Sdillo 	hfs_extent_record_t* out_rec,
68756c3e412Sdillo 	hfs_callback_args* cbargs)
68825e99827Sdillo {
689*dd6a5058Smrg 	hfs_node_descriptor_t		nd = { .num_recs = 0 };
69056c3e412Sdillo 	hfs_extent_descriptor_t*	extents;
69156c3e412Sdillo 	hfs_extent_record_t		lastrec;
69256c3e412Sdillo 	hfs_extent_key_t	curkey;
69325e99827Sdillo 	void**				recs;
69425e99827Sdillo 	void*				buffer;
69525e99827Sdillo 	uint64_t			bytesread;
69625e99827Sdillo 	uint32_t			curnode;
69725e99827Sdillo 	uint16_t*			recsizes;
69825e99827Sdillo 	uint16_t			numextents;
69925e99827Sdillo 	uint16_t			recnum;
70025e99827Sdillo 	int					keycompare;
70125e99827Sdillo 	int					result;
70225e99827Sdillo 
70325e99827Sdillo 	if (in_vol == NULL || in_key == NULL || out_rec == NULL)
70425e99827Sdillo 		return 1;
70525e99827Sdillo 
70625e99827Sdillo 	result = 1;
70725e99827Sdillo 	buffer = NULL;
70825e99827Sdillo 	extents = NULL;
70925e99827Sdillo 	recs = NULL;
71025e99827Sdillo 	recsizes = NULL;
71125e99827Sdillo 
71256c3e412Sdillo 	buffer = hfslib_malloc(in_vol->ehr.node_size, cbargs);
71325e99827Sdillo 	if (buffer == NULL)
71456c3e412Sdillo 		HFS_LIBERR("could not allocate node buffer");
71525e99827Sdillo 
71656c3e412Sdillo 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_EXTENTS,
71756c3e412Sdillo 		HFS_DATAFORK, &extents, cbargs);
71825e99827Sdillo 	if (numextents == 0)
71956c3e412Sdillo 		HFS_LIBERR("could not locate fork extents");
72025e99827Sdillo 
72125e99827Sdillo 	nd.num_recs = 0;
72225e99827Sdillo 	curnode = in_vol->ehr.root_node;
72325e99827Sdillo 
724547d41caSmaxv 	do {
72556c3e412Sdillo 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
72625e99827Sdillo 		recnum = 0;
72725e99827Sdillo 
72856c3e412Sdillo 		if (hfslib_readd_with_extents(in_vol, buffer, &bytesread,
72925e99827Sdillo 			in_vol->ehr.node_size, curnode * in_vol->ehr.node_size, extents,
73025e99827Sdillo 			numextents, cbargs) != 0)
73156c3e412Sdillo 			HFS_LIBERR("could not read extents overflow node #%i", curnode);
73225e99827Sdillo 
73356c3e412Sdillo 		if (hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_EXTENTS_FILE,
73425e99827Sdillo 			in_vol, cbargs) == 0)
73556c3e412Sdillo 			HFS_LIBERR("could not parse extents overflow node #%i",curnode);
73625e99827Sdillo 
737547d41caSmaxv 		for (recnum = 0; recnum < nd.num_recs; recnum++) {
73856c3e412Sdillo 			memcpy(&lastrec, out_rec, sizeof(hfs_extent_record_t));
73925e99827Sdillo 
74056c3e412Sdillo 			if (hfslib_read_extent_record(recs[recnum], out_rec, nd.kind,
74125e99827Sdillo 				&curkey, in_vol) == 0)
74256c3e412Sdillo 				HFS_LIBERR("could not read extents record #%i",recnum);
74325e99827Sdillo 
74456c3e412Sdillo 			keycompare = hfslib_compare_extent_keys(in_key, &curkey);
745547d41caSmaxv 			if (keycompare < 0) {
74625e99827Sdillo 				/* this should never happen for any legitimate key */
74725e99827Sdillo 				if (recnum == 0)
74825e99827Sdillo 					return 1;
74956c3e412Sdillo 				memcpy(out_rec, &lastrec, sizeof(hfs_extent_record_t));
75025e99827Sdillo 				break;
751547d41caSmaxv 			} else if (keycompare == 0 ||
75225e99827Sdillo 			    (recnum == nd.num_recs-1 && keycompare > 0))
75325e99827Sdillo 				break;
75425e99827Sdillo 		}
75525e99827Sdillo 
75656c3e412Sdillo 		if (nd.kind == HFS_INDEXNODE)
75725e99827Sdillo 			curnode = *((uint32_t *)out_rec); /* out_rec is a node ptr in this case */
75856c3e412Sdillo 		else if (nd.kind == HFS_LEAFNODE)
75925e99827Sdillo 			break;
76025e99827Sdillo 		else
7614e7cd698Smsaitoh 		    HFS_LIBERR("unknown node type for extents overflow node #%i",curnode);
762547d41caSmaxv 	} while (nd.kind != HFS_LEAFNODE);
76325e99827Sdillo 
76425e99827Sdillo 	result = 0;
76525e99827Sdillo 
76625e99827Sdillo 	/* FALLTHROUGH */
76725e99827Sdillo 
76825e99827Sdillo error:
76925e99827Sdillo 	if (buffer != NULL)
77056c3e412Sdillo 		hfslib_free(buffer, cbargs);
77125e99827Sdillo 	if (extents != NULL)
77256c3e412Sdillo 		hfslib_free(extents, cbargs);
77356c3e412Sdillo 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
77425e99827Sdillo 	return result;
77525e99827Sdillo }
77625e99827Sdillo 
77725e99827Sdillo /* out_extents may be NULL. */
77825e99827Sdillo uint16_t
hfslib_get_file_extents(hfs_volume * in_vol,hfs_cnid_t in_cnid,uint8_t in_forktype,hfs_extent_descriptor_t ** out_extents,hfs_callback_args * cbargs)77956c3e412Sdillo hfslib_get_file_extents(hfs_volume* in_vol,
78056c3e412Sdillo 	hfs_cnid_t in_cnid,
78125e99827Sdillo 	uint8_t in_forktype,
78256c3e412Sdillo 	hfs_extent_descriptor_t** out_extents,
78356c3e412Sdillo 	hfs_callback_args* cbargs)
78425e99827Sdillo {
78556c3e412Sdillo 	hfs_extent_descriptor_t*	dummy;
78656c3e412Sdillo 	hfs_extent_key_t		extentkey;
78756c3e412Sdillo 	hfs_file_record_t		file;
78856c3e412Sdillo 	hfs_catalog_key_t		filekey;
78956c3e412Sdillo 	hfs_thread_record_t	fileparent;
7900a2587beSapb 	hfs_fork_t		fork = {.logical_size = 0};
79156c3e412Sdillo 	hfs_extent_record_t	nextextentrec;
79225e99827Sdillo 	uint32_t	numblocks;
79325e99827Sdillo 	uint16_t	numextents, n;
79425e99827Sdillo 
79525e99827Sdillo 	if (in_vol == NULL || in_cnid == 0)
79625e99827Sdillo 		return 0;
79725e99827Sdillo 
798547d41caSmaxv 	if (out_extents != NULL) {
79956c3e412Sdillo 		*out_extents = hfslib_malloc(sizeof(hfs_extent_descriptor_t), cbargs);
80025e99827Sdillo 		if (*out_extents == NULL)
80125e99827Sdillo 			return 0;
80225e99827Sdillo 	}
80325e99827Sdillo 
80425e99827Sdillo 	switch(in_cnid)
80525e99827Sdillo 	{
80656c3e412Sdillo 		case HFS_CNID_CATALOG:
80725e99827Sdillo 			fork = in_vol->vh.catalog_file;
80825e99827Sdillo 			break;
80925e99827Sdillo 
81056c3e412Sdillo 		case HFS_CNID_EXTENTS:
81125e99827Sdillo 			fork = in_vol->vh.extents_file;
81225e99827Sdillo 			break;
81325e99827Sdillo 
81456c3e412Sdillo 		case HFS_CNID_ALLOCATION:
81525e99827Sdillo 			fork = in_vol->vh.allocation_file;
81625e99827Sdillo 			break;
81725e99827Sdillo 
81856c3e412Sdillo 		case HFS_CNID_ATTRIBUTES:
81925e99827Sdillo 			fork = in_vol->vh.attributes_file;
82025e99827Sdillo 			break;
82125e99827Sdillo 
82256c3e412Sdillo 		case HFS_CNID_STARTUP:
82325e99827Sdillo 			fork = in_vol->vh.startup_file;
82425e99827Sdillo 			break;
82525e99827Sdillo 
82625e99827Sdillo 		default:
82756c3e412Sdillo 			if (hfslib_find_parent_thread(in_vol, in_cnid, &fileparent,
82825e99827Sdillo 				cbargs) == 0)
82925e99827Sdillo 				goto error;
83025e99827Sdillo 
83156c3e412Sdillo 			if (hfslib_make_catalog_key(fileparent.parent_cnid,
83225e99827Sdillo 				fileparent.name.length, fileparent.name.unicode, &filekey) == 0)
83325e99827Sdillo 				goto error;
83425e99827Sdillo 
83556c3e412Sdillo 			if (hfslib_find_catalog_record_with_key(in_vol, &filekey,
83656c3e412Sdillo 				(hfs_catalog_keyed_record_t*)&file, cbargs) != 0)
83725e99827Sdillo 				goto error;
83825e99827Sdillo 
83925e99827Sdillo 			/* only files have extents, not folders or threads */
84056c3e412Sdillo 			if (file.rec_type != HFS_REC_FILE)
84125e99827Sdillo 				goto error;
84225e99827Sdillo 
84356c3e412Sdillo 			if (in_forktype == HFS_DATAFORK)
84425e99827Sdillo 				fork = file.data_fork;
84556c3e412Sdillo 			else if (in_forktype == HFS_RSRCFORK)
84625e99827Sdillo 				fork = file.rsrc_fork;
84725e99827Sdillo 	}
84825e99827Sdillo 
84925e99827Sdillo 	numextents = 0;
85025e99827Sdillo 	numblocks = 0;
85156c3e412Sdillo 	memcpy(&nextextentrec, &fork.extents, sizeof(hfs_extent_record_t));
85225e99827Sdillo 
853547d41caSmaxv 	while (1) {
854547d41caSmaxv 		for (n = 0; n < 8; n++) {
85525e99827Sdillo 			if (nextextentrec[n].block_count == 0)
85625e99827Sdillo 				break;
85725e99827Sdillo 			numblocks += nextextentrec[n].block_count;
85825e99827Sdillo 		}
859547d41caSmaxv 		if (out_extents != NULL) {
86056c3e412Sdillo 			dummy = hfslib_realloc(*out_extents,
86156c3e412Sdillo 			    (numextents+n) * sizeof(hfs_extent_descriptor_t),
86225e99827Sdillo 			    cbargs);
86325e99827Sdillo 			if (dummy == NULL)
86425e99827Sdillo 				goto error;
86525e99827Sdillo 			*out_extents = dummy;
86625e99827Sdillo 
86725e99827Sdillo 			memcpy(*out_extents + numextents,
86856c3e412Sdillo 			    &nextextentrec, n*sizeof(hfs_extent_descriptor_t));
86925e99827Sdillo 		}
87025e99827Sdillo 		numextents += n;
87125e99827Sdillo 
87225e99827Sdillo 		if (numblocks >= fork.total_blocks)
87325e99827Sdillo 			break;
87425e99827Sdillo 
87556c3e412Sdillo 		if (hfslib_make_extent_key(in_cnid, in_forktype, numblocks,
87625e99827Sdillo 			&extentkey) == 0)
87725e99827Sdillo 			goto error;
87825e99827Sdillo 
87956c3e412Sdillo 		if (hfslib_find_extent_record_with_key(in_vol, &extentkey,
88025e99827Sdillo 			&nextextentrec, cbargs) != 0)
88125e99827Sdillo 			goto error;
88225e99827Sdillo 	}
88325e99827Sdillo 
88425e99827Sdillo 	goto exit;
88525e99827Sdillo 
88625e99827Sdillo error:
887547d41caSmaxv 	if (out_extents != NULL && *out_extents != NULL) {
88856c3e412Sdillo 		hfslib_free(*out_extents, cbargs);
88925e99827Sdillo 		*out_extents = NULL;
89025e99827Sdillo 	}
89125e99827Sdillo 	return 0;
89225e99827Sdillo 
89325e99827Sdillo exit:
89425e99827Sdillo 	return numextents;
89525e99827Sdillo }
89625e99827Sdillo 
89725e99827Sdillo /*
89856c3e412Sdillo  * hfslib_get_directory_contents()
89925e99827Sdillo  *
90025e99827Sdillo  * Finds the immediate children of a given directory CNID and places their
90125e99827Sdillo  * CNIDs in an array allocated here. The first child is found by doing a
90225e99827Sdillo  * catalog search that only compares parent CNIDs (ignoring file/folder names)
90325e99827Sdillo  * and skips over thread records. Then the remaining children are listed in
90425e99827Sdillo  * ascending order by name, according to the HFS+ spec, so just read off each
90525e99827Sdillo  * successive leaf node until a different parent CNID is found.
90625e99827Sdillo  *
90725e99827Sdillo  * If out_childnames is not NULL, it will be allocated and set to an array of
90856c3e412Sdillo  * hfs_unistr255_t's which correspond to the name of the child with that same
90925e99827Sdillo  * index.
91025e99827Sdillo  *
91125e99827Sdillo  * out_children may be NULL.
91225e99827Sdillo  *
91325e99827Sdillo  * Returns 0 on success.
91425e99827Sdillo  */
91525e99827Sdillo int
hfslib_get_directory_contents(hfs_volume * in_vol,hfs_cnid_t in_dir,hfs_catalog_keyed_record_t ** out_children,hfs_unistr255_t ** out_childnames,uint32_t * out_numchildren,hfs_callback_args * cbargs)91656c3e412Sdillo hfslib_get_directory_contents(
91756c3e412Sdillo 	hfs_volume* in_vol,
91856c3e412Sdillo 	hfs_cnid_t in_dir,
91956c3e412Sdillo 	hfs_catalog_keyed_record_t** out_children,
92056c3e412Sdillo 	hfs_unistr255_t** out_childnames,
92125e99827Sdillo 	uint32_t* out_numchildren,
92256c3e412Sdillo 	hfs_callback_args* cbargs)
92325e99827Sdillo {
924*dd6a5058Smrg 	hfs_node_descriptor_t			nd = { .num_recs = 0 };
92556c3e412Sdillo 	hfs_extent_descriptor_t*		extents;
92656c3e412Sdillo 	hfs_catalog_keyed_record_t		currec;
92756c3e412Sdillo 	hfs_catalog_key_t	curkey;
92825e99827Sdillo 	void**				recs;
92925e99827Sdillo 	void*				buffer;
93025e99827Sdillo 	void*				ptr; /* temporary pointer for realloc() */
93125e99827Sdillo 	uint64_t			bytesread;
93225e99827Sdillo 	uint32_t			curnode;
93325e99827Sdillo 	uint32_t			lastnode;
93425e99827Sdillo 	uint16_t*			recsizes;
93525e99827Sdillo 	uint16_t			numextents;
93625e99827Sdillo 	uint16_t			recnum;
93725e99827Sdillo 	int16_t				leaftype;
93825e99827Sdillo 	int					keycompare;
93925e99827Sdillo 	int					result;
94025e99827Sdillo 
94125e99827Sdillo 	if (in_vol == NULL || in_dir == 0 || out_numchildren == NULL)
94225e99827Sdillo 		return 1;
94325e99827Sdillo 
94425e99827Sdillo 	result = 1;
94525e99827Sdillo 	buffer = NULL;
94625e99827Sdillo 	extents = NULL;
94725e99827Sdillo 	lastnode = 0;
94825e99827Sdillo 	recs = NULL;
94925e99827Sdillo 	recsizes = NULL;
95025e99827Sdillo 	*out_numchildren = 0;
95125e99827Sdillo 	if (out_children != NULL)
95225e99827Sdillo 		*out_children = NULL;
95325e99827Sdillo 	if (out_childnames != NULL)
95425e99827Sdillo 		*out_childnames = NULL;
95525e99827Sdillo 
95656c3e412Sdillo 	buffer = hfslib_malloc(in_vol->chr.node_size, cbargs);
95725e99827Sdillo 	if (buffer == NULL)
95856c3e412Sdillo 		HFS_LIBERR("could not allocate node buffer");
95925e99827Sdillo 
96056c3e412Sdillo 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_CATALOG,
96156c3e412Sdillo 		HFS_DATAFORK, &extents, cbargs);
96225e99827Sdillo 	if (numextents == 0)
96356c3e412Sdillo 		HFS_LIBERR("could not locate fork extents");
96425e99827Sdillo 
96525e99827Sdillo 	nd.num_recs = 0;
96625e99827Sdillo 	curnode = in_vol->chr.root_node;
96725e99827Sdillo 
96825e99827Sdillo 	while (1)
96925e99827Sdillo 	{
97056c3e412Sdillo 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
97125e99827Sdillo 		recnum = 0;
97225e99827Sdillo 
97356c3e412Sdillo 		if (hfslib_readd_with_extents(in_vol, buffer, &bytesread,
97425e99827Sdillo 			in_vol->chr.node_size, curnode * in_vol->chr.node_size, extents,
97525e99827Sdillo 			numextents, cbargs) != 0)
97656c3e412Sdillo 			HFS_LIBERR("could not read catalog node #%i", curnode);
97725e99827Sdillo 
97856c3e412Sdillo 		if (hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_CATALOG_FILE,
97925e99827Sdillo 			in_vol, cbargs) == 0)
98056c3e412Sdillo 			HFS_LIBERR("could not parse catalog node #%i", curnode);
98125e99827Sdillo 
98225e99827Sdillo 		for (recnum = 0; recnum < nd.num_recs; recnum++)
98325e99827Sdillo 		{
98425e99827Sdillo 			leaftype = nd.kind; /* needed b/c leaftype might be modified now */
98556c3e412Sdillo 			if (hfslib_read_catalog_keyed_record(recs[recnum], &currec,
98625e99827Sdillo 				&leaftype, &curkey, in_vol) == 0)
98756c3e412Sdillo 				HFS_LIBERR("could not read cat record %i:%i", curnode, recnum);
98825e99827Sdillo 
98956c3e412Sdillo 			if (nd.kind == HFS_INDEXNODE)
99025e99827Sdillo 			{
99125e99827Sdillo 				keycompare = in_dir - curkey.parent_cnid;
992547d41caSmaxv 				if (keycompare < 0) {
99325e99827Sdillo 					/* Check if key is less than *every* record, which should
99425e99827Sdillo 					 * never happen if the volume and key are good. */
99525e99827Sdillo 					if (recnum == 0)
99656c3e412Sdillo 						HFS_LIBERR("all records greater than key");
99725e99827Sdillo 
99825e99827Sdillo 					/* Otherwise, we've found the first record that exceeds our
99925e99827Sdillo 					 * key, so retrieve the previous, lesser record. */
100025e99827Sdillo 					curnode = lastnode;
100125e99827Sdillo 					break;
1002547d41caSmaxv 				} else if (keycompare == 0) {
100325e99827Sdillo 					/*
100425e99827Sdillo 					 * Normally, if we were doing a typical catalog lookup with
100525e99827Sdillo 					 * both a parent cnid AND a name, keycompare==0 would be an
100625e99827Sdillo 					 * exact match. However, since we are ignoring object names
100725e99827Sdillo 					 * in this case and only comparing parent cnids, a direct
100825e99827Sdillo 					 * match on only a parent cnid could mean that we've found
100925e99827Sdillo 					 * an object with that parent cnid BUT which is NOT the
101025e99827Sdillo 					 * first object (according to the HFS+ spec) with that
101125e99827Sdillo 					 * parent cnid. Thus, when we find a parent cnid match, we
101225e99827Sdillo 					 * still go back to the previously found leaf node and start
101325e99827Sdillo 					 * checking it for a possible prior instance of an object
101425e99827Sdillo 					 * with our desired parent cnid.
101525e99827Sdillo 					 */
101625e99827Sdillo 					curnode = lastnode;
101725e99827Sdillo 					break;
1018547d41caSmaxv 				} else if (recnum == nd.num_recs-1 && keycompare > 0) {
101925e99827Sdillo 					/* Descend to child node if we found an exact match, or if
102025e99827Sdillo 					 * this is the last pointer record. */
102125e99827Sdillo 					curnode = currec.child;
102225e99827Sdillo 					break;
102325e99827Sdillo 				}
102425e99827Sdillo 
102525e99827Sdillo 				lastnode = currec.child;
1026547d41caSmaxv 			} else {
102725e99827Sdillo 				/*
102825e99827Sdillo 				 * We have now descended down the hierarchy of index nodes into
102925e99827Sdillo 				 * the leaf node that contains the first catalog record with a
103025e99827Sdillo 				 * matching parent CNID. Since all leaf nodes are chained
103125e99827Sdillo 				 * through their flink/blink, we can simply walk forward through
103225e99827Sdillo 				 * this chain, copying every matching non-thread record, until
103325e99827Sdillo 				 * we hit a record with a different parent CNID. At that point,
103425e99827Sdillo 				 * we've retrieved all of our directory's items, if any.
103525e99827Sdillo 				 */
103625e99827Sdillo 				curnode = nd.flink;
103725e99827Sdillo 
1038547d41caSmaxv 				if (curkey.parent_cnid < in_dir) {
103925e99827Sdillo 					continue;
1040547d41caSmaxv 				} else if (curkey.parent_cnid == in_dir) {
104125e99827Sdillo 					/* Hide files/folders which are supposed to be invisible
104225e99827Sdillo 					 * to users, according to the hfs+ spec. */
104356c3e412Sdillo 					if (hfslib_is_private_file(&curkey))
104425e99827Sdillo 						continue;
104525e99827Sdillo 
104625e99827Sdillo 					/* leaftype has now been set to the catalog record type */
104756c3e412Sdillo 					if (leaftype == HFS_REC_FLDR || leaftype == HFS_REC_FILE)
104825e99827Sdillo 					{
104925e99827Sdillo 						(*out_numchildren)++;
105025e99827Sdillo 
1051547d41caSmaxv 						if (out_children != NULL) {
105256c3e412Sdillo 							ptr = hfslib_realloc(*out_children,
105325e99827Sdillo 								*out_numchildren *
105456c3e412Sdillo 								sizeof(hfs_catalog_keyed_record_t), cbargs);
105525e99827Sdillo 							if (ptr == NULL)
105656c3e412Sdillo 								HFS_LIBERR("could not allocate child record");
105725e99827Sdillo 							*out_children = ptr;
105825e99827Sdillo 
105925e99827Sdillo 							memcpy(&((*out_children)[*out_numchildren-1]),
106056c3e412Sdillo 								&currec, sizeof(hfs_catalog_keyed_record_t));
106125e99827Sdillo 						}
106225e99827Sdillo 
1063547d41caSmaxv 						if (out_childnames != NULL) {
106456c3e412Sdillo 							ptr = hfslib_realloc(*out_childnames,
106556c3e412Sdillo 								*out_numchildren * sizeof(hfs_unistr255_t),
106625e99827Sdillo 								cbargs);
106725e99827Sdillo 							if (ptr == NULL)
106856c3e412Sdillo 								HFS_LIBERR("could not allocate child name");
106925e99827Sdillo 							*out_childnames = ptr;
107025e99827Sdillo 
107125e99827Sdillo 							memcpy(&((*out_childnames)[*out_numchildren-1]),
107256c3e412Sdillo 								&curkey.name, sizeof(hfs_unistr255_t));
107325e99827Sdillo 						}
107425e99827Sdillo 					}
107525e99827Sdillo 				} else {
107625e99827Sdillo 					result = 0;
107725e99827Sdillo 					/* We have just now passed the last item in the desired
107825e99827Sdillo 					 * folder (or the folder was empty), so exit. */
107925e99827Sdillo 					goto exit;
108025e99827Sdillo 				}
108125e99827Sdillo 			}
108225e99827Sdillo 		}
108325e99827Sdillo 	}
108425e99827Sdillo 
108525e99827Sdillo 	result = 0;
108625e99827Sdillo 	goto exit;
108725e99827Sdillo 
108825e99827Sdillo error:
108925e99827Sdillo 	if (out_children != NULL && *out_children != NULL)
109056c3e412Sdillo 		hfslib_free(*out_children, cbargs);
109125e99827Sdillo 	if (out_childnames != NULL && *out_childnames != NULL)
109256c3e412Sdillo 		hfslib_free(*out_childnames, cbargs);
109325e99827Sdillo 	/* FALLTHROUGH */
109425e99827Sdillo 
109525e99827Sdillo exit:
109625e99827Sdillo 	if (extents != NULL)
109756c3e412Sdillo 		hfslib_free(extents, cbargs);
109856c3e412Sdillo 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
109925e99827Sdillo 	if (buffer != NULL)
110056c3e412Sdillo 		hfslib_free(buffer, cbargs);
110125e99827Sdillo 	return result;
110225e99827Sdillo }
110325e99827Sdillo 
110425e99827Sdillo int
hfslib_is_journal_clean(hfs_volume * in_vol)110556c3e412Sdillo hfslib_is_journal_clean(hfs_volume* in_vol)
110625e99827Sdillo {
110725e99827Sdillo 	if (in_vol == NULL)
110825e99827Sdillo 		return 0;
110925e99827Sdillo 
111025e99827Sdillo 	/* return true if no journal */
111156c3e412Sdillo 	if (!(in_vol->vh.attributes & (1<<HFS_VOL_JOURNALED)))
111225e99827Sdillo 		return 1;
111325e99827Sdillo 
111425e99827Sdillo 	return (in_vol->jh.start == in_vol->jh.end);
111525e99827Sdillo }
111625e99827Sdillo 
111725e99827Sdillo /*
111856c3e412Sdillo  * hfslib_is_private_file()
111925e99827Sdillo  *
112025e99827Sdillo  * Given a file/folder's key and parent CNID, determines if it should be hidden
112125e99827Sdillo  * from the user (e.g., the journal header file or the HFS+ Private Data folder)
112225e99827Sdillo  */
112325e99827Sdillo int
hfslib_is_private_file(hfs_catalog_key_t * filekey)112456c3e412Sdillo hfslib_is_private_file(hfs_catalog_key_t *filekey)
112525e99827Sdillo {
112656c3e412Sdillo 	hfs_catalog_key_t* curkey = NULL;
112725e99827Sdillo 	int i = 0;
112825e99827Sdillo 
112925e99827Sdillo 	/*
113025e99827Sdillo 	 * According to the HFS+ spec to date, all special objects are located in
113125e99827Sdillo 	 * the root directory of the volume, so don't bother going further if the
113225e99827Sdillo 	 * requested object is not.
113325e99827Sdillo 	 */
113456c3e412Sdillo 	if (filekey->parent_cnid != HFS_CNID_ROOT_FOLDER)
113525e99827Sdillo 		return 0;
113625e99827Sdillo 
1137547d41caSmaxv 	while ((curkey = hfs_gPrivateObjectKeys[i]) != NULL) {
113825e99827Sdillo 		/* XXX Always use binary compare here, or use volume's specific key
113925e99827Sdillo 		 * XXX comparison routine? */
1140547d41caSmaxv 		if (filekey->name.length == curkey->name.length &&
1141547d41caSmaxv 		    memcmp(filekey->name.unicode, curkey->name.unicode,
114225e99827Sdillo 				2 * curkey->name.length) == 0)
114325e99827Sdillo 			return 1;
114425e99827Sdillo 		i++;
114525e99827Sdillo 	}
114625e99827Sdillo 
114725e99827Sdillo 	return 0;
114825e99827Sdillo }
114925e99827Sdillo 
115025e99827Sdillo 
115125e99827Sdillo /* bool
115256c3e412Sdillo hfslib_is_journal_valid(hfs_volume* in_vol)
115325e99827Sdillo {
115425e99827Sdillo 	- check magic numbers
115525e99827Sdillo 	- check Other Things
115625e99827Sdillo }*/
115725e99827Sdillo 
115825e99827Sdillo #if 0
115925e99827Sdillo #pragma mark -
116025e99827Sdillo #pragma mark Major Structures
116125e99827Sdillo #endif
116225e99827Sdillo 
116325e99827Sdillo /*
116456c3e412Sdillo  *	hfslib_read_volume_header()
116525e99827Sdillo  *
116625e99827Sdillo  *	Reads in_bytes, formats the data appropriately, and places the result
116725e99827Sdillo  *	in out_header, which is assumed to be previously allocated. Returns number
116825e99827Sdillo  *	of bytes read, 0 if failed.
116925e99827Sdillo  */
117025e99827Sdillo 
117125e99827Sdillo size_t
hfslib_read_volume_header(void * in_bytes,hfs_volume_header_t * out_header)117256c3e412Sdillo hfslib_read_volume_header(void* in_bytes, hfs_volume_header_t* out_header)
117325e99827Sdillo {
117425e99827Sdillo 	void*	ptr;
117525e99827Sdillo 	size_t	last_bytes_read;
117625e99827Sdillo 	int		i;
117725e99827Sdillo 
117825e99827Sdillo 	if (in_bytes == NULL || out_header == NULL)
117925e99827Sdillo 		return 0;
118025e99827Sdillo 
118125e99827Sdillo 	ptr = in_bytes;
118225e99827Sdillo 
118325e99827Sdillo 	out_header->signature = be16tohp(&ptr);
118425e99827Sdillo 	out_header->version = be16tohp(&ptr);
118525e99827Sdillo 	out_header->attributes = be32tohp(&ptr);
118625e99827Sdillo 	out_header->last_mounting_version = be32tohp(&ptr);
118725e99827Sdillo 	out_header->journal_info_block = be32tohp(&ptr);
118825e99827Sdillo 
118925e99827Sdillo 	out_header->date_created = be32tohp(&ptr);
119025e99827Sdillo 	out_header->date_modified = be32tohp(&ptr);
119125e99827Sdillo 	out_header->date_backedup = be32tohp(&ptr);
119225e99827Sdillo 	out_header->date_checked = be32tohp(&ptr);
119325e99827Sdillo 
119425e99827Sdillo 	out_header->file_count = be32tohp(&ptr);
119525e99827Sdillo 	out_header->folder_count = be32tohp(&ptr);
119625e99827Sdillo 
119725e99827Sdillo 	out_header->block_size = be32tohp(&ptr);
119825e99827Sdillo 	out_header->total_blocks = be32tohp(&ptr);
119925e99827Sdillo 	out_header->free_blocks = be32tohp(&ptr);
120025e99827Sdillo 	out_header->next_alloc_block = be32tohp(&ptr);
120125e99827Sdillo 	out_header->rsrc_clump_size = be32tohp(&ptr);
120225e99827Sdillo 	out_header->data_clump_size = be32tohp(&ptr);
120325e99827Sdillo 	out_header->next_cnid = be32tohp(&ptr);
120425e99827Sdillo 
120525e99827Sdillo 	out_header->write_count = be32tohp(&ptr);
120625e99827Sdillo 	out_header->encodings = be64tohp(&ptr);
120725e99827Sdillo 
120825e99827Sdillo 	for (i =0 ; i < 8; i++)
120925e99827Sdillo 		out_header->finder_info[i] = be32tohp(&ptr);
121025e99827Sdillo 
121156c3e412Sdillo 	if ((last_bytes_read = hfslib_read_fork_descriptor(ptr,
121225e99827Sdillo 		&out_header->allocation_file)) == 0)
121325e99827Sdillo 		return 0;
121425e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
121525e99827Sdillo 
121656c3e412Sdillo 	if ((last_bytes_read = hfslib_read_fork_descriptor(ptr,
121725e99827Sdillo 		&out_header->extents_file)) == 0)
121825e99827Sdillo 		return 0;
121925e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
122025e99827Sdillo 
122156c3e412Sdillo 	if ((last_bytes_read = hfslib_read_fork_descriptor(ptr,
122225e99827Sdillo 		&out_header->catalog_file)) == 0)
122325e99827Sdillo 		return 0;
122425e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
122525e99827Sdillo 
122656c3e412Sdillo 	if ((last_bytes_read = hfslib_read_fork_descriptor(ptr,
122725e99827Sdillo 		&out_header->attributes_file)) == 0)
122825e99827Sdillo 		return 0;
122925e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
123025e99827Sdillo 
123156c3e412Sdillo 	if ((last_bytes_read = hfslib_read_fork_descriptor(ptr,
123225e99827Sdillo 		&out_header->startup_file)) == 0)
123325e99827Sdillo 		return 0;
123425e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
123525e99827Sdillo 
123625e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
123725e99827Sdillo }
123825e99827Sdillo 
123925e99827Sdillo /*
124041627b2cSdillo  *      hfsplib_read_master_directory_block()
124141627b2cSdillo  *
124241627b2cSdillo  *      Reads in_bytes, formats the data appropriately, and places the result
124341627b2cSdillo  *      in out_header, which is assumed to be previously allocated. Returns numb
124441627b2cSdillo er
124541627b2cSdillo  *      of bytes read, 0 if failed.
124641627b2cSdillo  */
124741627b2cSdillo 
124841627b2cSdillo size_t
hfslib_read_master_directory_block(void * in_bytes,hfs_hfs_master_directory_block_t * out_mdr)124941627b2cSdillo hfslib_read_master_directory_block(void* in_bytes,
125041627b2cSdillo     hfs_hfs_master_directory_block_t* out_mdr)
125141627b2cSdillo {
125241627b2cSdillo 	void*   ptr;
125341627b2cSdillo 	int     i;
125441627b2cSdillo 
125541627b2cSdillo 	if (in_bytes == NULL || out_mdr == NULL)
125641627b2cSdillo 		return 0;
125741627b2cSdillo 
125841627b2cSdillo 	ptr = in_bytes;
125941627b2cSdillo 
126041627b2cSdillo 	out_mdr->signature = be16tohp(&ptr);
126141627b2cSdillo 
126241627b2cSdillo 	out_mdr->date_created = be32tohp(&ptr);
126341627b2cSdillo 	out_mdr->date_modified = be32tohp(&ptr);
126441627b2cSdillo 
126541627b2cSdillo 	out_mdr->attributes = be16tohp(&ptr);
126641627b2cSdillo 	out_mdr->root_file_count = be16tohp(&ptr);
126741627b2cSdillo 	out_mdr->volume_bitmap = be16tohp(&ptr);
126841627b2cSdillo 
126941627b2cSdillo 	out_mdr->next_alloc_block = be16tohp(&ptr);
127041627b2cSdillo 	out_mdr->total_blocks = be16tohp(&ptr);
127141627b2cSdillo 	out_mdr->block_size = be32tohp(&ptr);
127241627b2cSdillo 
127341627b2cSdillo 	out_mdr->clump_size = be32tohp(&ptr);
127441627b2cSdillo 	out_mdr->first_block = be16tohp(&ptr);
127541627b2cSdillo 	out_mdr->next_cnid = be32tohp(&ptr);
127641627b2cSdillo 	out_mdr->free_blocks = be16tohp(&ptr);
127741627b2cSdillo 
127841627b2cSdillo 	memcpy(out_mdr->volume_name, ptr, 28);
127941627b2cSdillo 	ptr = (char *)ptr + 28;
128041627b2cSdillo 
128141627b2cSdillo 	out_mdr->date_backedup = be32tohp(&ptr);
128241627b2cSdillo 	out_mdr->backup_seqnum = be16tohp(&ptr);
128341627b2cSdillo 
128441627b2cSdillo 	out_mdr->write_count = be32tohp(&ptr);
128541627b2cSdillo 
128641627b2cSdillo 	out_mdr->extents_clump_size = be32tohp(&ptr);
128741627b2cSdillo 	out_mdr->catalog_clump_size = be32tohp(&ptr);
128841627b2cSdillo 
128941627b2cSdillo 	out_mdr->root_folder_count = be16tohp(&ptr);
129041627b2cSdillo 	out_mdr->file_count = be32tohp(&ptr);
129141627b2cSdillo 	out_mdr->folder_count = be32tohp(&ptr);
129241627b2cSdillo 
129341627b2cSdillo 	for (i = 0; i < 8; i++)
129441627b2cSdillo 		out_mdr->finder_info[i] = be32tohp(&ptr);
129541627b2cSdillo 
129641627b2cSdillo 	out_mdr->embedded_signature = be16tohp(&ptr);
129741627b2cSdillo 	out_mdr->embedded_extent.start_block = be16tohp(&ptr);
129841627b2cSdillo 	out_mdr->embedded_extent.block_count = be16tohp(&ptr);
129941627b2cSdillo 
130041627b2cSdillo 	out_mdr->extents_size = be32tohp(&ptr);
1301547d41caSmaxv 	for (i = 0; i < 3; i++) {
130241627b2cSdillo 		out_mdr->extents_extents[i].start_block = be16tohp(&ptr);
130341627b2cSdillo 		out_mdr->extents_extents[i].block_count = be16tohp(&ptr);
130441627b2cSdillo 	}
130541627b2cSdillo 
130641627b2cSdillo 	out_mdr->catalog_size = be32tohp(&ptr);
1307547d41caSmaxv 	for (i = 0; i < 3; i++) {
130841627b2cSdillo 		out_mdr->catalog_extents[i].start_block = be16tohp(&ptr);
130941627b2cSdillo 		out_mdr->catalog_extents[i].block_count = be16tohp(&ptr);
131041627b2cSdillo 	}
131141627b2cSdillo 
131241627b2cSdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
131341627b2cSdillo }
131441627b2cSdillo 
131541627b2cSdillo /*
131656c3e412Sdillo  *	hfslib_reada_node()
131725e99827Sdillo  *
131825e99827Sdillo  *	Given the pointer to and size of a buffer containing the entire, raw
131925e99827Sdillo  *	contents of any b-tree node from the disk, this function will:
132025e99827Sdillo  *
132125e99827Sdillo  *		1.	determine the type of node and read its contents
132225e99827Sdillo  *		2.	allocate memory for each record and fill it appropriately
132325e99827Sdillo  *		3.	set out_record_ptrs_array to point to an array (which it allocates)
132425e99827Sdillo  *			which has out_node_descriptor->num_recs many pointers to the
132525e99827Sdillo  *			records themselves
132625e99827Sdillo  *		4.	allocate out_record_ptr_sizes_array and fill it with the sizes of
132725e99827Sdillo  *			each record
132825e99827Sdillo  *		5.	return the number of bytes read (i.e., the size of the node)
132925e99827Sdillo  *			or 0 on failure
133025e99827Sdillo  *
133125e99827Sdillo  *	out_node_descriptor must be allocated by the caller and may not be NULL.
133225e99827Sdillo  *
133325e99827Sdillo  *	out_record_ptrs_array and out_record_ptr_sizes_array must both be specified,
133425e99827Sdillo  *	or both be NULL if the caller is not interested in reading the records.
133525e99827Sdillo  *
133625e99827Sdillo  *	out_record_ptr_sizes_array may be NULL if the caller is not interested in
133725e99827Sdillo  *	reading the records, but must not be NULL if out_record_ptrs_array is not.
133825e99827Sdillo  *
133956c3e412Sdillo  *	in_parent_file is HFS_CATALOG_FILE, HFS_EXTENTS_FILE, or
134056c3e412Sdillo  *	HFS_ATTRIBUTES_FILE, depending on the special file in which this node
134125e99827Sdillo  *	resides.
134225e99827Sdillo  *
134325e99827Sdillo  *	inout_volume must have its catnodesize or extnodesize field (depending on
134425e99827Sdillo  *	the parent file) set to the correct value if this is an index, leaf, or map
134525e99827Sdillo  *	node. If this is a header node, the field will be set to its correct value.
134625e99827Sdillo  */
134725e99827Sdillo size_t
hfslib_reada_node(void * in_bytes,hfs_node_descriptor_t * out_node_descriptor,void ** out_record_ptrs_array[],uint16_t * out_record_ptr_sizes_array[],hfs_btree_file_type in_parent_file,hfs_volume * inout_volume,hfs_callback_args * cbargs)134856c3e412Sdillo hfslib_reada_node(void* in_bytes,
134956c3e412Sdillo 	hfs_node_descriptor_t* out_node_descriptor,
135025e99827Sdillo 	void** out_record_ptrs_array[],
135125e99827Sdillo 	uint16_t* out_record_ptr_sizes_array[],
135256c3e412Sdillo 	hfs_btree_file_type in_parent_file,
135356c3e412Sdillo 	hfs_volume* inout_volume,
135456c3e412Sdillo 	hfs_callback_args* cbargs)
135525e99827Sdillo {
135625e99827Sdillo 	void*		ptr;
135725e99827Sdillo 	uint16_t*	rec_offsets;
135825e99827Sdillo 	size_t		last_bytes_read;
135925e99827Sdillo 	uint16_t	nodesize;
136025e99827Sdillo 	uint16_t	numrecords;
136125e99827Sdillo 	uint16_t	free_space_offset;	/* offset to free space in node */
136225e99827Sdillo 	int			keysizefieldsize;
136325e99827Sdillo 	int			i;
136425e99827Sdillo 
136525e99827Sdillo 	numrecords = 0;
136625e99827Sdillo 	rec_offsets = NULL;
136725e99827Sdillo 	if (out_record_ptrs_array != NULL)
136825e99827Sdillo 		*out_record_ptrs_array = NULL;
136925e99827Sdillo 	if (out_record_ptr_sizes_array != NULL)
137025e99827Sdillo 		*out_record_ptr_sizes_array = NULL;
137125e99827Sdillo 
137225e99827Sdillo 	if (in_bytes == NULL || inout_volume == NULL || out_node_descriptor == NULL
137325e99827Sdillo 		|| (out_record_ptrs_array == NULL && out_record_ptr_sizes_array != NULL)
137425e99827Sdillo 		|| (out_record_ptrs_array != NULL && out_record_ptr_sizes_array == NULL) )
137525e99827Sdillo 		goto error;
137625e99827Sdillo 
137725e99827Sdillo 	ptr = in_bytes;
137825e99827Sdillo 
137925e99827Sdillo 	out_node_descriptor->flink = be32tohp(&ptr);
138025e99827Sdillo 	out_node_descriptor->blink = be32tohp(&ptr);
138125e99827Sdillo 	out_node_descriptor->kind = *(((int8_t*)ptr));
138225e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
138325e99827Sdillo 	out_node_descriptor->height = *(((uint8_t*)ptr));
138425e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
138525e99827Sdillo 	out_node_descriptor->num_recs = be16tohp(&ptr);
138625e99827Sdillo 	out_node_descriptor->reserved = be16tohp(&ptr);
138725e99827Sdillo 
138825e99827Sdillo 	numrecords = out_node_descriptor->num_recs;
138925e99827Sdillo 
139025e99827Sdillo 	/*
139125e99827Sdillo 	 *	To go any further, we will need to know the size of this node, as well
139225e99827Sdillo 	 *	as the width of keyed records' key_len parameters for this btree. If
139325e99827Sdillo 	 *	this is an index, leaf, or map node, inout_volume already has the node
139425e99827Sdillo 	 *	size set in its catnodesize or extnodesize field and the key length set
139525e99827Sdillo 	 *	in the catkeysizefieldsize or extkeysizefieldsize for catalog files and
139625e99827Sdillo 	 *	extent files, respectively. However, if this is a header node, this
139725e99827Sdillo 	 *	information has not yet been determined, so this is the place to do it.
139825e99827Sdillo 	 */
139956c3e412Sdillo 	if (out_node_descriptor->kind == HFS_HEADERNODE)
140025e99827Sdillo 	{
140156c3e412Sdillo 		hfs_header_record_t	hr;
140225e99827Sdillo 		void*		header_rec_offset[1];
140325e99827Sdillo 		uint16_t	header_rec_size[1];
140425e99827Sdillo 
140525e99827Sdillo 		/* sanity check to ensure this is a good header node */
140625e99827Sdillo 		if (numrecords != 3)
140756c3e412Sdillo 			HFS_LIBERR("header node does not have exactly 3 records");
140825e99827Sdillo 
140925e99827Sdillo 		header_rec_offset[0] = ptr;
141056c3e412Sdillo 		header_rec_size[0] = sizeof(hfs_header_record_t);
141125e99827Sdillo 
141256c3e412Sdillo 		last_bytes_read = hfslib_read_header_node(header_rec_offset,
141325e99827Sdillo 			header_rec_size, 1, &hr, NULL, NULL);
141425e99827Sdillo 		if (last_bytes_read == 0)
141556c3e412Sdillo 			HFS_LIBERR("could not read header node");
141625e99827Sdillo 
141725e99827Sdillo 		switch(in_parent_file)
141825e99827Sdillo 		{
141956c3e412Sdillo 			case HFS_CATALOG_FILE:
142025e99827Sdillo 				inout_volume->chr.node_size = hr.node_size;
142125e99827Sdillo 				inout_volume->catkeysizefieldsize =
142256c3e412Sdillo 					(hr.attributes & HFS_BIG_KEYS_MASK) ?
142325e99827Sdillo 						sizeof(uint16_t):sizeof(uint8_t);
142425e99827Sdillo 				break;
142525e99827Sdillo 
142656c3e412Sdillo 			case HFS_EXTENTS_FILE:
142725e99827Sdillo 				inout_volume->ehr.node_size = hr.node_size;
142825e99827Sdillo 				inout_volume->extkeysizefieldsize =
142956c3e412Sdillo 					(hr.attributes & HFS_BIG_KEYS_MASK) ?
143025e99827Sdillo 						sizeof(uint16_t):sizeof(uint8_t);
143125e99827Sdillo 				break;
143225e99827Sdillo 
143356c3e412Sdillo 			case HFS_ATTRIBUTES_FILE:
143425e99827Sdillo 			default:
143556c3e412Sdillo 				HFS_LIBERR("invalid parent file type specified");
143625e99827Sdillo 				/* NOTREACHED */
143725e99827Sdillo 		}
143825e99827Sdillo 	}
143925e99827Sdillo 
144025e99827Sdillo 	switch (in_parent_file)
144125e99827Sdillo 	{
144256c3e412Sdillo 		case HFS_CATALOG_FILE:
144325e99827Sdillo 			nodesize = inout_volume->chr.node_size;
144425e99827Sdillo 			keysizefieldsize = inout_volume->catkeysizefieldsize;
144525e99827Sdillo 			break;
144625e99827Sdillo 
144756c3e412Sdillo 		case HFS_EXTENTS_FILE:
144825e99827Sdillo 			nodesize = inout_volume->ehr.node_size;
144925e99827Sdillo 			keysizefieldsize = inout_volume->extkeysizefieldsize;
145025e99827Sdillo 			break;
145125e99827Sdillo 
145256c3e412Sdillo 		case HFS_ATTRIBUTES_FILE:
145325e99827Sdillo 		default:
145456c3e412Sdillo 			HFS_LIBERR("invalid parent file type specified");
145525e99827Sdillo 			/* NOTREACHED */
145625e99827Sdillo 	}
145725e99827Sdillo 
145825e99827Sdillo 	/*
145925e99827Sdillo 	 *	Don't care about records so just exit after getting the node descriptor.
146025e99827Sdillo 	 *	Note: This happens after the header node code, and not before it, in
146125e99827Sdillo 	 *	case the caller calls this function and ignores the record data just to
146225e99827Sdillo 	 *	get at the node descriptor, but then tries to call it again on a non-
146325e99827Sdillo 	 *	header node without first setting inout_volume->cat/extnodesize.
146425e99827Sdillo 	 */
146525e99827Sdillo 	if (out_record_ptrs_array == NULL)
146625e99827Sdillo 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
146725e99827Sdillo 
146856c3e412Sdillo 	rec_offsets = hfslib_malloc(numrecords * sizeof(uint16_t), cbargs);
146925e99827Sdillo 	*out_record_ptr_sizes_array =
147056c3e412Sdillo 		hfslib_malloc(numrecords * sizeof(uint16_t), cbargs);
147125e99827Sdillo 	if (rec_offsets == NULL || *out_record_ptr_sizes_array == NULL)
147256c3e412Sdillo 		HFS_LIBERR("could not allocate node record offsets");
147325e99827Sdillo 
147456c3e412Sdillo 	*out_record_ptrs_array = hfslib_malloc(numrecords * sizeof(void*), cbargs);
147525e99827Sdillo 	if (*out_record_ptrs_array == NULL)
147656c3e412Sdillo 		HFS_LIBERR("could not allocate node records");
147725e99827Sdillo 
147856c3e412Sdillo 	last_bytes_read = hfslib_reada_node_offsets((uint8_t*)in_bytes + nodesize -
1479f9833f67Sriastradh 			numrecords * sizeof(uint16_t), rec_offsets, numrecords);
148025e99827Sdillo 	if (last_bytes_read == 0)
148156c3e412Sdillo 		HFS_LIBERR("could not read node record offsets");
148225e99827Sdillo 
148325e99827Sdillo 	/*	The size of the last record (i.e. the first one listed in the offsets)
148425e99827Sdillo 	 *	must be determined using the offset to the node's free space. */
148525e99827Sdillo 	free_space_offset = be16toh(*(uint16_t*)((uint8_t*)in_bytes + nodesize -
148625e99827Sdillo 			(numrecords+1) * sizeof(uint16_t)));
148725e99827Sdillo 
148825e99827Sdillo 	(*out_record_ptr_sizes_array)[numrecords-1] =
148925e99827Sdillo 		free_space_offset - rec_offsets[0];
1490547d41caSmaxv 	for (i = 1; i < numrecords; i++) {
149125e99827Sdillo 		(*out_record_ptr_sizes_array)[numrecords-i-1] =
149225e99827Sdillo 			rec_offsets[i-1] - rec_offsets[i];
149325e99827Sdillo 	}
149425e99827Sdillo 
149525e99827Sdillo 	for (i = 0; i < numrecords; i++)
149625e99827Sdillo 	{
149725e99827Sdillo 		(*out_record_ptrs_array)[i] =
149856c3e412Sdillo 			hfslib_malloc((*out_record_ptr_sizes_array)[i], cbargs);
149925e99827Sdillo 
150025e99827Sdillo 		if ((*out_record_ptrs_array)[i] == NULL)
150156c3e412Sdillo 			HFS_LIBERR("could not allocate node record #%i",i);
150225e99827Sdillo 
150325e99827Sdillo 		/*
150425e99827Sdillo 		 *	If this is a keyed node (i.e., a leaf or index node), there are two
150525e99827Sdillo 		 *	boundary rules that each record must obey:
150625e99827Sdillo 		 *
150725e99827Sdillo 		 *		1.	A pad byte must be placed between the key and data if the
150825e99827Sdillo 		 *			size of the key plus the size of the key_len field is odd.
150925e99827Sdillo 		 *
151025e99827Sdillo 		 *		2.	A pad byte must be placed after the data if the data size
151125e99827Sdillo 		 *			is odd.
151225e99827Sdillo 		 *
151325e99827Sdillo 		 *	So in the first case we increment the starting point of the data
151425e99827Sdillo 		 *	and correspondingly decrement the record size. In the second case
151525e99827Sdillo 		 *	we decrement the record size.
151625e99827Sdillo 		 */
151756c3e412Sdillo 		if (out_node_descriptor->kind == HFS_LEAFNODE ||
151856c3e412Sdillo 		    out_node_descriptor->kind == HFS_INDEXNODE)
151925e99827Sdillo 		{
152056c3e412Sdillo 			hfs_catalog_key_t	reckey;
152125e99827Sdillo 			uint16_t			rectype;
152225e99827Sdillo 
152325e99827Sdillo 			rectype = out_node_descriptor->kind;
152456c3e412Sdillo 			last_bytes_read = hfslib_read_catalog_keyed_record(ptr, NULL,
152525e99827Sdillo 				&rectype, &reckey, inout_volume);
152625e99827Sdillo 			if (last_bytes_read == 0)
152756c3e412Sdillo 				HFS_LIBERR("could not read node record");
152825e99827Sdillo 
1529547d41caSmaxv 			if ((reckey.key_len + keysizefieldsize) % 2 == 1) {
153025e99827Sdillo 				ptr = (uint8_t*)ptr + 1;
153125e99827Sdillo 				(*out_record_ptr_sizes_array)[i]--;
153225e99827Sdillo 			}
153325e99827Sdillo 
153425e99827Sdillo 			if ((*out_record_ptr_sizes_array)[i] % 2 == 1)
153525e99827Sdillo 				(*out_record_ptr_sizes_array)[i]--;
153625e99827Sdillo 		}
153725e99827Sdillo 
153825e99827Sdillo 		memcpy((*out_record_ptrs_array)[i], ptr,
153925e99827Sdillo 				(*out_record_ptr_sizes_array)[i]);
154025e99827Sdillo 		ptr = (uint8_t*)ptr + (*out_record_ptr_sizes_array)[i];
154125e99827Sdillo 	}
154225e99827Sdillo 
154325e99827Sdillo 	goto exit;
154425e99827Sdillo 
154525e99827Sdillo error:
154656c3e412Sdillo 	hfslib_free_recs(out_record_ptrs_array, out_record_ptr_sizes_array,
154725e99827Sdillo 		&numrecords, cbargs);
154825e99827Sdillo 
154925e99827Sdillo 	ptr = in_bytes;
155025e99827Sdillo 
155156c3e412Sdillo 	/* warn("error occurred in hfslib_reada_node()"); */
155225e99827Sdillo 
155325e99827Sdillo 	/* FALLTHROUGH */
155425e99827Sdillo 
155525e99827Sdillo exit:
155625e99827Sdillo 	if (rec_offsets != NULL)
155756c3e412Sdillo 		hfslib_free(rec_offsets, cbargs);
155825e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
155925e99827Sdillo }
156025e99827Sdillo 
156125e99827Sdillo /*
156256c3e412Sdillo  *	hfslib_reada_node_offsets()
156325e99827Sdillo  *
156425e99827Sdillo  *	Sets out_offset_array to contain the offsets to each record in the node,
156525e99827Sdillo  *	in reverse order. Does not read the free space offset.
156625e99827Sdillo  */
156725e99827Sdillo size_t
hfslib_reada_node_offsets(void * in_bytes,uint16_t * out_offset_array,uint16_t numrecords)1568f9833f67Sriastradh hfslib_reada_node_offsets(void* in_bytes, uint16_t* out_offset_array,
1569f9833f67Sriastradh     uint16_t numrecords)
157025e99827Sdillo {
157125e99827Sdillo 	void*		ptr;
157225e99827Sdillo 
157325e99827Sdillo 	if (in_bytes == NULL || out_offset_array == NULL)
157425e99827Sdillo 		return 0;
157525e99827Sdillo 
157625e99827Sdillo 	ptr = in_bytes;
157725e99827Sdillo 
157825e99827Sdillo 	/*
157925e99827Sdillo 	 * The offset for record 0 (which is the very last offset in the node) is
158025e99827Sdillo 	 * always equal to 14, the size of the node descriptor. So, once we hit
158125e99827Sdillo 	 * offset=14, we know this is the last offset. In this way, we don't need
158225e99827Sdillo 	 * to know the number of records beforehand.
158325e99827Sdillo 	 */
1584547d41caSmaxv 	do {
1585f9833f67Sriastradh 		if (numrecords-- == 0)
1586f9833f67Sriastradh 			return 0;
158725e99827Sdillo 		*out_offset_array = be16tohp(&ptr);
15886ecb13cdSriastradh 	} while (*out_offset_array++ != (uint16_t)14);
158925e99827Sdillo 
159025e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
159125e99827Sdillo }
159225e99827Sdillo 
159356c3e412Sdillo /*	hfslib_read_header_node()
159425e99827Sdillo  *
159525e99827Sdillo  *	out_header_record and/or out_map_record may be NULL if the caller doesn't
159625e99827Sdillo  *	care about their contents.
159725e99827Sdillo  */
159825e99827Sdillo size_t
hfslib_read_header_node(void ** in_recs,uint16_t * in_rec_sizes,uint16_t in_num_recs,hfs_header_record_t * out_hr,void * out_userdata,void * out_map)159956c3e412Sdillo hfslib_read_header_node(void** in_recs,
160025e99827Sdillo 	uint16_t* in_rec_sizes,
160125e99827Sdillo 	uint16_t in_num_recs,
160256c3e412Sdillo 	hfs_header_record_t* out_hr,
160325e99827Sdillo 	void* out_userdata,
160425e99827Sdillo 	void* out_map)
160525e99827Sdillo {
160625e99827Sdillo 	void*	ptr;
160725e99827Sdillo 	int		i;
160825e99827Sdillo 
16094b586f4eSjoerg 	KASSERT(out_hr != NULL);
16104b586f4eSjoerg 
161125e99827Sdillo 	if (in_recs == NULL || in_rec_sizes == NULL)
161225e99827Sdillo 		return 0;
161325e99827Sdillo 
161425e99827Sdillo 	ptr = in_recs[0];
161525e99827Sdillo 	out_hr->tree_depth = be16tohp(&ptr);
161625e99827Sdillo 	out_hr->root_node = be32tohp(&ptr);
161725e99827Sdillo 	out_hr->leaf_recs = be32tohp(&ptr);
161825e99827Sdillo 	out_hr->first_leaf = be32tohp(&ptr);
161925e99827Sdillo 	out_hr->last_leaf = be32tohp(&ptr);
162025e99827Sdillo 	out_hr->node_size = be16tohp(&ptr);
162125e99827Sdillo 	out_hr->max_key_len = be16tohp(&ptr);
162225e99827Sdillo 	out_hr->total_nodes = be32tohp(&ptr);
162325e99827Sdillo 	out_hr->free_nodes = be32tohp(&ptr);
162425e99827Sdillo 	out_hr->reserved = be16tohp(&ptr);
162525e99827Sdillo 	out_hr->clump_size = be32tohp(&ptr);
162625e99827Sdillo 	out_hr->btree_type = *(((uint8_t*)ptr));
162725e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
162825e99827Sdillo 	out_hr->keycomp_type = *(((uint8_t*)ptr));
162925e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
163025e99827Sdillo 	out_hr->attributes = be32tohp(&ptr);
163125e99827Sdillo 	for (i = 0; i < 16; i++)
163225e99827Sdillo 		out_hr->reserved2[i] = be32tohp(&ptr);
163325e99827Sdillo 
1634547d41caSmaxv 	if (out_userdata != NULL) {
163525e99827Sdillo 		memcpy(out_userdata, in_recs[1], in_rec_sizes[1]);
163625e99827Sdillo 	}
163725e99827Sdillo 	ptr = (uint8_t*)ptr + in_rec_sizes[1];	/* size of user data record */
163825e99827Sdillo 
1639547d41caSmaxv 	if (out_map != NULL) {
164025e99827Sdillo 		memcpy(out_map, in_recs[2], in_rec_sizes[2]);
164125e99827Sdillo 	}
164225e99827Sdillo 	ptr = (uint8_t*)ptr + in_rec_sizes[2];	/* size of map record */
164325e99827Sdillo 
164425e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_recs[0]);
164525e99827Sdillo }
164625e99827Sdillo 
164725e99827Sdillo /*
164856c3e412Sdillo  *	hfslib_read_catalog_keyed_record()
164925e99827Sdillo  *
165056c3e412Sdillo  *	out_recdata can be NULL. inout_rectype must be set to either HFS_LEAFNODE
165156c3e412Sdillo  *	or HFS_INDEXNODE upon calling this function, and will be set by the
165256c3e412Sdillo  *	function to one of HFS_REC_FLDR, HFS_REC_FILE, HFS_REC_FLDR_THREAD, or
165356c3e412Sdillo  *	HFS_REC_FLDR_THREAD upon return if the node is a leaf node. If it is an
165425e99827Sdillo  *	index node, inout_rectype will not be changed.
165525e99827Sdillo  */
165625e99827Sdillo size_t
hfslib_read_catalog_keyed_record(void * in_bytes,hfs_catalog_keyed_record_t * out_recdata,int16_t * inout_rectype,hfs_catalog_key_t * out_key,hfs_volume * in_volume)165756c3e412Sdillo hfslib_read_catalog_keyed_record(
165825e99827Sdillo 	void* in_bytes,
165956c3e412Sdillo 	hfs_catalog_keyed_record_t* out_recdata,
166025e99827Sdillo 	int16_t* inout_rectype,
166156c3e412Sdillo 	hfs_catalog_key_t* out_key,
166256c3e412Sdillo 	hfs_volume* in_volume)
166325e99827Sdillo {
166425e99827Sdillo 	void*		ptr;
166525e99827Sdillo 	size_t		last_bytes_read;
166625e99827Sdillo 
166725e99827Sdillo 	if (in_bytes == NULL || out_key == NULL || inout_rectype == NULL)
166825e99827Sdillo 		return 0;
166925e99827Sdillo 
167025e99827Sdillo 	ptr = in_bytes;
167125e99827Sdillo 
167225e99827Sdillo 	/*	For HFS+, the key length is always a 2-byte number. This is indicated
167356c3e412Sdillo 	 *	by the HFS_BIG_KEYS_MASK bit in the attributes field of the catalog
167425e99827Sdillo 	 *	header record. However, we just assume this bit is set, since all HFS+
167525e99827Sdillo 	 *	volumes should have it set anyway. */
167625e99827Sdillo 	if (in_volume->catkeysizefieldsize == sizeof(uint16_t))
167725e99827Sdillo 		out_key->key_len = be16tohp(&ptr);
167825e99827Sdillo 	else if (in_volume->catkeysizefieldsize == sizeof(uint8_t)) {
167925e99827Sdillo 		out_key->key_len = *(((uint8_t*)ptr));
168025e99827Sdillo 		ptr = (uint8_t*)ptr + 1;
168125e99827Sdillo 	}
168225e99827Sdillo 
168325e99827Sdillo 	out_key->parent_cnid = be32tohp(&ptr);
168425e99827Sdillo 
168556c3e412Sdillo 	last_bytes_read = hfslib_read_unistr255(ptr, &out_key->name);
168625e99827Sdillo 	if (last_bytes_read == 0)
168725e99827Sdillo 		return 0;
168825e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
168925e99827Sdillo 
169025e99827Sdillo 	/* don't waste time if the user just wanted the key and/or record type */
1691547d41caSmaxv 	if (out_recdata == NULL) {
169256c3e412Sdillo 		if (*inout_rectype == HFS_LEAFNODE)
169325e99827Sdillo 			*inout_rectype = be16tohp(&ptr);
169456c3e412Sdillo 		else if (*inout_rectype != HFS_INDEXNODE)
169525e99827Sdillo 			return 0;	/* should not happen if we were given valid arguments */
169625e99827Sdillo 
169725e99827Sdillo 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
169825e99827Sdillo 	}
169925e99827Sdillo 
1700547d41caSmaxv 	if (*inout_rectype == HFS_INDEXNODE) {
170125e99827Sdillo 		out_recdata->child = be32tohp(&ptr);
1702547d41caSmaxv 	} else {
170325e99827Sdillo 		/* first need to determine what kind of record this is */
170425e99827Sdillo 		*inout_rectype = be16tohp(&ptr);
170525e99827Sdillo 		out_recdata->type = *inout_rectype;
170625e99827Sdillo 
170725e99827Sdillo 		switch(out_recdata->type)
170825e99827Sdillo 		{
170956c3e412Sdillo 			case HFS_REC_FLDR:
171025e99827Sdillo 			{
171125e99827Sdillo 				out_recdata->folder.flags = be16tohp(&ptr);
171225e99827Sdillo 				out_recdata->folder.valence = be32tohp(&ptr);
171325e99827Sdillo 				out_recdata->folder.cnid = be32tohp(&ptr);
171425e99827Sdillo 				out_recdata->folder.date_created = be32tohp(&ptr);
171525e99827Sdillo 				out_recdata->folder.date_content_mod = be32tohp(&ptr);
171625e99827Sdillo 				out_recdata->folder.date_attrib_mod = be32tohp(&ptr);
171725e99827Sdillo 				out_recdata->folder.date_accessed = be32tohp(&ptr);
171825e99827Sdillo 				out_recdata->folder.date_backedup = be32tohp(&ptr);
171925e99827Sdillo 
172056c3e412Sdillo 				last_bytes_read = hfslib_read_bsd_data(ptr,
172125e99827Sdillo 					&out_recdata->folder.bsd);
172225e99827Sdillo 				if (last_bytes_read == 0)
172325e99827Sdillo 					return 0;
172425e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
172525e99827Sdillo 
172656c3e412Sdillo 				last_bytes_read = hfslib_read_folder_userinfo(ptr,
172725e99827Sdillo 					&out_recdata->folder.user_info);
172825e99827Sdillo 				if (last_bytes_read == 0)
172925e99827Sdillo 					return 0;
173025e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
173125e99827Sdillo 
173256c3e412Sdillo 				last_bytes_read = hfslib_read_folder_finderinfo(ptr,
173325e99827Sdillo 					&out_recdata->folder.finder_info);
173425e99827Sdillo 				if (last_bytes_read == 0)
173525e99827Sdillo 					return 0;
173625e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
173725e99827Sdillo 
173825e99827Sdillo 				out_recdata->folder.text_encoding = be32tohp(&ptr);
173925e99827Sdillo 				out_recdata->folder.reserved = be32tohp(&ptr);
174025e99827Sdillo 			}
174125e99827Sdillo 			break;
174225e99827Sdillo 
174356c3e412Sdillo 			case HFS_REC_FILE:
174425e99827Sdillo 			{
174525e99827Sdillo 				out_recdata->file.flags = be16tohp(&ptr);
174625e99827Sdillo 				out_recdata->file.reserved = be32tohp(&ptr);
174725e99827Sdillo 				out_recdata->file.cnid = be32tohp(&ptr);
174825e99827Sdillo 				out_recdata->file.date_created = be32tohp(&ptr);
174925e99827Sdillo 				out_recdata->file.date_content_mod = be32tohp(&ptr);
175025e99827Sdillo 				out_recdata->file.date_attrib_mod = be32tohp(&ptr);
175125e99827Sdillo 				out_recdata->file.date_accessed = be32tohp(&ptr);
175225e99827Sdillo 				out_recdata->file.date_backedup = be32tohp(&ptr);
175325e99827Sdillo 
175456c3e412Sdillo 				last_bytes_read = hfslib_read_bsd_data(ptr,
175525e99827Sdillo 					&out_recdata->file.bsd);
175625e99827Sdillo 				if (last_bytes_read == 0)
175725e99827Sdillo 					return 0;
175825e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
175925e99827Sdillo 
176056c3e412Sdillo 				last_bytes_read = hfslib_read_file_userinfo(ptr,
176125e99827Sdillo 					&out_recdata->file.user_info);
176225e99827Sdillo 				if (last_bytes_read == 0)
176325e99827Sdillo 					return 0;
176425e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
176525e99827Sdillo 
176656c3e412Sdillo 				last_bytes_read = hfslib_read_file_finderinfo(ptr,
176725e99827Sdillo 					&out_recdata->file.finder_info);
176825e99827Sdillo 				if (last_bytes_read == 0)
176925e99827Sdillo 					return 0;
177025e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
177125e99827Sdillo 
177225e99827Sdillo 				out_recdata->file.text_encoding = be32tohp(&ptr);
177325e99827Sdillo 				out_recdata->file.reserved2 = be32tohp(&ptr);
177425e99827Sdillo 
177556c3e412Sdillo 				last_bytes_read = hfslib_read_fork_descriptor(ptr,
177625e99827Sdillo 					&out_recdata->file.data_fork);
177725e99827Sdillo 				if (last_bytes_read == 0)
177825e99827Sdillo 					return 0;
177925e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
178025e99827Sdillo 
178156c3e412Sdillo 				last_bytes_read = hfslib_read_fork_descriptor(ptr,
178225e99827Sdillo 					&out_recdata->file.rsrc_fork);
178325e99827Sdillo 				if (last_bytes_read == 0)
178425e99827Sdillo 					return 0;
178525e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
178625e99827Sdillo 			}
178725e99827Sdillo 			break;
178825e99827Sdillo 
178956c3e412Sdillo 			case HFS_REC_FLDR_THREAD:
179056c3e412Sdillo 			case HFS_REC_FILE_THREAD:
179125e99827Sdillo 			{
179225e99827Sdillo 				out_recdata->thread.reserved = be16tohp(&ptr);
179325e99827Sdillo 				out_recdata->thread.parent_cnid = be32tohp(&ptr);
179425e99827Sdillo 
179556c3e412Sdillo 				last_bytes_read = hfslib_read_unistr255(ptr,
179625e99827Sdillo 					&out_recdata->thread.name);
179725e99827Sdillo 				if (last_bytes_read == 0)
179825e99827Sdillo 					return 0;
179925e99827Sdillo 				ptr = (uint8_t*)ptr + last_bytes_read;
180025e99827Sdillo 			}
180125e99827Sdillo 			break;
180225e99827Sdillo 
180325e99827Sdillo 			default:
180425e99827Sdillo 				return 1;
180525e99827Sdillo 				/* NOTREACHED */
180625e99827Sdillo 		}
180725e99827Sdillo 	}
180825e99827Sdillo 
180925e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
181025e99827Sdillo }
181125e99827Sdillo 
181225e99827Sdillo /* out_rec may be NULL */
181325e99827Sdillo size_t
hfslib_read_extent_record(void * in_bytes,hfs_extent_record_t * out_rec,hfs_node_kind in_nodekind,hfs_extent_key_t * out_key,hfs_volume * in_volume)181456c3e412Sdillo hfslib_read_extent_record(
181525e99827Sdillo 	void* in_bytes,
181656c3e412Sdillo 	hfs_extent_record_t* out_rec,
181756c3e412Sdillo 	hfs_node_kind in_nodekind,
181856c3e412Sdillo 	hfs_extent_key_t* out_key,
181956c3e412Sdillo 	hfs_volume* in_volume)
182025e99827Sdillo {
182125e99827Sdillo 	void*		ptr;
182225e99827Sdillo 	size_t		last_bytes_read;
182325e99827Sdillo 
182425e99827Sdillo 	if (in_bytes == NULL || out_key == NULL
182556c3e412Sdillo 	    || (in_nodekind!=HFS_LEAFNODE && in_nodekind!=HFS_INDEXNODE))
182625e99827Sdillo 		return 0;
182725e99827Sdillo 
182825e99827Sdillo 	ptr = in_bytes;
182925e99827Sdillo 
183025e99827Sdillo 	/* For HFS+, the key length is always a 2-byte number. This is indicated
183156c3e412Sdillo 	 * by the HFS_BIG_KEYS_MASK bit in the attributes field of the extent
183225e99827Sdillo 	 * overflow header record. However, we just assume this bit is set, since
183325e99827Sdillo 	 * all HFS+ volumes should have it set anyway. */
183425e99827Sdillo 	if (in_volume->extkeysizefieldsize == sizeof(uint16_t))
183525e99827Sdillo 		out_key->key_length = be16tohp(&ptr);
183625e99827Sdillo 	else if (in_volume->extkeysizefieldsize == sizeof(uint8_t)) {
183725e99827Sdillo 		out_key->key_length = *(((uint8_t*)ptr));
183825e99827Sdillo 		ptr = (uint8_t*)ptr + 1;
183925e99827Sdillo 	}
184025e99827Sdillo 
184125e99827Sdillo 	out_key->fork_type = *(((uint8_t*)ptr));
184225e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
184325e99827Sdillo 	out_key->padding = *(((uint8_t*)ptr));
184425e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
184525e99827Sdillo 	out_key->file_cnid = be32tohp(&ptr);
184625e99827Sdillo 	out_key->start_block = be32tohp(&ptr);
184725e99827Sdillo 
184825e99827Sdillo 	/* don't waste time if the user just wanted the key */
184925e99827Sdillo 	if (out_rec == NULL)
185025e99827Sdillo 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
185125e99827Sdillo 
1852547d41caSmaxv 	if (in_nodekind == HFS_LEAFNODE) {
185356c3e412Sdillo 		last_bytes_read = hfslib_read_extent_descriptors(ptr, out_rec);
185425e99827Sdillo 		if (last_bytes_read == 0)
185525e99827Sdillo 			return 0;
185625e99827Sdillo 		ptr = (uint8_t*)ptr + last_bytes_read;
1857547d41caSmaxv 	} else {
185825e99827Sdillo 		/* XXX: this is completely bogus */
185925e99827Sdillo 		/*      (uint32_t*)*out_rec = be32tohp(&ptr); */
186025e99827Sdillo 	    uint32_t *ptr_32 = (uint32_t *)out_rec;
186125e99827Sdillo 		*ptr_32 = be32tohp(&ptr);
186225e99827Sdillo 		/* (*out_rec)[0].start_block = be32tohp(&ptr); */
186325e99827Sdillo 	}
186425e99827Sdillo 
186525e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
186625e99827Sdillo }
186725e99827Sdillo 
186825e99827Sdillo void
hfslib_free_recs(void *** inout_node_recs,uint16_t ** inout_rec_sizes,uint16_t * inout_num_recs,hfs_callback_args * cbargs)186956c3e412Sdillo hfslib_free_recs(
187025e99827Sdillo 	void*** inout_node_recs,
187125e99827Sdillo 	uint16_t** inout_rec_sizes,
187225e99827Sdillo 	uint16_t* inout_num_recs,
187356c3e412Sdillo 	hfs_callback_args* cbargs)
187425e99827Sdillo {
187525e99827Sdillo 	uint16_t	i;
187625e99827Sdillo 
187725e99827Sdillo 	if (inout_num_recs == NULL || *inout_num_recs == 0)
187825e99827Sdillo 		return;
187925e99827Sdillo 
1880547d41caSmaxv 	if (inout_node_recs != NULL && *inout_node_recs != NULL) {
1881547d41caSmaxv 		for (i = 0 ; i < *inout_num_recs; i++) {
1882547d41caSmaxv 			if ((*inout_node_recs)[i] != NULL) {
188356c3e412Sdillo 				hfslib_free((*inout_node_recs)[i], cbargs);
188425e99827Sdillo 				(*inout_node_recs)[i] = NULL;
188525e99827Sdillo 			}
188625e99827Sdillo 		}
188756c3e412Sdillo 		hfslib_free(*inout_node_recs, cbargs);
188825e99827Sdillo 		*inout_node_recs = NULL;
188925e99827Sdillo 	}
189025e99827Sdillo 
1891547d41caSmaxv 	if (inout_rec_sizes != NULL && *inout_rec_sizes != NULL) {
189256c3e412Sdillo 		hfslib_free(*inout_rec_sizes, cbargs);
189325e99827Sdillo 		*inout_rec_sizes = NULL;
189425e99827Sdillo 	}
189525e99827Sdillo 
189625e99827Sdillo 	*inout_num_recs = 0;
189725e99827Sdillo }
189825e99827Sdillo 
189925e99827Sdillo #if 0
190025e99827Sdillo #pragma mark -
190125e99827Sdillo #pragma mark Individual Fields
190225e99827Sdillo #endif
190325e99827Sdillo 
190425e99827Sdillo size_t
hfslib_read_fork_descriptor(void * in_bytes,hfs_fork_t * out_forkdata)190556c3e412Sdillo hfslib_read_fork_descriptor(void* in_bytes, hfs_fork_t* out_forkdata)
190625e99827Sdillo {
190725e99827Sdillo 	void*	ptr;
190825e99827Sdillo 	size_t	last_bytes_read;
190925e99827Sdillo 
191025e99827Sdillo 	if (in_bytes == NULL || out_forkdata == NULL)
191125e99827Sdillo 		return 0;
191225e99827Sdillo 
191325e99827Sdillo 	ptr = in_bytes;
191425e99827Sdillo 
191525e99827Sdillo 	out_forkdata->logical_size = be64tohp(&ptr);
191625e99827Sdillo 	out_forkdata->clump_size = be32tohp(&ptr);
191725e99827Sdillo 	out_forkdata->total_blocks = be32tohp(&ptr);
191825e99827Sdillo 
191956c3e412Sdillo 	if ((last_bytes_read = hfslib_read_extent_descriptors(ptr,
192025e99827Sdillo 		&out_forkdata->extents)) == 0)
192125e99827Sdillo 		return 0;
192225e99827Sdillo 	ptr = (uint8_t*)ptr + last_bytes_read;
192325e99827Sdillo 
192425e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
192525e99827Sdillo }
192625e99827Sdillo 
192725e99827Sdillo size_t
hfslib_read_extent_descriptors(void * in_bytes,hfs_extent_record_t * out_extentrecord)192856c3e412Sdillo hfslib_read_extent_descriptors(
192925e99827Sdillo 	void* in_bytes,
193056c3e412Sdillo 	hfs_extent_record_t* out_extentrecord)
193125e99827Sdillo {
193225e99827Sdillo 	void*	ptr;
193325e99827Sdillo 	int		i;
193425e99827Sdillo 
193525e99827Sdillo 	if (in_bytes == NULL || out_extentrecord == NULL)
193625e99827Sdillo 		return 0;
193725e99827Sdillo 
193825e99827Sdillo 	ptr = in_bytes;
193925e99827Sdillo 
1940547d41caSmaxv 	for (i = 0; i < 8; i++) {
194156c3e412Sdillo 		(((hfs_extent_descriptor_t*)*out_extentrecord)[i]).start_block =
194225e99827Sdillo 			be32tohp(&ptr);
194356c3e412Sdillo 		(((hfs_extent_descriptor_t*)*out_extentrecord)[i]).block_count =
194425e99827Sdillo 			be32tohp(&ptr);
194525e99827Sdillo 	}
194625e99827Sdillo 
194725e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
194825e99827Sdillo }
194925e99827Sdillo 
195025e99827Sdillo size_t
hfslib_read_unistr255(void * in_bytes,hfs_unistr255_t * out_string)195156c3e412Sdillo hfslib_read_unistr255(void* in_bytes, hfs_unistr255_t* out_string)
195225e99827Sdillo {
195325e99827Sdillo 	void*		ptr;
195425e99827Sdillo 	uint16_t	i, length;
195525e99827Sdillo 
195625e99827Sdillo 	if (in_bytes == NULL || out_string == NULL)
195725e99827Sdillo 		return 0;
195825e99827Sdillo 
195925e99827Sdillo 	ptr = in_bytes;
196025e99827Sdillo 
196125e99827Sdillo 	length = be16tohp(&ptr);
196225e99827Sdillo 	if (length > 255)
196325e99827Sdillo 		length = 255; /* hfs+ folder/file names have a limit of 255 chars */
196425e99827Sdillo 	out_string->length = length;
196525e99827Sdillo 
1966547d41caSmaxv 	for (i = 0; i < length; i++) {
196725e99827Sdillo 		out_string->unicode[i] = be16tohp(&ptr);
196825e99827Sdillo 	}
196925e99827Sdillo 
197025e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
197125e99827Sdillo }
197225e99827Sdillo 
197325e99827Sdillo size_t
hfslib_read_bsd_data(void * in_bytes,hfs_bsd_data_t * out_perms)197456c3e412Sdillo hfslib_read_bsd_data(void* in_bytes, hfs_bsd_data_t* out_perms)
197525e99827Sdillo {
197625e99827Sdillo 	void*	ptr;
197725e99827Sdillo 
197825e99827Sdillo 	if (in_bytes == NULL || out_perms == NULL)
197925e99827Sdillo 		return 0;
198025e99827Sdillo 
198125e99827Sdillo 	ptr = in_bytes;
198225e99827Sdillo 
198325e99827Sdillo 	out_perms->owner_id = be32tohp(&ptr);
198425e99827Sdillo 	out_perms->group_id = be32tohp(&ptr);
198525e99827Sdillo 	out_perms->admin_flags = *(((uint8_t*)ptr));
198625e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
198725e99827Sdillo 	out_perms->owner_flags = *(((uint8_t*)ptr));
198825e99827Sdillo 	ptr = (uint8_t*)ptr + 1;
198925e99827Sdillo 	out_perms->file_mode = be16tohp(&ptr);
199025e99827Sdillo 	out_perms->special.inode_num = be32tohp(&ptr); /* this field is a union */
199125e99827Sdillo 
199225e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
199325e99827Sdillo }
199425e99827Sdillo 
199525e99827Sdillo size_t
hfslib_read_file_userinfo(void * in_bytes,hfs_macos_file_info_t * out_info)199656c3e412Sdillo hfslib_read_file_userinfo(void* in_bytes, hfs_macos_file_info_t* out_info)
199725e99827Sdillo {
199825e99827Sdillo 	void*	ptr;
199925e99827Sdillo 
200025e99827Sdillo 	if (in_bytes == NULL || out_info == NULL)
200125e99827Sdillo 		return 0;
200225e99827Sdillo 
200325e99827Sdillo 	ptr = in_bytes;
200425e99827Sdillo 
200525e99827Sdillo 	out_info->file_type = be32tohp(&ptr);
200625e99827Sdillo 	out_info->file_creator = be32tohp(&ptr);
200725e99827Sdillo 	out_info->finder_flags = be16tohp(&ptr);
200825e99827Sdillo 	out_info->location.v = be16tohp(&ptr);
200925e99827Sdillo 	out_info->location.h = be16tohp(&ptr);
201025e99827Sdillo 	out_info->reserved = be16tohp(&ptr);
201125e99827Sdillo 
201225e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
201325e99827Sdillo }
201425e99827Sdillo 
201525e99827Sdillo size_t
hfslib_read_file_finderinfo(void * in_bytes,hfs_macos_extended_file_info_t * out_info)201656c3e412Sdillo hfslib_read_file_finderinfo(
201725e99827Sdillo 	void* in_bytes,
201856c3e412Sdillo 	hfs_macos_extended_file_info_t* out_info)
201925e99827Sdillo {
202025e99827Sdillo 	void*	ptr;
202125e99827Sdillo 
202225e99827Sdillo 	if (in_bytes == NULL || out_info == NULL)
202325e99827Sdillo 		return 0;
202425e99827Sdillo 
202525e99827Sdillo 	ptr = in_bytes;
202625e99827Sdillo 
202725e99827Sdillo #if 0
202825e99827Sdillo 	#pragma warn Fill in with real code!
202925e99827Sdillo #endif
203025e99827Sdillo 	/* FIXME: Fill in with real code! */
203125e99827Sdillo 	memset(out_info, 0, sizeof(*out_info));
203256c3e412Sdillo 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_extended_file_info_t);
203325e99827Sdillo 
203425e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
203525e99827Sdillo }
203625e99827Sdillo 
203725e99827Sdillo size_t
hfslib_read_folder_userinfo(void * in_bytes,hfs_macos_folder_info_t * out_info)203856c3e412Sdillo hfslib_read_folder_userinfo(void* in_bytes, hfs_macos_folder_info_t* out_info)
203925e99827Sdillo {
204025e99827Sdillo 	void*	ptr;
204125e99827Sdillo 
204225e99827Sdillo 	if (in_bytes == NULL || out_info == NULL)
204325e99827Sdillo 		return 0;
204425e99827Sdillo 
204525e99827Sdillo 	ptr = in_bytes;
204625e99827Sdillo 
204725e99827Sdillo #if 0
204825e99827Sdillo 	#pragma warn Fill in with real code!
204925e99827Sdillo #endif
205025e99827Sdillo 	/* FIXME: Fill in with real code! */
205125e99827Sdillo 	memset(out_info, 0, sizeof(*out_info));
205256c3e412Sdillo 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_folder_info_t);
205325e99827Sdillo 
205425e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
205525e99827Sdillo }
205625e99827Sdillo 
205725e99827Sdillo size_t
hfslib_read_folder_finderinfo(void * in_bytes,hfs_macos_extended_folder_info_t * out_info)205856c3e412Sdillo hfslib_read_folder_finderinfo(
205925e99827Sdillo 	void* in_bytes,
206056c3e412Sdillo 	hfs_macos_extended_folder_info_t* out_info)
206125e99827Sdillo {
206225e99827Sdillo 	void*	ptr;
206325e99827Sdillo 
206425e99827Sdillo 	if (in_bytes == NULL || out_info == NULL)
206525e99827Sdillo 		return 0;
206625e99827Sdillo 
206725e99827Sdillo 	ptr = in_bytes;
206825e99827Sdillo 
206925e99827Sdillo #if 0
207025e99827Sdillo 	#pragma warn Fill in with real code!
207125e99827Sdillo #endif
207225e99827Sdillo 	/* FIXME: Fill in with real code! */
207325e99827Sdillo 	memset(out_info, 0, sizeof(*out_info));
207456c3e412Sdillo 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_extended_folder_info_t);
207525e99827Sdillo 
207625e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
207725e99827Sdillo }
207825e99827Sdillo 
207925e99827Sdillo size_t
hfslib_read_journal_info(void * in_bytes,hfs_journal_info_t * out_info)208056c3e412Sdillo hfslib_read_journal_info(void* in_bytes, hfs_journal_info_t* out_info)
208125e99827Sdillo {
208225e99827Sdillo 	void*	ptr;
208325e99827Sdillo 	int		i;
208425e99827Sdillo 
208525e99827Sdillo 	if (in_bytes == NULL || out_info == NULL)
208625e99827Sdillo 		return 0;
208725e99827Sdillo 
208825e99827Sdillo 	ptr = in_bytes;
208925e99827Sdillo 
209025e99827Sdillo 	out_info->flags = be32tohp(&ptr);
2091547d41caSmaxv 	for (i = 0; i < 8; i++) {
209225e99827Sdillo 		out_info->device_signature[i] = be32tohp(&ptr);
209325e99827Sdillo 	}
209425e99827Sdillo 	out_info->offset = be64tohp(&ptr);
209525e99827Sdillo 	out_info->size = be64tohp(&ptr);
2096547d41caSmaxv 	for (i = 0; i < 32; i++) {
209725e99827Sdillo 		out_info->reserved[i] = be64tohp(&ptr);
209825e99827Sdillo 	}
209925e99827Sdillo 
210025e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
210125e99827Sdillo }
210225e99827Sdillo 
210325e99827Sdillo size_t
hfslib_read_journal_header(void * in_bytes,hfs_journal_header_t * out_header)210456c3e412Sdillo hfslib_read_journal_header(void* in_bytes, hfs_journal_header_t* out_header)
210525e99827Sdillo {
210625e99827Sdillo 	void*	ptr;
210725e99827Sdillo 
210825e99827Sdillo 	if (in_bytes == NULL || out_header == NULL)
210925e99827Sdillo 		return 0;
211025e99827Sdillo 
211125e99827Sdillo 	ptr = in_bytes;
211225e99827Sdillo 
211325e99827Sdillo 	out_header->magic = be32tohp(&ptr);
211425e99827Sdillo 	out_header->endian = be32tohp(&ptr);
211525e99827Sdillo 	out_header->start = be64tohp(&ptr);
211625e99827Sdillo 	out_header->end = be64tohp(&ptr);
211725e99827Sdillo 	out_header->size = be64tohp(&ptr);
211825e99827Sdillo 	out_header->blocklist_header_size = be32tohp(&ptr);
211925e99827Sdillo 	out_header->checksum = be32tohp(&ptr);
212025e99827Sdillo 	out_header->journal_header_size = be32tohp(&ptr);
212125e99827Sdillo 
212225e99827Sdillo 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
212325e99827Sdillo }
212425e99827Sdillo 
212525e99827Sdillo #if 0
212625e99827Sdillo #pragma mark -
212725e99827Sdillo #pragma mark Disk Access
212825e99827Sdillo #endif
212925e99827Sdillo 
213025e99827Sdillo /*
213156c3e412Sdillo  *	hfslib_readd_with_extents()
213225e99827Sdillo  *
213325e99827Sdillo  *	This function reads the contents of a file from the volume, given an array
213425e99827Sdillo  *	of extent descriptors which specify where every extent of the file is
213525e99827Sdillo  *	located (in addition to the usual pread() arguments). out_bytes is presumed
213625e99827Sdillo  *  to exist and be large enough to hold in_length number of bytes. Returns 0
213725e99827Sdillo  *	on success.
213825e99827Sdillo  */
213925e99827Sdillo int
hfslib_readd_with_extents(hfs_volume * in_vol,void * out_bytes,uint64_t * out_bytesread,uint64_t in_length,uint64_t in_offset,hfs_extent_descriptor_t in_extents[],uint16_t in_numextents,hfs_callback_args * cbargs)214056c3e412Sdillo hfslib_readd_with_extents(
214156c3e412Sdillo 	hfs_volume*	in_vol,
214225e99827Sdillo 	void*		out_bytes,
214325e99827Sdillo 	uint64_t*	out_bytesread,
214425e99827Sdillo 	uint64_t	in_length,
214525e99827Sdillo 	uint64_t	in_offset,
214656c3e412Sdillo 	hfs_extent_descriptor_t in_extents[],
214725e99827Sdillo 	uint16_t	in_numextents,
214856c3e412Sdillo 	hfs_callback_args*	cbargs)
214925e99827Sdillo {
215025e99827Sdillo 	uint64_t	ext_length, last_offset;
215125e99827Sdillo 	uint16_t	i;
215225e99827Sdillo 	int			error;
215325e99827Sdillo 
2154547d41caSmaxv 	if (in_vol == NULL || out_bytes == NULL || in_extents == NULL ||
2155547d41caSmaxv 	    in_numextents == 0 || out_bytesread == NULL)
215625e99827Sdillo 		return -1;
215725e99827Sdillo 
215825e99827Sdillo 	*out_bytesread = 0;
215925e99827Sdillo 	last_offset = 0;
216025e99827Sdillo 
216125e99827Sdillo 	for (i = 0; i < in_numextents; i++)
216225e99827Sdillo 	{
216325e99827Sdillo 		if (in_extents[i].block_count == 0)
216425e99827Sdillo 			continue;
216525e99827Sdillo 
216625e99827Sdillo 		ext_length = in_extents[i].block_count * in_vol->vh.block_size;
216725e99827Sdillo 
216825e99827Sdillo 		if (in_offset < last_offset+ext_length
216925e99827Sdillo 			&& in_offset+in_length >= last_offset)
217025e99827Sdillo 		{
217125e99827Sdillo 			uint64_t	isect_start, isect_end;
217225e99827Sdillo 
217325e99827Sdillo 			isect_start = max(in_offset, last_offset);
217425e99827Sdillo 			isect_end = min(in_offset+in_length, last_offset+ext_length);
217556c3e412Sdillo 			error = hfslib_readd(in_vol, out_bytes, isect_end-isect_start,
217625e99827Sdillo 				isect_start - last_offset + (uint64_t)in_extents[i].start_block
217725e99827Sdillo 					* in_vol->vh.block_size, cbargs);
217825e99827Sdillo 
217925e99827Sdillo 			if (error != 0)
218025e99827Sdillo 				return error;
218125e99827Sdillo 
218225e99827Sdillo 			*out_bytesread += isect_end-isect_start;
218325e99827Sdillo 			out_bytes = (uint8_t*)out_bytes + isect_end-isect_start;
218425e99827Sdillo 		}
218525e99827Sdillo 
218625e99827Sdillo 		last_offset += ext_length;
218725e99827Sdillo 	}
218825e99827Sdillo 
218925e99827Sdillo 	return 0;
219025e99827Sdillo }
219125e99827Sdillo 
219225e99827Sdillo #if 0
219325e99827Sdillo #pragma mark -
219425e99827Sdillo #pragma mark Callback Wrappers
219525e99827Sdillo #endif
219625e99827Sdillo 
219725e99827Sdillo void
hfslib_error(const char * in_format,const char * in_file,int in_line,...)219856c3e412Sdillo hfslib_error(const char* in_format, const char* in_file, int in_line, ...)
219925e99827Sdillo {
220025e99827Sdillo 	va_list		ap;
220125e99827Sdillo 
220225e99827Sdillo 	if (in_format == NULL)
220325e99827Sdillo 		return;
220425e99827Sdillo 
2205547d41caSmaxv 	if (hfs_gcb.error != NULL) {
220625e99827Sdillo 		va_start(ap, in_line);
220756c3e412Sdillo 		hfs_gcb.error(in_format, in_file, in_line, ap);
220825e99827Sdillo 		va_end(ap);
220925e99827Sdillo 	}
221025e99827Sdillo }
221125e99827Sdillo 
221225e99827Sdillo void*
hfslib_malloc(size_t size,hfs_callback_args * cbargs)221356c3e412Sdillo hfslib_malloc(size_t size, hfs_callback_args* cbargs)
221425e99827Sdillo {
221556c3e412Sdillo 	if (hfs_gcb.allocmem != NULL)
221656c3e412Sdillo 		return hfs_gcb.allocmem(size, cbargs);
221725e99827Sdillo 
221825e99827Sdillo 	return NULL;
221925e99827Sdillo }
222025e99827Sdillo 
222125e99827Sdillo void*
hfslib_realloc(void * ptr,size_t size,hfs_callback_args * cbargs)222256c3e412Sdillo hfslib_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
222325e99827Sdillo {
222456c3e412Sdillo 	if (hfs_gcb.reallocmem != NULL)
222556c3e412Sdillo 		return hfs_gcb.reallocmem(ptr, size, cbargs);
222625e99827Sdillo 
222725e99827Sdillo 	return NULL;
222825e99827Sdillo }
222925e99827Sdillo 
223025e99827Sdillo void
hfslib_free(void * ptr,hfs_callback_args * cbargs)223156c3e412Sdillo hfslib_free(void* ptr, hfs_callback_args* cbargs)
223225e99827Sdillo {
223356c3e412Sdillo 	if (hfs_gcb.freemem != NULL && ptr != NULL)
223456c3e412Sdillo 		hfs_gcb.freemem(ptr, cbargs);
223525e99827Sdillo }
223625e99827Sdillo 
223725e99827Sdillo int
hfslib_openvoldevice(hfs_volume * in_vol,const char * in_device,hfs_callback_args * cbargs)223856c3e412Sdillo hfslib_openvoldevice(
223956c3e412Sdillo 	hfs_volume* in_vol,
224025e99827Sdillo 	const char* in_device,
224156c3e412Sdillo 	hfs_callback_args* cbargs)
224225e99827Sdillo {
224356c3e412Sdillo 	if (hfs_gcb.openvol != NULL && in_device != NULL)
224441627b2cSdillo 		return hfs_gcb.openvol(in_vol, in_device, cbargs);
224525e99827Sdillo 
224625e99827Sdillo 	return 1;
224725e99827Sdillo }
224825e99827Sdillo 
224925e99827Sdillo void
hfslib_closevoldevice(hfs_volume * in_vol,hfs_callback_args * cbargs)225056c3e412Sdillo hfslib_closevoldevice(hfs_volume* in_vol, hfs_callback_args* cbargs)
225125e99827Sdillo {
225256c3e412Sdillo 	if (hfs_gcb.closevol != NULL)
225356c3e412Sdillo 		hfs_gcb.closevol(in_vol, cbargs);
225425e99827Sdillo }
225525e99827Sdillo 
225625e99827Sdillo int
hfslib_readd(hfs_volume * in_vol,void * out_bytes,uint64_t in_length,uint64_t in_offset,hfs_callback_args * cbargs)225756c3e412Sdillo hfslib_readd(
225856c3e412Sdillo 	hfs_volume* in_vol,
225925e99827Sdillo 	void* out_bytes,
226025e99827Sdillo 	uint64_t in_length,
226125e99827Sdillo 	uint64_t in_offset,
226256c3e412Sdillo 	hfs_callback_args* cbargs)
226325e99827Sdillo {
226425e99827Sdillo 	if (in_vol == NULL || out_bytes == NULL)
226525e99827Sdillo 		return -1;
226625e99827Sdillo 
226756c3e412Sdillo 	if (hfs_gcb.read != NULL)
226856c3e412Sdillo 		return hfs_gcb.read(in_vol, out_bytes, in_length, in_offset, cbargs);
226925e99827Sdillo 
227025e99827Sdillo 	return -1;
227125e99827Sdillo }
227225e99827Sdillo 
227325e99827Sdillo #if 0
227425e99827Sdillo #pragma mark -
227525e99827Sdillo #pragma mark Other
227625e99827Sdillo #endif
227725e99827Sdillo 
227825e99827Sdillo /* returns key length */
227925e99827Sdillo uint16_t
hfslib_make_catalog_key(hfs_cnid_t in_parent_cnid,uint16_t in_name_len,unichar_t * in_unicode,hfs_catalog_key_t * out_key)228056c3e412Sdillo hfslib_make_catalog_key(
228156c3e412Sdillo 	hfs_cnid_t in_parent_cnid,
228225e99827Sdillo 	uint16_t in_name_len,
228325e99827Sdillo 	unichar_t* in_unicode,
228456c3e412Sdillo 	hfs_catalog_key_t* out_key)
228525e99827Sdillo {
2286547d41caSmaxv 	if (in_parent_cnid == 0 || (in_name_len > 0 && in_unicode == NULL) ||
2287547d41caSmaxv 	    out_key == 0)
228825e99827Sdillo 		return 0;
228925e99827Sdillo 
229025e99827Sdillo 	if (in_name_len > 255)
229125e99827Sdillo 		in_name_len = 255;
229225e99827Sdillo 
229325e99827Sdillo 	out_key->key_len = 6 + 2 * in_name_len;
229425e99827Sdillo 	out_key->parent_cnid = in_parent_cnid;
229525e99827Sdillo 	out_key->name.length = in_name_len;
229625e99827Sdillo 	if (in_name_len > 0)
229725e99827Sdillo 		memcpy(&out_key->name.unicode, in_unicode, in_name_len*2);
229825e99827Sdillo 
229925e99827Sdillo 	return out_key->key_len;
230025e99827Sdillo }
230125e99827Sdillo 
230225e99827Sdillo /* returns key length */
230325e99827Sdillo uint16_t
hfslib_make_extent_key(hfs_cnid_t in_cnid,uint8_t in_forktype,uint32_t in_startblock,hfs_extent_key_t * out_key)230456c3e412Sdillo hfslib_make_extent_key(
230556c3e412Sdillo 	hfs_cnid_t in_cnid,
230625e99827Sdillo 	uint8_t in_forktype,
230725e99827Sdillo 	uint32_t in_startblock,
230856c3e412Sdillo 	hfs_extent_key_t* out_key)
230925e99827Sdillo {
231025e99827Sdillo 	if (in_cnid == 0 || out_key == 0)
231125e99827Sdillo 		return 0;
231225e99827Sdillo 
231356c3e412Sdillo 	out_key->key_length = HFS_MAX_EXT_KEY_LEN;
231425e99827Sdillo 	out_key->fork_type = in_forktype;
231525e99827Sdillo 	out_key->padding = 0;
231625e99827Sdillo 	out_key->file_cnid = in_cnid;
231725e99827Sdillo 	out_key->start_block = in_startblock;
231825e99827Sdillo 
231925e99827Sdillo 	return out_key->key_length;
232025e99827Sdillo }
232125e99827Sdillo 
232225e99827Sdillo /* case-folding */
232325e99827Sdillo int
hfslib_compare_catalog_keys_cf(const void * ap,const void * bp)232456c3e412Sdillo hfslib_compare_catalog_keys_cf (
232525e99827Sdillo 	const void *ap,
232625e99827Sdillo 	const void *bp)
232725e99827Sdillo {
232856c3e412Sdillo 	const hfs_catalog_key_t	*a, *b;
232925e99827Sdillo 	unichar_t	ac, bc; /* current character from a, b */
233025e99827Sdillo 	unichar_t	lc; /* lowercase version of current character */
233125e99827Sdillo 	uint8_t		apos, bpos; /* current character indices */
233225e99827Sdillo 
233356c3e412Sdillo 	a = (const hfs_catalog_key_t*)ap;
233456c3e412Sdillo 	b = (const hfs_catalog_key_t*)bp;
233525e99827Sdillo 
2336547d41caSmaxv 	if (a->parent_cnid != b->parent_cnid) {
233725e99827Sdillo 		return (a->parent_cnid - b->parent_cnid);
2338547d41caSmaxv 	} else {
233925e99827Sdillo 		/*
234025e99827Sdillo 		 * The following code implements the pseudocode suggested by
234125e99827Sdillo 		 * the HFS+ technote.
234225e99827Sdillo 		 */
234325e99827Sdillo 
234425e99827Sdillo /*
234525e99827Sdillo  * XXX These need to be revised to be endian-independent!
234625e99827Sdillo  */
234725e99827Sdillo #define hbyte(x) ((x) >> 8)
234825e99827Sdillo #define lbyte(x) ((x) & 0x00FF)
234925e99827Sdillo 
235025e99827Sdillo 		apos = bpos = 0;
235125e99827Sdillo 		while (1)
235225e99827Sdillo 		{
235325e99827Sdillo 			/* get next valid character from a */
235425e99827Sdillo 			for (lc = 0; lc == 0 && apos < a->name.length; apos++) {
235525e99827Sdillo 				ac = a->name.unicode[apos];
235656c3e412Sdillo 				lc = hfs_gcft[hbyte(ac)];
235725e99827Sdillo 				if (lc == 0)
235825e99827Sdillo 					lc = ac;
235925e99827Sdillo 				else
236056c3e412Sdillo 					lc = hfs_gcft[lc + lbyte(ac)];
236125e99827Sdillo 			};
236225e99827Sdillo 			ac = lc;
236325e99827Sdillo 
236425e99827Sdillo 			/* get next valid character from b */
236525e99827Sdillo 			for (lc = 0; lc == 0 && bpos < b->name.length; bpos++) {
236625e99827Sdillo 				bc = b->name.unicode[bpos];
236756c3e412Sdillo 				lc = hfs_gcft[hbyte(bc)];
236825e99827Sdillo 				if (lc == 0)
236925e99827Sdillo 					lc = bc;
237025e99827Sdillo 				else
237156c3e412Sdillo 					lc = hfs_gcft[lc + lbyte(bc)];
237225e99827Sdillo 			};
237325e99827Sdillo 			bc = lc;
237425e99827Sdillo 
237525e99827Sdillo 			/* on end of string ac/bc are 0, otherwise > 0 */
237625e99827Sdillo 			if (ac != bc || (ac == 0 && bc == 0))
237725e99827Sdillo 				return ac - bc;
237825e99827Sdillo 		}
237925e99827Sdillo #undef hbyte
238025e99827Sdillo #undef lbyte
238125e99827Sdillo 	}
238225e99827Sdillo }
238325e99827Sdillo 
238425e99827Sdillo /* binary compare (i.e., not case folding) */
238525e99827Sdillo int
hfslib_compare_catalog_keys_bc(const void * ap,const void * bp)238656c3e412Sdillo hfslib_compare_catalog_keys_bc (
2387ffb79aacSsevan 	const void *ap,
2388ffb79aacSsevan 	const void *bp)
238925e99827Sdillo {
2390ffb79aacSsevan 	int c;
2391ffb79aacSsevan 	const hfs_catalog_key_t *a, *b;
2392ffb79aacSsevan 
2393ffb79aacSsevan 	a = (const hfs_catalog_key_t *) ap;
2394ffb79aacSsevan 	b = (const hfs_catalog_key_t *) bp;
2395ffb79aacSsevan 
2396ffb79aacSsevan 	if (a->parent_cnid == b->parent_cnid)
239725e99827Sdillo 	{
2398ffb79aacSsevan 		if (a->name.length == 0 && b->name.length == 0)
239925e99827Sdillo 			return 0;
240025e99827Sdillo 
2401ffb79aacSsevan 		if (a->name.length == 0)
240225e99827Sdillo 			return -1;
2403ffb79aacSsevan 		if (b->name.length == 0)
240425e99827Sdillo 			return 1;
240525e99827Sdillo 
240625e99827Sdillo 		/* FIXME: This does a byte-per-byte comparison, whereas the HFS spec
240725e99827Sdillo 		 * mandates a uint16_t chunk comparison. */
2408ffb79aacSsevan 		c = memcmp(a->name.unicode, b->name.unicode,
2409ffb79aacSsevan 			sizeof(unichar_t)*min(a->name.length, b->name.length));
2410ffb79aacSsevan 		if (c != 0)
2411ffb79aacSsevan 			return c;
2412ffb79aacSsevan 		else
2413ffb79aacSsevan 			return (a->name.length - b->name.length);
2414547d41caSmaxv 	} else {
2415ffb79aacSsevan 		return (a->parent_cnid - b->parent_cnid);
241625e99827Sdillo 	}
241725e99827Sdillo }
241825e99827Sdillo 
241925e99827Sdillo int
hfslib_compare_extent_keys(const void * ap,const void * bp)242056c3e412Sdillo hfslib_compare_extent_keys (
2421ffb79aacSsevan 	const void *ap,
2422ffb79aacSsevan 	const void *bp)
242325e99827Sdillo {
242425e99827Sdillo 	/*
242525e99827Sdillo 	 *	Comparison order, in descending importance:
242625e99827Sdillo 	 *
242725e99827Sdillo 	 *		CNID -> fork type -> start block
242825e99827Sdillo 	 */
242925e99827Sdillo 
2430ffb79aacSsevan 	const hfs_extent_key_t *a, *b;
2431ffb79aacSsevan 	a = (const hfs_extent_key_t *) ap;
2432ffb79aacSsevan 	b = (const hfs_extent_key_t *) bp;
2433ffb79aacSsevan 
2434ffb79aacSsevan 	if (a->file_cnid == b->file_cnid)
243525e99827Sdillo 	{
2436ffb79aacSsevan 		if (a->fork_type == b->fork_type)
243725e99827Sdillo 		{
2438ffb79aacSsevan 			if (a->start_block == b->start_block)
243925e99827Sdillo 			{
244025e99827Sdillo 				return 0;
2441547d41caSmaxv 			} else {
2442ffb79aacSsevan 				return (a->start_block - b->start_block);
244325e99827Sdillo 			}
2444547d41caSmaxv 		} else {
2445ffb79aacSsevan 			return (a->fork_type - b->fork_type);
244625e99827Sdillo 		}
2447547d41caSmaxv 	} else {
2448ffb79aacSsevan 		return (a->file_cnid - b->file_cnid);
244925e99827Sdillo 	}
245025e99827Sdillo }
245125e99827Sdillo 
245225e99827Sdillo /* 1+10 tables of 16 rows and 16 columns, each 2 bytes wide = 5632 bytes */
245325e99827Sdillo int
hfslib_create_casefolding_table(void)245456c3e412Sdillo hfslib_create_casefolding_table(void)
245525e99827Sdillo {
245656c3e412Sdillo 	hfs_callback_args	cbargs;
245725e99827Sdillo 	unichar_t*	t; /* convenience */
245825e99827Sdillo 	uint16_t	s; /* current subtable * 256 */
245925e99827Sdillo 	uint16_t	i; /* current subtable index (0 to 255) */
246025e99827Sdillo 
246156c3e412Sdillo 	if (hfs_gcft != NULL)
246225e99827Sdillo 		return 0; /* no sweat, table already exists */
246325e99827Sdillo 
246456c3e412Sdillo 	hfslib_init_cbargs(&cbargs);
246556c3e412Sdillo 	hfs_gcft = hfslib_malloc(5632, &cbargs);
246656c3e412Sdillo 	if (hfs_gcft == NULL)
246756c3e412Sdillo 		HFS_LIBERR("could not allocate case folding table");
246825e99827Sdillo 
246956c3e412Sdillo 	t = hfs_gcft;	 /* easier to type :) */
247025e99827Sdillo 
247125e99827Sdillo 	/*
247225e99827Sdillo 	 * high byte indices
247325e99827Sdillo 	 */
247425e99827Sdillo 	s = 0 * 256;
247525e99827Sdillo 	memset(t, 0x00, 512);
247625e99827Sdillo 	t[s+  0] = 0x0100;
247725e99827Sdillo 	t[s+  1] = 0x0200;
247825e99827Sdillo 	t[s+  3] = 0x0300;
247925e99827Sdillo 	t[s+  4] = 0x0400;
248025e99827Sdillo 	t[s+  5] = 0x0500;
248125e99827Sdillo 	t[s+ 16] = 0x0600;
248225e99827Sdillo 	t[s+ 32] = 0x0700;
248325e99827Sdillo 	t[s+ 33] = 0x0800;
248425e99827Sdillo 	t[s+254] = 0x0900;
248525e99827Sdillo 	t[s+255] = 0x0a00;
248625e99827Sdillo 
248725e99827Sdillo 	/*
248825e99827Sdillo 	 * table 1 (high byte 0x00)
248925e99827Sdillo 	 */
249025e99827Sdillo 	s = 1 * 256;
249125e99827Sdillo 	for (i = 0; i < 65; i++)
249225e99827Sdillo 		t[s+i] = i;
249325e99827Sdillo 	t[s+  0] = 0xffff;
249425e99827Sdillo 	for (i = 65; i < 91; i++)
249525e99827Sdillo 		t[s+i] = i + 0x20;
249625e99827Sdillo 	for (i = 91; i < 256; i++)
249725e99827Sdillo 		t[s+i] = i;
249825e99827Sdillo 	t[s+198] = 0x00e6;
249925e99827Sdillo 	t[s+208] = 0x00f0;
250025e99827Sdillo 	t[s+216] = 0x00f8;
250125e99827Sdillo 	t[s+222] = 0x00fe;
250225e99827Sdillo 
250325e99827Sdillo 	/*
250425e99827Sdillo 	 * table 2 (high byte 0x01)
250525e99827Sdillo 	 */
250625e99827Sdillo 	s = 2 * 256;
250725e99827Sdillo 	for (i = 0; i < 256; i++)
250825e99827Sdillo 		t[s+i] = i + 0x0100;
250925e99827Sdillo 	t[s+ 16] = 0x0111;
251025e99827Sdillo 	t[s+ 38] = 0x0127;
251125e99827Sdillo 	t[s+ 50] = 0x0133;
251225e99827Sdillo 	t[s+ 63] = 0x0140;
251325e99827Sdillo 	t[s+ 65] = 0x0142;
251425e99827Sdillo 	t[s+ 74] = 0x014b;
251525e99827Sdillo 	t[s+ 82] = 0x0153;
251625e99827Sdillo 	t[s+102] = 0x0167;
251725e99827Sdillo 	t[s+129] = 0x0253;
251825e99827Sdillo 	t[s+130] = 0x0183;
251925e99827Sdillo 	t[s+132] = 0x0185;
252025e99827Sdillo 	t[s+134] = 0x0254;
252125e99827Sdillo 	t[s+135] = 0x0188;
252225e99827Sdillo 	t[s+137] = 0x0256;
252325e99827Sdillo 	t[s+138] = 0x0257;
252425e99827Sdillo 	t[s+139] = 0x018c;
252525e99827Sdillo 	t[s+142] = 0x01dd;
252625e99827Sdillo 	t[s+143] = 0x0259;
252725e99827Sdillo 	t[s+144] = 0x025b;
252825e99827Sdillo 	t[s+145] = 0x0192;
252925e99827Sdillo 	t[s+147] = 0x0260;
253025e99827Sdillo 	t[s+148] = 0x0263;
253125e99827Sdillo 	t[s+150] = 0x0269;
253225e99827Sdillo 	t[s+151] = 0x0268;
253325e99827Sdillo 	t[s+152] = 0x0199;
253425e99827Sdillo 	t[s+156] = 0x026f;
253525e99827Sdillo 	t[s+157] = 0x0272;
253625e99827Sdillo 	t[s+159] = 0x0275;
253725e99827Sdillo 	t[s+162] = 0x01a3;
253825e99827Sdillo 	t[s+164] = 0x01a5;
253925e99827Sdillo 	t[s+167] = 0x01a8;
254025e99827Sdillo 	t[s+169] = 0x0283;
254125e99827Sdillo 	t[s+172] = 0x01ad;
254225e99827Sdillo 	t[s+174] = 0x0288;
254325e99827Sdillo 	t[s+177] = 0x028a;
254425e99827Sdillo 	t[s+178] = 0x028b;
254525e99827Sdillo 	t[s+179] = 0x01b4;
254625e99827Sdillo 	t[s+181] = 0x01b6;
254725e99827Sdillo 	t[s+183] = 0x0292;
254825e99827Sdillo 	t[s+184] = 0x01b9;
254925e99827Sdillo 	t[s+188] = 0x01bd;
255025e99827Sdillo 	t[s+196] = 0x01c6;
255125e99827Sdillo 	t[s+197] = 0x01c6;
255225e99827Sdillo 	t[s+199] = 0x01c9;
255325e99827Sdillo 	t[s+200] = 0x01c9;
255425e99827Sdillo 	t[s+202] = 0x01cc;
255525e99827Sdillo 	t[s+203] = 0x01cc;
255625e99827Sdillo 	t[s+228] = 0x01e5;
255725e99827Sdillo 	t[s+241] = 0x01f3;
255825e99827Sdillo 	t[s+242] = 0x01f3;
255925e99827Sdillo 
256025e99827Sdillo 	/*
256125e99827Sdillo 	 * table 3 (high byte 0x03)
256225e99827Sdillo 	 */
256325e99827Sdillo 	s = 3 * 256;
256425e99827Sdillo 	for (i = 0; i < 145; i++)
256525e99827Sdillo 		t[s+i] = i + 0x0300;
256625e99827Sdillo 	for (i = 145; i < 170; i++)
256725e99827Sdillo 		t[s+i] = i + 0x0320;
256825e99827Sdillo 	t[s+162] = 0x03a2;
256925e99827Sdillo 	for (i = 170; i < 256; i++)
257025e99827Sdillo 		t[s+i] = i + 0x0300;
257125e99827Sdillo 
257225e99827Sdillo 	for (i = 226; i < 239; i += 2)
257325e99827Sdillo 		t[s+i] = i + 0x0301;
257425e99827Sdillo 
257525e99827Sdillo 	/*
257625e99827Sdillo 	 * table 4 (high byte 0x04)
257725e99827Sdillo 	 */
257825e99827Sdillo 	s = 4 * 256;
257925e99827Sdillo 	for (i = 0; i < 16; i++)
258025e99827Sdillo 		t[s+i] = i + 0x0400;
258125e99827Sdillo 	t[s+  2] = 0x0452;
258225e99827Sdillo 	t[s+  4] = 0x0454;
258325e99827Sdillo 	t[s+  5] = 0x0455;
258425e99827Sdillo 	t[s+  6] = 0x0456;
258525e99827Sdillo 	t[s+  8] = 0x0458;
258625e99827Sdillo 	t[s+  9] = 0x0459;
258725e99827Sdillo 	t[s+ 10] = 0x045a;
258825e99827Sdillo 	t[s+ 11] = 0x045b;
258925e99827Sdillo 	t[s+ 15] = 0x045f;
259025e99827Sdillo 
259125e99827Sdillo 	for (i = 16; i < 48; i++)
259225e99827Sdillo 		t[s+i] = i + 0x0420;
259325e99827Sdillo 	t[s+ 25] = 0x0419;
259425e99827Sdillo 	for (i = 48; i < 256; i++)
259525e99827Sdillo 		t[s+i] = i + 0x0400;
259625e99827Sdillo 	t[s+195] = 0x04c4;
259725e99827Sdillo 	t[s+199] = 0x04c8;
259825e99827Sdillo 	t[s+203] = 0x04cc;
259925e99827Sdillo 
260025e99827Sdillo 	for (i = 96; i < 129; i += 2)
260125e99827Sdillo 		t[s+i] = i + 0x0401;
260225e99827Sdillo 	t[s+118] = 0x0476;
260325e99827Sdillo 	for (i = 144; i < 191; i += 2)
260425e99827Sdillo 		t[s+i] = i + 0x0401;
260525e99827Sdillo 
260625e99827Sdillo 	/*
260725e99827Sdillo 	 * table 5 (high byte 0x05)
260825e99827Sdillo 	 */
260925e99827Sdillo 	s = 5 * 256;
261025e99827Sdillo 	for (i = 0; i < 49; i++)
261125e99827Sdillo 		t[s+i] = i + 0x0500;
261225e99827Sdillo 	for (i = 49; i < 87; i++)
261325e99827Sdillo 		t[s+i] = i + 0x0530;
261425e99827Sdillo 	for (i = 87; i < 256; i++)
261525e99827Sdillo 		t[s+i] = i + 0x0500;
261625e99827Sdillo 
261725e99827Sdillo 	/*
261825e99827Sdillo 	 * table 6 (high byte 0x10)
261925e99827Sdillo 	 */
262025e99827Sdillo 	s = 6 * 256;
262125e99827Sdillo 	for (i = 0; i < 160; i++)
262225e99827Sdillo 		t[s+i] = i + 0x1000;
262325e99827Sdillo 	for (i = 160; i < 198; i++)
262425e99827Sdillo 		t[s+i] = i + 0x1030;
262525e99827Sdillo 	for (i = 198; i < 256; i++)
262625e99827Sdillo 		t[s+i] = i + 0x1000;
262725e99827Sdillo 
262825e99827Sdillo 	/*
262925e99827Sdillo 	 * table 7 (high byte 0x20)
263025e99827Sdillo 	 */
263125e99827Sdillo 	s = 7 * 256;
263225e99827Sdillo 	for (i = 0; i < 256; i++)
263325e99827Sdillo 		t[s+i] = i + 0x2000;
263425e99827Sdillo 	{
263525e99827Sdillo 		uint8_t zi[15] = { 12,  13,  14,  15,
263625e99827Sdillo 						   42,  43,  44,  45,  46,
263725e99827Sdillo 						  106, 107, 108, 109, 110, 111};
263825e99827Sdillo 
263925e99827Sdillo 		for (i = 0; i < 15; i++)
264025e99827Sdillo 			t[s+zi[i]] = 0x0000;
264125e99827Sdillo 	}
264225e99827Sdillo 
264325e99827Sdillo 	/*
264425e99827Sdillo 	 * table 8 (high byte 0x21)
264525e99827Sdillo 	 */
264625e99827Sdillo 	s = 8 * 256;
264725e99827Sdillo 	for (i = 0; i < 96; i++)
264825e99827Sdillo 		t[s+i] = i + 0x2100;
264925e99827Sdillo 	for (i = 96; i < 112; i++)
265025e99827Sdillo 		t[s+i] = i + 0x2110;
265125e99827Sdillo 	for (i = 112; i < 256; i++)
265225e99827Sdillo 		t[s+i] = i + 0x2100;
265325e99827Sdillo 
265425e99827Sdillo 	/*
265525e99827Sdillo 	 * table 9 (high byte 0xFE)
265625e99827Sdillo 	 */
265725e99827Sdillo 	s = 9 * 256;
265825e99827Sdillo 	for (i = 0; i < 256; i++)
265925e99827Sdillo 		t[s+i] = i + 0xFE00;
266025e99827Sdillo 	t[s+255] = 0x0000;
266125e99827Sdillo 
266225e99827Sdillo 	/*
266325e99827Sdillo 	 * table 10 (high byte 0xFF)
266425e99827Sdillo 	 */
266525e99827Sdillo 	s = 10 * 256;
266625e99827Sdillo 	for (i = 0; i < 33; i++)
266725e99827Sdillo 		t[s+i] = i + 0xFF00;
266825e99827Sdillo 	for (i = 33; i < 59; i++)
266925e99827Sdillo 		t[s+i] = i + 0xFF20;
267025e99827Sdillo 	for (i = 59; i < 256; i++)
267125e99827Sdillo 		t[s+i] = i + 0xFF00;
267225e99827Sdillo 
267325e99827Sdillo 	return 0;
267425e99827Sdillo 
267525e99827Sdillo error:
267625e99827Sdillo 	return 1;
267725e99827Sdillo }
267825e99827Sdillo 
267925e99827Sdillo int
hfslib_get_hardlink(hfs_volume * vol,uint32_t inode_num,hfs_catalog_keyed_record_t * rec,hfs_callback_args * cbargs)268056c3e412Sdillo hfslib_get_hardlink(hfs_volume *vol, uint32_t inode_num,
268156c3e412Sdillo 		     hfs_catalog_keyed_record_t *rec,
268256c3e412Sdillo 		     hfs_callback_args *cbargs)
268325e99827Sdillo {
268456c3e412Sdillo 	hfs_catalog_keyed_record_t metadata;
268556c3e412Sdillo 	hfs_catalog_key_t key;
268625e99827Sdillo 	char name[16];
268725e99827Sdillo 	unichar_t name_uni[16];
268825e99827Sdillo 	int i, len;
268925e99827Sdillo 
269025e99827Sdillo 	/* XXX: cache this */
269156c3e412Sdillo 	if (hfslib_find_catalog_record_with_key(vol,
269256c3e412Sdillo 						 &hfs_gMetadataDirectoryKey,
269325e99827Sdillo 						 &metadata, cbargs) != 0
269456c3e412Sdillo 		|| metadata.type != HFS_REC_FLDR)
269525e99827Sdillo 		return -1;
269625e99827Sdillo 
269725e99827Sdillo 	len = snprintf(name, sizeof(name), "iNode%d", inode_num);
269825e99827Sdillo 	for (i = 0; i < len; i++)
269925e99827Sdillo 		name_uni[i] = name[i];
270025e99827Sdillo 
270156c3e412Sdillo 	if (hfslib_make_catalog_key(metadata.folder.cnid, len, name_uni,
270225e99827Sdillo 				     &key) == 0)
270325e99827Sdillo 		return -1;
270425e99827Sdillo 
270556c3e412Sdillo 	return hfslib_find_catalog_record_with_key(vol, &key, rec, cbargs);
270625e99827Sdillo }
2707