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