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