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