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