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