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