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