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