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