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