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