Home | History | Annotate | Line # | Download | only in hfs
libhfs.c revision 1.5
      1 /*	$NetBSD: libhfs.c,v 1.5 2007/12/11 12:04:23 lukem Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2005, 2007 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Yevgeny Binder, Dieter Baron, and Pelle Johansson.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  *  All functions and variable types have the prefix "hfs_". All constants
     34  *  have the prefix "HFS_".
     35  *
     36  *  Naming convention for functions which read/write raw, linear data
     37  *	into/from a structured form:
     38  *
     39  *  hfs_read/write[d][a]_foo_bar
     40  *      [d] - read/write from/to [d]isk instead of a memory buffer
     41  *      [a] - [a]llocate output buffer instead of using an existing one
     42  *            (not applicable for writing functions)
     43  *
     44  *  Most functions do not have either of these options, so they will read from
     45  *	or write to a memory buffer, which has been previously allocated by the
     46  *	caller.
     47  */
     48 
     49 #include <sys/cdefs.h>
     50 __KERNEL_RCSID(0, "$NetBSD: libhfs.c,v 1.5 2007/12/11 12:04:23 lukem Exp $");
     51 
     52 #include "libhfs.h"
     53 
     54 /* global private file/folder keys */
     55 hfs_catalog_key_t hfs_gMetadataDirectoryKey; /* contains HFS+ inodes */
     56 hfs_catalog_key_t hfs_gJournalInfoBlockFileKey;
     57 hfs_catalog_key_t hfs_gJournalBufferFileKey;
     58 hfs_catalog_key_t* hfs_gPrivateObjectKeys[4] = {
     59 	&hfs_gMetadataDirectoryKey,
     60 	&hfs_gJournalInfoBlockFileKey,
     61 	&hfs_gJournalBufferFileKey,
     62 	NULL};
     63 
     64 
     65 extern uint16_t be16tohp(void** inout_ptr);
     66 extern uint32_t be32tohp(void** inout_ptr);
     67 extern uint64_t be64tohp(void** inout_ptr);
     68 
     69 int hfslib_create_casefolding_table(void);
     70 
     71 #ifdef DLO_DEBUG
     72 #include <stdio.h>
     73 void
     74 dlo_print_key(hfs_catalog_key_t *key)
     75 {
     76 	int i;
     77 
     78 	printf("%ld:[", (long)key->parent_cnid);
     79 	for (i=0; i<key->name.length; i++) {
     80 		if (key->name.unicode[i] < 256
     81 		    && isprint(key->name.unicode[i]))
     82 			putchar(key->name.unicode[i]);
     83 		else
     84 			printf("<%04x>", key->name.unicode[i]);
     85 	}
     86 	printf("]");
     87 }
     88 #endif
     89 
     90 void
     91 hfslib_init(hfs_callbacks* in_callbacks)
     92 {
     93 	unichar_t	temp[256];
     94 
     95 	if(in_callbacks!=NULL)
     96 		memcpy(&hfs_gcb, in_callbacks, sizeof(hfs_callbacks));
     97 
     98 	hfs_gcft = NULL;
     99 
    100 	/*
    101 	 * Create keys for the HFS+ "private" files so we can reuse them whenever
    102 	 * we perform a user-visible operation, such as listing directory contents.
    103 	 */
    104 
    105 #define ATOU(str, len) /* quick & dirty ascii-to-unicode conversion */ \
    106 	do{ int i; for(i=0; i<len; i++) temp[i]=str[i]; } \
    107 	while( /*CONSTCOND*/ 0)
    108 
    109 	ATOU("\0\0\0\0HFS+ Private Data", 21);
    110 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 21, temp,
    111 		&hfs_gMetadataDirectoryKey);
    112 
    113 	ATOU(".journal_info_block", 19);
    114 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 19, temp,
    115 		&hfs_gJournalInfoBlockFileKey);
    116 
    117 	ATOU(".journal", 8);
    118 	hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 8, temp,
    119 		&hfs_gJournalBufferFileKey);
    120 
    121 #undef ATOU
    122 }
    123 
    124 void
    125 hfslib_done(void)
    126 {
    127 	hfs_callback_args	cbargs;
    128 
    129 	if(hfs_gcft!=NULL) {
    130 		hfslib_init_cbargs(&cbargs);
    131 		hfslib_free(hfs_gcft, &cbargs);
    132 		hfs_gcft = NULL;
    133 	}
    134 
    135 	return;
    136 }
    137 
    138 void
    139 hfslib_init_cbargs(hfs_callback_args* ptr)
    140 {
    141 	memset(ptr, 0, sizeof(hfs_callback_args));
    142 }
    143 
    144 #if 0
    145 #pragma mark -
    146 #pragma mark High-Level Routines
    147 #endif
    148 
    149 int
    150 hfslib_open_volume(
    151 	const char* in_device,
    152 	int in_readonly,
    153 	hfs_volume* out_vol,
    154 	hfs_callback_args* cbargs)
    155 {
    156 	hfs_catalog_key_t		rootkey;
    157 	hfs_thread_record_t	rootthread;
    158 	hfs_hfs_master_directory_block_t mdb;
    159 	uint16_t	node_rec_sizes[1];
    160 	void*		node_recs[1];
    161 	void*		buffer;
    162 	void*		buffer2;	/* used as temporary pointer for realloc() */
    163 	int			result;
    164 
    165 	result = 1;
    166 	buffer = NULL;
    167 
    168 	if(in_device==NULL || out_vol==NULL)
    169 		return 1;
    170 
    171 	out_vol->readonly = in_readonly;
    172 	out_vol->offset = 0;
    173 
    174 	if(hfslib_openvoldevice(out_vol, in_device, cbargs) != 0)
    175 		HFS_LIBERR("could not open device");
    176 
    177 	/*
    178 	 *	Read the volume header.
    179 	 */
    180 	buffer = hfslib_malloc(max(sizeof(hfs_volume_header_t),
    181 		sizeof(hfs_hfs_master_directory_block_t)), cbargs);
    182 	if(buffer==NULL)
    183 		HFS_LIBERR("could not allocate volume header");
    184 	if(hfslib_readd(out_vol, buffer, max(sizeof(hfs_volume_header_t),
    185 			    sizeof(hfs_hfs_master_directory_block_t)),
    186 	       HFS_VOLUME_HEAD_RESERVE_SIZE, cbargs)!=0)
    187 		HFS_LIBERR("could not read volume header");
    188 
    189 	if (be16toh(*((uint16_t *)buffer)) == HFS_SIG_HFS) {
    190 		if (hfslib_read_master_directory_block(buffer, &mdb) == 0)
    191 			HFS_LIBERR("could not parse master directory block");
    192 		if (mdb.embedded_signature == HFS_SIG_HFSP)
    193 		{
    194 			/* XXX: is 512 always correct? */
    195 			out_vol->offset =
    196 			    mdb.first_block * 512
    197 			    + mdb.embedded_extent.start_block
    198 			    * (uint64_t)mdb.block_size;
    199 
    200 			if(hfslib_readd(out_vol, buffer,
    201 			       sizeof(hfs_volume_header_t),
    202 			       HFS_VOLUME_HEAD_RESERVE_SIZE, cbargs)!=0)
    203 				HFS_LIBERR("could not read volume header");
    204 		}
    205 		else
    206 			HFS_LIBERR("Plain HFS volumes not currently supported");
    207 	}
    208 
    209 	if(hfslib_read_volume_header(buffer, &(out_vol->vh))==0)
    210 		HFS_LIBERR("could not parse volume header");
    211 
    212 	/*
    213 	 * Check the volume signature to see if this is a legitimate HFS+ or HFSX
    214 	 * volume. If so, set the key comparison function pointers appropriately.
    215 	 */
    216 	switch(out_vol->vh.signature)
    217 	{
    218 		case HFS_SIG_HFSP:
    219 			out_vol->keycmp = hfslib_compare_catalog_keys_cf;
    220 			break;
    221 
    222 		case HFS_SIG_HFSX:
    223 			out_vol->keycmp = NULL; /* will be set below */
    224 			break;
    225 
    226 		default:
    227 			HFS_LIBERR("unrecognized volume format");
    228 	}
    229 
    230 
    231 	/*
    232 	 *	Read the catalog header.
    233 	 */
    234 	buffer2 = hfslib_realloc(buffer, 512, cbargs);
    235 	if(buffer2==NULL)
    236 		HFS_LIBERR("could not allocate catalog header node");
    237 	buffer = buffer2;
    238 
    239 	/*
    240 	  We are only interested in the node header, so read the first
    241 	  512 bytes and construct the node descriptor by hand.
    242 	*/
    243 	if(hfslib_readd(out_vol, buffer, 512,
    244 	       out_vol->vh.catalog_file.extents[0].start_block
    245 	       *(uint64_t)out_vol->vh.block_size,
    246 		cbargs) != 0)
    247 		HFS_LIBERR("could not read catalog header node");
    248 	node_recs[0] = (char *)buffer+14;
    249 	node_rec_sizes[0] = 120;
    250 	if(hfslib_read_header_node(node_recs, node_rec_sizes, 1,
    251 		&out_vol->chr, NULL, NULL)==0)
    252 		HFS_LIBERR("could not parse catalog header node");
    253 
    254 	/* If this is an HFSX volume, the catalog header specifies the type of
    255 	 * key comparison method (case-folding or binary compare) we should use. */
    256 	if(out_vol->keycmp == NULL)
    257 	{
    258 		if(out_vol->chr.keycomp_type == HFS_KEY_CASEFOLD)
    259 			out_vol->keycmp = hfslib_compare_catalog_keys_cf;
    260 		else if(out_vol->chr.keycomp_type == HFS_KEY_BINARY)
    261 			out_vol->keycmp = hfslib_compare_catalog_keys_bc;
    262 		else
    263 			HFS_LIBERR("undefined key compare method");
    264 	}
    265 
    266 	out_vol->catkeysizefieldsize
    267 	    = (out_vol->chr.attributes & HFS_BIG_KEYS_MASK) ?
    268 	    sizeof(uint16_t) : sizeof(uint8_t);
    269 
    270 	/*
    271 	 *	Read the extent overflow header.
    272 	 */
    273 	/*
    274 	  We are only interested in the node header, so read the first
    275 	  512 bytes and construct the node descriptor by hand.
    276 	  buffer is already 512 bytes long.
    277 	*/
    278 	if(hfslib_readd(out_vol, buffer, 512,
    279 	       out_vol->vh.extents_file.extents[0].start_block
    280 	       *(uint64_t)out_vol->vh.block_size,
    281 		cbargs) != 0)
    282 		HFS_LIBERR("could not read extent header node");
    283 
    284 	node_recs[0] = (char *)buffer+14;
    285 	node_rec_sizes[0] = 120;
    286 	if(hfslib_read_header_node(node_recs, node_rec_sizes, 1,
    287 		&out_vol->ehr, NULL, NULL)==0)
    288 		HFS_LIBERR("could not parse extent header node");
    289 	out_vol->extkeysizefieldsize
    290 	    = (out_vol->ehr.attributes & HFS_BIG_KEYS_MASK) ?
    291 	    sizeof(uint16_t):sizeof(uint8_t);
    292 	/*
    293 	 * Read the journal info block and journal header (if volume journaled).
    294 	 */
    295 	if(out_vol->vh.attributes & (1<<HFS_VOL_JOURNALED))
    296 	{
    297 		/* journal info block */
    298 		buffer2 = hfslib_realloc(buffer, sizeof(hfs_journal_info_t), cbargs);
    299 		if(buffer2==NULL)
    300 			HFS_LIBERR("could not allocate journal info block");
    301 		buffer = buffer2;
    302 
    303 		if(hfslib_readd(out_vol, buffer, sizeof(hfs_journal_info_t),
    304 			out_vol->vh.journal_info_block * out_vol->vh.block_size,
    305 			cbargs) != 0)
    306 			HFS_LIBERR("could not read journal info block");
    307 
    308 		if(hfslib_read_journal_info(buffer, &out_vol->jib)==0)
    309 			HFS_LIBERR("could not parse journal info block");
    310 
    311 		/* journal header */
    312 		buffer2 = hfslib_realloc(buffer, sizeof(hfs_journal_header_t),cbargs);
    313 		if(buffer2==NULL)
    314 			HFS_LIBERR("could not allocate journal header");
    315 		buffer = buffer2;
    316 
    317 		if(hfslib_readd(out_vol, buffer, sizeof(hfs_journal_header_t),
    318 			out_vol->jib.offset, cbargs) != 0)
    319 			HFS_LIBERR("could not read journal header");
    320 
    321 		if(hfslib_read_journal_header(buffer, &out_vol->jh)==0)
    322 			HFS_LIBERR("could not parse journal header");
    323 
    324 		out_vol->journaled = 1;
    325 	}
    326 	else
    327 	{
    328 		out_vol->journaled = 0;
    329 	}
    330 
    331 	/*
    332 	 * If this volume uses case-folding comparison and the folding table hasn't
    333 	 * been created yet, do that here. (We don't do this in hfslib_init()
    334 	 * because the table is large and we might never even need to use it.)
    335 	 */
    336 	if(out_vol->keycmp==hfslib_compare_catalog_keys_cf && hfs_gcft==NULL)
    337 		result = hfslib_create_casefolding_table();
    338 	else
    339 		result = 0;
    340 
    341 	/*
    342 	 * Find and store the volume name.
    343 	 */
    344 	if(hfslib_make_catalog_key(HFS_CNID_ROOT_FOLDER, 0, NULL, &rootkey)==0)
    345 		HFS_LIBERR("could not make root search key");
    346 
    347 	if(hfslib_find_catalog_record_with_key(out_vol, &rootkey,
    348 		(hfs_catalog_keyed_record_t*)&rootthread, cbargs)!=0)
    349 		HFS_LIBERR("could not find root parent");
    350 
    351 	memcpy(&out_vol->name, &rootthread.name, sizeof(hfs_unistr255_t));
    352 
    353 
    354 	/* FALLTHROUGH */
    355 error:
    356 	if(buffer!=NULL)
    357 		hfslib_free(buffer, cbargs);
    358 
    359 	return result;
    360 }
    361 
    362 void
    363 hfslib_close_volume(hfs_volume* in_vol, hfs_callback_args* cbargs)
    364 {
    365 	if(in_vol==NULL)
    366 		return;
    367 
    368 	hfslib_closevoldevice(in_vol, cbargs);
    369 }
    370 
    371 int
    372 hfslib_path_to_cnid(hfs_volume* in_vol,
    373 	hfs_cnid_t in_cnid,
    374 	char** out_unicode,
    375 	uint16_t* out_length,
    376 	hfs_callback_args* cbargs)
    377 {
    378 	hfs_thread_record_t	parent_thread;
    379 	hfs_cnid_t	parent_cnid, child_cnid;
    380 	char*		newpath;
    381 	char*		path;
    382 	int			path_offset = 0;
    383 	int			result;
    384 	uint16_t*	ptr;	/* dummy var */
    385 	uint16_t	uchar;	/* dummy var */
    386 	uint16_t	total_path_length;
    387 
    388 	if(in_vol==NULL || in_cnid==0 || out_unicode==NULL || out_length==NULL)
    389 		return 1;
    390 
    391 	result = 1;
    392 	*out_unicode = NULL;
    393 	*out_length = 0;
    394 	path = NULL;
    395 	total_path_length = 0;
    396 
    397 	path = hfslib_malloc(514, cbargs); /* 256 unichars plus a forward slash */
    398 	if(path==NULL)
    399 		return 1;
    400 
    401 	child_cnid = in_cnid;
    402 	parent_cnid = child_cnid; /* skips loop in case in_cnid is root id */
    403 	while(parent_cnid != HFS_CNID_ROOT_FOLDER
    404 		&& parent_cnid != HFS_CNID_ROOT_PARENT)
    405 	{
    406 		if(child_cnid!=in_cnid)
    407 		{
    408 			newpath = hfslib_realloc(path, 514 + total_path_length*2, cbargs);
    409 
    410 			if(newpath==NULL)
    411 				goto exit;
    412 			path = newpath;
    413 
    414 			memmove(path + 514, path + path_offset, total_path_length*2);
    415 		}
    416 
    417 		parent_cnid = hfslib_find_parent_thread(in_vol, child_cnid,
    418 			&parent_thread, cbargs);
    419 		if(parent_cnid==0)
    420 			goto exit;
    421 
    422 		path_offset = 512 - parent_thread.name.length*2;
    423 
    424 		memcpy(path + path_offset, parent_thread.name.unicode,
    425 			parent_thread.name.length*2);
    426 
    427 		/*	Add a forward slash. The unicode string was specified in big endian
    428 		 *	format, so convert to core format if necessary. */
    429 		path[512]=0x00;
    430 		path[513]=0x2F;
    431 
    432 		ptr = (uint16_t*)path + 256;
    433 		uchar = be16tohp((void*)&ptr);
    434 		*(ptr-1) = uchar;
    435 
    436 		total_path_length += parent_thread.name.length + 1;
    437 
    438 		child_cnid = parent_cnid;
    439 	}
    440 
    441 	/*
    442 	 *	At this point, 'path' holds a sequence of unicode characters which
    443 	 *	represent the absolute path to the given cnid. This string is missing
    444 	 *	a terminating null char and an initial forward slash that represents
    445 	 *	the root of the filesystem. It most likely also has extra space in
    446 	 *	the beginning, due to the fact that we reserve 512 bytes for each path
    447 	 *	component and won't usually use all that space. So, we allocate the
    448 	 *	final string based on the actual length of the absolute path, plus four
    449 	 *	additional bytes (two unichars) for the forward slash and the null char.
    450 	 */
    451 
    452 	*out_unicode = hfslib_malloc((total_path_length+2)*2, cbargs);
    453 	if(*out_unicode == NULL)
    454 		goto exit;
    455 
    456 	/* copy only the bytes that are actually used */
    457 	memcpy(*out_unicode+2, path + path_offset, total_path_length*2);
    458 
    459 	/* insert forward slash at start */
    460 	(*out_unicode)[0] = 0x00;
    461 	(*out_unicode)[1] = 0x2F;
    462 	ptr = (uint16_t*)*out_unicode;
    463 	uchar = be16tohp((void*)&ptr);
    464 	*(ptr-1) = uchar;
    465 
    466 	/* insert null char at end */
    467 	(*out_unicode)[total_path_length*2+2] = 0x00;
    468 	(*out_unicode)[total_path_length*2+3] = 0x00;
    469 
    470 	*out_length = total_path_length + 1 /* extra for forward slash */ ;
    471 
    472 	result = 0;
    473 
    474 exit:
    475 	if(path!=NULL)
    476 		hfslib_free(path, cbargs);
    477 
    478 	return result;
    479 }
    480 
    481 hfs_cnid_t
    482 hfslib_find_parent_thread(
    483 	hfs_volume* in_vol,
    484 	hfs_cnid_t in_child,
    485 	hfs_thread_record_t* out_thread,
    486 	hfs_callback_args* cbargs)
    487 {
    488 	hfs_catalog_key_t	childkey;
    489 
    490 	if(in_vol==NULL || in_child==0 || out_thread==NULL)
    491 		return 0;
    492 
    493 	if(hfslib_make_catalog_key(in_child, 0, NULL, &childkey)==0)
    494 		return 0;
    495 
    496 	if(hfslib_find_catalog_record_with_key(in_vol, &childkey,
    497 		(hfs_catalog_keyed_record_t*)out_thread, cbargs)!=0)
    498 		return 0;
    499 
    500 	return out_thread->parent_cnid;
    501 }
    502 
    503 /*
    504  * hfslib_find_catalog_record_with_cnid()
    505  *
    506  * Looks up a catalog record by calling hfslib_find_parent_thread() and
    507  * hfslib_find_catalog_record_with_key(). out_key may be NULL; if not, the key
    508  * corresponding to this cnid is stuffed in it. Returns 0 on success.
    509  */
    510 int
    511 hfslib_find_catalog_record_with_cnid(
    512 	hfs_volume* in_vol,
    513 	hfs_cnid_t in_cnid,
    514 	hfs_catalog_keyed_record_t* out_rec,
    515 	hfs_catalog_key_t* out_key,
    516 	hfs_callback_args* cbargs)
    517 {
    518 	hfs_cnid_t					parentcnid;
    519 	hfs_thread_record_t		parentthread;
    520 	hfs_catalog_key_t			key;
    521 
    522 	if(in_vol==NULL || in_cnid==0 || out_rec==NULL)
    523 		return 0;
    524 
    525 	parentcnid =
    526 		hfslib_find_parent_thread(in_vol, in_cnid, &parentthread, cbargs);
    527 	if(parentcnid == 0)
    528 		HFS_LIBERR("could not find parent thread for cnid %i", in_cnid);
    529 
    530 	if(hfslib_make_catalog_key(parentthread.parent_cnid,
    531 		parentthread.name.length, parentthread.name.unicode, &key) == 0)
    532 		HFS_LIBERR("could not make catalog search key");
    533 
    534 	if(out_key!=NULL)
    535 		memcpy(out_key, &key, sizeof(key));
    536 
    537 	return hfslib_find_catalog_record_with_key(in_vol, &key, out_rec, cbargs);
    538 
    539 error:
    540 	return 1;
    541 }
    542 
    543 /* Returns 0 on success, 1 on error, and -1 if record was not found. */
    544 int
    545 hfslib_find_catalog_record_with_key(
    546 	hfs_volume* in_vol,
    547 	hfs_catalog_key_t* in_key,
    548 	hfs_catalog_keyed_record_t* out_rec,
    549 	hfs_callback_args* cbargs)
    550 {
    551 	hfs_node_descriptor_t			nd;
    552 	hfs_extent_descriptor_t*		extents;
    553 	hfs_catalog_keyed_record_t		lastrec;
    554 	hfs_catalog_key_t*	curkey;
    555 	void**				recs;
    556 	void*				buffer;
    557 	uint64_t			bytesread;
    558 	uint32_t			curnode;
    559 	uint16_t*			recsizes;
    560 	uint16_t			numextents;
    561 	uint16_t			recnum;
    562 	int16_t				leaftype;
    563 	int					keycompare;
    564 	int					result;
    565 
    566 	if(in_key==NULL || out_rec==NULL || in_vol==NULL)
    567 		return 1;
    568 
    569 	result = 1;
    570 	buffer = NULL;
    571 	curkey = NULL;
    572 	extents = NULL;
    573 	recs = NULL;
    574 	recsizes = NULL;
    575 
    576 	/* The key takes up over half a kb of ram, which is a lot for the BSD
    577 	 * kernel stack. So allocate it in the heap instead to play it safe. */
    578 	curkey = hfslib_malloc(sizeof(hfs_catalog_key_t), cbargs);
    579 	if(curkey==NULL)
    580 		HFS_LIBERR("could not allocate catalog search key");
    581 
    582 	buffer = hfslib_malloc(in_vol->chr.node_size, cbargs);
    583 	if(buffer==NULL)
    584 		HFS_LIBERR("could not allocate node buffer");
    585 
    586 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_CATALOG,
    587 		HFS_DATAFORK, &extents, cbargs);
    588 	if(numextents==0)
    589 		HFS_LIBERR("could not locate fork extents");
    590 
    591 	nd.num_recs = 0;
    592 	curnode = in_vol->chr.root_node;
    593 
    594 #ifdef DLO_DEBUG
    595 	printf("-> key ");
    596 	dlo_print_key(in_key);
    597 	printf("\n");
    598 #endif
    599 
    600 	do
    601 	{
    602 #ifdef DLO_DEBUG
    603 		printf("--> node %d\n", curnode);
    604 #endif
    605 
    606 		if(hfslib_readd_with_extents(in_vol, buffer,
    607 			&bytesread,in_vol->chr.node_size, curnode * in_vol->chr.node_size,
    608 			extents, numextents, cbargs)!=0)
    609 			HFS_LIBERR("could not read catalog node #%i", curnode);
    610 
    611 		if(hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_CATALOG_FILE,
    612 			in_vol, cbargs)==0)
    613 			HFS_LIBERR("could not parse catalog node #%i", curnode);
    614 
    615 		for(recnum=0; recnum<nd.num_recs; recnum++)
    616 		{
    617 			leaftype = nd.kind;
    618 			if(hfslib_read_catalog_keyed_record(recs[recnum], out_rec,
    619 				&leaftype, curkey, in_vol)==0)
    620 				HFS_LIBERR("could not read catalog record #%i",recnum);
    621 
    622 #ifdef DLO_DEBUG
    623 			printf("---> record %d: ", recnum);
    624 			dlo_print_key(curkey);
    625 			fflush(stdout);
    626 #endif
    627 			keycompare = in_vol->keycmp(in_key, curkey);
    628 #ifdef DLO_DEBUG
    629 			printf(" %c\n",
    630 			       keycompare < 0 ? '<'
    631 			       : keycompare == 0 ? '=' : '>');
    632 #endif
    633 
    634 			if(keycompare < 0)
    635 			{
    636 				/* Check if key is less than *every* record, which should never
    637 				 * happen if the volume is consistent and the key legit. */
    638 				if(recnum==0)
    639 					HFS_LIBERR("all records greater than key");
    640 
    641 				/* Otherwise, we've found the first record that exceeds our key,
    642 				 * so retrieve the previous record, which is still less... */
    643 				memcpy(out_rec, &lastrec,
    644 					sizeof(hfs_catalog_keyed_record_t));
    645 
    646 				/* ...unless this is a leaf node, which means we've gone from
    647 				 * a key which is smaller than the search key, in the previous
    648 				 * loop, to a key which is larger, in this loop, and that
    649 				 * implies that our search key does not exist on the volume. */
    650 				if(nd.kind==HFS_LEAFNODE)
    651 					result = -1;
    652 
    653 				break;
    654 			}
    655 			else if(keycompare == 0)
    656 			{
    657 				/* If leaf node, found an exact match. */
    658 				result = 0;
    659 				break;
    660 			}
    661 			else if(recnum==nd.num_recs-1 && keycompare > 0)
    662 			{
    663 				/* If leaf node, we've reached the last record with no match,
    664 				 * which means this key is not present on the volume. */
    665 				result = -1;
    666 				break;
    667 			}
    668 
    669 			memcpy(&lastrec, out_rec, sizeof(hfs_catalog_keyed_record_t));
    670 		}
    671 
    672 		if(nd.kind==HFS_INDEXNODE)
    673 			curnode = out_rec->child;
    674 		else if(nd.kind==HFS_LEAFNODE)
    675 			break;
    676 
    677 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
    678 	}
    679 	while(nd.kind!=HFS_LEAFNODE);
    680 
    681 	/* FALLTHROUGH */
    682 error:
    683 	if(extents!=NULL)
    684 		hfslib_free(extents, cbargs);
    685 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
    686 	if(curkey!=NULL)
    687 		hfslib_free(curkey, cbargs);
    688 	if(buffer!=NULL)
    689 		hfslib_free(buffer, cbargs);
    690 
    691 	return result;
    692 }
    693 
    694 /* returns 0 on success */
    695 /* XXX Need to look this over and make sure it gracefully handles cases where
    696  * XXX the key is not found. */
    697 int
    698 hfslib_find_extent_record_with_key(hfs_volume* in_vol,
    699 	hfs_extent_key_t* in_key,
    700 	hfs_extent_record_t* out_rec,
    701 	hfs_callback_args* cbargs)
    702 {
    703 	hfs_node_descriptor_t		nd;
    704 	hfs_extent_descriptor_t*	extents;
    705 	hfs_extent_record_t		lastrec;
    706 	hfs_extent_key_t	curkey;
    707 	void**				recs;
    708 	void*				buffer;
    709 	uint64_t			bytesread;
    710 	uint32_t			curnode;
    711 	uint16_t*			recsizes;
    712 	uint16_t			numextents;
    713 	uint16_t			recnum;
    714 	int					keycompare;
    715 	int					result;
    716 
    717 	if(in_vol==NULL || in_key==NULL || out_rec==NULL)
    718 		return 1;
    719 
    720 	result = 1;
    721 	buffer = NULL;
    722 	extents = NULL;
    723 	recs = NULL;
    724 	recsizes = NULL;
    725 
    726 	buffer = hfslib_malloc(in_vol->ehr.node_size, cbargs);
    727 	if(buffer==NULL)
    728 		HFS_LIBERR("could not allocate node buffer");
    729 
    730 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_EXTENTS,
    731 		HFS_DATAFORK, &extents, cbargs);
    732 	if(numextents==0)
    733 		HFS_LIBERR("could not locate fork extents");
    734 
    735 	nd.num_recs = 0;
    736 	curnode = in_vol->ehr.root_node;
    737 
    738 	do
    739 	{
    740 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
    741 		recnum = 0;
    742 
    743 		if(hfslib_readd_with_extents(in_vol, buffer, &bytesread,
    744 			in_vol->ehr.node_size, curnode * in_vol->ehr.node_size, extents,
    745 			numextents, cbargs)!=0)
    746 			HFS_LIBERR("could not read extents overflow node #%i", curnode);
    747 
    748 		if(hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_EXTENTS_FILE,
    749 			in_vol, cbargs)==0)
    750 			HFS_LIBERR("could not parse extents overflow node #%i",curnode);
    751 
    752 		for(recnum=0; recnum<nd.num_recs; recnum++)
    753 		{
    754 			memcpy(&lastrec, out_rec, sizeof(hfs_extent_record_t));
    755 
    756 			if(hfslib_read_extent_record(recs[recnum], out_rec, nd.kind,
    757 				&curkey, in_vol)==0)
    758 				HFS_LIBERR("could not read extents record #%i",recnum);
    759 
    760 			keycompare = hfslib_compare_extent_keys(in_key, &curkey);
    761 			if(keycompare < 0)
    762 			{
    763 				/* this should never happen for any legitimate key */
    764 				if(recnum==0)
    765 					return 1;
    766 
    767 				memcpy(out_rec, &lastrec, sizeof(hfs_extent_record_t));
    768 
    769 				break;
    770 			}
    771 			else if(keycompare == 0 ||
    772 				(recnum==nd.num_recs-1 && keycompare > 0))
    773 				break;
    774 		}
    775 
    776 		if(nd.kind==HFS_INDEXNODE)
    777 			curnode = *((uint32_t *)out_rec); /* out_rec is a node ptr in this case */
    778 		else if(nd.kind==HFS_LEAFNODE)
    779 			break;
    780 		else
    781 		    HFS_LIBERR("unknwon node type for extents overflow node #%i",curnode);
    782 	}
    783 	while(nd.kind!=HFS_LEAFNODE);
    784 
    785 	result = 0;
    786 
    787 	/* FALLTHROUGH */
    788 
    789 error:
    790 	if(buffer!=NULL)
    791 		hfslib_free(buffer, cbargs);
    792 	if(extents!=NULL)
    793 		hfslib_free(extents, cbargs);
    794 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
    795 
    796 	return result;
    797 }
    798 
    799 /* out_extents may be NULL. */
    800 uint16_t
    801 hfslib_get_file_extents(hfs_volume* in_vol,
    802 	hfs_cnid_t in_cnid,
    803 	uint8_t in_forktype,
    804 	hfs_extent_descriptor_t** out_extents,
    805 	hfs_callback_args* cbargs)
    806 {
    807 	hfs_extent_descriptor_t*	dummy;
    808 	hfs_extent_key_t		extentkey;
    809 	hfs_file_record_t		file;
    810 	hfs_catalog_key_t		filekey;
    811 	hfs_thread_record_t	fileparent;
    812 	hfs_fork_t				fork;
    813 	hfs_extent_record_t	nextextentrec;
    814 	uint32_t	numblocks;
    815 	uint16_t	numextents, n;
    816 
    817 	if(in_vol==NULL || in_cnid==0)
    818 		return 0;
    819 
    820 	if(out_extents!=NULL)
    821 	{
    822 		*out_extents = hfslib_malloc(sizeof(hfs_extent_descriptor_t), cbargs);
    823 		if(*out_extents==NULL)
    824 			return 0;
    825 	}
    826 
    827 	switch(in_cnid)
    828 	{
    829 		case HFS_CNID_CATALOG:
    830 			fork = in_vol->vh.catalog_file;
    831 			break;
    832 
    833 		case HFS_CNID_EXTENTS:
    834 			fork = in_vol->vh.extents_file;
    835 			break;
    836 
    837 		case HFS_CNID_ALLOCATION:
    838 			fork = in_vol->vh.allocation_file;
    839 			break;
    840 
    841 		case HFS_CNID_ATTRIBUTES:
    842 			fork = in_vol->vh.attributes_file;
    843 			break;
    844 
    845 		case HFS_CNID_STARTUP:
    846 			fork = in_vol->vh.startup_file;
    847 			break;
    848 
    849 		default:
    850 			if(hfslib_find_parent_thread(in_vol, in_cnid, &fileparent,
    851 				cbargs)==0)
    852 				goto error;
    853 
    854 			if(hfslib_make_catalog_key(fileparent.parent_cnid,
    855 				fileparent.name.length, fileparent.name.unicode, &filekey)==0)
    856 				goto error;
    857 
    858 			if(hfslib_find_catalog_record_with_key(in_vol, &filekey,
    859 				(hfs_catalog_keyed_record_t*)&file, cbargs)!=0)
    860 				goto error;
    861 
    862 			/* only files have extents, not folders or threads */
    863 			if(file.rec_type!=HFS_REC_FILE)
    864 				goto error;
    865 
    866 			if(in_forktype==HFS_DATAFORK)
    867 				fork = file.data_fork;
    868 			else if(in_forktype==HFS_RSRCFORK)
    869 				fork = file.rsrc_fork;
    870 	}
    871 
    872 	numextents = 0;
    873 	numblocks = 0;
    874 	memcpy(&nextextentrec, &fork.extents, sizeof(hfs_extent_record_t));
    875 
    876 	while(1)
    877 	{
    878 		for(n=0; n<8; n++)
    879 		{
    880 			if(nextextentrec[n].block_count==0)
    881 				break;
    882 
    883 			numblocks += nextextentrec[n].block_count;
    884 		}
    885 
    886 		if(out_extents!=NULL)
    887 		{
    888 			dummy = hfslib_realloc(*out_extents,
    889 			    (numextents+n) * sizeof(hfs_extent_descriptor_t),
    890 			    cbargs);
    891 			if(dummy==NULL)
    892 				goto error;
    893 			*out_extents = dummy;
    894 
    895 			memcpy(*out_extents + numextents,
    896 			    &nextextentrec, n*sizeof(hfs_extent_descriptor_t));
    897 		}
    898 		numextents += n;
    899 
    900 		if(numblocks >= fork.total_blocks)
    901 			break;
    902 
    903 		if(hfslib_make_extent_key(in_cnid, in_forktype, numblocks,
    904 			&extentkey)==0)
    905 			goto error;
    906 
    907 		if(hfslib_find_extent_record_with_key(in_vol, &extentkey,
    908 			&nextextentrec, cbargs)!=0)
    909 			goto error;
    910 	}
    911 
    912 	goto exit;
    913 
    914 error:
    915 	if(out_extents!=NULL && *out_extents!=NULL)
    916 	{
    917 		hfslib_free(*out_extents, cbargs);
    918 		*out_extents = NULL;
    919 	}
    920 	return 0;
    921 
    922 exit:
    923 	return numextents;
    924 }
    925 
    926 /*
    927  * hfslib_get_directory_contents()
    928  *
    929  * Finds the immediate children of a given directory CNID and places their
    930  * CNIDs in an array allocated here. The first child is found by doing a
    931  * catalog search that only compares parent CNIDs (ignoring file/folder names)
    932  * and skips over thread records. Then the remaining children are listed in
    933  * ascending order by name, according to the HFS+ spec, so just read off each
    934  * successive leaf node until a different parent CNID is found.
    935  *
    936  * If out_childnames is not NULL, it will be allocated and set to an array of
    937  * hfs_unistr255_t's which correspond to the name of the child with that same
    938  * index.
    939  *
    940  * out_children may be NULL.
    941  *
    942  * Returns 0 on success.
    943  */
    944 int
    945 hfslib_get_directory_contents(
    946 	hfs_volume* in_vol,
    947 	hfs_cnid_t in_dir,
    948 	hfs_catalog_keyed_record_t** out_children,
    949 	hfs_unistr255_t** out_childnames,
    950 	uint32_t* out_numchildren,
    951 	hfs_callback_args* cbargs)
    952 {
    953 	hfs_node_descriptor_t			nd;
    954 	hfs_extent_descriptor_t*		extents;
    955 	hfs_catalog_keyed_record_t		currec;
    956 	hfs_catalog_key_t	curkey;
    957 	void**				recs;
    958 	void*				buffer;
    959 	void*				ptr; /* temporary pointer for realloc() */
    960 	uint64_t			bytesread;
    961 	uint32_t			curnode;
    962 	uint32_t			lastnode;
    963 	uint16_t*			recsizes;
    964 	uint16_t			numextents;
    965 	uint16_t			recnum;
    966 	int16_t				leaftype;
    967 	int					keycompare;
    968 	int					result;
    969 
    970 	if(in_vol==NULL || in_dir==0 || out_numchildren==NULL)
    971 		return 1;
    972 
    973 	result = 1;
    974 	buffer = NULL;
    975 	extents = NULL;
    976 	lastnode = 0;
    977 	recs = NULL;
    978 	recsizes = NULL;
    979 	*out_numchildren = 0;
    980 	if(out_children!=NULL)
    981 		*out_children = NULL;
    982 	if(out_childnames!=NULL)
    983 		*out_childnames = NULL;
    984 
    985 	buffer = hfslib_malloc(in_vol->chr.node_size, cbargs);
    986 	if(buffer==NULL)
    987 		HFS_LIBERR("could not allocate node buffer");
    988 
    989 	numextents = hfslib_get_file_extents(in_vol, HFS_CNID_CATALOG,
    990 		HFS_DATAFORK, &extents, cbargs);
    991 	if(numextents==0)
    992 		HFS_LIBERR("could not locate fork extents");
    993 
    994 	nd.num_recs = 0;
    995 	curnode = in_vol->chr.root_node;
    996 
    997 	while(1)
    998 	{
    999 		hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
   1000 		recnum = 0;
   1001 
   1002 		if(hfslib_readd_with_extents(in_vol, buffer, &bytesread,
   1003 			in_vol->chr.node_size, curnode * in_vol->chr.node_size, extents,
   1004 			numextents, cbargs)!=0)
   1005 			HFS_LIBERR("could not read catalog node #%i", curnode);
   1006 
   1007 		if(hfslib_reada_node(buffer, &nd, &recs, &recsizes, HFS_CATALOG_FILE,
   1008 			in_vol, cbargs)==0)
   1009 			HFS_LIBERR("could not parse catalog node #%i", curnode);
   1010 
   1011 		for(recnum=0; recnum<nd.num_recs; recnum++)
   1012 		{
   1013 			leaftype = nd.kind; /* needed b/c leaftype might be modified now */
   1014 			if(hfslib_read_catalog_keyed_record(recs[recnum], &currec,
   1015 				&leaftype, &curkey, in_vol)==0)
   1016 				HFS_LIBERR("could not read cat record %i:%i", curnode, recnum);
   1017 
   1018 			if(nd.kind==HFS_INDEXNODE)
   1019 			{
   1020 				keycompare = in_dir - curkey.parent_cnid;
   1021 				if(keycompare < 0)
   1022 				{
   1023 					/* Check if key is less than *every* record, which should
   1024 					 * never happen if the volume and key are good. */
   1025 					if(recnum==0)
   1026 						HFS_LIBERR("all records greater than key");
   1027 
   1028 					/* Otherwise, we've found the first record that exceeds our
   1029 					 * key, so retrieve the previous, lesser record. */
   1030 					curnode = lastnode;
   1031 					break;
   1032 				}
   1033 				else if(keycompare == 0)
   1034 				{
   1035 					/*
   1036 					 * Normally, if we were doing a typical catalog lookup with
   1037 					 * both a parent cnid AND a name, keycompare==0 would be an
   1038 					 * exact match. However, since we are ignoring object names
   1039 					 * in this case and only comparing parent cnids, a direct
   1040 					 * match on only a parent cnid could mean that we've found
   1041 					 * an object with that parent cnid BUT which is NOT the
   1042 					 * first object (according to the HFS+ spec) with that
   1043 					 * parent cnid. Thus, when we find a parent cnid match, we
   1044 					 * still go back to the previously found leaf node and start
   1045 					 * checking it for a possible prior instance of an object
   1046 					 * with our desired parent cnid.
   1047 					 */
   1048 					curnode = lastnode;
   1049 					break;
   1050 				}
   1051 				else if (recnum==nd.num_recs-1 && keycompare > 0)
   1052 				{
   1053 					/* Descend to child node if we found an exact match, or if
   1054 					 * this is the last pointer record. */
   1055 					curnode = currec.child;
   1056 					break;
   1057 				}
   1058 
   1059 				lastnode = currec.child;
   1060 			}
   1061 			else
   1062 			{
   1063 				/*
   1064 				 * We have now descended down the hierarchy of index nodes into
   1065 				 * the leaf node that contains the first catalog record with a
   1066 				 * matching parent CNID. Since all leaf nodes are chained
   1067 				 * through their flink/blink, we can simply walk forward through
   1068 				 * this chain, copying every matching non-thread record, until
   1069 				 * we hit a record with a different parent CNID. At that point,
   1070 				 * we've retrieved all of our directory's items, if any.
   1071 				 */
   1072 				curnode = nd.flink;
   1073 
   1074 				if(curkey.parent_cnid<in_dir)
   1075 					continue;
   1076 				else if(curkey.parent_cnid==in_dir)
   1077 				{
   1078 					/* Hide files/folders which are supposed to be invisible
   1079 					 * to users, according to the hfs+ spec. */
   1080 					if(hfslib_is_private_file(&curkey))
   1081 						continue;
   1082 
   1083 					/* leaftype has now been set to the catalog record type */
   1084 					if(leaftype==HFS_REC_FLDR || leaftype==HFS_REC_FILE)
   1085 					{
   1086 						(*out_numchildren)++;
   1087 
   1088 						if(out_children!=NULL)
   1089 						{
   1090 							ptr = hfslib_realloc(*out_children,
   1091 								*out_numchildren *
   1092 								sizeof(hfs_catalog_keyed_record_t), cbargs);
   1093 							if(ptr==NULL)
   1094 								HFS_LIBERR("could not allocate child record");
   1095 							*out_children = ptr;
   1096 
   1097 							memcpy(&((*out_children)[*out_numchildren-1]),
   1098 								&currec, sizeof(hfs_catalog_keyed_record_t));
   1099 						}
   1100 
   1101 						if(out_childnames!=NULL)
   1102 						{
   1103 							ptr = hfslib_realloc(*out_childnames,
   1104 								*out_numchildren * sizeof(hfs_unistr255_t),
   1105 								cbargs);
   1106 							if(ptr==NULL)
   1107 								HFS_LIBERR("could not allocate child name");
   1108 							*out_childnames = ptr;
   1109 
   1110 							memcpy(&((*out_childnames)[*out_numchildren-1]),
   1111 								&curkey.name, sizeof(hfs_unistr255_t));
   1112 						}
   1113 					}
   1114 				} else {
   1115 					result = 0;
   1116 					/* We have just now passed the last item in the desired
   1117 					 * folder (or the folder was empty), so exit. */
   1118 					goto exit;
   1119 				}
   1120 			}
   1121 		}
   1122 	}
   1123 
   1124 	result = 0;
   1125 
   1126 	goto exit;
   1127 
   1128 error:
   1129 	if(out_children!=NULL && *out_children!=NULL)
   1130 		hfslib_free(*out_children, cbargs);
   1131 	if(out_childnames!=NULL && *out_childnames!=NULL)
   1132 		hfslib_free(*out_childnames, cbargs);
   1133 
   1134 	/* FALLTHROUGH */
   1135 
   1136 exit:
   1137 	if(extents!=NULL)
   1138 		hfslib_free(extents, cbargs);
   1139 	hfslib_free_recs(&recs, &recsizes, &nd.num_recs, cbargs);
   1140 	if(buffer!=NULL)
   1141 		hfslib_free(buffer, cbargs);
   1142 
   1143 	return result;
   1144 }
   1145 
   1146 int
   1147 hfslib_is_journal_clean(hfs_volume* in_vol)
   1148 {
   1149 	if(in_vol==NULL)
   1150 		return 0;
   1151 
   1152 	/* return true if no journal */
   1153 	if(!(in_vol->vh.attributes & (1<<HFS_VOL_JOURNALED)))
   1154 		return 1;
   1155 
   1156 	return (in_vol->jh.start == in_vol->jh.end);
   1157 }
   1158 
   1159 /*
   1160  * hfslib_is_private_file()
   1161  *
   1162  * Given a file/folder's key and parent CNID, determines if it should be hidden
   1163  * from the user (e.g., the journal header file or the HFS+ Private Data folder)
   1164  */
   1165 int
   1166 hfslib_is_private_file(hfs_catalog_key_t *filekey)
   1167 {
   1168 	hfs_catalog_key_t* curkey = NULL;
   1169 	int i = 0;
   1170 
   1171 	/*
   1172 	 * According to the HFS+ spec to date, all special objects are located in
   1173 	 * the root directory of the volume, so don't bother going further if the
   1174 	 * requested object is not.
   1175 	 */
   1176 	if(filekey->parent_cnid != HFS_CNID_ROOT_FOLDER)
   1177 		return 0;
   1178 
   1179 	while((curkey = hfs_gPrivateObjectKeys[i]) != NULL)
   1180 	{
   1181 		/* XXX Always use binary compare here, or use volume's specific key
   1182 		 * XXX comparison routine? */
   1183 		if(filekey->name.length == curkey->name.length
   1184 			&& memcmp(filekey->name.unicode, curkey->name.unicode,
   1185 				2 * curkey->name.length)==0)
   1186 			return 1;
   1187 
   1188 		i++;
   1189 	}
   1190 
   1191 	return 0;
   1192 }
   1193 
   1194 
   1195 /* bool
   1196 hfslib_is_journal_valid(hfs_volume* in_vol)
   1197 {
   1198 	- check magic numbers
   1199 	- check Other Things
   1200 }*/
   1201 
   1202 #if 0
   1203 #pragma mark -
   1204 #pragma mark Major Structures
   1205 #endif
   1206 
   1207 /*
   1208  *	hfslib_read_volume_header()
   1209  *
   1210  *	Reads in_bytes, formats the data appropriately, and places the result
   1211  *	in out_header, which is assumed to be previously allocated. Returns number
   1212  *	of bytes read, 0 if failed.
   1213  */
   1214 
   1215 size_t
   1216 hfslib_read_volume_header(void* in_bytes, hfs_volume_header_t* out_header)
   1217 {
   1218 	void*	ptr;
   1219 	size_t	last_bytes_read;
   1220 	int		i;
   1221 
   1222 	if(in_bytes==NULL || out_header==NULL)
   1223 		return 0;
   1224 
   1225 	ptr = in_bytes;
   1226 
   1227 	out_header->signature = be16tohp(&ptr);
   1228 	out_header->version = be16tohp(&ptr);
   1229 	out_header->attributes = be32tohp(&ptr);
   1230 	out_header->last_mounting_version = be32tohp(&ptr);
   1231 	out_header->journal_info_block = be32tohp(&ptr);
   1232 
   1233 	out_header->date_created = be32tohp(&ptr);
   1234 	out_header->date_modified = be32tohp(&ptr);
   1235 	out_header->date_backedup = be32tohp(&ptr);
   1236 	out_header->date_checked = be32tohp(&ptr);
   1237 
   1238 	out_header->file_count = be32tohp(&ptr);
   1239 	out_header->folder_count = be32tohp(&ptr);
   1240 
   1241 	out_header->block_size = be32tohp(&ptr);
   1242 	out_header->total_blocks = be32tohp(&ptr);
   1243 	out_header->free_blocks = be32tohp(&ptr);
   1244 	out_header->next_alloc_block = be32tohp(&ptr);
   1245 	out_header->rsrc_clump_size = be32tohp(&ptr);
   1246 	out_header->data_clump_size = be32tohp(&ptr);
   1247 	out_header->next_cnid = be32tohp(&ptr);
   1248 
   1249 	out_header->write_count = be32tohp(&ptr);
   1250 	out_header->encodings = be64tohp(&ptr);
   1251 
   1252 	for(i=0;i<8;i++)
   1253 		out_header->finder_info[i] = be32tohp(&ptr);
   1254 
   1255 	if((last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1256 		&out_header->allocation_file))==0)
   1257 		return 0;
   1258 	ptr = (uint8_t*)ptr + last_bytes_read;
   1259 
   1260 	if((last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1261 		&out_header->extents_file))==0)
   1262 		return 0;
   1263 	ptr = (uint8_t*)ptr + last_bytes_read;
   1264 
   1265 	if((last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1266 		&out_header->catalog_file))==0)
   1267 		return 0;
   1268 	ptr = (uint8_t*)ptr + last_bytes_read;
   1269 
   1270 	if((last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1271 		&out_header->attributes_file))==0)
   1272 		return 0;
   1273 	ptr = (uint8_t*)ptr + last_bytes_read;
   1274 
   1275 	if((last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1276 		&out_header->startup_file))==0)
   1277 		return 0;
   1278 	ptr = (uint8_t*)ptr + last_bytes_read;
   1279 
   1280 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1281 }
   1282 
   1283 /*
   1284  *      hfsplib_read_master_directory_block()
   1285  *
   1286  *      Reads in_bytes, formats the data appropriately, and places the result
   1287  *      in out_header, which is assumed to be previously allocated. Returns numb
   1288 er
   1289  *      of bytes read, 0 if failed.
   1290  */
   1291 
   1292 size_t
   1293 hfslib_read_master_directory_block(void* in_bytes,
   1294     hfs_hfs_master_directory_block_t* out_mdr)
   1295 {
   1296         void*   ptr;
   1297         int     i;
   1298 
   1299         if(in_bytes==NULL || out_mdr==NULL)
   1300                 return 0;
   1301 
   1302         ptr = in_bytes;
   1303 
   1304         out_mdr->signature = be16tohp(&ptr);
   1305 
   1306         out_mdr->date_created = be32tohp(&ptr);
   1307         out_mdr->date_modified = be32tohp(&ptr);
   1308 
   1309         out_mdr->attributes = be16tohp(&ptr);
   1310         out_mdr->root_file_count = be16tohp(&ptr);
   1311         out_mdr->volume_bitmap = be16tohp(&ptr);
   1312 
   1313         out_mdr->next_alloc_block = be16tohp(&ptr);
   1314         out_mdr->total_blocks = be16tohp(&ptr);
   1315         out_mdr->block_size = be32tohp(&ptr);
   1316 
   1317         out_mdr->clump_size = be32tohp(&ptr);
   1318         out_mdr->first_block = be16tohp(&ptr);
   1319         out_mdr->next_cnid = be32tohp(&ptr);
   1320         out_mdr->free_blocks = be16tohp(&ptr);
   1321 
   1322         memcpy(out_mdr->volume_name, ptr, 28);
   1323         ptr = (char *)ptr + 28;
   1324 
   1325         out_mdr->date_backedup = be32tohp(&ptr);
   1326         out_mdr->backup_seqnum = be16tohp(&ptr);
   1327 
   1328         out_mdr->write_count = be32tohp(&ptr);
   1329 
   1330         out_mdr->extents_clump_size = be32tohp(&ptr);
   1331         out_mdr->catalog_clump_size = be32tohp(&ptr);
   1332 
   1333         out_mdr->root_folder_count = be16tohp(&ptr);
   1334         out_mdr->file_count = be32tohp(&ptr);
   1335         out_mdr->folder_count = be32tohp(&ptr);
   1336 
   1337         for(i=0;i<8;i++)
   1338                 out_mdr->finder_info[i] = be32tohp(&ptr);
   1339 
   1340         out_mdr->embedded_signature = be16tohp(&ptr);
   1341         out_mdr->embedded_extent.start_block = be16tohp(&ptr);
   1342         out_mdr->embedded_extent.block_count = be16tohp(&ptr);
   1343 
   1344         out_mdr->extents_size = be32tohp(&ptr);
   1345         for (i = 0; i < 3; i++)
   1346         {
   1347                 out_mdr->extents_extents[i].start_block = be16tohp(&ptr);
   1348                 out_mdr->extents_extents[i].block_count = be16tohp(&ptr);
   1349         }
   1350 
   1351         out_mdr->catalog_size = be32tohp(&ptr);
   1352         for (i = 0; i < 3; i++)
   1353         {
   1354                 out_mdr->catalog_extents[i].start_block = be16tohp(&ptr);
   1355                 out_mdr->catalog_extents[i].block_count = be16tohp(&ptr);
   1356         }
   1357 
   1358         return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1359 }
   1360 
   1361 /*
   1362  *	hfslib_reada_node()
   1363  *
   1364  *	Given the pointer to and size of a buffer containing the entire, raw
   1365  *	contents of any b-tree node from the disk, this function will:
   1366  *
   1367  *		1.	determine the type of node and read its contents
   1368  *		2.	allocate memory for each record and fill it appropriately
   1369  *		3.	set out_record_ptrs_array to point to an array (which it allocates)
   1370  *			which has out_node_descriptor->num_recs many pointers to the
   1371  *			records themselves
   1372  *		4.	allocate out_record_ptr_sizes_array and fill it with the sizes of
   1373  *			each record
   1374  *		5.	return the number of bytes read (i.e., the size of the node)
   1375  *			or 0 on failure
   1376  *
   1377  *	out_node_descriptor must be allocated by the caller and may not be NULL.
   1378  *
   1379  *	out_record_ptrs_array and out_record_ptr_sizes_array must both be specified,
   1380  *	or both be NULL if the caller is not interested in reading the records.
   1381  *
   1382  *	out_record_ptr_sizes_array may be NULL if the caller is not interested in
   1383  *	reading the records, but must not be NULL if out_record_ptrs_array is not.
   1384  *
   1385  *	in_parent_file is HFS_CATALOG_FILE, HFS_EXTENTS_FILE, or
   1386  *	HFS_ATTRIBUTES_FILE, depending on the special file in which this node
   1387  *	resides.
   1388  *
   1389  *	inout_volume must have its catnodesize or extnodesize field (depending on
   1390  *	the parent file) set to the correct value if this is an index, leaf, or map
   1391  *	node. If this is a header node, the field will be set to its correct value.
   1392  */
   1393 size_t
   1394 hfslib_reada_node(void* in_bytes,
   1395 	hfs_node_descriptor_t* out_node_descriptor,
   1396 	void** out_record_ptrs_array[],
   1397 	uint16_t* out_record_ptr_sizes_array[],
   1398 	hfs_btree_file_type in_parent_file,
   1399 	hfs_volume* inout_volume,
   1400 	hfs_callback_args* cbargs)
   1401 {
   1402 	void*		ptr;
   1403 	uint16_t*	rec_offsets;
   1404 	size_t		last_bytes_read;
   1405 	uint16_t	nodesize;
   1406 	uint16_t	numrecords;
   1407 	uint16_t	free_space_offset;	/* offset to free space in node */
   1408 	int			keysizefieldsize;
   1409 	int			i;
   1410 
   1411 	numrecords = 0;
   1412 	rec_offsets = NULL;
   1413 	if(out_record_ptrs_array!=NULL)
   1414 		*out_record_ptrs_array = NULL;
   1415 	if(out_record_ptr_sizes_array!=NULL)
   1416 		*out_record_ptr_sizes_array = NULL;
   1417 
   1418 	if(in_bytes==NULL || inout_volume==NULL || out_node_descriptor==NULL
   1419 		|| (out_record_ptrs_array==NULL && out_record_ptr_sizes_array!=NULL)
   1420 		|| (out_record_ptrs_array!=NULL && out_record_ptr_sizes_array==NULL) )
   1421 		goto error;
   1422 
   1423 	ptr = in_bytes;
   1424 
   1425 	out_node_descriptor->flink = be32tohp(&ptr);
   1426 	out_node_descriptor->blink = be32tohp(&ptr);
   1427 	out_node_descriptor->kind = *(((int8_t*)ptr));
   1428 	ptr = (uint8_t*)ptr + 1;
   1429 	out_node_descriptor->height = *(((uint8_t*)ptr));
   1430 	ptr = (uint8_t*)ptr + 1;
   1431 	out_node_descriptor->num_recs = be16tohp(&ptr);
   1432 	out_node_descriptor->reserved = be16tohp(&ptr);
   1433 
   1434 	numrecords = out_node_descriptor->num_recs;
   1435 
   1436 	/*
   1437 	 *	To go any further, we will need to know the size of this node, as well
   1438 	 *	as the width of keyed records' key_len parameters for this btree. If
   1439 	 *	this is an index, leaf, or map node, inout_volume already has the node
   1440 	 *	size set in its catnodesize or extnodesize field and the key length set
   1441 	 *	in the catkeysizefieldsize or extkeysizefieldsize for catalog files and
   1442 	 *	extent files, respectively. However, if this is a header node, this
   1443 	 *	information has not yet been determined, so this is the place to do it.
   1444 	 */
   1445 	if(out_node_descriptor->kind == HFS_HEADERNODE)
   1446 	{
   1447 		hfs_header_record_t	hr;
   1448 		void*		header_rec_offset[1];
   1449 		uint16_t	header_rec_size[1];
   1450 
   1451 		/* sanity check to ensure this is a good header node */
   1452 		if(numrecords!=3)
   1453 			HFS_LIBERR("header node does not have exactly 3 records");
   1454 
   1455 		header_rec_offset[0] = ptr;
   1456 		header_rec_size[0] = sizeof(hfs_header_record_t);
   1457 
   1458 		last_bytes_read = hfslib_read_header_node(header_rec_offset,
   1459 			header_rec_size, 1, &hr, NULL, NULL);
   1460 		if(last_bytes_read==0)
   1461 			HFS_LIBERR("could not read header node");
   1462 
   1463 		switch(in_parent_file)
   1464 		{
   1465 			case HFS_CATALOG_FILE:
   1466 				inout_volume->chr.node_size = hr.node_size;
   1467 				inout_volume->catkeysizefieldsize =
   1468 					(hr.attributes & HFS_BIG_KEYS_MASK) ?
   1469 						sizeof(uint16_t):sizeof(uint8_t);
   1470 				break;
   1471 
   1472 			case HFS_EXTENTS_FILE:
   1473 				inout_volume->ehr.node_size = hr.node_size;
   1474 				inout_volume->extkeysizefieldsize =
   1475 					(hr.attributes & HFS_BIG_KEYS_MASK) ?
   1476 						sizeof(uint16_t):sizeof(uint8_t);
   1477 				break;
   1478 
   1479 			case HFS_ATTRIBUTES_FILE:
   1480 			default:
   1481 				HFS_LIBERR("invalid parent file type specified");
   1482 				/* NOTREACHED */
   1483 		}
   1484 	}
   1485 
   1486 	switch(in_parent_file)
   1487 	{
   1488 		case HFS_CATALOG_FILE:
   1489 			nodesize = inout_volume->chr.node_size;
   1490 			keysizefieldsize = inout_volume->catkeysizefieldsize;
   1491 			break;
   1492 
   1493 		case HFS_EXTENTS_FILE:
   1494 			nodesize = inout_volume->ehr.node_size;
   1495 			keysizefieldsize = inout_volume->extkeysizefieldsize;
   1496 			break;
   1497 
   1498 		case HFS_ATTRIBUTES_FILE:
   1499 		default:
   1500 			HFS_LIBERR("invalid parent file type specified");
   1501 			/* NOTREACHED */
   1502 	}
   1503 
   1504 	/*
   1505 	 *	Don't care about records so just exit after getting the node descriptor.
   1506 	 *	Note: This happens after the header node code, and not before it, in
   1507 	 *	case the caller calls this function and ignores the record data just to
   1508 	 *	get at the node descriptor, but then tries to call it again on a non-
   1509 	 *	header node without first setting inout_volume->cat/extnodesize.
   1510 	 */
   1511 	if(out_record_ptrs_array==NULL)
   1512 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1513 
   1514 	rec_offsets = hfslib_malloc(numrecords * sizeof(uint16_t), cbargs);
   1515 	*out_record_ptr_sizes_array =
   1516 		hfslib_malloc(numrecords * sizeof(uint16_t), cbargs);
   1517 	if(rec_offsets==NULL || *out_record_ptr_sizes_array==NULL)
   1518 		HFS_LIBERR("could not allocate node record offsets");
   1519 
   1520 	*out_record_ptrs_array = hfslib_malloc(numrecords * sizeof(void*), cbargs);
   1521 	if(*out_record_ptrs_array==NULL)
   1522 		HFS_LIBERR("could not allocate node records");
   1523 
   1524 	last_bytes_read = hfslib_reada_node_offsets((uint8_t*)in_bytes + nodesize -
   1525 			numrecords * sizeof(uint16_t), rec_offsets);
   1526 	if(last_bytes_read==0)
   1527 		HFS_LIBERR("could not read node record offsets");
   1528 
   1529 	/*	The size of the last record (i.e. the first one listed in the offsets)
   1530 	 *	must be determined using the offset to the node's free space. */
   1531 	free_space_offset = be16toh(*(uint16_t*)((uint8_t*)in_bytes + nodesize -
   1532 			(numrecords+1) * sizeof(uint16_t)));
   1533 
   1534 	(*out_record_ptr_sizes_array)[numrecords-1] =
   1535 		free_space_offset - rec_offsets[0];
   1536 	for(i=1;i<numrecords;i++)
   1537 	{
   1538 		(*out_record_ptr_sizes_array)[numrecords-i-1] =
   1539 			rec_offsets[i-1] - rec_offsets[i];
   1540 	}
   1541 
   1542 	for(i=0;i<numrecords;i++)
   1543 	{
   1544 		(*out_record_ptrs_array)[i] =
   1545 			hfslib_malloc((*out_record_ptr_sizes_array)[i], cbargs);
   1546 
   1547 		if((*out_record_ptrs_array)[i]==NULL)
   1548 			HFS_LIBERR("could not allocate node record #%i",i);
   1549 
   1550 		/*
   1551 		 *	If this is a keyed node (i.e., a leaf or index node), there are two
   1552 		 *	boundary rules that each record must obey:
   1553 		 *
   1554 		 *		1.	A pad byte must be placed between the key and data if the
   1555 		 *			size of the key plus the size of the key_len field is odd.
   1556 		 *
   1557 		 *		2.	A pad byte must be placed after the data if the data size
   1558 		 *			is odd.
   1559 		 *
   1560 		 *	So in the first case we increment the starting point of the data
   1561 		 *	and correspondingly decrement the record size. In the second case
   1562 		 *	we decrement the record size.
   1563 		 */
   1564 		if(out_node_descriptor->kind == HFS_LEAFNODE ||
   1565 		   out_node_descriptor->kind == HFS_INDEXNODE)
   1566 		{
   1567 			hfs_catalog_key_t	reckey;
   1568 			uint16_t			rectype;
   1569 
   1570 			rectype = out_node_descriptor->kind;
   1571 			last_bytes_read = hfslib_read_catalog_keyed_record(ptr, NULL,
   1572 				&rectype, &reckey, inout_volume);
   1573 			if(last_bytes_read==0)
   1574 				HFS_LIBERR("could not read node record");
   1575 
   1576 			if((reckey.key_len + keysizefieldsize) % 2 == 1)
   1577 			{
   1578 				ptr = (uint8_t*)ptr + 1;
   1579 				(*out_record_ptr_sizes_array)[i]--;
   1580 			}
   1581 
   1582 			if((*out_record_ptr_sizes_array)[i] % 2 == 1)
   1583 				(*out_record_ptr_sizes_array)[i]--;
   1584 		}
   1585 
   1586 		memcpy((*out_record_ptrs_array)[i], ptr,
   1587 				(*out_record_ptr_sizes_array)[i]);
   1588 		ptr = (uint8_t*)ptr + (*out_record_ptr_sizes_array)[i];
   1589 	}
   1590 
   1591 	goto exit;
   1592 
   1593 error:
   1594 	hfslib_free_recs(out_record_ptrs_array, out_record_ptr_sizes_array,
   1595 		&numrecords, cbargs);
   1596 
   1597 	ptr = in_bytes;
   1598 
   1599 	/* warn("error occurred in hfslib_reada_node()"); */
   1600 
   1601 	/* FALLTHROUGH */
   1602 
   1603 exit:
   1604 	if(rec_offsets!=NULL)
   1605 		hfslib_free(rec_offsets, cbargs);
   1606 
   1607 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1608 }
   1609 
   1610 /*
   1611  *	hfslib_reada_node_offsets()
   1612  *
   1613  *	Sets out_offset_array to contain the offsets to each record in the node,
   1614  *	in reverse order. Does not read the free space offset.
   1615  */
   1616 size_t
   1617 hfslib_reada_node_offsets(void* in_bytes, uint16_t* out_offset_array)
   1618 {
   1619 	void*		ptr;
   1620 
   1621 	if(in_bytes==NULL || out_offset_array==NULL)
   1622 		return 0;
   1623 
   1624 	ptr = in_bytes;
   1625 
   1626 	/*
   1627 	 *	The offset for record 0 (which is the very last offset in the node) is
   1628 	 *	always equal to 14, the size of the node descriptor. So, once we hit
   1629 	 *	offset=14, we know this is the last offset. In this way, we don't need
   1630 	 *	to know the number of records beforehand.
   1631 	*/
   1632 	out_offset_array--;
   1633 	do
   1634 	{
   1635 		out_offset_array++;
   1636 		*out_offset_array = be16tohp(&ptr);
   1637 	}
   1638 	while(*out_offset_array != (uint16_t)14);
   1639 
   1640 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1641 }
   1642 
   1643 /*	hfslib_read_header_node()
   1644  *
   1645  *	out_header_record and/or out_map_record may be NULL if the caller doesn't
   1646  *	care about their contents.
   1647  */
   1648 size_t
   1649 hfslib_read_header_node(void** in_recs,
   1650 	uint16_t* in_rec_sizes,
   1651 	uint16_t in_num_recs,
   1652 	hfs_header_record_t* out_hr,
   1653 	void* out_userdata,
   1654 	void* out_map)
   1655 {
   1656 	void*	ptr;
   1657 	int		i;
   1658 
   1659 	if(in_recs==NULL || in_rec_sizes==NULL)
   1660 		return 0;
   1661 
   1662 	if(out_hr!=NULL)
   1663 	{
   1664 		ptr = in_recs[0];
   1665 
   1666 		out_hr->tree_depth = be16tohp(&ptr);
   1667 		out_hr->root_node = be32tohp(&ptr);
   1668 		out_hr->leaf_recs = be32tohp(&ptr);
   1669 		out_hr->first_leaf = be32tohp(&ptr);
   1670 		out_hr->last_leaf = be32tohp(&ptr);
   1671 		out_hr->node_size = be16tohp(&ptr);
   1672 		out_hr->max_key_len = be16tohp(&ptr);
   1673 		out_hr->total_nodes = be32tohp(&ptr);
   1674 		out_hr->free_nodes = be32tohp(&ptr);
   1675 		out_hr->reserved = be16tohp(&ptr);
   1676 		out_hr->clump_size = be32tohp(&ptr);
   1677 		out_hr->btree_type = *(((uint8_t*)ptr));
   1678 		ptr = (uint8_t*)ptr + 1;
   1679 		out_hr->keycomp_type = *(((uint8_t*)ptr));
   1680 		ptr = (uint8_t*)ptr + 1;
   1681 		out_hr->attributes = be32tohp(&ptr);
   1682 		for(i=0;i<16;i++)
   1683 			out_hr->reserved2[i] = be32tohp(&ptr);
   1684 	}
   1685 
   1686 	if(out_userdata!=NULL)
   1687 	{
   1688 		memcpy(out_userdata, in_recs[1], in_rec_sizes[1]);
   1689 	}
   1690 	ptr = (uint8_t*)ptr + in_rec_sizes[1];	/* size of user data record */
   1691 
   1692 	if(out_map!=NULL)
   1693 	{
   1694 		memcpy(out_map, in_recs[2], in_rec_sizes[2]);
   1695 	}
   1696 	ptr = (uint8_t*)ptr + in_rec_sizes[2];	/* size of map record */
   1697 
   1698 	return ((uint8_t*)ptr - (uint8_t*)in_recs[0]);
   1699 }
   1700 
   1701 /*
   1702  *	hfslib_read_catalog_keyed_record()
   1703  *
   1704  *	out_recdata can be NULL. inout_rectype must be set to either HFS_LEAFNODE
   1705  *	or HFS_INDEXNODE upon calling this function, and will be set by the
   1706  *	function to one of HFS_REC_FLDR, HFS_REC_FILE, HFS_REC_FLDR_THREAD, or
   1707  *	HFS_REC_FLDR_THREAD upon return if the node is a leaf node. If it is an
   1708  *	index node, inout_rectype will not be changed.
   1709  */
   1710 size_t
   1711 hfslib_read_catalog_keyed_record(
   1712 	void* in_bytes,
   1713 	hfs_catalog_keyed_record_t* out_recdata,
   1714 	int16_t* inout_rectype,
   1715 	hfs_catalog_key_t* out_key,
   1716 	hfs_volume* in_volume)
   1717 {
   1718 	void*		ptr;
   1719 	size_t		last_bytes_read;
   1720 
   1721 	if(in_bytes==NULL || out_key==NULL || inout_rectype==NULL)
   1722 		return 0;
   1723 
   1724 	ptr = in_bytes;
   1725 
   1726 	/*	For HFS+, the key length is always a 2-byte number. This is indicated
   1727 	 *	by the HFS_BIG_KEYS_MASK bit in the attributes field of the catalog
   1728 	 *	header record. However, we just assume this bit is set, since all HFS+
   1729 	 *	volumes should have it set anyway. */
   1730 	if(in_volume->catkeysizefieldsize == sizeof(uint16_t))
   1731 		out_key->key_len = be16tohp(&ptr);
   1732 	else if (in_volume->catkeysizefieldsize == sizeof(uint8_t)) {
   1733 		out_key->key_len = *(((uint8_t*)ptr));
   1734 		ptr = (uint8_t*)ptr + 1;
   1735 	}
   1736 
   1737 	out_key->parent_cnid = be32tohp(&ptr);
   1738 
   1739 	last_bytes_read = hfslib_read_unistr255(ptr, &out_key->name);
   1740 	if(last_bytes_read==0)
   1741 		return 0;
   1742 	ptr = (uint8_t*)ptr + last_bytes_read;
   1743 
   1744 	/* don't waste time if the user just wanted the key and/or record type */
   1745 	if(out_recdata==NULL)
   1746 	{
   1747 		if(*inout_rectype == HFS_LEAFNODE)
   1748 			*inout_rectype = be16tohp(&ptr);
   1749 		else if(*inout_rectype != HFS_INDEXNODE)
   1750 			return 0;	/* should not happen if we were given valid arguments */
   1751 
   1752 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1753 	}
   1754 
   1755 	if(*inout_rectype == HFS_INDEXNODE)
   1756 	{
   1757 		out_recdata->child = be32tohp(&ptr);
   1758 	}
   1759 	else
   1760 	{
   1761 		/* first need to determine what kind of record this is */
   1762 		*inout_rectype = be16tohp(&ptr);
   1763 		out_recdata->type = *inout_rectype;
   1764 
   1765 		switch(out_recdata->type)
   1766 		{
   1767 			case HFS_REC_FLDR:
   1768 			{
   1769 				out_recdata->folder.flags = be16tohp(&ptr);
   1770 				out_recdata->folder.valence = be32tohp(&ptr);
   1771 				out_recdata->folder.cnid = be32tohp(&ptr);
   1772 				out_recdata->folder.date_created = be32tohp(&ptr);
   1773 				out_recdata->folder.date_content_mod = be32tohp(&ptr);
   1774 				out_recdata->folder.date_attrib_mod = be32tohp(&ptr);
   1775 				out_recdata->folder.date_accessed = be32tohp(&ptr);
   1776 				out_recdata->folder.date_backedup = be32tohp(&ptr);
   1777 
   1778 				last_bytes_read = hfslib_read_bsd_data(ptr,
   1779 					&out_recdata->folder.bsd);
   1780 				if(last_bytes_read==0)
   1781 					return 0;
   1782 				ptr = (uint8_t*)ptr + last_bytes_read;
   1783 
   1784 				last_bytes_read = hfslib_read_folder_userinfo(ptr,
   1785 					&out_recdata->folder.user_info);
   1786 				if(last_bytes_read==0)
   1787 					return 0;
   1788 				ptr = (uint8_t*)ptr + last_bytes_read;
   1789 
   1790 				last_bytes_read = hfslib_read_folder_finderinfo(ptr,
   1791 					&out_recdata->folder.finder_info);
   1792 				if(last_bytes_read==0)
   1793 					return 0;
   1794 				ptr = (uint8_t*)ptr + last_bytes_read;
   1795 
   1796 				out_recdata->folder.text_encoding = be32tohp(&ptr);
   1797 				out_recdata->folder.reserved = be32tohp(&ptr);
   1798 			}
   1799 			break;
   1800 
   1801 			case HFS_REC_FILE:
   1802 			{
   1803 				out_recdata->file.flags = be16tohp(&ptr);
   1804 				out_recdata->file.reserved = be32tohp(&ptr);
   1805 				out_recdata->file.cnid = be32tohp(&ptr);
   1806 				out_recdata->file.date_created = be32tohp(&ptr);
   1807 				out_recdata->file.date_content_mod = be32tohp(&ptr);
   1808 				out_recdata->file.date_attrib_mod = be32tohp(&ptr);
   1809 				out_recdata->file.date_accessed = be32tohp(&ptr);
   1810 				out_recdata->file.date_backedup = be32tohp(&ptr);
   1811 
   1812 				last_bytes_read = hfslib_read_bsd_data(ptr,
   1813 					&out_recdata->file.bsd);
   1814 				if(last_bytes_read==0)
   1815 					return 0;
   1816 				ptr = (uint8_t*)ptr + last_bytes_read;
   1817 
   1818 				last_bytes_read = hfslib_read_file_userinfo(ptr,
   1819 					&out_recdata->file.user_info);
   1820 				if(last_bytes_read==0)
   1821 					return 0;
   1822 				ptr = (uint8_t*)ptr + last_bytes_read;
   1823 
   1824 				last_bytes_read = hfslib_read_file_finderinfo(ptr,
   1825 					&out_recdata->file.finder_info);
   1826 				if(last_bytes_read==0)
   1827 					return 0;
   1828 				ptr = (uint8_t*)ptr + last_bytes_read;
   1829 
   1830 				out_recdata->file.text_encoding = be32tohp(&ptr);
   1831 				out_recdata->file.reserved2 = be32tohp(&ptr);
   1832 
   1833 				last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1834 					&out_recdata->file.data_fork);
   1835 				if(last_bytes_read==0)
   1836 					return 0;
   1837 				ptr = (uint8_t*)ptr + last_bytes_read;
   1838 
   1839 				last_bytes_read = hfslib_read_fork_descriptor(ptr,
   1840 					&out_recdata->file.rsrc_fork);
   1841 				if(last_bytes_read==0)
   1842 					return 0;
   1843 				ptr = (uint8_t*)ptr + last_bytes_read;
   1844 			}
   1845 			break;
   1846 
   1847 			case HFS_REC_FLDR_THREAD:
   1848 			case HFS_REC_FILE_THREAD:
   1849 			{
   1850 				out_recdata->thread.reserved = be16tohp(&ptr);
   1851 				out_recdata->thread.parent_cnid = be32tohp(&ptr);
   1852 
   1853 				last_bytes_read = hfslib_read_unistr255(ptr,
   1854 					&out_recdata->thread.name);
   1855 				if(last_bytes_read==0)
   1856 					return 0;
   1857 				ptr = (uint8_t*)ptr + last_bytes_read;
   1858 			}
   1859 			break;
   1860 
   1861 			default:
   1862 				return 1;
   1863 				/* NOTREACHED */
   1864 		}
   1865 	}
   1866 
   1867 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1868 }
   1869 
   1870 /* out_rec may be NULL */
   1871 size_t
   1872 hfslib_read_extent_record(
   1873 	void* in_bytes,
   1874 	hfs_extent_record_t* out_rec,
   1875 	hfs_node_kind in_nodekind,
   1876 	hfs_extent_key_t* out_key,
   1877 	hfs_volume* in_volume)
   1878 {
   1879 	void*		ptr;
   1880 	size_t		last_bytes_read;
   1881 
   1882 	if(in_bytes==NULL || out_key==NULL
   1883 		|| (in_nodekind!=HFS_LEAFNODE && in_nodekind!=HFS_INDEXNODE))
   1884 		return 0;
   1885 
   1886 	ptr = in_bytes;
   1887 
   1888 	/*	For HFS+, the key length is always a 2-byte number. This is indicated
   1889 	 *	by the HFS_BIG_KEYS_MASK bit in the attributes field of the extent
   1890 	 *	overflow header record. However, we just assume this bit is set, since
   1891 	 *	all HFS+ volumes should have it set anyway. */
   1892 	if(in_volume->extkeysizefieldsize == sizeof(uint16_t))
   1893 		out_key->key_length = be16tohp(&ptr);
   1894 	else if (in_volume->extkeysizefieldsize == sizeof(uint8_t)) {
   1895 		out_key->key_length = *(((uint8_t*)ptr));
   1896 		ptr = (uint8_t*)ptr + 1;
   1897 	}
   1898 
   1899 	out_key->fork_type = *(((uint8_t*)ptr));
   1900 	ptr = (uint8_t*)ptr + 1;
   1901 	out_key->padding = *(((uint8_t*)ptr));
   1902 	ptr = (uint8_t*)ptr + 1;
   1903 	out_key->file_cnid = be32tohp(&ptr);
   1904 	out_key->start_block = be32tohp(&ptr);
   1905 
   1906 	/* don't waste time if the user just wanted the key */
   1907 	if(out_rec==NULL)
   1908 		return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1909 
   1910 	if(in_nodekind==HFS_LEAFNODE)
   1911 	{
   1912 		last_bytes_read = hfslib_read_extent_descriptors(ptr, out_rec);
   1913 		if(last_bytes_read==0)
   1914 			return 0;
   1915 		ptr = (uint8_t*)ptr + last_bytes_read;
   1916 	}
   1917 	else
   1918 	{
   1919 		/* XXX: this is completely bogus */
   1920                 /*      (uint32_t*)*out_rec = be32tohp(&ptr); */
   1921 	    uint32_t *ptr_32 = (uint32_t *)out_rec;
   1922 		*ptr_32 = be32tohp(&ptr);
   1923 	        /* (*out_rec)[0].start_block = be32tohp(&ptr); */
   1924 	}
   1925 
   1926 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1927 }
   1928 
   1929 void
   1930 hfslib_free_recs(
   1931 	void*** inout_node_recs,
   1932 	uint16_t** inout_rec_sizes,
   1933 	uint16_t* inout_num_recs,
   1934 	hfs_callback_args* cbargs)
   1935 {
   1936 	uint16_t	i;
   1937 
   1938 	if(inout_num_recs==NULL || *inout_num_recs==0)
   1939 		return;
   1940 
   1941 	if(inout_node_recs!=NULL && *inout_node_recs!=NULL)
   1942 	{
   1943 		for(i=0;i<*inout_num_recs;i++)
   1944 		{
   1945 			if((*inout_node_recs)[i]!=NULL)
   1946 			{
   1947 				hfslib_free((*inout_node_recs)[i], cbargs);
   1948 				(*inout_node_recs)[i] = NULL;
   1949 			}
   1950 		}
   1951 
   1952 		hfslib_free(*inout_node_recs, cbargs);
   1953 		*inout_node_recs = NULL;
   1954 	}
   1955 
   1956 	if(inout_rec_sizes!=NULL && *inout_rec_sizes!=NULL)
   1957 	{
   1958 		hfslib_free(*inout_rec_sizes, cbargs);
   1959 		*inout_rec_sizes = NULL;
   1960 	}
   1961 
   1962 	*inout_num_recs = 0;
   1963 }
   1964 
   1965 #if 0
   1966 #pragma mark -
   1967 #pragma mark Individual Fields
   1968 #endif
   1969 
   1970 size_t
   1971 hfslib_read_fork_descriptor(void* in_bytes, hfs_fork_t* out_forkdata)
   1972 {
   1973 	void*	ptr;
   1974 	size_t	last_bytes_read;
   1975 
   1976 	if(in_bytes==NULL || out_forkdata==NULL)
   1977 		return 0;
   1978 
   1979 	ptr = in_bytes;
   1980 
   1981 	out_forkdata->logical_size = be64tohp(&ptr);
   1982 	out_forkdata->clump_size = be32tohp(&ptr);
   1983 	out_forkdata->total_blocks = be32tohp(&ptr);
   1984 
   1985 	if((last_bytes_read = hfslib_read_extent_descriptors(ptr,
   1986 		&out_forkdata->extents))==0)
   1987 		return 0;
   1988 	ptr = (uint8_t*)ptr + last_bytes_read;
   1989 
   1990 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   1991 }
   1992 
   1993 size_t
   1994 hfslib_read_extent_descriptors(
   1995 	void* in_bytes,
   1996 	hfs_extent_record_t* out_extentrecord)
   1997 {
   1998 	void*	ptr;
   1999 	int		i;
   2000 
   2001 	if(in_bytes==NULL || out_extentrecord==NULL)
   2002 		return 0;
   2003 
   2004 	ptr = in_bytes;
   2005 
   2006 	for(i=0;i<8;i++)
   2007 	{
   2008 		(((hfs_extent_descriptor_t*)*out_extentrecord)[i]).start_block =
   2009 			be32tohp(&ptr);
   2010 		(((hfs_extent_descriptor_t*)*out_extentrecord)[i]).block_count =
   2011 			be32tohp(&ptr);
   2012 	}
   2013 
   2014 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2015 }
   2016 
   2017 size_t
   2018 hfslib_read_unistr255(void* in_bytes, hfs_unistr255_t* out_string)
   2019 {
   2020 	void*		ptr;
   2021 	uint16_t	i, length;
   2022 
   2023 	if(in_bytes==NULL || out_string==NULL)
   2024 		return 0;
   2025 
   2026 	ptr = in_bytes;
   2027 
   2028 	length = be16tohp(&ptr);
   2029 	if(length>255)
   2030 		length = 255; /* hfs+ folder/file names have a limit of 255 chars */
   2031 	out_string->length = length;
   2032 
   2033 	for(i=0; i<length; i++)
   2034 	{
   2035 		out_string->unicode[i] = be16tohp(&ptr);
   2036 	}
   2037 
   2038 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2039 }
   2040 
   2041 size_t
   2042 hfslib_read_bsd_data(void* in_bytes, hfs_bsd_data_t* out_perms)
   2043 {
   2044 	void*	ptr;
   2045 
   2046 	if(in_bytes==NULL || out_perms==NULL)
   2047 		return 0;
   2048 
   2049 	ptr = in_bytes;
   2050 
   2051 	out_perms->owner_id = be32tohp(&ptr);
   2052 	out_perms->group_id = be32tohp(&ptr);
   2053 	out_perms->admin_flags = *(((uint8_t*)ptr));
   2054 	ptr = (uint8_t*)ptr + 1;
   2055 	out_perms->owner_flags = *(((uint8_t*)ptr));
   2056 	ptr = (uint8_t*)ptr + 1;
   2057 	out_perms->file_mode = be16tohp(&ptr);
   2058 	out_perms->special.inode_num = be32tohp(&ptr); /* this field is a union */
   2059 
   2060 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2061 }
   2062 
   2063 size_t
   2064 hfslib_read_file_userinfo(void* in_bytes, hfs_macos_file_info_t* out_info)
   2065 {
   2066 	void*	ptr;
   2067 
   2068 	if(in_bytes==NULL || out_info==NULL)
   2069 		return 0;
   2070 
   2071 	ptr = in_bytes;
   2072 
   2073 	out_info->file_type = be32tohp(&ptr);
   2074 	out_info->file_creator = be32tohp(&ptr);
   2075 	out_info->finder_flags = be16tohp(&ptr);
   2076 	out_info->location.v = be16tohp(&ptr);
   2077 	out_info->location.h = be16tohp(&ptr);
   2078 	out_info->reserved = be16tohp(&ptr);
   2079 
   2080 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2081 }
   2082 
   2083 size_t
   2084 hfslib_read_file_finderinfo(
   2085 	void* in_bytes,
   2086 	hfs_macos_extended_file_info_t* out_info)
   2087 {
   2088 	void*	ptr;
   2089 
   2090 	if(in_bytes==NULL || out_info==NULL)
   2091 		return 0;
   2092 
   2093 	ptr = in_bytes;
   2094 
   2095 #if 0
   2096 	#pragma warn Fill in with real code!
   2097 #endif
   2098 	/* FIXME: Fill in with real code! */
   2099 	memset(out_info, 0, sizeof(*out_info));
   2100 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_extended_file_info_t);
   2101 
   2102 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2103 }
   2104 
   2105 size_t
   2106 hfslib_read_folder_userinfo(void* in_bytes, hfs_macos_folder_info_t* out_info)
   2107 {
   2108 	void*	ptr;
   2109 
   2110 	if(in_bytes==NULL || out_info==NULL)
   2111 		return 0;
   2112 
   2113 	ptr = in_bytes;
   2114 
   2115 #if 0
   2116 	#pragma warn Fill in with real code!
   2117 #endif
   2118 	/* FIXME: Fill in with real code! */
   2119 	memset(out_info, 0, sizeof(*out_info));
   2120 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_folder_info_t);
   2121 
   2122 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2123 }
   2124 
   2125 size_t
   2126 hfslib_read_folder_finderinfo(
   2127 	void* in_bytes,
   2128 	hfs_macos_extended_folder_info_t* out_info)
   2129 {
   2130 	void*	ptr;
   2131 
   2132 	if(in_bytes==NULL || out_info==NULL)
   2133 		return 0;
   2134 
   2135 	ptr = in_bytes;
   2136 
   2137 #if 0
   2138 	#pragma warn Fill in with real code!
   2139 #endif
   2140 	/* FIXME: Fill in with real code! */
   2141 	memset(out_info, 0, sizeof(*out_info));
   2142 	ptr = (uint8_t*)ptr + sizeof(hfs_macos_extended_folder_info_t);
   2143 
   2144 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2145 }
   2146 
   2147 size_t
   2148 hfslib_read_journal_info(void* in_bytes, hfs_journal_info_t* out_info)
   2149 {
   2150 	void*	ptr;
   2151 	int		i;
   2152 
   2153 	if(in_bytes==NULL || out_info==NULL)
   2154 		return 0;
   2155 
   2156 	ptr = in_bytes;
   2157 
   2158 	out_info->flags = be32tohp(&ptr);
   2159 	for(i=0; i<8; i++)
   2160 	{
   2161 		out_info->device_signature[i] = be32tohp(&ptr);
   2162 	}
   2163 	out_info->offset = be64tohp(&ptr);
   2164 	out_info->size = be64tohp(&ptr);
   2165 	for(i=0; i<32; i++)
   2166 	{
   2167 		out_info->reserved[i] = be64tohp(&ptr);
   2168 	}
   2169 
   2170 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2171 }
   2172 
   2173 size_t
   2174 hfslib_read_journal_header(void* in_bytes, hfs_journal_header_t* out_header)
   2175 {
   2176 	void*	ptr;
   2177 
   2178 	if(in_bytes==NULL || out_header==NULL)
   2179 		return 0;
   2180 
   2181 	ptr = in_bytes;
   2182 
   2183 	out_header->magic = be32tohp(&ptr);
   2184 	out_header->endian = be32tohp(&ptr);
   2185 	out_header->start = be64tohp(&ptr);
   2186 	out_header->end = be64tohp(&ptr);
   2187 	out_header->size = be64tohp(&ptr);
   2188 	out_header->blocklist_header_size = be32tohp(&ptr);
   2189 	out_header->checksum = be32tohp(&ptr);
   2190 	out_header->journal_header_size = be32tohp(&ptr);
   2191 
   2192 	return ((uint8_t*)ptr - (uint8_t*)in_bytes);
   2193 }
   2194 
   2195 #if 0
   2196 #pragma mark -
   2197 #pragma mark Disk Access
   2198 #endif
   2199 
   2200 /*
   2201  *	hfslib_readd_with_extents()
   2202  *
   2203  *	This function reads the contents of a file from the volume, given an array
   2204  *	of extent descriptors which specify where every extent of the file is
   2205  *	located (in addition to the usual pread() arguments). out_bytes is presumed
   2206  *  to exist and be large enough to hold in_length number of bytes. Returns 0
   2207  *	on success.
   2208  */
   2209 int
   2210 hfslib_readd_with_extents(
   2211 	hfs_volume*	in_vol,
   2212 	void*		out_bytes,
   2213 	uint64_t*	out_bytesread,
   2214 	uint64_t	in_length,
   2215 	uint64_t	in_offset,
   2216 	hfs_extent_descriptor_t in_extents[],
   2217 	uint16_t	in_numextents,
   2218 	hfs_callback_args*	cbargs)
   2219 {
   2220 	uint64_t	ext_length, last_offset;
   2221 	uint16_t	i;
   2222 	int			error;
   2223 
   2224 	if(in_vol==NULL || out_bytes==NULL || in_extents==NULL || in_numextents==0
   2225 		|| out_bytesread==NULL)
   2226 		return -1;
   2227 
   2228 	*out_bytesread = 0;
   2229 	last_offset = 0;
   2230 
   2231 	for(i=0; i<in_numextents; i++)
   2232 	{
   2233 		if(in_extents[i].block_count==0)
   2234 			continue;
   2235 
   2236 		ext_length = in_extents[i].block_count * in_vol->vh.block_size;
   2237 
   2238 		if(in_offset < last_offset+ext_length
   2239 			&& in_offset+in_length >= last_offset)
   2240 		{
   2241 			uint64_t	isect_start, isect_end;
   2242 
   2243 			isect_start = max(in_offset, last_offset);
   2244 			isect_end = min(in_offset+in_length, last_offset+ext_length);
   2245 			error = hfslib_readd(in_vol, out_bytes, isect_end-isect_start,
   2246 				isect_start - last_offset + (uint64_t)in_extents[i].start_block
   2247 					* in_vol->vh.block_size, cbargs);
   2248 
   2249 			if(error!=0)
   2250 				return error;
   2251 
   2252 			*out_bytesread += isect_end-isect_start;
   2253 			out_bytes = (uint8_t*)out_bytes + isect_end-isect_start;
   2254 		}
   2255 
   2256 		last_offset += ext_length;
   2257 	}
   2258 
   2259 
   2260 	return 0;
   2261 }
   2262 
   2263 #if 0
   2264 #pragma mark -
   2265 #pragma mark Callback Wrappers
   2266 #endif
   2267 
   2268 void
   2269 hfslib_error(const char* in_format, const char* in_file, int in_line, ...)
   2270 {
   2271 	va_list		ap;
   2272 
   2273 	if(in_format==NULL)
   2274 		return;
   2275 
   2276 	if(hfs_gcb.error!=NULL)
   2277 	{
   2278 		va_start(ap, in_line);
   2279 
   2280 		hfs_gcb.error(in_format, in_file, in_line, ap);
   2281 
   2282 		va_end(ap);
   2283 	}
   2284 }
   2285 
   2286 void*
   2287 hfslib_malloc(size_t size, hfs_callback_args* cbargs)
   2288 {
   2289 	if(hfs_gcb.allocmem!=NULL)
   2290 		return hfs_gcb.allocmem(size, cbargs);
   2291 
   2292 	return NULL;
   2293 }
   2294 
   2295 void*
   2296 hfslib_realloc(void* ptr, size_t size, hfs_callback_args* cbargs)
   2297 {
   2298 	if(hfs_gcb.reallocmem!=NULL)
   2299 		return hfs_gcb.reallocmem(ptr, size, cbargs);
   2300 
   2301 	return NULL;
   2302 }
   2303 
   2304 void
   2305 hfslib_free(void* ptr, hfs_callback_args* cbargs)
   2306 {
   2307 	if(hfs_gcb.freemem!=NULL && ptr!=NULL)
   2308 		hfs_gcb.freemem(ptr, cbargs);
   2309 }
   2310 
   2311 int
   2312 hfslib_openvoldevice(
   2313 	hfs_volume* in_vol,
   2314 	const char* in_device,
   2315 	hfs_callback_args* cbargs)
   2316 {
   2317 	if(hfs_gcb.openvol!=NULL && in_device!=NULL)
   2318 		return hfs_gcb.openvol(in_vol, in_device, cbargs);
   2319 
   2320 	return 1;
   2321 }
   2322 
   2323 void
   2324 hfslib_closevoldevice(hfs_volume* in_vol, hfs_callback_args* cbargs)
   2325 {
   2326 	if(hfs_gcb.closevol!=NULL)
   2327 		hfs_gcb.closevol(in_vol, cbargs);
   2328 }
   2329 
   2330 int
   2331 hfslib_readd(
   2332 	hfs_volume* in_vol,
   2333 	void* out_bytes,
   2334 	uint64_t in_length,
   2335 	uint64_t in_offset,
   2336 	hfs_callback_args* cbargs)
   2337 {
   2338 	if(in_vol==NULL || out_bytes==NULL)
   2339 		return -1;
   2340 
   2341 	if(hfs_gcb.read!=NULL)
   2342 		return hfs_gcb.read(in_vol, out_bytes, in_length, in_offset, cbargs);
   2343 
   2344 	return -1;
   2345 }
   2346 
   2347 #if 0
   2348 #pragma mark -
   2349 #pragma mark Other
   2350 #endif
   2351 
   2352 /* returns key length */
   2353 uint16_t
   2354 hfslib_make_catalog_key(
   2355 	hfs_cnid_t in_parent_cnid,
   2356 	uint16_t in_name_len,
   2357 	unichar_t* in_unicode,
   2358 	hfs_catalog_key_t* out_key)
   2359 {
   2360 	if(in_parent_cnid==0 || (in_name_len>0 && in_unicode==NULL) || out_key==0)
   2361 		return 0;
   2362 
   2363 	if(in_name_len>255)
   2364 		in_name_len = 255;
   2365 
   2366 	out_key->key_len = 6 + 2 * in_name_len;
   2367 	out_key->parent_cnid = in_parent_cnid;
   2368 	out_key->name.length = in_name_len;
   2369 	if(in_name_len>0)
   2370 		memcpy(&out_key->name.unicode, in_unicode, in_name_len*2);
   2371 
   2372 	return out_key->key_len;
   2373 }
   2374 
   2375 /* returns key length */
   2376 uint16_t
   2377 hfslib_make_extent_key(
   2378 	hfs_cnid_t in_cnid,
   2379 	uint8_t in_forktype,
   2380 	uint32_t in_startblock,
   2381 	hfs_extent_key_t* out_key)
   2382 {
   2383 	if(in_cnid==0 || out_key==0)
   2384 		return 0;
   2385 
   2386 	out_key->key_length = HFS_MAX_EXT_KEY_LEN;
   2387 	out_key->fork_type = in_forktype;
   2388 	out_key->padding = 0;
   2389 	out_key->file_cnid = in_cnid;
   2390 	out_key->start_block = in_startblock;
   2391 
   2392 	return out_key->key_length;
   2393 }
   2394 
   2395 /* case-folding */
   2396 int
   2397 hfslib_compare_catalog_keys_cf (
   2398 	const void *ap,
   2399 	const void *bp)
   2400 {
   2401 	const hfs_catalog_key_t	*a, *b;
   2402 	unichar_t	ac, bc; /* current character from a, b */
   2403 	unichar_t	lc; /* lowercase version of current character */
   2404 	uint8_t		apos, bpos; /* current character indices */
   2405 
   2406 	a = (const hfs_catalog_key_t*)ap;
   2407 	b = (const hfs_catalog_key_t*)bp;
   2408 
   2409 	if(a->parent_cnid != b->parent_cnid)
   2410 	{
   2411 		return (a->parent_cnid - b->parent_cnid);
   2412 	}
   2413 	else
   2414 	{
   2415 		/*
   2416 		 * The following code implements the pseudocode suggested by
   2417 		 * the HFS+ technote.
   2418 		 */
   2419 
   2420 /*
   2421  * XXX These need to be revised to be endian-independent!
   2422  */
   2423 #define hbyte(x) ((x) >> 8)
   2424 #define lbyte(x) ((x) & 0x00FF)
   2425 
   2426 		apos = bpos = 0;
   2427 		while(1)
   2428 		{
   2429 			/* get next valid character from a */
   2430 			for (lc=0; lc == 0 && apos < a->name.length; apos++) {
   2431 				ac = a->name.unicode[apos];
   2432 				lc = hfs_gcft[hbyte(ac)];
   2433 				if(lc==0)
   2434 					lc = ac;
   2435 				else
   2436 					lc = hfs_gcft[lc + lbyte(ac)];
   2437 			};
   2438 			ac=lc;
   2439 
   2440 			/* get next valid character from b */
   2441 			for (lc=0; lc == 0 && bpos < b->name.length; bpos++) {
   2442 				bc = b->name.unicode[bpos];
   2443 				lc = hfs_gcft[hbyte(bc)];
   2444 				if(lc==0)
   2445 					lc = bc;
   2446 				else
   2447 					lc = hfs_gcft[lc + lbyte(bc)];
   2448 			};
   2449 			bc=lc;
   2450 
   2451 			/* on end of string ac/bc are 0, otherwise > 0 */
   2452 			if (ac != bc || (ac == 0  && bc == 0))
   2453 				return ac - bc;
   2454 		}
   2455 #undef hbyte
   2456 #undef lbyte
   2457 	}
   2458 }
   2459 
   2460 /* binary compare (i.e., not case folding) */
   2461 int
   2462 hfslib_compare_catalog_keys_bc (
   2463 	const void *a,
   2464 	const void *b)
   2465 {
   2466 	if(((const hfs_catalog_key_t*)a)->parent_cnid
   2467 		== ((const hfs_catalog_key_t*)b)->parent_cnid)
   2468 	{
   2469 		if(((const hfs_catalog_key_t*)a)->name.length == 0 &&
   2470 			((const hfs_catalog_key_t*)b)->name.length == 0)
   2471 			return 0;
   2472 
   2473 		if(((const hfs_catalog_key_t*)a)->name.length == 0)
   2474 			return -1;
   2475 		if(((const hfs_catalog_key_t*)b)->name.length == 0)
   2476 			return 1;
   2477 
   2478 		/* FIXME: This does a byte-per-byte comparison, whereas the HFS spec
   2479 		 * mandates a uint16_t chunk comparison. */
   2480 		return memcmp(((const hfs_catalog_key_t*)a)->name.unicode,
   2481 			((const hfs_catalog_key_t*)b)->name.unicode,
   2482 			min(((const hfs_catalog_key_t*)a)->name.length,
   2483 				((const hfs_catalog_key_t*)b)->name.length));
   2484 	}
   2485 	else
   2486 	{
   2487 		return (((const hfs_catalog_key_t*)a)->parent_cnid -
   2488 				((const hfs_catalog_key_t*)b)->parent_cnid);
   2489 	}
   2490 }
   2491 
   2492 int
   2493 hfslib_compare_extent_keys (
   2494 	const void *a,
   2495 	const void *b)
   2496 {
   2497 	/*
   2498 	 *	Comparison order, in descending importance:
   2499 	 *
   2500 	 *		CNID -> fork type -> start block
   2501 	 */
   2502 
   2503 	if(((const hfs_extent_key_t*)a)->file_cnid
   2504 		== ((const hfs_extent_key_t*)b)->file_cnid)
   2505 	{
   2506 		if(((const hfs_extent_key_t*)a)->fork_type
   2507 			== ((const hfs_extent_key_t*)b)->fork_type)
   2508 		{
   2509 			if(((const hfs_extent_key_t*)a)->start_block
   2510 				== ((const hfs_extent_key_t*)b)->start_block)
   2511 			{
   2512 				return 0;
   2513 			}
   2514 			else
   2515 			{
   2516 				return (((const hfs_extent_key_t*)a)->start_block -
   2517 						((const hfs_extent_key_t*)b)->start_block);
   2518 			}
   2519 		}
   2520 		else
   2521 		{
   2522 			return (((const hfs_extent_key_t*)a)->fork_type -
   2523 					((const hfs_extent_key_t*)b)->fork_type);
   2524 		}
   2525 	}
   2526 	else
   2527 	{
   2528 		return (((const hfs_extent_key_t*)a)->file_cnid -
   2529 				((const hfs_extent_key_t*)b)->file_cnid);
   2530 	}
   2531 }
   2532 
   2533 /* 1+10 tables of 16 rows and 16 columns, each 2 bytes wide = 5632 bytes */
   2534 int
   2535 hfslib_create_casefolding_table(void)
   2536 {
   2537 	hfs_callback_args	cbargs;
   2538 	unichar_t*	t; /* convenience */
   2539 	uint16_t	s; /* current subtable * 256 */
   2540 	uint16_t	i; /* current subtable index (0 to 255) */
   2541 
   2542 	if(hfs_gcft!=NULL)
   2543 		return 0; /* no sweat, table already exists */
   2544 
   2545 	hfslib_init_cbargs(&cbargs);
   2546 	hfs_gcft = hfslib_malloc(5632, &cbargs);
   2547 	if(hfs_gcft==NULL)
   2548 		HFS_LIBERR("could not allocate case folding table");
   2549 
   2550 	t = hfs_gcft;	 /* easier to type :) */
   2551 
   2552 	/*
   2553 	 * high byte indices
   2554 	 */
   2555 	s = 0 * 256;
   2556 	memset(t, 0x00, 512);
   2557 	t[s+  0] = 0x0100;
   2558 	t[s+  1] = 0x0200;
   2559 	t[s+  3] = 0x0300;
   2560 	t[s+  4] = 0x0400;
   2561 	t[s+  5] = 0x0500;
   2562 	t[s+ 16] = 0x0600;
   2563 	t[s+ 32] = 0x0700;
   2564 	t[s+ 33] = 0x0800;
   2565 	t[s+254] = 0x0900;
   2566 	t[s+255] = 0x0a00;
   2567 
   2568 	/*
   2569 	 * table 1 (high byte 0x00)
   2570 	 */
   2571 	s = 1 * 256;
   2572 	for(i=0; i<65; i++)
   2573 		t[s+i] = i;
   2574 	t[s+  0] = 0xffff;
   2575 	for(i=65; i<91; i++)
   2576 		t[s+i] = i + 0x20;
   2577 	for(i=91; i<256; i++)
   2578 		t[s+i] = i;
   2579 	t[s+198] = 0x00e6;
   2580 	t[s+208] = 0x00f0;
   2581 	t[s+216] = 0x00f8;
   2582 	t[s+222] = 0x00fe;
   2583 
   2584 	/*
   2585 	 * table 2 (high byte 0x01)
   2586 	 */
   2587 	s = 2 * 256;
   2588 	for(i=0; i<256; i++)
   2589 		t[s+i] = i + 0x0100;
   2590 	t[s+ 16] = 0x0111;
   2591 	t[s+ 38] = 0x0127;
   2592 	t[s+ 50] = 0x0133;
   2593 	t[s+ 63] = 0x0140;
   2594 	t[s+ 65] = 0x0142;
   2595 	t[s+ 74] = 0x014b;
   2596 	t[s+ 82] = 0x0153;
   2597 	t[s+102] = 0x0167;
   2598 	t[s+129] = 0x0253;
   2599 	t[s+130] = 0x0183;
   2600 	t[s+132] = 0x0185;
   2601 	t[s+134] = 0x0254;
   2602 	t[s+135] = 0x0188;
   2603 	t[s+137] = 0x0256;
   2604 	t[s+138] = 0x0257;
   2605 	t[s+139] = 0x018c;
   2606 	t[s+142] = 0x01dd;
   2607 	t[s+143] = 0x0259;
   2608 	t[s+144] = 0x025b;
   2609 	t[s+145] = 0x0192;
   2610 	t[s+147] = 0x0260;
   2611 	t[s+148] = 0x0263;
   2612 	t[s+150] = 0x0269;
   2613 	t[s+151] = 0x0268;
   2614 	t[s+152] = 0x0199;
   2615 	t[s+156] = 0x026f;
   2616 	t[s+157] = 0x0272;
   2617 	t[s+159] = 0x0275;
   2618 	t[s+162] = 0x01a3;
   2619 	t[s+164] = 0x01a5;
   2620 	t[s+167] = 0x01a8;
   2621 	t[s+169] = 0x0283;
   2622 	t[s+172] = 0x01ad;
   2623 	t[s+174] = 0x0288;
   2624 	t[s+177] = 0x028a;
   2625 	t[s+178] = 0x028b;
   2626 	t[s+179] = 0x01b4;
   2627 	t[s+181] = 0x01b6;
   2628 	t[s+183] = 0x0292;
   2629 	t[s+184] = 0x01b9;
   2630 	t[s+188] = 0x01bd;
   2631 	t[s+196] = 0x01c6;
   2632 	t[s+197] = 0x01c6;
   2633 	t[s+199] = 0x01c9;
   2634 	t[s+200] = 0x01c9;
   2635 	t[s+202] = 0x01cc;
   2636 	t[s+203] = 0x01cc;
   2637 	t[s+228] = 0x01e5;
   2638 	t[s+241] = 0x01f3;
   2639 	t[s+242] = 0x01f3;
   2640 
   2641 	/*
   2642 	 * table 3 (high byte 0x03)
   2643 	 */
   2644 	s = 3 * 256;
   2645 	for(i=0; i<145; i++)
   2646 		t[s+i] = i + 0x0300;
   2647 	for(i=145; i<170; i++)
   2648 		t[s+i] = i + 0x0320;
   2649 	t[s+162] = 0x03a2;
   2650 	for(i=170; i<256; i++)
   2651 		t[s+i] = i + 0x0300;
   2652 
   2653 	for(i=226; i<239; i+=2)
   2654 		t[s+i] = i + 0x0301;
   2655 
   2656 	/*
   2657 	 * table 4 (high byte 0x04)
   2658 	 */
   2659 	s = 4 * 256;
   2660 	for(i=0; i<16; i++)
   2661 		t[s+i] = i + 0x0400;
   2662 	t[s+  2] = 0x0452;
   2663 	t[s+  4] = 0x0454;
   2664 	t[s+  5] = 0x0455;
   2665 	t[s+  6] = 0x0456;
   2666 	t[s+  8] = 0x0458;
   2667 	t[s+  9] = 0x0459;
   2668 	t[s+ 10] = 0x045a;
   2669 	t[s+ 11] = 0x045b;
   2670 	t[s+ 15] = 0x045f;
   2671 
   2672 	for(i=16; i<48; i++)
   2673 		t[s+i] = i + 0x0420;
   2674 	t[s+ 25] = 0x0419;
   2675 	for(i=48; i<256; i++)
   2676 		t[s+i] = i + 0x0400;
   2677 	t[s+195] = 0x04c4;
   2678 	t[s+199] = 0x04c8;
   2679 	t[s+203] = 0x04cc;
   2680 
   2681 	for(i=96; i<129; i+=2)
   2682 		t[s+i] = i + 0x0401;
   2683 	t[s+118] = 0x0476;
   2684 	for(i=144; i<191; i+=2)
   2685 		t[s+i] = i + 0x0401;
   2686 
   2687 	/*
   2688 	 * table 5 (high byte 0x05)
   2689 	 */
   2690 	s = 5 * 256;
   2691 	for(i=0; i<49; i++)
   2692 		t[s+i] = i + 0x0500;
   2693 	for(i=49; i<87; i++)
   2694 		t[s+i] = i + 0x0530;
   2695 	for(i=87; i<256; i++)
   2696 		t[s+i] = i + 0x0500;
   2697 
   2698 	/*
   2699 	 * table 6 (high byte 0x10)
   2700 	 */
   2701 	s = 6 * 256;
   2702 	for(i=0; i<160; i++)
   2703 		t[s+i] = i + 0x1000;
   2704 	for(i=160; i<198; i++)
   2705 		t[s+i] = i + 0x1030;
   2706 	for(i=198; i<256; i++)
   2707 		t[s+i] = i + 0x1000;
   2708 
   2709 	/*
   2710 	 * table 7 (high byte 0x20)
   2711 	 */
   2712 	s = 7 * 256;
   2713 	for(i=0; i<256; i++)
   2714 		t[s+i] = i + 0x2000;
   2715 	{
   2716 		uint8_t zi[15] = { 12,  13,  14,  15,
   2717 						   42,  43,  44,  45,  46,
   2718 						  106, 107, 108, 109, 110, 111};
   2719 
   2720 		for(i=0; i<15; i++)
   2721 			t[s+zi[i]] = 0x0000;
   2722 	}
   2723 
   2724 	/*
   2725 	 * table 8 (high byte 0x21)
   2726 	 */
   2727 	s = 8 * 256;
   2728 	for(i=0; i<96; i++)
   2729 		t[s+i] = i + 0x2100;
   2730 	for(i=96; i<112; i++)
   2731 		t[s+i] = i + 0x2110;
   2732 	for(i=112; i<256; i++)
   2733 		t[s+i] = i + 0x2100;
   2734 
   2735 	/*
   2736 	 * table 9 (high byte 0xFE)
   2737 	 */
   2738 	s = 9 * 256;
   2739 	for(i=0; i<256; i++)
   2740 		t[s+i] = i + 0xFE00;
   2741 	t[s+255] = 0x0000;
   2742 
   2743 	/*
   2744 	 * table 10 (high byte 0xFF)
   2745 	 */
   2746 	s = 10 * 256;
   2747 	for(i=0; i<33; i++)
   2748 		t[s+i] = i + 0xFF00;
   2749 	for(i=33; i<59; i++)
   2750 		t[s+i] = i + 0xFF20;
   2751 	for(i=59; i<256; i++)
   2752 		t[s+i] = i + 0xFF00;
   2753 
   2754 	return 0;
   2755 
   2756 error:
   2757 	return 1;
   2758 }
   2759 
   2760 int
   2761 hfslib_get_hardlink(hfs_volume *vol, uint32_t inode_num,
   2762 		     hfs_catalog_keyed_record_t *rec,
   2763 		     hfs_callback_args *cbargs)
   2764 {
   2765 	hfs_catalog_keyed_record_t metadata;
   2766 	hfs_catalog_key_t key;
   2767 	char name[16];
   2768 	unichar_t name_uni[16];
   2769 	int i, len;
   2770 
   2771 	/* XXX: cache this */
   2772 	if (hfslib_find_catalog_record_with_key(vol,
   2773 						 &hfs_gMetadataDirectoryKey,
   2774 						 &metadata, cbargs) != 0
   2775 		|| metadata.type != HFS_REC_FLDR)
   2776 		return -1;
   2777 
   2778 	len = snprintf(name, sizeof(name), "iNode%d", inode_num);
   2779 	for (i=0; i<len; i++)
   2780 		name_uni[i] = name[i];
   2781 
   2782 	if (hfslib_make_catalog_key(metadata.folder.cnid, len, name_uni,
   2783 				     &key) == 0)
   2784 		return -1;
   2785 
   2786 	return hfslib_find_catalog_record_with_key(vol, &key, rec, cbargs);
   2787 }
   2788