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