1/* 2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include <sys/systm.h> 30#include <sys/kernel.h> 31#include <sys/malloc.h> 32#include <sys/stat.h> 33#include <sys/mount.h> 34#include <sys/vnode.h> 35#include <sys/dirent.h> 36#include <vfs/vfs_support.h> 37#include <libkern/libkern.h> 38 39#include <sys/utfconv.h> 40 41#include "hfs.h" 42#include "hfs_catalog.h" 43#include "hfs_format.h" 44#include "hfs_endian.h" 45 46#include "hfscommon/headers/BTreesInternal.h" 47#include "hfscommon/headers/BTreesPrivate.h" 48#include "hfscommon/headers/HFSUnicodeWrappers.h" 49 50 51/* 52 * Initialization of an FSBufferDescriptor structure. 53 */ 54#define BDINIT(bd, addr) { \ 55 (bd).bufferAddress = (addr); \ 56 (bd).itemSize = sizeof(*(addr)); \ 57 (bd).itemCount = 1; \ 58} 59 60 61struct btobj { 62 BTreeIterator iterator; 63 HFSPlusCatalogKey key; 64 CatalogRecord data; 65}; 66 67struct update_state { 68 struct cat_desc * s_desc; 69 struct cat_attr * s_attr; 70 struct cat_fork * s_datafork; 71 struct cat_fork * s_rsrcfork; 72 struct hfsmount * s_hfsmp; 73}; 74 75struct position_state { 76 int error; 77 u_int32_t count; 78 u_int32_t index; 79 u_int32_t parentID; 80 struct hfsmount *hfsmp; 81}; 82 83/* Map file mode type to directory entry types */ 84u_char modetodirtype[16] = { 85 DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN, 86 DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN, 87 DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN, 88 DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN 89}; 90#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12]) 91 92 93static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc, 94 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid); 95 96static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, 97 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp); 98 99/* Internal catalog support routines */ 100 101static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, 102 struct position_state *state); 103 104static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino); 105 106static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key); 107 108static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, 109 HFSPlusCatalogKey *key, int retry); 110 111static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key); 112 113static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, u_int32_t *recordSize); 114 115static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state); 116 117static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding, 118 int isdir, struct cat_desc *descp); 119 120static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp); 121 122static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding); 123static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp); 124static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp); 125 126static cnid_t getcnid(const CatalogRecord *crp); 127static u_long getencoding(const CatalogRecord *crp); 128static cnid_t getparentcnid(const CatalogRecord *recp); 129 130static int isadir(const CatalogRecord *crp); 131 132static int buildthread(void *keyp, void *recp, int std_hfs, int directory); 133 134static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp); 135 136 137__private_extern__ 138int 139cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p) 140{ 141 int lockflags = 0; 142 int result; 143 144 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread()) 145 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); 146 147 result = BTReserveSpace(hfsmp->hfs_catalog_cp->c_datafork, ops, (void*)cookie); 148 149 if (lockflags) 150 hfs_systemfile_unlock(hfsmp, lockflags); 151 152 return MacToVFSError(result); 153} 154 155__private_extern__ 156void 157cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p) 158{ 159 int lockflags = 0; 160 161 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread()) 162 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); 163 164 (void) BTReleaseReserve(hfsmp->hfs_catalog_cp->c_datafork, (void*)cookie); 165 166 if (lockflags) 167 hfs_systemfile_unlock(hfsmp, lockflags); 168} 169 170 171__private_extern__ 172void 173cat_convertattr( 174 struct hfsmount *hfsmp, 175 CatalogRecord * recp, 176 struct cat_attr *attrp, 177 struct cat_fork *datafp, 178 struct cat_fork *rsrcfp) 179{ 180 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord; 181 182 if (std_hfs) { 183 struct HFSPlusCatalogFile cnoderec; 184 185 promoteattr(hfsmp, recp, &cnoderec); 186 getbsdattr(hfsmp, &cnoderec, attrp); 187 } else { 188 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp); 189 } 190 191 if (isadir(recp)) 192 bzero(datafp, sizeof(*datafp)); 193 else if (std_hfs) { 194 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp); 195 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp); 196 } else { 197 /* Convert the data fork. */ 198 datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize; 199 datafp->cf_new_size = 0; 200 datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks; 201 if ((hfsmp->hfc_stage == HFC_RECORDING) && 202 (attrp->ca_atime >= hfsmp->hfc_timebase)) { 203 datafp->cf_bytesread = 204 recp->hfsPlusFile.dataFork.clumpSize * 205 HFSTOVCB(hfsmp)->blockSize; 206 } else { 207 datafp->cf_bytesread = 0; 208 } 209 datafp->cf_vblocks = 0; 210 bcopy(&recp->hfsPlusFile.dataFork.extents[0], 211 &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord)); 212 213 /* Convert the resource fork. */ 214 rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize; 215 rsrcfp->cf_new_size = 0; 216 rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks; 217 if ((hfsmp->hfc_stage == HFC_RECORDING) && 218 (attrp->ca_atime >= hfsmp->hfc_timebase)) { 219 datafp->cf_bytesread = 220 recp->hfsPlusFile.resourceFork.clumpSize * 221 HFSTOVCB(hfsmp)->blockSize; 222 } else { 223 datafp->cf_bytesread = 0; 224 } 225 rsrcfp->cf_vblocks = 0; 226 bcopy(&recp->hfsPlusFile.resourceFork.extents[0], 227 &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord)); 228 } 229} 230 231/* 232 * Convert a raw catalog key and record into an in-core catalog descriptor. 233 * 234 * Note: The caller is responsible for releasing the catalog descriptor. 235 */ 236__private_extern__ 237int 238cat_convertkey( 239 struct hfsmount *hfsmp, 240 CatalogKey *key, 241 CatalogRecord * recp, 242 struct cat_desc *descp) 243{ 244 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord; 245 HFSPlusCatalogKey * pluskey = NULL; 246 u_long encoding; 247 248 if (std_hfs) { 249 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 250 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding); 251 252 } else { 253 pluskey = (HFSPlusCatalogKey *)key; 254 encoding = getencoding(recp); 255 } 256 257 builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp); 258 if (std_hfs) { 259 FREE(pluskey, M_TEMP); 260 } 261 return (0); 262} 263 264 265/* 266 * cat_releasedesc 267 */ 268__private_extern__ 269void 270cat_releasedesc(struct cat_desc *descp) 271{ 272 const u_int8_t * name; 273 274 if (descp == NULL) 275 return; 276 277 if ((descp->cd_flags & CD_HASBUF) && 278 (descp->cd_nameptr != NULL)) { 279 name = descp->cd_nameptr; 280 descp->cd_nameptr = NULL; 281 descp->cd_namelen = 0; 282 vfs_removename((const char *)name); 283 } 284 descp->cd_nameptr = NULL; 285 descp->cd_namelen = 0; 286 descp->cd_flags &= ~CD_HASBUF; 287} 288 289/* 290 * These Catalog functions allow access to the HFS Catalog (database). 291 * The catalog b-tree lock must be acquired before calling any of these routines. 292 */ 293 294/* 295 * cat_lookup - lookup a catalog node using a cnode descriptor 296 * 297 * Note: The caller is responsible for releasing the output 298 * catalog descriptor (when supplied outdescp is non-null). 299 */ 300__private_extern__ 301int 302cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, 303 struct cat_desc *outdescp, struct cat_attr *attrp, 304 struct cat_fork *forkp, cnid_t *desc_cnid) 305{ 306 CatalogKey * keyp; 307 int std_hfs; 308 int result; 309 310 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); 311 312 MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK); 313 314 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1); 315 if (result) 316 goto exit; 317 318 result = cat_lookupbykey(hfsmp, keyp, 0, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid); 319 320 if (result == ENOENT) { 321 if (!std_hfs) { 322 struct cat_desc temp_desc; 323 if (outdescp == NULL) { 324 bzero(&temp_desc, sizeof(temp_desc)); 325 outdescp = &temp_desc; 326 } 327 result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp); 328 if (desc_cnid) { 329 *desc_cnid = outdescp->cd_cnid; 330 } 331 if (outdescp == &temp_desc) { 332 /* Release the local copy of desc */ 333 cat_releasedesc(outdescp); 334 } 335 } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) { 336 // make MacRoman key from utf-8 337 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp); 338 // update desc text encoding so that other catalog ops succeed 339 } 340 } 341exit: 342 FREE(keyp, M_TEMP); 343 344 return (result); 345} 346 347__private_extern__ 348int 349cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp) 350{ 351 struct BTreeIterator *iterator; 352 struct FSBufferDescriptor file_data; 353 struct HFSCatalogFile file_rec; 354 u_int16_t datasize; 355 FCB *fcb; 356 int result; 357 358 if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord) 359 return (EINVAL); 360 361 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum); 362 363 MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK); 364 bzero(&iterator[0], 2* sizeof(*iterator)); 365 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0); 366 if (result) 367 goto exit; 368 369 BDINIT(file_data, &file_rec); 370 result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]); 371 if (result) 372 goto exit; 373 374 if (file_rec.recordType != kHFSFileRecord) { 375 result = EISDIR; 376 goto exit; 377 } 378 379 if ((file_rec.flags & kHFSThreadExistsMask) == 0) { 380 struct FSBufferDescriptor thread_data; 381 struct HFSCatalogThread thread_rec; 382 383 file_rec.flags |= kHFSThreadExistsMask; 384 BDINIT(thread_data, &thread_rec); 385 thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0); 386 buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key); 387 388 result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize); 389 if (result) 390 goto exit; 391 392 (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize); 393 (void) BTFlushPath(fcb); 394 } 395exit: 396 (void) BTFlushPath(fcb); 397 FREE(iterator, M_TEMP); 398 399 return MacToVFSError(result); 400} 401 402 403/* 404 * cat_findname - obtain a descriptor from cnid 405 * 406 * Only a thread lookup is performed. 407 * 408 * Note: The caller is responsible for releasing the output 409 * catalog descriptor (when supplied outdescp is non-null). 410 411 */ 412__private_extern__ 413int 414cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp) 415{ 416 struct BTreeIterator * iterator; 417 FSBufferDescriptor btdata; 418 CatalogKey * keyp; 419 CatalogRecord * recp; 420 int isdir; 421 int result; 422 int std_hfs; 423 424 isdir = 0; 425 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); 426 427 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 428 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key); 429 iterator->hint.nodeNum = 0; 430 431 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); 432 BDINIT(btdata, recp); 433 434 result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL); 435 if (result) 436 goto exit; 437 438 /* Turn thread record into a cnode key (in place). */ 439 switch (recp->recordType) { 440 case kHFSFolderThreadRecord: 441 isdir = 1; 442 /* fall through */ 443 case kHFSFileThreadRecord: 444 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); 445 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; 446 break; 447 448 case kHFSPlusFolderThreadRecord: 449 isdir = 1; 450 /* fall through */ 451 case kHFSPlusFileThreadRecord: 452 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; 453 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + 454 (keyp->hfsPlus.nodeName.length * 2); 455 break; 456 default: 457 result = ENOENT; 458 goto exit; 459 } 460 if (std_hfs) { 461 HFSPlusCatalogKey * pluskey = NULL; 462 u_long encoding; 463 464 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 465 promotekey(hfsmp, &keyp->hfs, pluskey, &encoding); 466 builddesc(pluskey, cnid, 0, encoding, isdir, outdescp); 467 FREE(pluskey, M_TEMP); 468 469 } else { 470 builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp); 471 } 472exit: 473 FREE(recp, M_TEMP); 474 FREE(iterator, M_TEMP); 475 476 return MacToVFSError(result); 477} 478 479/* 480 * cat_idlookup - lookup a catalog node using a cnode id 481 * 482 * Note: The caller is responsible for releasing the output 483 * catalog descriptor (when supplied outdescp is non-null). 484 */ 485__private_extern__ 486int 487cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, 488 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp) 489{ 490 struct BTreeIterator * iterator; 491 FSBufferDescriptor btdata; 492 u_int16_t datasize; 493 CatalogKey * keyp; 494 CatalogRecord * recp; 495 int result; 496 int std_hfs; 497 498 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); 499 500 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 501 bzero(iterator, sizeof(*iterator)); 502 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key); 503 504 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); 505 BDINIT(btdata, recp); 506 507 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator, 508 &btdata, &datasize, iterator); 509 if (result) 510 goto exit; 511 512 /* Turn thread record into a cnode key (in place) */ 513 switch (recp->recordType) { 514 case kHFSFileThreadRecord: 515 case kHFSFolderThreadRecord: 516 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); 517 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; 518 break; 519 520 case kHFSPlusFileThreadRecord: 521 case kHFSPlusFolderThreadRecord: 522 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; 523 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + 524 (keyp->hfsPlus.nodeName.length * 2); 525 break; 526 527 default: 528 result = ENOENT; 529 goto exit; 530 } 531 532 result = cat_lookupbykey(hfsmp, keyp, allow_system_files, 0, 0, outdescp, attrp, forkp, NULL); 533 /* No corresponding file/folder record found for a thread record, 534 * mark the volume inconsistent. 535 */ 536 if (result == 0 && outdescp) { 537 cnid_t dcnid = outdescp->cd_cnid; 538 /* 539 * Just for sanity's case, let's make sure that 540 * the key in the thread matches the key in the record. 541 */ 542 if (cnid != dcnid) { 543 printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid); 544 result = ENOENT; 545 } 546 } 547exit: 548 FREE(recp, M_TEMP); 549 FREE(iterator, M_TEMP); 550 551 return MacToVFSError(result); 552} 553 554 555/* 556 * cat_lookupmangled - lookup a catalog node using a mangled name 557 */ 558static int 559cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, 560 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp) 561{ 562 cnid_t fileID; 563 u_int32_t prefixlen; 564 int result; 565 566 if (wantrsrc) 567 return (ENOENT); 568 569 fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen); 570 if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID) 571 return (ENOENT); 572 573 if (fileID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || 574 fileID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid || 575 fileID == hfsmp->hfs_jnlfileid || 576 fileID == hfsmp->hfs_jnlinfoblkid) { 577 return (ENOENT); 578 } 579 580 result = cat_idlookup(hfsmp, fileID, 0, outdescp, attrp, forkp); 581 if (result) 582 return (ENOENT); 583 /* It must be in the correct directory */ 584 if (descp->cd_parentcnid != outdescp->cd_parentcnid) 585 goto falsematch; 586 587 if (((u_int16_t)outdescp->cd_namelen < prefixlen) || 588 bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0) 589 goto falsematch; 590 591 return (0); 592 593falsematch: 594 cat_releasedesc(outdescp); 595 return (ENOENT); 596} 597 598 599/* 600 * cat_lookupbykey - lookup a catalog node using a cnode key 601 */ 602static int 603cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc, 604 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid) 605{ 606 struct BTreeIterator * iterator; 607 FSBufferDescriptor btdata; 608 CatalogRecord * recp; 609 u_int16_t datasize; 610 int result; 611 int std_hfs; 612 u_long ilink = 0; 613 cnid_t cnid = 0; 614 u_long encoding = 0; 615 616 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); 617 618 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); 619 BDINIT(btdata, recp); 620 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 621 bzero(iterator, sizeof(*iterator)); 622 iterator->hint.nodeNum = hint; 623 bcopy(keyp, &iterator->key, sizeof(CatalogKey)); 624 625 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator, 626 &btdata, &datasize, iterator); 627 if (result) 628 goto exit; 629 630 /* Save the cnid and encoding now in case there's a hard link */ 631 cnid = getcnid(recp); 632 encoding = getencoding(recp); 633 hint = iterator->hint.nodeNum; 634 635 /* Hide the journal files (if any) */ 636 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) && 637 ((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) && 638 !allow_system_files) { 639 640 result = ENOENT; 641 goto exit; 642 } 643 644 /* 645 * When a hardlink link is encountered, auto resolve it. 646 * 647 * The catalog record will change, and possibly its type. 648 */ 649 if (!std_hfs 650 && (attrp || forkp) 651 && (recp->recordType == kHFSPlusFileRecord) 652 && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) || 653 (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) { 654 int isdirlink = 0; 655 int isfilelink = 0; 656 657 if ((SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) && 658 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) { 659 isfilelink = 1; 660 } else if ((recp->hfsPlusFile.flags & kHFSHasLinkChainMask) && 661 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && 662 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) { 663 isdirlink = 1; 664 } 665 if (isfilelink || isdirlink) { 666 ilink = recp->hfsPlusFile.hl_linkReference; 667 (void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp); 668 } 669 } 670 671 if (attrp != NULL) { 672 if (std_hfs) { 673 struct HFSPlusCatalogFile cnoderec; 674 675 promoteattr(hfsmp, recp, &cnoderec); 676 getbsdattr(hfsmp, &cnoderec, attrp); 677 } else { 678 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp); 679 if (ilink) 680 attrp->ca_linkref = ilink; 681 } 682 } 683 if (forkp != NULL) { 684 if (isadir(recp)) { 685 bzero(forkp, sizeof(*forkp)); 686 } else if (std_hfs) { 687 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp); 688 } else if (wantrsrc) { 689 /* Convert the resource fork. */ 690 forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize; 691 forkp->cf_new_size = 0; 692 forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks; 693 if ((hfsmp->hfc_stage == HFC_RECORDING) && 694 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) { 695 forkp->cf_bytesread = 696 recp->hfsPlusFile.resourceFork.clumpSize * 697 HFSTOVCB(hfsmp)->blockSize; 698 } else { 699 forkp->cf_bytesread = 0; 700 } 701 forkp->cf_vblocks = 0; 702 bcopy(&recp->hfsPlusFile.resourceFork.extents[0], 703 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord)); 704 } else { 705 int i; 706 u_int32_t validblks; 707 708 /* Convert the data fork. */ 709 forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize; 710 forkp->cf_new_size = 0; 711 forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks; 712 if ((hfsmp->hfc_stage == HFC_RECORDING) && 713 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) { 714 forkp->cf_bytesread = 715 recp->hfsPlusFile.dataFork.clumpSize * 716 HFSTOVCB(hfsmp)->blockSize; 717 } else { 718 forkp->cf_bytesread = 0; 719 } 720 forkp->cf_vblocks = 0; 721 bcopy(&recp->hfsPlusFile.dataFork.extents[0], 722 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord)); 723 724 /* Validate the fork's resident extents. */ 725 validblks = 0; 726 for (i = 0; i < kHFSPlusExtentDensity; ++i) { 727 if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) { 728 /* Suppress any bad extents so a remove can succeed. */ 729 forkp->cf_extents[i].startBlock = 0; 730 forkp->cf_extents[i].blockCount = 0; 731 /* Disable writes */ 732 if (attrp != NULL) { 733 attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH; 734 } 735 } else { 736 validblks += forkp->cf_extents[i].blockCount; 737 } 738 } 739 /* Adjust for any missing blocks. */ 740 if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) { 741 off_t psize; 742 743 forkp->cf_blocks = validblks; 744 if (attrp != NULL) { 745 attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks; 746 } 747 psize = (off_t)validblks * (off_t)hfsmp->blockSize; 748 if (psize < forkp->cf_size) { 749 forkp->cf_size = psize; 750 } 751 752 } 753 } 754 } 755 if (descp != NULL) { 756 HFSPlusCatalogKey * pluskey = NULL; 757 758 if (std_hfs) { 759 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 760 promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding); 761 762 } else { 763 pluskey = (HFSPlusCatalogKey *)&iterator->key; 764 } 765 builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp); 766 if (std_hfs) { 767 FREE(pluskey, M_TEMP); 768 } 769 } 770 771 if (desc_cnid != NULL) { 772 *desc_cnid = cnid; 773 } 774exit: 775 FREE(iterator, M_TEMP); 776 FREE(recp, M_TEMP); 777 778 return MacToVFSError(result); 779} 780 781 782/* 783 * cat_create - create a node in the catalog 784 * 785 * NOTE: both the catalog file and attribute file locks must 786 * be held before calling this function. 787 * 788 * The caller is responsible for releasing the output 789 * catalog descriptor (when supplied outdescp is non-null). 790 */ 791__private_extern__ 792int 793cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp, 794 struct cat_desc *out_descp) 795{ 796 FCB * fcb; 797 struct btobj * bto; 798 FSBufferDescriptor btdata; 799 u_int32_t nextCNID; 800 u_int32_t datalen; 801 int std_hfs; 802 int result = 0; 803 u_long encoding = kTextEncodingMacRoman; 804 int modeformat; 805 806 modeformat = attrp->ca_mode & S_IFMT; 807 808 fcb = hfsmp->hfs_catalog_cp->c_datafork; 809 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); 810 811 /* 812 * Get the next CNID. We can change it since we hold the catalog lock. 813 */ 814 nextCNID = hfsmp->vcbNxtCNID; 815 if (nextCNID == 0xFFFFFFFF) { 816 if (std_hfs) { 817 return (ENOSPC); 818 } else { 819 HFS_MOUNT_LOCK(hfsmp, TRUE) 820 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; 821 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask; 822 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 823 } 824 } else { 825 hfsmp->vcbNxtCNID++; 826 } 827 MarkVCBDirty(hfsmp); 828 829 /* Get space for iterator, key and data */ 830 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK); 831 bto->iterator.hint.nodeNum = 0; 832 833 result = buildkey(hfsmp, descp, &bto->key, 0); 834 if (result) 835 goto exit; 836 837 if (!std_hfs) { 838 encoding = hfs_pickencoding(bto->key.nodeName.unicode, 839 bto->key.nodeName.length); 840 hfs_setencodingbits(hfsmp, encoding); 841 } 842 843 /* 844 * Insert the thread record first 845 */ 846 if (!std_hfs || (modeformat == S_IFDIR)) { 847 datalen = buildthread((void*)&bto->key, &bto->data, std_hfs, 848 S_ISDIR(attrp->ca_mode)); 849 btdata.bufferAddress = &bto->data; 850 btdata.itemSize = datalen; 851 btdata.itemCount = 1; 852 853 for (;;) { 854 // this call requires the attribute file lock to be held 855 result = file_attribute_exist(hfsmp, nextCNID); 856 if (result == EEXIST) { 857 // that cnid has orphaned attributes so just skip it. 858 if (++nextCNID < kHFSFirstUserCatalogNodeID) { 859 nextCNID = kHFSFirstUserCatalogNodeID; 860 } 861 continue; 862 } 863 if (result) goto exit; 864 865 buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key); 866 867 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); 868 if ((result == btExists) && !std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { 869 /* 870 * Allow CNIDs on HFS Plus volumes to wrap around 871 */ 872 if (++nextCNID < kHFSFirstUserCatalogNodeID) { 873 nextCNID = kHFSFirstUserCatalogNodeID; 874 } 875 continue; 876 } 877 break; 878 } 879 if (result) goto exit; 880 } 881 882 /* 883 * CNID is now established. If we have wrapped then 884 * update the vcbNxtCNID. 885 */ 886 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { 887 hfsmp->vcbNxtCNID = nextCNID + 1; 888 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) { 889 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; 890 } 891 } 892 893 /* 894 * Now insert the file/directory record 895 */ 896 buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen); 897 btdata.bufferAddress = &bto->data; 898 btdata.itemSize = datalen; 899 btdata.itemCount = 1; 900 901 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key)); 902 903 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); 904 if (result) { 905 if (result == btExists) 906 result = EEXIST; 907 908 /* Back out the thread record */ 909 if (!std_hfs || S_ISDIR(attrp->ca_mode)) { 910 buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key); 911 if (BTDeleteRecord(fcb, &bto->iterator)) { 912 /* Error on deleting extra thread record, mark 913 * volume inconsistent 914 */ 915 printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp->vcbVN); 916 hfs_mark_volume_inconsistent(hfsmp); 917 } 918 } 919 goto exit; 920 } 921 922 /* 923 * Insert was successful, update name, parent and volume 924 */ 925 if (out_descp != NULL) { 926 HFSPlusCatalogKey * pluskey = NULL; 927 928 if (std_hfs) { 929 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 930 promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding); 931 932 } else 933 pluskey = (HFSPlusCatalogKey *)&bto->iterator.key; 934 935 builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum, 936 encoding, S_ISDIR(attrp->ca_mode), out_descp); 937 if (std_hfs) { 938 FREE(pluskey, M_TEMP); 939 } 940 } 941 attrp->ca_fileid = nextCNID; 942 943exit: 944 (void) BTFlushPath(fcb); 945 FREE(bto, M_TEMP); 946 947 return MacToVFSError(result); 948} 949 950 951/* 952 * cnode_rename - rename a catalog node 953 * 954 * Assumes that the target's directory exists. 955 * 956 * Order of B-tree operations: 957 * 1. BTSearchRecord(from_cnode, &data); 958 * 2. BTInsertRecord(to_cnode, &data); 959 * 3. BTDeleteRecord(from_cnode); 960 * 4. BTDeleteRecord(from_thread); 961 * 5. BTInsertRecord(to_thread); 962 * 963 * Note: The caller is responsible for releasing the output 964 * catalog descriptor (when supplied out_cdp is non-null). 965 */ 966__private_extern__ 967int 968cat_rename ( 969 struct hfsmount * hfsmp, 970 struct cat_desc * from_cdp, 971 struct cat_desc * todir_cdp, 972 struct cat_desc * to_cdp, 973 struct cat_desc * out_cdp ) 974{ 975 struct BTreeIterator * to_iterator = NULL; 976 struct BTreeIterator * from_iterator = NULL; 977 FSBufferDescriptor btdata; 978 CatalogRecord * recp = NULL; 979 HFSPlusCatalogKey * to_key; 980 ExtendedVCB * vcb; 981 FCB * fcb; 982 u_int16_t datasize; 983 int result = 0; 984 int sourcegone = 0; 985 int skipthread = 0; 986 int directory = from_cdp->cd_flags & CD_ISDIR; 987 int is_dirlink = 0; 988 int std_hfs; 989 u_long encoding = 0; 990 991 vcb = HFSTOVCB(hfsmp); 992 fcb = GetFileControlBlock(vcb->catalogRefNum); 993 std_hfs = (vcb->vcbSigWord == kHFSSigWord); 994 995 if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0) 996 return (EINVAL); 997 998 MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK); 999 bzero(from_iterator, sizeof(*from_iterator)); 1000 if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0))) 1001 goto exit; 1002 1003 MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK); 1004 bzero(to_iterator, sizeof(*to_iterator)); 1005 if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0))) 1006 goto exit; 1007 1008 to_key = (HFSPlusCatalogKey *)&to_iterator->key; 1009 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); 1010 BDINIT(btdata, recp); 1011 1012 /* 1013 * When moving a directory, make sure its a valid move. 1014 */ 1015 if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) { 1016 struct BTreeIterator iterator; 1017 cnid_t cnid = from_cdp->cd_cnid; 1018 cnid_t pathcnid = todir_cdp->cd_parentcnid; 1019 1020 /* First check the obvious ones */ 1021 if (cnid == fsRtDirID || 1022 cnid == to_cdp->cd_parentcnid || 1023 cnid == pathcnid) { 1024 result = EINVAL; 1025 goto exit; 1026 } 1027 bzero(&iterator, sizeof(iterator)); 1028 /* 1029 * Traverse destination path all the way back to the root 1030 * making sure that source directory is not encountered. 1031 * 1032 */ 1033 while (pathcnid > fsRtDirID) { 1034 buildthreadkey(pathcnid, std_hfs, 1035 (CatalogKey *)&iterator.key); 1036 result = BTSearchRecord(fcb, &iterator, &btdata, 1037 &datasize, NULL); 1038 if (result) goto exit; 1039 1040 pathcnid = getparentcnid(recp); 1041 if (pathcnid == cnid || pathcnid == 0) { 1042 result = EINVAL; 1043 goto exit; 1044 } 1045 } 1046 } 1047 1048 /* 1049 * Step 1: Find cnode data at old location 1050 */ 1051 result = BTSearchRecord(fcb, from_iterator, &btdata, 1052 &datasize, from_iterator); 1053 if (result) { 1054 if (std_hfs || (result != btNotFound)) 1055 goto exit; 1056 1057 struct cat_desc temp_desc; 1058 1059 /* Probably the node has mangled name */ 1060 result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL); 1061 if (result) 1062 goto exit; 1063 1064 /* The file has mangled name. Search the cnode data using full name */ 1065 bzero(from_iterator, sizeof(*from_iterator)); 1066 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0); 1067 if (result) { 1068 cat_releasedesc(&temp_desc); 1069 goto exit; 1070 } 1071 1072 result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator); 1073 if (result) { 1074 cat_releasedesc(&temp_desc); 1075 goto exit; 1076 } 1077 1078 cat_releasedesc(&temp_desc); 1079 } 1080 1081 /* Check if the source is directory hard link. We do not change 1082 * directory flag because it is later used to initialize result descp 1083 */ 1084 if ((!std_hfs) && 1085 (directory) && 1086 (recp->recordType == kHFSPlusFileRecord) && 1087 (recp->hfsPlusFile.flags & kHFSHasLinkChainMask)) { 1088 is_dirlink = 1; 1089 } 1090 1091 /* 1092 * Update the text encoding (on disk and in descriptor). 1093 * 1094 * Note that hardlink inodes don't require a text encoding hint. 1095 */ 1096 if (!std_hfs && 1097 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid && 1098 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 1099 encoding = hfs_pickencoding(to_key->nodeName.unicode, to_key->nodeName.length); 1100 hfs_setencodingbits(hfsmp, encoding); 1101 recp->hfsPlusFile.textEncoding = encoding; 1102 if (out_cdp) 1103 out_cdp->cd_encoding = encoding; 1104 } 1105 1106 if (std_hfs && !directory && 1107 !(recp->hfsFile.flags & kHFSThreadExistsMask)) 1108 skipthread = 1; 1109#if 0 1110 /* 1111 * If the keys are identical then there's nothing left to do! 1112 * 1113 * update the hint and exit 1114 * 1115 */ 1116 if (std_hfs && hfskeycompare(to_key, iter->key) == 0) 1117 goto exit; 1118 if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0) 1119 goto exit; 1120#endif 1121 1122 /* Step 2: Insert cnode at new location */ 1123 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize); 1124 if (result == btExists) { 1125 int fromtype = recp->recordType; 1126 1127 if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid) 1128 goto exit; /* EEXIST */ 1129 1130 /* Find cnode data at new location */ 1131 result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL); 1132 if (result) 1133 goto exit; 1134 1135 if ((fromtype != recp->recordType) || 1136 (from_cdp->cd_cnid != getcnid(recp))) { 1137 result = EEXIST; 1138 goto exit; /* EEXIST */ 1139 } 1140 /* The old name is a case variant and must be removed */ 1141 result = BTDeleteRecord(fcb, from_iterator); 1142 if (result) 1143 goto exit; 1144 1145 /* Insert cnode (now that case duplicate is gone) */ 1146 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize); 1147 if (result) { 1148 /* Try and restore original before leaving */ 1149 // XXXdbg 1150 #if 1 1151 { 1152 int err; 1153 err = BTInsertRecord(fcb, from_iterator, &btdata, datasize); 1154 if (err) { 1155 printf("cat_create: could not undo (BTInsert = %d)", err); 1156 hfs_mark_volume_inconsistent(hfsmp); 1157 result = err; 1158 goto exit; 1159 } 1160 } 1161 #else 1162 (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize); 1163 #endif 1164 goto exit; 1165 } 1166 sourcegone = 1; 1167 } 1168 if (result) 1169 goto exit; 1170 1171 /* Step 3: Remove cnode from old location */ 1172 if (!sourcegone) { 1173 result = BTDeleteRecord(fcb, from_iterator); 1174 if (result) { 1175 /* Try and delete new record before leaving */ 1176 // XXXdbg 1177 #if 1 1178 { 1179 int err; 1180 err = BTDeleteRecord(fcb, to_iterator); 1181 if (err) { 1182 printf("cat_create: could not undo (BTDelete = %d)", err); 1183 hfs_mark_volume_inconsistent(hfsmp); 1184 result = err; 1185 goto exit; 1186 } 1187 } 1188 #else 1189 (void) BTDeleteRecord(fcb, to_iterator); 1190 #endif 1191 goto exit; 1192 } 1193 } 1194 1195 /* #### POINT OF NO RETURN #### */ 1196 1197 /* 1198 * Step 4: Remove cnode's old thread record 1199 */ 1200 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key); 1201 (void) BTDeleteRecord(fcb, from_iterator); 1202 1203 /* 1204 * Step 5: Insert cnode's new thread record 1205 * (optional for HFS files) 1206 */ 1207 if (!skipthread) { 1208 /* For directory hard links, always create a file thread 1209 * record. For everything else, use the directory flag. 1210 */ 1211 if (is_dirlink) { 1212 datasize = buildthread(&to_iterator->key, recp, std_hfs, false); 1213 } else { 1214 datasize = buildthread(&to_iterator->key, recp, std_hfs, directory); 1215 } 1216 btdata.itemSize = datasize; 1217 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key); 1218 result = BTInsertRecord(fcb, from_iterator, &btdata, datasize); 1219 } 1220 1221 if (out_cdp) { 1222 HFSPlusCatalogKey * pluskey = NULL; 1223 1224 if (std_hfs) { 1225 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 1226 promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding); 1227 1228 /* Save the real encoding hint in the Finder Info (field 4). */ 1229 if (directory && from_cdp->cd_cnid == kHFSRootFolderID) { 1230 u_long realhint; 1231 1232 realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length); 1233 vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint); 1234 } 1235 1236 } else 1237 pluskey = (HFSPlusCatalogKey *)&to_iterator->key; 1238 1239 builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum, 1240 encoding, directory, out_cdp); 1241 if (std_hfs) { 1242 FREE(pluskey, M_TEMP); 1243 } 1244 } 1245exit: 1246 (void) BTFlushPath(fcb); 1247 if (from_iterator) 1248 FREE(from_iterator, M_TEMP); 1249 if (to_iterator) 1250 FREE(to_iterator, M_TEMP); 1251 if (recp) 1252 FREE(recp, M_TEMP); 1253 return MacToVFSError(result); 1254} 1255 1256 1257/* 1258 * cat_delete - delete a node from the catalog 1259 * 1260 * Order of B-tree operations: 1261 * 1. BTDeleteRecord(cnode); 1262 * 2. BTDeleteRecord(thread); 1263 * 3. BTUpdateRecord(parent); 1264 */ 1265__private_extern__ 1266int 1267cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp) 1268{ 1269 FCB * fcb; 1270 BTreeIterator *iterator; 1271 cnid_t cnid; 1272 int std_hfs; 1273 int result; 1274 1275 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1276 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); 1277 1278 /* Preflight check: 1279 * 1280 * The root directory cannot be deleted 1281 * A directory must be empty 1282 * A file must be zero length (no blocks) 1283 */ 1284 if (descp->cd_cnid < kHFSFirstUserCatalogNodeID || 1285 descp->cd_parentcnid == kHFSRootParentID) 1286 return (EINVAL); 1287 1288 /* XXX Preflight Missing */ 1289 1290 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 1291 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 1292 iterator->hint.nodeNum = 0; 1293 1294 /* 1295 * Derive a key from either the file ID (for a virtual inode) 1296 * or the descriptor. 1297 */ 1298 if (descp->cd_namelen == 0) { 1299 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key); 1300 cnid = attrp->ca_fileid; 1301 } else { 1302 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0); 1303 cnid = descp->cd_cnid; 1304 } 1305 if (result) 1306 goto exit; 1307 1308 /* Delete record */ 1309 result = BTDeleteRecord(fcb, iterator); 1310 if (result) { 1311 if (std_hfs || (result != btNotFound)) 1312 goto exit; 1313 1314 struct cat_desc temp_desc; 1315 1316 /* Probably the node has mangled name */ 1317 result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL); 1318 if (result) 1319 goto exit; 1320 1321 /* The file has mangled name. Delete the file using full name */ 1322 bzero(iterator, sizeof(*iterator)); 1323 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0); 1324 cnid = temp_desc.cd_cnid; 1325 if (result) { 1326 cat_releasedesc(&temp_desc); 1327 goto exit; 1328 } 1329 1330 result = BTDeleteRecord(fcb, iterator); 1331 if (result) { 1332 cat_releasedesc(&temp_desc); 1333 goto exit; 1334 } 1335 1336 cat_releasedesc(&temp_desc); 1337 } 1338 1339 /* Delete thread record. On error, mark volume inconsistent */ 1340 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key); 1341 if (BTDeleteRecord(fcb, iterator)) { 1342 if (!std_hfs) { 1343 printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp->vcbVN); 1344 hfs_mark_volume_inconsistent(hfsmp); 1345 } 1346 } 1347 1348exit: 1349 (void) BTFlushPath(fcb); 1350 1351 return MacToVFSError(result); 1352} 1353 1354 1355/* 1356 * cnode_update - update the catalog node described by descp 1357 * using the data from attrp and forkp. 1358 */ 1359__private_extern__ 1360int 1361cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp, 1362 struct cat_fork *dataforkp, struct cat_fork *rsrcforkp) 1363{ 1364 FCB * fcb; 1365 BTreeIterator * iterator; 1366 struct update_state state; 1367 int std_hfs; 1368 int result; 1369 1370 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1371 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD); 1372 1373 state.s_desc = descp; 1374 state.s_attr = attrp; 1375 state.s_datafork = dataforkp; 1376 state.s_rsrcfork = rsrcforkp; 1377 state.s_hfsmp = hfsmp; 1378 1379 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 1380 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 1381 1382 /* 1383 * For open-deleted files we need to do a lookup by cnid 1384 * (using thread rec). 1385 * 1386 * For hard links, the target of the update is the inode 1387 * itself (not the link record) so a lookup by fileid 1388 * (i.e. thread rec) is needed. 1389 */ 1390 if ((descp->cd_cnid != attrp->ca_fileid) || 1391 (descp->cd_namelen == 0) || 1392 (attrp->ca_recflags & kHFSHasLinkChainMask)) { 1393 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key); 1394 } else { 1395 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0); 1396 } 1397 if (result) 1398 goto exit; 1399 1400 /* Pass a node hint */ 1401 iterator->hint.nodeNum = descp->cd_hint; 1402 1403 result = BTUpdateRecord(fcb, iterator, 1404 (IterateCallBackProcPtr)catrec_update, &state); 1405 if (result) 1406 goto exit; 1407 1408 /* Update the node hint. */ 1409 descp->cd_hint = iterator->hint.nodeNum; 1410 1411exit: 1412 (void) BTFlushPath(fcb); 1413 1414 return MacToVFSError(result); 1415} 1416 1417/* 1418 * catrec_update - Update the fields of a catalog record 1419 * This is called from within BTUpdateRecord. 1420 */ 1421static int 1422catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state) 1423{ 1424 struct cat_desc *descp; 1425 struct cat_attr *attrp; 1426 struct cat_fork *forkp; 1427 struct hfsmount *hfsmp; 1428 long blksize; 1429 int i; 1430 1431 descp = state->s_desc; 1432 attrp = state->s_attr; 1433 hfsmp = state->s_hfsmp; 1434 blksize = HFSTOVCB(hfsmp)->blockSize; 1435 1436 switch (crp->recordType) { 1437 case kHFSFolderRecord: { 1438 HFSCatalogFolder *dir; 1439 1440 dir = (struct HFSCatalogFolder *)crp; 1441 /* Do a quick sanity check */ 1442 if ((ckp->hfs.parentID != descp->cd_parentcnid) || 1443 (dir->folderID != descp->cd_cnid)) 1444 return (btNotFound); 1445 dir->valence = attrp->ca_entries; 1446 dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime)); 1447 dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime)); 1448 dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime)); 1449 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16); 1450 bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16); 1451 break; 1452 } 1453 case kHFSFileRecord: { 1454 HFSCatalogFile *file; 1455 1456 file = (struct HFSCatalogFile *)crp; 1457 /* Do a quick sanity check */ 1458 if ((ckp->hfs.parentID != descp->cd_parentcnid) || 1459 (file->fileID != attrp->ca_fileid)) 1460 return (btNotFound); 1461 file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime)); 1462 file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime)); 1463 file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime)); 1464 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16); 1465 bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16); 1466 if (state->s_rsrcfork) { 1467 forkp = state->s_rsrcfork; 1468 file->rsrcLogicalSize = forkp->cf_size; 1469 file->rsrcPhysicalSize = forkp->cf_blocks * blksize; 1470 for (i = 0; i < kHFSExtentDensity; ++i) { 1471 file->rsrcExtents[i].startBlock = 1472 (u_int16_t)forkp->cf_extents[i].startBlock; 1473 file->rsrcExtents[i].blockCount = 1474 (u_int16_t)forkp->cf_extents[i].blockCount; 1475 } 1476 } 1477 if (state->s_datafork) { 1478 forkp = state->s_datafork; 1479 file->dataLogicalSize = forkp->cf_size; 1480 file->dataPhysicalSize = forkp->cf_blocks * blksize; 1481 for (i = 0; i < kHFSExtentDensity; ++i) { 1482 file->dataExtents[i].startBlock = 1483 (u_int16_t)forkp->cf_extents[i].startBlock; 1484 file->dataExtents[i].blockCount = 1485 (u_int16_t)forkp->cf_extents[i].blockCount; 1486 } 1487 } 1488 1489 /* Synchronize the lock state */ 1490 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) 1491 file->flags |= kHFSFileLockedMask; 1492 else 1493 file->flags &= ~kHFSFileLockedMask; 1494 break; 1495 } 1496 case kHFSPlusFolderRecord: { 1497 HFSPlusCatalogFolder *dir; 1498 1499 dir = (struct HFSPlusCatalogFolder *)crp; 1500 /* Do a quick sanity check */ 1501 if (dir->folderID != attrp->ca_fileid) { 1502 printf("catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid); 1503 return (btNotFound); 1504 } 1505 dir->flags = attrp->ca_recflags; 1506 dir->valence = attrp->ca_entries; 1507 dir->createDate = to_hfs_time(attrp->ca_itime); 1508 dir->contentModDate = to_hfs_time(attrp->ca_mtime); 1509 dir->backupDate = to_hfs_time(attrp->ca_btime); 1510 dir->accessDate = to_hfs_time(attrp->ca_atime); 1511 attrp->ca_atimeondisk = attrp->ca_atime; 1512 dir->attributeModDate = to_hfs_time(attrp->ca_ctime); 1513 /* Note: directory hardlink inodes don't require a text encoding hint. */ 1514 if (ckp->hfsPlus.parentID != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 1515 dir->textEncoding = descp->cd_encoding; 1516 } 1517 dir->folderCount = attrp->ca_dircount; 1518 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32); 1519 /* 1520 * Update the BSD Info if it was already initialized on 1521 * disk or if the runtime values have been modified. 1522 * 1523 * If the BSD info was already initialized, but 1524 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are 1525 * probably different than what was on disk. We don't want 1526 * to overwrite the on-disk values (so if we turn off 1527 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again). 1528 * This way, we can still change fields like the mode or 1529 * dates even when MNT_UNKNOWNPERMISSIONS is set. 1530 * 1531 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown 1532 * won't change the uid or gid from their defaults. So, if 1533 * the BSD info wasn't set, and the runtime values are not 1534 * default, then what changed was the mode or flags. We 1535 * have to set the uid and gid to something, so use the 1536 * supplied values (which will be default), which has the 1537 * same effect as creating a new file while 1538 * MNT_UNKNOWNPERMISSIONS is set. 1539 */ 1540 if ((dir->bsdInfo.fileMode != 0) || 1541 (attrp->ca_flags != 0) || 1542 (attrp->ca_uid != hfsmp->hfs_uid) || 1543 (attrp->ca_gid != hfsmp->hfs_gid) || 1544 ((attrp->ca_mode & ALLPERMS) != 1545 (hfsmp->hfs_dir_mask & ACCESSPERMS))) { 1546 if ((dir->bsdInfo.fileMode == 0) || 1547 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) { 1548 dir->bsdInfo.ownerID = attrp->ca_uid; 1549 dir->bsdInfo.groupID = attrp->ca_gid; 1550 } 1551 dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF; 1552 dir->bsdInfo.adminFlags = attrp->ca_flags >> 16; 1553 dir->bsdInfo.fileMode = attrp->ca_mode; 1554 /* A directory hardlink has a link count. */ 1555 if (attrp->ca_linkcount > 1 || dir->hl_linkCount > 1) { 1556 dir->hl_linkCount = attrp->ca_linkcount; 1557 } 1558 } 1559 break; 1560 } 1561 case kHFSPlusFileRecord: { 1562 HFSPlusCatalogFile *file; 1563 1564 file = (struct HFSPlusCatalogFile *)crp; 1565 /* Do a quick sanity check */ 1566 if (file->fileID != attrp->ca_fileid) 1567 return (btNotFound); 1568 file->flags = attrp->ca_recflags; 1569 file->createDate = to_hfs_time(attrp->ca_itime); 1570 file->contentModDate = to_hfs_time(attrp->ca_mtime); 1571 file->backupDate = to_hfs_time(attrp->ca_btime); 1572 file->accessDate = to_hfs_time(attrp->ca_atime); 1573 attrp->ca_atimeondisk = attrp->ca_atime; 1574 file->attributeModDate = to_hfs_time(attrp->ca_ctime); 1575 /* 1576 * Note: file hardlink inodes don't require a text encoding 1577 * hint, but they do have a first link value. 1578 */ 1579 if (ckp->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) { 1580 file->hl_firstLinkID = attrp->ca_firstlink; 1581 } else { 1582 file->textEncoding = descp->cd_encoding; 1583 } 1584 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32); 1585 /* 1586 * Update the BSD Info if it was already initialized on 1587 * disk or if the runtime values have been modified. 1588 * 1589 * If the BSD info was already initialized, but 1590 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are 1591 * probably different than what was on disk. We don't want 1592 * to overwrite the on-disk values (so if we turn off 1593 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again). 1594 * This way, we can still change fields like the mode or 1595 * dates even when MNT_UNKNOWNPERMISSIONS is set. 1596 * 1597 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown 1598 * won't change the uid or gid from their defaults. So, if 1599 * the BSD info wasn't set, and the runtime values are not 1600 * default, then what changed was the mode or flags. We 1601 * have to set the uid and gid to something, so use the 1602 * supplied values (which will be default), which has the 1603 * same effect as creating a new file while 1604 * MNT_UNKNOWNPERMISSIONS is set. 1605 */ 1606 if ((file->bsdInfo.fileMode != 0) || 1607 (attrp->ca_flags != 0) || 1608 (attrp->ca_uid != hfsmp->hfs_uid) || 1609 (attrp->ca_gid != hfsmp->hfs_gid) || 1610 ((attrp->ca_mode & ALLPERMS) != 1611 (hfsmp->hfs_file_mask & ACCESSPERMS))) { 1612 if ((file->bsdInfo.fileMode == 0) || 1613 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) { 1614 file->bsdInfo.ownerID = attrp->ca_uid; 1615 file->bsdInfo.groupID = attrp->ca_gid; 1616 } 1617 file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF; 1618 file->bsdInfo.adminFlags = attrp->ca_flags >> 16; 1619 file->bsdInfo.fileMode = attrp->ca_mode; 1620 } 1621 if (state->s_rsrcfork) { 1622 forkp = state->s_rsrcfork; 1623 file->resourceFork.logicalSize = forkp->cf_size; 1624 file->resourceFork.totalBlocks = forkp->cf_blocks; 1625 bcopy(&forkp->cf_extents[0], &file->resourceFork.extents, 1626 sizeof(HFSPlusExtentRecord)); 1627 /* Push blocks read to disk */ 1628 file->resourceFork.clumpSize = 1629 howmany(forkp->cf_bytesread, blksize); 1630 } 1631 if (state->s_datafork) { 1632 forkp = state->s_datafork; 1633 file->dataFork.logicalSize = forkp->cf_size; 1634 file->dataFork.totalBlocks = forkp->cf_blocks; 1635 bcopy(&forkp->cf_extents[0], &file->dataFork.extents, 1636 sizeof(HFSPlusExtentRecord)); 1637 /* Push blocks read to disk */ 1638 file->dataFork.clumpSize = 1639 howmany(forkp->cf_bytesread, blksize); 1640 } 1641 1642 if ((file->resourceFork.extents[0].startBlock != 0) && 1643 (file->resourceFork.extents[0].startBlock == 1644 file->dataFork.extents[0].startBlock)) { 1645 panic("catrec_update: rsrc fork == data fork"); 1646 } 1647 1648 /* Synchronize the lock state */ 1649 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) 1650 file->flags |= kHFSFileLockedMask; 1651 else 1652 file->flags &= ~kHFSFileLockedMask; 1653 1654 /* Push out special field if necessary */ 1655 if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) { 1656 file->bsdInfo.special.rawDevice = attrp->ca_rdev; 1657 } else if (descp->cd_cnid != attrp->ca_fileid || attrp->ca_linkcount == 2) { 1658 file->hl_linkCount = attrp->ca_linkcount; 1659 } 1660 break; 1661 } 1662 default: 1663 return (btNotFound); 1664 } 1665 return (0); 1666} 1667 1668/* This function sets kHFSHasChildLinkBit in a directory hierarchy in the 1669 * catalog btree of given cnid by walking up the parent chain till it reaches 1670 * either the root folder, or the private metadata directory for storing 1671 * directory hard links. This function updates the corresponding in-core 1672 * cnode, if any, and the directory record in the catalog btree. 1673 * On success, returns zero. On failure, returns non-zero value. 1674 */ 1675__private_extern__ 1676int 1677cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid) 1678{ 1679 int retval = 0; 1680 int lockflags = 0; 1681 struct cat_desc desc; 1682 struct cat_attr attr; 1683 1684 while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) && 1685 (cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) { 1686 /* Update the bit in corresponding cnode, if any, in the hash. 1687 * If the cnode has the bit already set, stop the traversal. 1688 */ 1689 retval = hfs_chash_set_childlinkbit(hfsmp->hfs_raw_dev, cnid); 1690 if (retval == 0) { 1691 break; 1692 } 1693 1694 /* Update the catalog record on disk if either cnode was not 1695 * found in the hash, or if a cnode was found and the cnode 1696 * did not have the bit set previously. 1697 */ 1698 retval = hfs_start_transaction(hfsmp); 1699 if (retval) { 1700 break; 1701 } 1702 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK); 1703 1704 /* Look up our catalog folder record */ 1705 retval = cat_idlookup(hfsmp, cnid, 0, &desc, &attr, NULL); 1706 if (retval) { 1707 hfs_systemfile_unlock(hfsmp, lockflags); 1708 hfs_end_transaction(hfsmp); 1709 break; 1710 } 1711 1712 /* Update the bit in the catalog record */ 1713 attr.ca_recflags |= kHFSHasChildLinkMask; 1714 retval = cat_update(hfsmp, &desc, &attr, NULL, NULL); 1715 if (retval) { 1716 hfs_systemfile_unlock(hfsmp, lockflags); 1717 hfs_end_transaction(hfsmp); 1718 cat_releasedesc(&desc); 1719 break; 1720 } 1721 1722 hfs_systemfile_unlock(hfsmp, lockflags); 1723 hfs_end_transaction(hfsmp); 1724 1725 cnid = desc.cd_parentcnid; 1726 cat_releasedesc(&desc); 1727 } 1728 1729 return retval; 1730} 1731 1732/* This function traverses the parent directory hierarchy from the given 1733 * directory to one level below root directory and checks if any of its 1734 * ancestors is - 1735 * 1. A directory hard link. 1736 * 2. The 'pointed at' directory. 1737 * If any of these conditions fail or an internal error is encountered 1738 * during look up of the catalog record, this function returns non-zero value. 1739 */ 1740__private_extern__ 1741int 1742cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid) 1743{ 1744 HFSPlusCatalogKey *keyp; 1745 BTreeIterator *ip; 1746 FSBufferDescriptor btdata; 1747 HFSPlusCatalogFolder folder; 1748 FCB *fcb; 1749 int invalid; 1750 int result; 1751 1752 invalid = 0; 1753 BDINIT(btdata, &folder); 1754 MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK); 1755 keyp = (HFSPlusCatalogKey *)&ip->key; 1756 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1757 1758 while (cnid != kHFSRootParentID) { 1759 /* Check if the 'pointed at' directory is an ancestor */ 1760 if (pointed_at_cnid == cnid) { 1761 invalid = 1; 1762 break; 1763 } 1764 if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) { 1765 printf("cat_check_link_ancestry: getkey for %u failed\n", cnid); 1766 invalid = 1; /* On errors, assume an invalid parent */ 1767 break; 1768 } 1769 if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) { 1770 printf("cat_check_link_ancestry: cannot find %u\n", cnid); 1771 invalid = 1; /* On errors, assume an invalid parent */ 1772 break; 1773 } 1774 /* Check if this ancestor is a directory hard link */ 1775 if (folder.flags & kHFSHasLinkChainMask) { 1776 invalid = 1; 1777 break; 1778 } 1779 cnid = keyp->parentID; 1780 } 1781 FREE(ip, M_TEMP); 1782 return (invalid); 1783} 1784 1785 1786/* 1787 * updatelink_callback - update a link's chain 1788 */ 1789 1790struct linkupdate_state { 1791 cnid_t filelinkid; 1792 cnid_t prevlinkid; 1793 cnid_t nextlinkid; 1794}; 1795 1796static int 1797updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state) 1798{ 1799 HFSPlusCatalogFile *file; 1800 1801 if (crp->recordType != kHFSPlusFileRecord) { 1802 printf("updatelink_callback: unexpected rec type %d\n", crp->recordType); 1803 return (btNotFound); 1804 } 1805 1806 file = (struct HFSPlusCatalogFile *)crp; 1807 if (file->flags & kHFSHasLinkChainMask) { 1808 if (state->prevlinkid != HFS_IGNORABLE_LINK) { 1809 file->hl_prevLinkID = state->prevlinkid; 1810 } 1811 if (state->nextlinkid != HFS_IGNORABLE_LINK) { 1812 file->hl_nextLinkID = state->nextlinkid; 1813 } 1814 } else { 1815 printf("updatelink_callback: file %d isn't a chain\n", file->fileID); 1816 } 1817 return (0); 1818} 1819 1820/* 1821 * cat_updatelink - update a link's chain 1822 */ 1823__private_extern__ 1824int 1825cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid) 1826{ 1827 FCB * fcb; 1828 BTreeIterator * iterator; 1829 struct linkupdate_state state; 1830 int result; 1831 1832 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1833 state.filelinkid = linkfileid; 1834 state.prevlinkid = prevlinkid; 1835 state.nextlinkid = nextlinkid; 1836 1837 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 1838 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 1839 iterator->hint.nodeNum = 0; 1840 1841 result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key); 1842 if (result == 0) { 1843 result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)updatelink_callback, &state); 1844 (void) BTFlushPath(fcb); 1845 } else { 1846 printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid); 1847 } 1848 return MacToVFSError(result); 1849} 1850 1851/* 1852 * cat_lookuplink - lookup a link by it's name 1853 */ 1854__private_extern__ 1855int 1856cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid) 1857{ 1858 FCB * fcb; 1859 BTreeIterator * iterator; 1860 struct FSBufferDescriptor btdata; 1861 struct HFSPlusCatalogFile file; 1862 int result; 1863 1864 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1865 1866 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 1867 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 1868 iterator->hint.nodeNum = 0; 1869 1870 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) { 1871 goto exit; 1872 } 1873 BDINIT(btdata, &file); 1874 1875 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) { 1876 goto exit; 1877 } 1878 if (file.recordType != kHFSPlusFileRecord) { 1879 result = ENOENT; 1880 goto exit; 1881 } 1882 *linkfileid = file.fileID; 1883 1884 if (file.flags & kHFSHasLinkChainMask) { 1885 *prevlinkid = file.hl_prevLinkID; 1886 *nextlinkid = file.hl_nextLinkID; 1887 } else { 1888 *prevlinkid = 0; 1889 *nextlinkid = 0; 1890 } 1891exit: 1892 return MacToVFSError(result); 1893} 1894 1895 1896/* 1897 * cat_lookuplink - lookup a link by its cnid 1898 */ 1899__private_extern__ 1900int 1901cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid) 1902{ 1903 FCB * fcb; 1904 BTreeIterator * iterator; 1905 struct FSBufferDescriptor btdata; 1906 struct HFSPlusCatalogFile file; 1907 int result; 1908 1909 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1910 1911 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 1912 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 1913 iterator->hint.nodeNum = 0; 1914 1915 if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) { 1916 printf("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result); 1917 goto exit; 1918 } 1919 BDINIT(btdata, &file); 1920 1921 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) { 1922 printf("cat_lookuplinkbyid: cannot find %d\n", linkfileid); 1923 goto exit; 1924 } 1925 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */ 1926 if (file.flags & kHFSHasLinkChainMask) { 1927 cnid_t parent; 1928 1929 parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID; 1930 1931 /* ADL inodes don't have a chain (its in an EA) */ 1932 if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 1933 result = ENOLINK; /* signal to caller to get head of list */ 1934 } else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) { 1935 *prevlinkid = 0; 1936 *nextlinkid = file.hl_firstLinkID; 1937 } else { 1938 *prevlinkid = file.hl_prevLinkID; 1939 *nextlinkid = file.hl_nextLinkID; 1940 } 1941 } else { 1942 *prevlinkid = 0; 1943 *nextlinkid = 0; 1944 } 1945exit: 1946 return MacToVFSError(result); 1947} 1948 1949 1950/* 1951 * cat_createlink - create a link in the catalog 1952 * 1953 * The following cat_attr fields are expected to be set: 1954 * ca_linkref 1955 * ca_itime 1956 * ca_mode (S_IFREG) 1957 * ca_recflags 1958 * ca_flags 1959 * ca_finderinfo (type and creator) 1960 */ 1961__private_extern__ 1962int 1963cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp, 1964 cnid_t nextlinkid, cnid_t *linkfileid) 1965{ 1966 FCB * fcb; 1967 struct btobj * bto; 1968 FSBufferDescriptor btdata; 1969 HFSPlusForkData *rsrcforkp; 1970 u_int32_t nextCNID; 1971 u_int32_t datalen; 1972 u_long encoding; 1973 int thread_inserted = 0; 1974 int alias_allocated = 0; 1975 int result = 0; 1976 1977 fcb = hfsmp->hfs_catalog_cp->c_datafork; 1978 1979 /* 1980 * Get the next CNID. We can change it since we hold the catalog lock. 1981 */ 1982 nextCNID = hfsmp->vcbNxtCNID; 1983 if (nextCNID == 0xFFFFFFFF) { 1984 HFS_MOUNT_LOCK(hfsmp, TRUE) 1985 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; 1986 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask; 1987 HFS_MOUNT_UNLOCK(hfsmp, TRUE); 1988 } else { 1989 hfsmp->vcbNxtCNID++; 1990 } 1991 MarkVCBDirty(hfsmp); 1992 1993 /* Get space for iterator, key and data */ 1994 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK); 1995 bto->iterator.hint.nodeNum = 0; 1996 rsrcforkp = &bto->data.hfsPlusFile.resourceFork; 1997 1998 result = buildkey(hfsmp, descp, &bto->key, 0); 1999 if (result) { 2000 printf("cat_createlink: err %d from buildkey\n", result); 2001 goto exit; 2002 } 2003 2004 /* This is our only chance to set the encoding (other than a rename). */ 2005 encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length); 2006 2007 /* Insert the thread record first. */ 2008 datalen = buildthread((void*)&bto->key, &bto->data, 0, 0); 2009 btdata.bufferAddress = &bto->data; 2010 btdata.itemSize = datalen; 2011 btdata.itemCount = 1; 2012 2013 for (;;) { 2014 buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key); 2015 2016 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); 2017 if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { 2018 /* 2019 * Allow CNIDs on HFS Plus volumes to wrap around 2020 */ 2021 if (++nextCNID < kHFSFirstUserCatalogNodeID) { 2022 nextCNID = kHFSFirstUserCatalogNodeID; 2023 } 2024 continue; 2025 } 2026 if (result == 0) { 2027 thread_inserted = 1; 2028 } 2029 break; 2030 } 2031 if (result) 2032 goto exit; 2033 2034 /* 2035 * CNID is now established. If we have wrapped then 2036 * update the vcbNxtCNID. 2037 */ 2038 if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) { 2039 hfsmp->vcbNxtCNID = nextCNID + 1; 2040 if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) { 2041 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID; 2042 } 2043 } 2044 2045 /* 2046 * Now insert the link record. 2047 */ 2048 buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen); 2049 2050 bto->data.hfsPlusFile.hl_prevLinkID = 0; 2051 bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid; 2052 bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref; 2053 2054 /* For directory hard links, create alias in resource fork */ 2055 if (descp->cd_flags & CD_ISDIR) { 2056 if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) { 2057 goto exit; 2058 } 2059 alias_allocated = 1; 2060 } 2061 btdata.bufferAddress = &bto->data; 2062 btdata.itemSize = datalen; 2063 btdata.itemCount = 1; 2064 2065 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key)); 2066 2067 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen); 2068 if (result) { 2069 if (result == btExists) 2070 result = EEXIST; 2071 goto exit; 2072 } 2073 if (linkfileid != NULL) { 2074 *linkfileid = nextCNID; 2075 } 2076exit: 2077 if (result) { 2078 if (thread_inserted) { 2079 printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result)); 2080 2081 buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key); 2082 if (BTDeleteRecord(fcb, &bto->iterator)) { 2083 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN); 2084 hfs_mark_volume_inconsistent(hfsmp); 2085 } 2086 } 2087 if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) { 2088 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, 2089 rsrcforkp->extents[0].blockCount); 2090 rsrcforkp->extents[0].startBlock = 0; 2091 rsrcforkp->extents[0].blockCount = 0; 2092 } 2093 } 2094 (void) BTFlushPath(fcb); 2095 FREE(bto, M_TEMP); 2096 2097 return MacToVFSError(result); 2098} 2099 2100/* Directory hard links are visible as aliases on pre-Leopard systems and 2101 * as normal directories on Leopard or later. All directory hard link aliases 2102 * have the same resource fork content except for the three uniquely 2103 * identifying values that are updated in the resource fork data when the alias 2104 * is created. The following array is the constant resource fork data used 2105 * only for creating directory hard link aliases. 2106 */ 2107static const char hfs_dirlink_alias_rsrc[] = { 2108 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 2109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2121 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2124 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 2125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b, 2127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2129 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2131 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 2132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 2133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 2134 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 2135 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73, 2136 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 2137}; 2138 2139/* Constants for directory hard link alias */ 2140enum { 2141 /* Size of resource fork data array for directory hard link alias */ 2142 kHFSAliasSize = 0x1d0, 2143 2144 /* Volume type for ejectable devices like disk image */ 2145 kHFSAliasVolTypeEjectable = 0x5, 2146 2147 /* Offset for volume create date, in Mac OS local time */ 2148 kHFSAliasVolCreateDateOffset = 0x12a, 2149 2150 /* Offset for the type of volume */ 2151 kHFSAliasVolTypeOffset = 0x130, 2152 2153 /* Offset for folder ID of the parent directory of the directory inode */ 2154 kHFSAliasParentIDOffset = 0x132, 2155 2156 /* Offset for folder ID of the directory inode */ 2157 kHFSAliasTargetIDOffset = 0x176, 2158}; 2159 2160/* Create and write an alias that points at the directory represented by given 2161 * inode number on the same volume. Directory hard links are visible as 2162 * aliases in pre-Leopard systems and this function creates these aliases. 2163 * 2164 * Note: This code is very specific to creating alias for the purpose 2165 * of directory hard links only, and should not be generalized. 2166 */ 2167static int 2168cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp) 2169{ 2170 struct buf *bp; 2171 daddr64_t blkno; 2172 u_int32_t blkcount; 2173 int blksize; 2174 int sectorsize; 2175 int result; 2176 HFSPlusForkData *rsrcforkp; 2177 char *alias; 2178 uint32_t *valptr; 2179 2180 rsrcforkp = &(crp->resourceFork); 2181 2182 blksize = hfsmp->blockSize; 2183 blkcount = howmany(kHFSAliasSize, blksize); 2184 sectorsize = hfsmp->hfs_logical_block_size; 2185 bzero(rsrcforkp, sizeof(HFSPlusForkData)); 2186 2187 /* Allocate some disk space for the alias content. */ 2188 result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 1, 1, 2189 &rsrcforkp->extents[0].startBlock, 2190 &rsrcforkp->extents[0].blockCount); 2191 if (result) { 2192 rsrcforkp->extents[0].startBlock = 0; 2193 goto exit; 2194 } 2195 2196 /* Acquire a buffer cache block for our block. */ 2197 blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize; 2198 blkno += hfsmp->hfsPlusIOPosOffset / sectorsize; 2199 2200 bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META); 2201 if (hfsmp->jnl) { 2202 journal_modify_block_start(hfsmp->jnl, bp); 2203 } 2204 2205 /* Generate alias content */ 2206 alias = (char *)buf_dataptr(bp); 2207 bzero(alias, buf_size(bp)); 2208 bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize); 2209 2210 /* Set the volume create date, local time in Mac OS format */ 2211 valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset); 2212 *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate); 2213 2214 /* If the file system is on a virtual device like disk image, 2215 * update the volume type to be ejectable device. 2216 */ 2217 if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) { 2218 *(uint16_t *)(alias + kHFSAliasVolTypeOffset) = 2219 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable); 2220 } 2221 2222 /* Set id of the parent of the target directory */ 2223 valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset); 2224 *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid); 2225 2226 /* Set id of the target directory */ 2227 valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset); 2228 *valptr = OSSwapHostToBigInt32(inode_num); 2229 2230 /* Write alias content to disk. */ 2231 if (hfsmp->jnl) { 2232 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL); 2233 } else if ((result = buf_bwrite(bp))) { 2234 goto exit; 2235 } 2236 2237 /* Finish initializing the fork data. */ 2238 rsrcforkp->logicalSize = kHFSAliasSize; 2239 rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount; 2240 2241exit: 2242 if (result && rsrcforkp->extents[0].startBlock != 0) { 2243 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount); 2244 rsrcforkp->extents[0].startBlock = 0; 2245 rsrcforkp->extents[0].blockCount = 0; 2246 rsrcforkp->logicalSize = 0; 2247 rsrcforkp->totalBlocks = 0; 2248 } 2249 return (result); 2250} 2251 2252/* 2253 * cat_deletelink - delete a link from the catalog 2254 */ 2255__private_extern__ 2256int 2257cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp) 2258{ 2259 struct HFSPlusCatalogFile file; 2260 struct cat_attr cattr; 2261 uint32_t totalBlocks; 2262 int i; 2263 int result; 2264 2265 bzero(&file, sizeof (file)); 2266 bzero(&cattr, sizeof (cattr)); 2267 cattr.ca_fileid = descp->cd_cnid; 2268 2269 /* Directory links have alias content to remove. */ 2270 if (descp->cd_flags & CD_ISDIR) { 2271 FCB * fcb; 2272 BTreeIterator * iterator; 2273 struct FSBufferDescriptor btdata; 2274 2275 fcb = hfsmp->hfs_catalog_cp->c_datafork; 2276 2277 /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 2278 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator; 2279 iterator->hint.nodeNum = 0; 2280 2281 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) { 2282 goto exit; 2283 } 2284 BDINIT(btdata, &file); 2285 2286 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) { 2287 goto exit; 2288 } 2289 } 2290 2291 result = cat_delete(hfsmp, descp, &cattr); 2292 2293 if ((result == 0) && 2294 (descp->cd_flags & CD_ISDIR) && 2295 (file.recordType == kHFSPlusFileRecord)) { 2296 2297 totalBlocks = file.resourceFork.totalBlocks; 2298 2299 for (i = 0; (i < 8) && (totalBlocks > 0); i++) { 2300 if ((file.resourceFork.extents[i].blockCount == 0) && 2301 (file.resourceFork.extents[i].startBlock == 0)) { 2302 break; 2303 } 2304 2305 (void) BlockDeallocate(hfsmp, 2306 file.resourceFork.extents[i].startBlock, 2307 file.resourceFork.extents[i].blockCount); 2308 2309 totalBlocks -= file.resourceFork.extents[i].blockCount; 2310 file.resourceFork.extents[i].startBlock = 0; 2311 file.resourceFork.extents[i].blockCount = 0; 2312 } 2313 } 2314exit: 2315 return (result); 2316} 2317 2318 2319/* 2320 * Callback to collect directory entries. 2321 * Called with readattr_state for each item in a directory. 2322 */ 2323struct readattr_state { 2324 struct hfsmount *hfsmp; 2325 struct cat_entrylist *list; 2326 cnid_t dir_cnid; 2327 int stdhfs; 2328 int error; 2329}; 2330 2331static int 2332getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec, 2333 struct readattr_state *state) 2334{ 2335 struct cat_entrylist *list = state->list; 2336 struct hfsmount *hfsmp = state->hfsmp; 2337 struct cat_entry *cep; 2338 cnid_t parentcnid; 2339 2340 if (list->realentries >= list->maxentries) 2341 return (0); /* stop */ 2342 2343 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID; 2344 2345 switch(rec->recordType) { 2346 case kHFSPlusFolderRecord: 2347 case kHFSPlusFileRecord: 2348 case kHFSFolderRecord: 2349 case kHFSFileRecord: 2350 if (parentcnid != state->dir_cnid) { 2351 state->error = ENOENT; 2352 return (0); /* stop */ 2353 } 2354 break; 2355 default: 2356 state->error = ENOENT; 2357 return (0); /* stop */ 2358 } 2359 2360 /* Hide the private system directories and journal files */ 2361 if (parentcnid == kHFSRootFolderID) { 2362 if (rec->recordType == kHFSPlusFolderRecord) { 2363 if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || 2364 rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 2365 list->skipentries++; 2366 return (1); /* continue */ 2367 } 2368 } 2369 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) && 2370 (rec->recordType == kHFSPlusFileRecord) && 2371 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) || 2372 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) { 2373 list->skipentries++; 2374 return (1); /* continue */ 2375 } 2376 } 2377 2378 cep = &list->entry[list->realentries++]; 2379 2380 if (state->stdhfs) { 2381 struct HFSPlusCatalogFile cnoderec; 2382 HFSPlusCatalogKey * pluskey; 2383 u_long encoding; 2384 2385 promoteattr(hfsmp, rec, &cnoderec); 2386 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr); 2387 2388 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK); 2389 promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding); 2390 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc); 2391 FREE(pluskey, M_TEMP); 2392 2393 if (rec->recordType == kHFSFileRecord) { 2394 int blksize = HFSTOVCB(hfsmp)->blockSize; 2395 2396 cep->ce_datasize = rec->hfsFile.dataLogicalSize; 2397 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize; 2398 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize; 2399 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize; 2400 } 2401 } else { 2402 getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr); 2403 builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec), 2404 isadir(rec), &cep->ce_desc); 2405 2406 if (rec->recordType == kHFSPlusFileRecord) { 2407 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize; 2408 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks; 2409 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize; 2410 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks; 2411 2412 /* Save link reference for later processing. */ 2413 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) && 2414 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) { 2415 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; 2416 } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) && 2417 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && 2418 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) { 2419 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum; 2420 } 2421 } 2422 } 2423 2424 return (list->realentries < list->maxentries); 2425} 2426 2427/* 2428 * Pack a cat_entrylist buffer with attributes from the catalog 2429 * 2430 * Note: index is zero relative 2431 */ 2432__private_extern__ 2433int 2434cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list) 2435{ 2436 FCB* fcb; 2437 CatalogKey * key; 2438 BTreeIterator * iterator; 2439 struct readattr_state state; 2440 cnid_t parentcnid; 2441 int i; 2442 int std_hfs; 2443 int index; 2444 int have_key; 2445 int result = 0; 2446 2447 ce_list->realentries = 0; 2448 2449 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum); 2450 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); 2451 parentcnid = dirhint->dh_desc.cd_parentcnid; 2452 2453 state.hfsmp = hfsmp; 2454 state.list = ce_list; 2455 state.dir_cnid = parentcnid; 2456 state.stdhfs = std_hfs; 2457 state.error = 0; 2458 2459 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 2460 bzero(iterator, sizeof(*iterator)); 2461 key = (CatalogKey *)&iterator->key; 2462 have_key = 0; 2463 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint; 2464 index = dirhint->dh_index + 1; 2465 2466 /* 2467 * Attempt to build a key from cached filename 2468 */ 2469 if (dirhint->dh_desc.cd_namelen != 0) { 2470 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) { 2471 have_key = 1; 2472 } 2473 } 2474 2475 /* 2476 * If the last entry wasn't cached then position the btree iterator 2477 */ 2478 if ((index == 0) || !have_key) { 2479 /* 2480 * Position the iterator at the directory's thread record. 2481 * (i.e. just before the first entry) 2482 */ 2483 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key); 2484 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator); 2485 if (result) { 2486 result = MacToVFSError(result); 2487 goto exit; 2488 } 2489 2490 /* 2491 * Iterate until we reach the entry just 2492 * before the one we want to start with. 2493 */ 2494 if (index > 0) { 2495 struct position_state ps; 2496 2497 ps.error = 0; 2498 ps.count = 0; 2499 ps.index = index; 2500 ps.parentID = dirhint->dh_desc.cd_parentcnid; 2501 ps.hfsmp = hfsmp; 2502 2503 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator, 2504 (IterateCallBackProcPtr)cat_findposition, &ps); 2505 if (ps.error) 2506 result = ps.error; 2507 else 2508 result = MacToVFSError(result); 2509 if (result) { 2510 result = MacToVFSError(result); 2511 goto exit; 2512 } 2513 } 2514 } 2515 2516 /* Fill list with entries starting at iterator->key. */ 2517 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator, 2518 (IterateCallBackProcPtr)getentriesattr_callback, &state); 2519 2520 if (state.error) 2521 result = state.error; 2522 else if (ce_list->realentries == 0) 2523 result = ENOENT; 2524 else 2525 result = MacToVFSError(result); 2526 2527 if (std_hfs) 2528 goto exit; 2529 2530 /* 2531 * Resolve any hard links. 2532 */ 2533 for (i = 0; i < (int)ce_list->realentries; ++i) { 2534 struct FndrFileInfo *fip; 2535 struct cat_entry *cep; 2536 struct HFSPlusCatalogFile filerec; 2537 int isdirlink = 0; 2538 int isfilelink = 0; 2539 2540 cep = &ce_list->entry[i]; 2541 if (cep->ce_attr.ca_linkref == 0) 2542 continue; 2543 2544 /* Note: Finder info is still in Big Endian */ 2545 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo; 2546 2547 if (S_ISREG(cep->ce_attr.ca_mode) && 2548 (SWAP_BE32(fip->fdType) == kHardLinkFileType) && 2549 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) { 2550 isfilelink = 1; 2551 } 2552 if (S_ISREG(cep->ce_attr.ca_mode) && 2553 (SWAP_BE32(fip->fdType) == kHFSAliasType) && 2554 (SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) && 2555 (cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) { 2556 isdirlink = 1; 2557 } 2558 if (isfilelink || isdirlink) { 2559 if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0) 2560 continue; 2561 /* Repack entry from inode record. */ 2562 getbsdattr(hfsmp, &filerec, &cep->ce_attr); 2563 cep->ce_datasize = filerec.dataFork.logicalSize; 2564 cep->ce_datablks = filerec.dataFork.totalBlocks; 2565 cep->ce_rsrcsize = filerec.resourceFork.logicalSize; 2566 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks; 2567 } 2568 } 2569exit: 2570 FREE(iterator, M_TEMP); 2571 2572 return MacToVFSError(result); 2573} 2574 2575#define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8) 2576 2577/* 2578 * Callback to pack directory entries. 2579 * Called with packdirentry_state for each item in a directory. 2580 */ 2581 2582/* Hard link information collected during cat_getdirentries. */ 2583struct linkinfo { 2584 u_long link_ref; 2585 user_addr_t dirent_addr; 2586}; 2587typedef struct linkinfo linkinfo_t; 2588 2589/* State information for the getdirentries_callback function. */ 2590struct packdirentry_state { 2591 int cbs_extended; 2592 u_int32_t cbs_parentID; 2593 u_int32_t cbs_index; 2594 uio_t cbs_uio; 2595 ExtendedVCB * cbs_hfsmp; 2596 int cbs_result; 2597 int32_t cbs_nlinks; 2598 int32_t cbs_maxlinks; 2599 linkinfo_t * cbs_linkinfo; 2600 struct cat_desc * cbs_desc; 2601 u_int8_t * cbs_namebuf; 2602 /* 2603 * The following fields are only used for NFS readdir, which 2604 * uses the next file id as the seek offset of each entry. 2605 */ 2606 struct direntry * cbs_direntry; 2607 struct direntry * cbs_prevdirentry; 2608 u_int32_t cbs_previlinkref; 2609 Boolean cbs_hasprevdirentry; 2610 Boolean cbs_eof; 2611}; 2612 2613/* 2614 * getdirentries callback for HFS Plus directories. 2615 */ 2616static int 2617getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp, 2618 struct packdirentry_state *state) 2619{ 2620 struct hfsmount *hfsmp; 2621 const CatalogName *cnp; 2622 cnid_t curID; 2623 OSErr result; 2624 struct dirent catent; 2625 struct direntry * entry = NULL; 2626 time_t itime; 2627 u_int32_t ilinkref = 0; 2628 u_int32_t curlinkref = 0; 2629 cnid_t cnid; 2630 int hide = 0; 2631 u_int8_t type = DT_UNKNOWN; 2632 u_int8_t is_mangled = 0; 2633 u_int8_t *nameptr; 2634 user_addr_t uiobase = USER_ADDR_NULL; 2635 size_t namelen = 0; 2636 size_t maxnamelen; 2637 size_t uiosize = 0; 2638 caddr_t uioaddr; 2639 Boolean stop_after_pack = false; 2640 2641 hfsmp = state->cbs_hfsmp; 2642 curID = ckp->hfsPlus.parentID; 2643 2644 /* We're done when parent directory changes */ 2645 if (state->cbs_parentID != curID) { 2646 if (state->cbs_extended) { 2647 /* The last record has not been returned yet, so we 2648 * want to stop after packing the last item 2649 */ 2650 if (state->cbs_hasprevdirentry) { 2651 stop_after_pack = true; 2652 } else { 2653 state->cbs_result = ENOENT; 2654 return (0); /* stop */ 2655 } 2656 } else { 2657 state->cbs_result = ENOENT; 2658 return (0); /* stop */ 2659 } 2660 } 2661 2662 if (state->cbs_extended) { 2663 entry = state->cbs_direntry; 2664 nameptr = (u_int8_t *)&entry->d_name[0]; 2665 maxnamelen = NAME_MAX; 2666 } else { 2667 nameptr = (u_int8_t *)&catent.d_name[0]; 2668 maxnamelen = NAME_MAX; 2669 } 2670 2671 if (state->cbs_extended && stop_after_pack) { 2672 /* The last item returns a non-zero invalid cookie */ 2673 cnid = INT_MAX; 2674 } else { 2675 switch(crp->recordType) { 2676 case kHFSPlusFolderRecord: 2677 type = DT_DIR; 2678 cnid = crp->hfsPlusFolder.folderID; 2679 /* Hide our private system directories. */ 2680 if (curID == kHFSRootFolderID) { 2681 if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid || 2682 cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) { 2683 hide = 1; 2684 } 2685 } 2686 break; 2687 case kHFSPlusFileRecord: 2688 itime = to_bsd_time(crp->hfsPlusFile.createDate); 2689 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode); 2690 cnid = crp->hfsPlusFile.fileID; 2691 /* 2692 * When a hardlink link is encountered save its link ref. 2693 */ 2694 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) && 2695 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) && 2696 ((itime == (time_t)hfsmp->hfs_itime) || 2697 (itime == (time_t)hfsmp->hfs_metadata_createdate))) { 2698 /* If link ref is inode's file id then use it directly. */ 2699 if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) { 2700 cnid = crp->hfsPlusFile.hl_linkReference; 2701 } else { 2702 ilinkref = crp->hfsPlusFile.hl_linkReference; 2703 } 2704 } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && 2705 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) && 2706 (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) && 2707 (crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) && 2708 ((itime == (time_t)hfsmp->hfs_itime) || 2709 (itime == (time_t)hfsmp->hfs_metadata_createdate))) { 2710 /* A directory's link resolves to a directory. */ 2711 type = DT_DIR; 2712 /* A directory's link ref is always inode's file id. */ 2713 cnid = crp->hfsPlusFile.hl_linkReference; 2714 } 2715 /* Hide the journal files */ 2716 if ((curID == kHFSRootFolderID) && 2717 ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) && 2718 ((cnid == hfsmp->hfs_jnlfileid) || 2719 (cnid == hfsmp->hfs_jnlinfoblkid))) { 2720 hide = 1; 2721 } 2722 break; 2723 default: 2724 return (0); /* stop */ 2725 }; 2726 2727 cnp = (const CatalogName*) &ckp->hfsPlus.nodeName; 2728 2729 namelen = cnp->ustr.length; 2730 /* 2731 * For MacRoman encoded names, assume that its ascii and 2732 * convert it directly in an attempt to avoid the more 2733 * expensive utf8_encodestr conversion. 2734 */ 2735 if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) { 2736 int i; 2737 u_int16_t ch; 2738 const u_int16_t *chp; 2739 2740 chp = &cnp->ustr.unicode[0]; 2741 for (i = 0; i < (int)namelen; ++i) { 2742 ch = *chp++; 2743 if (ch > 0x007f || ch == 0x0000) { 2744 /* Perform expensive utf8_encodestr conversion */ 2745 goto encodestr; 2746 } 2747 nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch; 2748 } 2749 nameptr[namelen] = '\0'; 2750 result = 0; 2751 } else { 2752encodestr: 2753 result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar), 2754 nameptr, &namelen, maxnamelen + 1, ':', 0); 2755 } 2756 2757 /* Check result returned from encoding the filename to utf8 */ 2758 if (result == ENAMETOOLONG) { 2759 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar), 2760 cnp->ustr.unicode, maxnamelen + 1, 2761 (ByteCount*)&namelen, nameptr, cnid); 2762 is_mangled = 1; 2763 } 2764 } 2765 2766 if (state->cbs_extended) { 2767 /* 2768 * The index is 1 relative and includes "." and ".." 2769 * 2770 * Also stuff the cnid in the upper 32 bits of the cookie. 2771 * The cookie is stored to the previous entry, which will 2772 * be packed and copied this time 2773 */ 2774 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32); 2775 uiosize = state->cbs_prevdirentry->d_reclen; 2776 uioaddr = (caddr_t) state->cbs_prevdirentry; 2777 } else { 2778 catent.d_type = type; 2779 catent.d_namlen = namelen; 2780 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen); 2781 if (hide) 2782 catent.d_fileno = 0; /* file number = 0 means skip entry */ 2783 else 2784 catent.d_fileno = cnid; 2785 uioaddr = (caddr_t) &catent; 2786 } 2787 2788 /* Save current base address for post processing of hard-links. */ 2789 if (ilinkref || state->cbs_previlinkref) { 2790 uiobase = uio_curriovbase(state->cbs_uio); 2791 } 2792 /* If this entry won't fit then we're done */ 2793 if ((uiosize > uio_resid(state->cbs_uio)) || 2794 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) { 2795 return (0); /* stop */ 2796 } 2797 2798 if (!state->cbs_extended || state->cbs_hasprevdirentry) { 2799 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio); 2800 if (state->cbs_result == 0) { 2801 ++state->cbs_index; 2802 2803 /* Remember previous entry */ 2804 state->cbs_desc->cd_cnid = cnid; 2805 if (type == DT_DIR) { 2806 state->cbs_desc->cd_flags |= CD_ISDIR; 2807 } else { 2808 state->cbs_desc->cd_flags &= ~CD_ISDIR; 2809 } 2810 if (state->cbs_desc->cd_nameptr != NULL) { 2811 state->cbs_desc->cd_namelen = 0; 2812 } 2813#if 0 2814 state->cbs_desc->cd_encoding = xxxx; 2815#endif 2816 if (!is_mangled) { 2817 state->cbs_desc->cd_namelen = namelen; 2818 bcopy(nameptr, state->cbs_namebuf, namelen + 1); 2819 } else { 2820 /* Store unmangled name for the directory hint else it will 2821 * restart readdir at the last location again 2822 */ 2823 u_int8_t *new_nameptr; 2824 size_t bufsize; 2825 size_t tmp_namelen = 0; 2826 2827 cnp = (const CatalogName *)&ckp->hfsPlus.nodeName; 2828 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode, 2829 cnp->ustr.length * sizeof(UniChar), 2830 ':', 0); 2831 MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK); 2832 result = utf8_encodestr(cnp->ustr.unicode, 2833 cnp->ustr.length * sizeof(UniChar), 2834 new_nameptr, &tmp_namelen, bufsize, ':', 0); 2835 2836 state->cbs_desc->cd_namelen = tmp_namelen; 2837 bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1); 2838 2839 FREE(new_nameptr, M_TEMP); 2840 } 2841 } 2842 if (state->cbs_hasprevdirentry) { 2843 curlinkref = ilinkref; /* save current */ 2844 ilinkref = state->cbs_previlinkref; /* use previous */ 2845 } 2846 /* 2847 * Record any hard links for post processing. 2848 */ 2849 if ((ilinkref != 0) && 2850 (state->cbs_result == 0) && 2851 (state->cbs_nlinks < state->cbs_maxlinks)) { 2852 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase; 2853 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref; 2854 state->cbs_nlinks++; 2855 } 2856 if (state->cbs_hasprevdirentry) { 2857 ilinkref = curlinkref; /* restore current */ 2858 } 2859 } 2860 2861 /* Fill the direntry to be used the next time */ 2862 if (state->cbs_extended) { 2863 if (stop_after_pack) { 2864 state->cbs_eof = true; 2865 return (0); /* stop */ 2866 } 2867 entry->d_type = type; 2868 entry->d_namlen = namelen; 2869 entry->d_reclen = EXT_DIRENT_LEN(namelen); 2870 if (hide) { 2871 /* File number = 0 means skip entry */ 2872 entry->d_fileno = 0; 2873 } else { 2874 entry->d_fileno = cnid; 2875 } 2876 /* swap the current and previous entry */ 2877 struct direntry * tmp; 2878 tmp = state->cbs_direntry; 2879 state->cbs_direntry = state->cbs_prevdirentry; 2880 state->cbs_prevdirentry = tmp; 2881 state->cbs_hasprevdirentry = true; 2882 state->cbs_previlinkref = ilinkref; 2883 } 2884 2885 /* Continue iteration if there's room */ 2886 return (state->cbs_result == 0 && 2887 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE); 2888} 2889 2890/* 2891 * getdirentries callback for standard HFS (non HFS+) directories. 2892 */ 2893static int 2894getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp, 2895 struct packdirentry_state *state) 2896{ 2897 struct hfsmount *hfsmp; 2898 const CatalogName *cnp; 2899 cnid_t curID; 2900 OSErr result; 2901 struct dirent catent; 2902 cnid_t cnid; 2903 u_int8_t type = DT_UNKNOWN; 2904 u_int8_t *nameptr; 2905 size_t namelen = 0; 2906 size_t maxnamelen; 2907 size_t uiosize = 0; 2908 caddr_t uioaddr; 2909 2910 hfsmp = state->cbs_hfsmp; 2911 2912 curID = ckp->hfs.parentID; 2913 2914 /* We're done when parent directory changes */ 2915 if (state->cbs_parentID != curID) { 2916 state->cbs_result = ENOENT; 2917 return (0); /* stop */ 2918 } 2919 2920 nameptr = (u_int8_t *)&catent.d_name[0]; 2921 maxnamelen = NAME_MAX; 2922 2923 switch(crp->recordType) { 2924 case kHFSFolderRecord: 2925 type = DT_DIR; 2926 cnid = crp->hfsFolder.folderID; 2927 break; 2928 case kHFSFileRecord: 2929 type = DT_REG; 2930 cnid = crp->hfsFile.fileID; 2931 break; 2932 default: 2933 return (0); /* stop */ 2934 }; 2935 2936 cnp = (const CatalogName*) ckp->hfs.nodeName; 2937 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr); 2938 /* 2939 * When an HFS name cannot be encoded with the current 2940 * volume encoding we use MacRoman as a fallback. 2941 */ 2942 if (result) { 2943 result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr); 2944 } 2945 catent.d_type = type; 2946 catent.d_namlen = namelen; 2947 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen); 2948 catent.d_fileno = cnid; 2949 uioaddr = (caddr_t) &catent; 2950 2951 /* If this entry won't fit then we're done */ 2952 if (uiosize > uio_resid(state->cbs_uio)) { 2953 return (0); /* stop */ 2954 } 2955 2956 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio); 2957 if (state->cbs_result == 0) { 2958 ++state->cbs_index; 2959 2960 /* Remember previous entry */ 2961 state->cbs_desc->cd_cnid = cnid; 2962 if (type == DT_DIR) { 2963 state->cbs_desc->cd_flags |= CD_ISDIR; 2964 } else { 2965 state->cbs_desc->cd_flags &= ~CD_ISDIR; 2966 } 2967 if (state->cbs_desc->cd_nameptr != NULL) { 2968 state->cbs_desc->cd_namelen = 0; 2969 } 2970 state->cbs_desc->cd_namelen = namelen; 2971 bcopy(nameptr, state->cbs_namebuf, namelen + 1); 2972 } 2973 2974 /* Continue iteration if there's room */ 2975 return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE); 2976} 2977 2978/* 2979 * Pack a uio buffer with directory entries from the catalog 2980 */ 2981__private_extern__ 2982int 2983cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint, 2984 uio_t uio, int extended, int * items, int * eofflag) 2985{ 2986 FCB* fcb; 2987 BTreeIterator * iterator; 2988 CatalogKey * key; 2989 struct packdirentry_state state; 2990 void * buffer; 2991 int bufsize; 2992 int maxlinks; 2993 int result; 2994 int index; 2995 int have_key; 2996 2997 if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) { 2998 return (ENOTSUP); 2999 } 3000 fcb = hfsmp->hfs_catalog_cp->c_datafork; 3001 3002 /* 3003 * Get a buffer for link info array, btree iterator and a direntry: 3004 */ 3005 maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE); 3006 bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator); 3007 if (extended) { 3008 bufsize += 2*sizeof(struct direntry); 3009 } 3010 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK); 3011 bzero(buffer, bufsize); 3012 3013 state.cbs_extended = extended; 3014 state.cbs_hasprevdirentry = false; 3015 state.cbs_previlinkref = 0; 3016 state.cbs_nlinks = 0; 3017 state.cbs_maxlinks = maxlinks; 3018 state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN); 3019 3020 iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t))); 3021 key = (CatalogKey *)&iterator->key; 3022 have_key = 0; 3023 index = dirhint->dh_index + 1; 3024 if (extended) { 3025 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator)); 3026 state.cbs_prevdirentry = state.cbs_direntry + 1; 3027 state.cbs_eof = false; 3028 } 3029 /* 3030 * Attempt to build a key from cached filename 3031 */ 3032 if (dirhint->dh_desc.cd_namelen != 0) { 3033 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) { 3034 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint; 3035 have_key = 1; 3036 } 3037 } 3038 3039 if (index == 0 && dirhint->dh_threadhint != 0) { 3040 /* 3041 * Position the iterator at the directory's thread record. 3042 * (i.e. just before the first entry) 3043 */ 3044 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key); 3045 iterator->hint.nodeNum = dirhint->dh_threadhint; 3046 iterator->hint.index = 0; 3047 have_key = 1; 3048 } 3049 3050 /* 3051 * If the last entry wasn't cached then position the btree iterator 3052 */ 3053 if (!have_key) { 3054 /* 3055 * Position the iterator at the directory's thread record. 3056 * (i.e. just before the first entry) 3057 */ 3058 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key); 3059 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator); 3060 if (result) { 3061 result = MacToVFSError(result); 3062 goto cleanup; 3063 } 3064 if (index == 0) { 3065 dirhint->dh_threadhint = iterator->hint.nodeNum; 3066 } 3067 /* 3068 * Iterate until we reach the entry just 3069 * before the one we want to start with. 3070 */ 3071 if (index > 0) { 3072 struct position_state ps; 3073 3074 ps.error = 0; 3075 ps.count = 0; 3076 ps.index = index; 3077 ps.parentID = dirhint->dh_desc.cd_parentcnid; 3078 ps.hfsmp = hfsmp; 3079 3080 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator, 3081 (IterateCallBackProcPtr)cat_findposition, &ps); 3082 if (ps.error) 3083 result = ps.error; 3084 else 3085 result = MacToVFSError(result); 3086 if (result) { 3087 result = MacToVFSError(result); 3088 goto cleanup; 3089 } 3090 } 3091 } 3092 3093 state.cbs_index = index; 3094 state.cbs_hfsmp = hfsmp; 3095 state.cbs_uio = uio; 3096 state.cbs_desc = &dirhint->dh_desc; 3097 state.cbs_namebuf = (u_int8_t *)buffer; 3098 state.cbs_result = 0; 3099 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid; 3100 3101 /* Use a temporary buffer to hold intermediate descriptor names. */ 3102 if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) { 3103 bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1); 3104 if (dirhint->dh_desc.cd_flags & CD_HASBUF) { 3105 dirhint->dh_desc.cd_flags &= ~CD_HASBUF; 3106 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr); 3107 } 3108 } 3109 dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer; 3110 3111 enum BTreeIterationOperations op; 3112 if (extended && index != 0 && have_key) 3113 op = kBTreeCurrentRecord; 3114 else 3115 op = kBTreeNextRecord; 3116 3117 /* 3118 * Process as many entries as possible starting at iterator->key. 3119 */ 3120 if (hfsmp->hfs_flags & HFS_STANDARD) 3121 result = BTIterateRecords(fcb, op, iterator, 3122 (IterateCallBackProcPtr)getdirentries_std_callback, &state); 3123 else { 3124 result = BTIterateRecords(fcb, op, iterator, 3125 (IterateCallBackProcPtr)getdirentries_callback, &state); 3126 3127 /* For extended calls, every call to getdirentries_callback() 3128 * transfers the previous directory entry found to the user 3129 * buffer. Therefore when BTIterateRecords reaches the end of 3130 * Catalog BTree, call getdirentries_callback() again with 3131 * dummy values to copy the last directory entry stored in 3132 * packdirentry_state 3133 */ 3134 if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) { 3135 CatalogKey ckp; 3136 CatalogRecord crp; 3137 3138 bzero(&ckp, sizeof(ckp)); 3139 bzero(&crp, sizeof(crp)); 3140 3141 result = getdirentries_callback(&ckp, &crp, &state); 3142 } 3143 } 3144 3145 /* Note that state.cbs_index is still valid on errors */ 3146 *items = state.cbs_index - index; 3147 index = state.cbs_index; 3148 3149 if (state.cbs_eof) { 3150 *eofflag = 1; 3151 } 3152 3153 /* Finish updating the catalog iterator. */ 3154 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum; 3155 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED; 3156 dirhint->dh_index = index - 1; 3157 3158 /* Fix up the name. */ 3159 if (dirhint->dh_desc.cd_namelen > 0) { 3160 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0); 3161 dirhint->dh_desc.cd_flags |= CD_HASBUF; 3162 } else { 3163 dirhint->dh_desc.cd_nameptr = NULL; 3164 dirhint->dh_desc.cd_namelen = 0; 3165 } 3166 3167 /* 3168 * Post process any hard links to get the real file id. 3169 */ 3170 if (state.cbs_nlinks > 0) { 3171 u_int32_t fileid = 0; 3172 user_addr_t address; 3173 int i; 3174 3175 for (i = 0; i < state.cbs_nlinks; ++i) { 3176 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0) 3177 continue; 3178 /* This assumes that d_ino is always first field. */ 3179 address = state.cbs_linkinfo[i].dirent_addr; 3180 if (address == (user_addr_t)0) 3181 continue; 3182 if (uio_isuserspace(uio)) { 3183 if (extended) { 3184 ino64_t fileid_64 = (ino64_t)fileid; 3185 (void) copyout(&fileid_64, address, sizeof(fileid_64)); 3186 } else { 3187 (void) copyout(&fileid, address, sizeof(fileid)); 3188 } 3189 } else /* system space */ { 3190 if (extended) { 3191 ino64_t fileid_64 = (ino64_t)fileid; 3192 bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64)); 3193 } else { 3194 bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid)); 3195 } 3196 } 3197 } 3198 } 3199 3200 if (state.cbs_result) 3201 result = state.cbs_result; 3202 else 3203 result = MacToVFSError(result); 3204 3205 if (result == ENOENT) { 3206 result = 0; 3207 } 3208 3209cleanup: 3210 FREE(buffer, M_TEMP); 3211 3212 return (result); 3213} 3214 3215 3216/* 3217 * Callback to establish directory position. 3218 * Called with position_state for each item in a directory. 3219 */ 3220static int 3221cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp, 3222 struct position_state *state) 3223{ 3224 cnid_t curID; 3225 3226 if (state->hfsmp->hfs_flags & HFS_STANDARD) 3227 curID = ckp->hfs.parentID; 3228 else 3229 curID = ckp->hfsPlus.parentID; 3230 3231 /* Make sure parent directory didn't change */ 3232 if (state->parentID != curID) { 3233 state->error = EINVAL; 3234 return (0); /* stop */ 3235 } 3236 3237 /* Count this entry */ 3238 switch(crp->recordType) { 3239 case kHFSPlusFolderRecord: 3240 case kHFSPlusFileRecord: 3241 case kHFSFolderRecord: 3242 case kHFSFileRecord: 3243 ++state->count; 3244 break; 3245 default: 3246 printf("cat_findposition: invalid record type %d in dir %d\n", 3247 crp->recordType, curID); 3248 state->error = EINVAL; 3249 return (0); /* stop */ 3250 }; 3251 3252 return (state->count < state->index); 3253} 3254 3255 3256/* 3257 * cat_binarykeycompare - compare two HFS Plus catalog keys. 3258 3259 * The name portion of the key is compared using a 16-bit binary comparison. 3260 * This is called from the b-tree code. 3261 */ 3262__private_extern__ 3263int 3264cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey) 3265{ 3266 u_int32_t searchParentID, trialParentID; 3267 int result; 3268 3269 searchParentID = searchKey->parentID; 3270 trialParentID = trialKey->parentID; 3271 result = 0; 3272 3273 if (searchParentID > trialParentID) { 3274 ++result; 3275 } else if (searchParentID < trialParentID) { 3276 --result; 3277 } else { 3278 u_int16_t * str1 = &searchKey->nodeName.unicode[0]; 3279 u_int16_t * str2 = &trialKey->nodeName.unicode[0]; 3280 int length1 = searchKey->nodeName.length; 3281 int length2 = trialKey->nodeName.length; 3282 u_int16_t c1, c2; 3283 int length; 3284 3285 if (length1 < length2) { 3286 length = length1; 3287 --result; 3288 } else if (length1 > length2) { 3289 length = length2; 3290 ++result; 3291 } else { 3292 length = length1; 3293 } 3294 3295 while (length--) { 3296 c1 = *(str1++); 3297 c2 = *(str2++); 3298 3299 if (c1 > c2) { 3300 result = 1; 3301 break; 3302 } 3303 if (c1 < c2) { 3304 result = -1; 3305 break; 3306 } 3307 } 3308 } 3309 3310 return result; 3311} 3312 3313 3314/* 3315 * Compare two standard HFS catalog keys 3316 * 3317 * Result: +n search key > trial key 3318 * 0 search key = trial key 3319 * -n search key < trial key 3320 */ 3321int 3322CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey) 3323{ 3324 cnid_t searchParentID, trialParentID; 3325 int result; 3326 3327 searchParentID = searchKey->parentID; 3328 trialParentID = trialKey->parentID; 3329 3330 if (searchParentID > trialParentID) 3331 result = 1; 3332 else if (searchParentID < trialParentID) 3333 result = -1; 3334 else /* parent dirID's are equal, compare names */ 3335 result = FastRelString(searchKey->nodeName, trialKey->nodeName); 3336 3337 return result; 3338} 3339 3340 3341/* 3342 * Compare two HFS+ catalog keys 3343 * 3344 * Result: +n search key > trial key 3345 * 0 search key = trial key 3346 * -n search key < trial key 3347 */ 3348int 3349CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey) 3350{ 3351 cnid_t searchParentID, trialParentID; 3352 int result; 3353 3354 searchParentID = searchKey->parentID; 3355 trialParentID = trialKey->parentID; 3356 3357 if (searchParentID > trialParentID) { 3358 result = 1; 3359 } 3360 else if (searchParentID < trialParentID) { 3361 result = -1; 3362 } else { 3363 /* parent node ID's are equal, compare names */ 3364 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 ) 3365 result = searchKey->nodeName.length - trialKey->nodeName.length; 3366 else 3367 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0], 3368 searchKey->nodeName.length, 3369 &trialKey->nodeName.unicode[0], 3370 trialKey->nodeName.length); 3371 } 3372 3373 return result; 3374} 3375 3376 3377/* 3378 * buildkey - build a Catalog b-tree key from a cnode descriptor 3379 */ 3380static int 3381buildkey(struct hfsmount *hfsmp, struct cat_desc *descp, 3382 HFSPlusCatalogKey *key, int retry) 3383{ 3384 int utf8_flags = UTF_ESCAPE_ILLEGAL; 3385 int result = 0; 3386 size_t unicodeBytes = 0; 3387 3388 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0') 3389 return (EINVAL); /* invalid name */ 3390 3391 key->parentID = descp->cd_parentcnid; 3392 key->nodeName.length = 0; 3393 /* 3394 * Convert filename from UTF-8 into Unicode 3395 */ 3396 3397 if ((descp->cd_flags & CD_DECOMPOSED) == 0) 3398 utf8_flags |= UTF_DECOMPOSED; 3399 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen, 3400 key->nodeName.unicode, &unicodeBytes, 3401 sizeof(key->nodeName.unicode), ':', utf8_flags); 3402 key->nodeName.length = unicodeBytes / sizeof(UniChar); 3403 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes; 3404 if (result) { 3405 if (result != ENAMETOOLONG) 3406 result = EINVAL; /* name has invalid characters */ 3407 return (result); 3408 } 3409 3410 /* 3411 * For HFS volumes convert to an HFS compatible key 3412 * 3413 * XXX need to save the encoding that succeeded 3414 */ 3415 if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) { 3416 HFSCatalogKey hfskey; 3417 3418 bzero(&hfskey, sizeof(hfskey)); 3419 hfskey.keyLength = kHFSCatalogKeyMinimumLength; 3420 hfskey.parentID = key->parentID; 3421 hfskey.nodeName[0] = 0; 3422 if (key->nodeName.length > 0) { 3423 if (unicode_to_hfs(HFSTOVCB(hfsmp), 3424 key->nodeName.length * 2, 3425 key->nodeName.unicode, 3426 &hfskey.nodeName[0], retry) != 0) { 3427 return (EINVAL); 3428 } 3429 hfskey.keyLength += hfskey.nodeName[0]; 3430 } 3431 bcopy(&hfskey, key, sizeof(hfskey)); 3432 } 3433 return (0); 3434 } 3435 3436 3437/* 3438 * Resolve hard link reference to obtain the inode record. 3439 */ 3440__private_extern__ 3441int 3442cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HFSPlusCatalogFile *recp) 3443{ 3444 FSBufferDescriptor btdata; 3445 struct BTreeIterator *iterator; 3446 struct cat_desc idesc; 3447 char inodename[32]; 3448 cnid_t parentcnid; 3449 int result = 0; 3450 3451 BDINIT(btdata, recp); 3452 3453 if (isdirlink) { 3454 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref); 3455 parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid; 3456 } else { 3457 MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref); 3458 parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid; 3459 } 3460 3461 /* Get space for iterator */ 3462 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 3463 bzero(iterator, sizeof(*iterator)); 3464 3465 /* Build a descriptor for private dir. */ 3466 idesc.cd_parentcnid = parentcnid; 3467 idesc.cd_nameptr = (const u_int8_t *)inodename; 3468 idesc.cd_namelen = strlen(inodename); 3469 idesc.cd_flags = 0; 3470 idesc.cd_hint = 0; 3471 idesc.cd_encoding = 0; 3472 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0); 3473 3474 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator, 3475 &btdata, NULL, NULL); 3476 3477 if (result == 0) { 3478 /* Make sure there's a reference */ 3479 if (recp->hl_linkCount == 0) 3480 recp->hl_linkCount = 2; 3481 } else { 3482 printf("HFS resolvelink: can't find %s\n", inodename); 3483 } 3484 3485 FREE(iterator, M_TEMP); 3486 3487 return (result ? ENOENT : 0); 3488} 3489 3490/* 3491 * Resolve hard link reference to obtain the inode number. 3492 */ 3493static int 3494resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino) 3495{ 3496 struct HFSPlusCatalogFile record; 3497 int error; 3498 3499 /* 3500 * Since we know resolvelinkid is only called from 3501 * cat_getdirentries, we can assume that only file 3502 * hardlinks need to be resolved (cat_getdirentries 3503 * can resolve directory hardlinks in place). 3504 */ 3505 error = cat_resolvelink(hfsmp, linkref, 0, &record); 3506 if (error == 0) { 3507 if (record.fileID == 0) 3508 error = ENOENT; 3509 else 3510 *ino = record.fileID; 3511 } 3512 return (error); 3513} 3514 3515/* 3516 * getkey - get a key from id by doing a thread lookup 3517 */ 3518static int 3519getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key) 3520{ 3521 struct BTreeIterator * iterator; 3522 FSBufferDescriptor btdata; 3523 u_int16_t datasize; 3524 CatalogKey * keyp; 3525 CatalogRecord * recp; 3526 int result; 3527 int std_hfs; 3528 3529 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord); 3530 3531 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK); 3532 bzero(iterator, sizeof(*iterator)); 3533 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key); 3534 3535 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK); 3536 BDINIT(btdata, recp); 3537 3538 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator, 3539 &btdata, &datasize, iterator); 3540 if (result) 3541 goto exit; 3542 3543 /* Turn thread record into a cnode key (in place) */ 3544 switch (recp->recordType) { 3545 case kHFSFileThreadRecord: 3546 case kHFSFolderThreadRecord: 3547 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6); 3548 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0]; 3549 bcopy(keyp, key, keyp->hfs.keyLength + 1); 3550 break; 3551 3552 case kHFSPlusFileThreadRecord: 3553 case kHFSPlusFolderThreadRecord: 3554 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved; 3555 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength + 3556 (keyp->hfsPlus.nodeName.length * 2); 3557 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2); 3558 break; 3559 3560 default: 3561 result = ENOENT; 3562 break; 3563 } 3564 3565exit: 3566 FREE(iterator, M_TEMP); 3567 FREE(recp, M_TEMP); 3568 3569 return MacToVFSError(result); 3570} 3571 3572/* 3573 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass 3574 * null arguments to cat_idlookup instead, but we save around 10% by not building the 3575 * cat_desc here). Both key and attrp must point to real structures. 3576 * 3577 * The key's parent id is the only part of the key expected to be used by the caller. 3578 * The name portion of the key may not always be valid (ie in the case of a hard link). 3579 */ 3580__private_extern__ 3581int 3582cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp) 3583{ 3584 int result; 3585 3586 result = getkey(hfsmp, cnid, key); 3587 3588 if (result == 0) { 3589 result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL); 3590 } 3591 /* 3592 * Check for a raw file hardlink inode. 3593 * Fix up the parent id in the key if necessary. 3594 * Only hard links created by Mac OS X 10.5 or later can be resolved here. 3595 */ 3596 if ((result == 0) && 3597 (key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) && 3598 (attrp->ca_recflags & kHFSHasLinkChainMask)) { 3599 cnid_t nextlinkid = 0; 3600 cnid_t prevlinkid = 0; 3601 struct cat_desc linkdesc; 3602 3603 /* 3604 * Pick up the first link in the chain and get a descriptor for it. 3605 * This allows blind bulk access checks to work for hardlinks. 3606 */ 3607 if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) && 3608 (nextlinkid != 0)) { 3609 if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) { 3610 key->hfsPlus.parentID = linkdesc.cd_parentcnid; 3611 cat_releasedesc(&linkdesc); 3612 } 3613 } 3614 } 3615 return MacToVFSError(result); 3616} 3617 3618 3619/* 3620 * buildrecord - build a default catalog directory or file record 3621 */ 3622static void 3623buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, 3624 CatalogRecord *crp, u_int32_t *recordSize) 3625{ 3626 int type = attrp->ca_mode & S_IFMT; 3627 u_int32_t createtime = to_hfs_time(attrp->ca_itime); 3628 3629 if (std_hfs) { 3630 createtime = UTCToLocal(createtime); 3631 if (type == S_IFDIR) { 3632 bzero(crp, sizeof(HFSCatalogFolder)); 3633 crp->recordType = kHFSFolderRecord; 3634 crp->hfsFolder.folderID = cnid; 3635 crp->hfsFolder.createDate = createtime; 3636 crp->hfsFolder.modifyDate = createtime; 3637 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32); 3638 *recordSize = sizeof(HFSCatalogFolder); 3639 } else { 3640 bzero(crp, sizeof(HFSCatalogFile)); 3641 crp->recordType = kHFSFileRecord; 3642 crp->hfsFile.fileID = cnid; 3643 crp->hfsFile.createDate = createtime; 3644 crp->hfsFile.modifyDate = createtime; 3645 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16); 3646 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16); 3647 *recordSize = sizeof(HFSCatalogFile); 3648 } 3649 } else { 3650 struct HFSPlusBSDInfo * bsdp = NULL; 3651 3652 if (type == S_IFDIR) { 3653 crp->recordType = kHFSPlusFolderRecord; 3654 crp->hfsPlusFolder.flags = attrp->ca_recflags; 3655 crp->hfsPlusFolder.valence = 0; 3656 crp->hfsPlusFolder.folderID = cnid; 3657 crp->hfsPlusFolder.createDate = createtime; 3658 crp->hfsPlusFolder.contentModDate = createtime; 3659 crp->hfsPlusFolder.attributeModDate = createtime; 3660 crp->hfsPlusFolder.accessDate = createtime; 3661 crp->hfsPlusFolder.backupDate = 0; 3662 crp->hfsPlusFolder.textEncoding = encoding; 3663 crp->hfsPlusFolder.folderCount = 0; 3664 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32); 3665 bsdp = &crp->hfsPlusFolder.bsdInfo; 3666 bsdp->special.linkCount = 1; 3667 *recordSize = sizeof(HFSPlusCatalogFolder); 3668 } else { 3669 crp->recordType = kHFSPlusFileRecord; 3670 crp->hfsPlusFile.flags = attrp->ca_recflags; 3671 crp->hfsPlusFile.reserved1 = 0; 3672 crp->hfsPlusFile.fileID = cnid; 3673 crp->hfsPlusFile.createDate = createtime; 3674 crp->hfsPlusFile.contentModDate = createtime; 3675 crp->hfsPlusFile.accessDate = createtime; 3676 crp->hfsPlusFile.attributeModDate = createtime; 3677 crp->hfsPlusFile.backupDate = 0; 3678 crp->hfsPlusFile.textEncoding = encoding; 3679 crp->hfsPlusFile.reserved2 = 0; 3680 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32); 3681 bsdp = &crp->hfsPlusFile.bsdInfo; 3682 /* BLK/CHR need to save the device info */ 3683 if (type == S_IFBLK || type == S_IFCHR) { 3684 bsdp->special.rawDevice = attrp->ca_rdev; 3685 } else { 3686 bsdp->special.linkCount = 1; 3687 } 3688 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData)); 3689 *recordSize = sizeof(HFSPlusCatalogFile); 3690 } 3691 bsdp->ownerID = attrp->ca_uid; 3692 bsdp->groupID = attrp->ca_gid; 3693 bsdp->fileMode = attrp->ca_mode; 3694 bsdp->adminFlags = attrp->ca_flags >> 16; 3695 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF; 3696 } 3697} 3698 3699 3700/* 3701 * builddesc - build a cnode descriptor from an HFS+ key 3702 */ 3703static int 3704builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding, 3705 int isdir, struct cat_desc *descp) 3706{ 3707 int result = 0; 3708 unsigned char * nameptr; 3709 size_t bufsize; 3710 size_t utf8len; 3711 unsigned char tmpbuff[128]; 3712 3713 /* guess a size... */ 3714 bufsize = (3 * key->nodeName.length) + 1; 3715 if (bufsize >= sizeof(tmpbuff) - 1) { 3716 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK); 3717 } else { 3718 nameptr = &tmpbuff[0]; 3719 } 3720 3721 result = utf8_encodestr(key->nodeName.unicode, 3722 key->nodeName.length * sizeof(UniChar), 3723 nameptr, (size_t *)&utf8len, 3724 bufsize, ':', 0); 3725 3726 if (result == ENAMETOOLONG) { 3727 bufsize = 1 + utf8_encodelen(key->nodeName.unicode, 3728 key->nodeName.length * sizeof(UniChar), 3729 ':', 0); 3730 FREE(nameptr, M_TEMP); 3731 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK); 3732 3733 result = utf8_encodestr(key->nodeName.unicode, 3734 key->nodeName.length * sizeof(UniChar), 3735 nameptr, (size_t *)&utf8len, 3736 bufsize, ':', 0); 3737 } 3738 descp->cd_parentcnid = key->parentID; 3739 descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0); 3740 descp->cd_namelen = utf8len; 3741 descp->cd_cnid = cnid; 3742 descp->cd_hint = hint; 3743 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF; 3744 if (isdir) 3745 descp->cd_flags |= CD_ISDIR; 3746 descp->cd_encoding = encoding; 3747 if (nameptr != &tmpbuff[0]) { 3748 FREE(nameptr, M_TEMP); 3749 } 3750 return result; 3751} 3752 3753 3754/* 3755 * getbsdattr - get attributes in bsd format 3756 * 3757 */ 3758static void 3759getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp) 3760{ 3761 int isDirectory = (crp->recordType == kHFSPlusFolderRecord); 3762 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo; 3763 3764 attrp->ca_recflags = crp->flags; 3765 attrp->ca_atime = to_bsd_time(crp->accessDate); 3766 attrp->ca_atimeondisk = attrp->ca_atime; 3767 attrp->ca_mtime = to_bsd_time(crp->contentModDate); 3768 attrp->ca_ctime = to_bsd_time(crp->attributeModDate); 3769 attrp->ca_itime = to_bsd_time(crp->createDate); 3770 attrp->ca_btime = to_bsd_time(crp->backupDate); 3771 3772 if ((bsd->fileMode & S_IFMT) == 0) { 3773 attrp->ca_flags = 0; 3774 attrp->ca_uid = hfsmp->hfs_uid; 3775 attrp->ca_gid = hfsmp->hfs_gid; 3776 if (isDirectory) { 3777 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS); 3778 } else { 3779 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS); 3780 } 3781 attrp->ca_linkcount = 1; 3782 attrp->ca_rdev = 0; 3783 } else { 3784 attrp->ca_linkcount = 1; /* may be overridden below */ 3785 attrp->ca_rdev = 0; 3786 attrp->ca_uid = bsd->ownerID; 3787 attrp->ca_gid = bsd->groupID; 3788 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16); 3789 attrp->ca_mode = (mode_t)bsd->fileMode; 3790 switch (attrp->ca_mode & S_IFMT) { 3791 case S_IFCHR: /* fall through */ 3792 case S_IFBLK: 3793 attrp->ca_rdev = bsd->special.rawDevice; 3794 break; 3795 3796 case S_IFDIR: /* fall through */ 3797 case S_IFREG: 3798 /* Pick up the hard link count */ 3799 if (bsd->special.linkCount > 0) 3800 attrp->ca_linkcount = bsd->special.linkCount; 3801 break; 3802 } 3803 3804 /* 3805 * Override the permissions as determined by the mount auguments 3806 * in ALMOST the same way unset permissions are treated but keep 3807 * track of whether or not the file or folder is hfs locked 3808 * by leaving the h_pflags field unchanged from what was unpacked 3809 * out of the catalog. 3810 */ 3811 /* 3812 * This code was used to do UID translation with MNT_IGNORE_OWNERS 3813 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done 3814 * at the VFS layer, so there is no need to do it here now; this also 3815 * allows VFS to let root see the real UIDs. 3816 * 3817 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) { 3818 * attrp->ca_uid = hfsmp->hfs_uid; 3819 * attrp->ca_gid = hfsmp->hfs_gid; 3820 * } 3821 */ 3822 } 3823 3824 if (isDirectory) { 3825 if (!S_ISDIR(attrp->ca_mode)) { 3826 attrp->ca_mode &= ~S_IFMT; 3827 attrp->ca_mode |= S_IFDIR; 3828 } 3829 attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence; 3830 attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ? 3831 ((const HFSPlusCatalogFolder *)crp)->folderCount : 0; 3832 3833 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */ 3834 if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) 3835 attrp->ca_flags |= UF_HIDDEN; 3836 } else { 3837 /* Keep IMMUTABLE bits in sync with HFS locked flag */ 3838 if (crp->flags & kHFSFileLockedMask) { 3839 /* The file's supposed to be locked: 3840 Make sure at least one of the IMMUTABLE bits is set: */ 3841 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0) 3842 attrp->ca_flags |= UF_IMMUTABLE; 3843 } else { 3844 /* The file's supposed to be unlocked: */ 3845 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE); 3846 } 3847 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */ 3848 if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask)) 3849 attrp->ca_flags |= UF_HIDDEN; 3850 /* get total blocks (both forks) */ 3851 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks; 3852 3853 /* On HFS+ the ThreadExists flag must always be set. */ 3854 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) 3855 attrp->ca_recflags |= kHFSThreadExistsMask; 3856 3857 /* Pick up the hardlink first link, if any. */ 3858 attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0; 3859 } 3860 3861 attrp->ca_fileid = crp->fileID; 3862 3863 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32); 3864} 3865 3866/* 3867 * promotekey - promote hfs key to hfs plus key 3868 * 3869 */ 3870static void 3871promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, 3872 HFSPlusCatalogKey *keyp, u_long *encoding) 3873{ 3874 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode; 3875 u_int32_t uniCount; 3876 int error; 3877 3878 *encoding = hfsmp->hfs_encoding; 3879 3880 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode, 3881 kHFSPlusMaxFileNameChars, &uniCount); 3882 /* 3883 * When an HFS name cannot be encoded with the current 3884 * encoding use MacRoman as a fallback. 3885 */ 3886 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) { 3887 *encoding = 0; 3888 (void) mac_roman_to_unicode(hfskey->nodeName, 3889 keyp->nodeName.unicode, 3890 kHFSPlusMaxFileNameChars, 3891 &uniCount); 3892 } 3893 3894 keyp->nodeName.length = uniCount; 3895 keyp->parentID = hfskey->parentID; 3896} 3897 3898/* 3899 * promotefork - promote hfs fork info to hfs plus 3900 * 3901 */ 3902static void 3903promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep, 3904 int resource, struct cat_fork * forkp) 3905{ 3906 struct HFSPlusExtentDescriptor *xp; 3907 u_long blocksize = HFSTOVCB(hfsmp)->blockSize; 3908 3909 bzero(forkp, sizeof(*forkp)); 3910 xp = &forkp->cf_extents[0]; 3911 if (resource) { 3912 forkp->cf_size = filep->rsrcLogicalSize; 3913 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize; 3914 forkp->cf_bytesread = 0; 3915 forkp->cf_vblocks = 0; 3916 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock; 3917 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount; 3918 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock; 3919 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount; 3920 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock; 3921 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount; 3922 } else { 3923 forkp->cf_size = filep->dataLogicalSize; 3924 forkp->cf_blocks = filep->dataPhysicalSize / blocksize; 3925 forkp->cf_bytesread = 0; 3926 forkp->cf_vblocks = 0; 3927 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock; 3928 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount; 3929 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock; 3930 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount; 3931 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock; 3932 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount; 3933 } 3934} 3935 3936/* 3937 * promoteattr - promote standard hfs catalog attributes to hfs plus 3938 * 3939 */ 3940static void 3941promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp) 3942{ 3943 u_long blocksize = HFSTOVCB(hfsmp)->blockSize; 3944 3945 if (dataPtr->recordType == kHFSFolderRecord) { 3946 const struct HFSCatalogFolder * folder; 3947 3948 folder = (const struct HFSCatalogFolder *) dataPtr; 3949 crp->recordType = kHFSPlusFolderRecord; 3950 crp->flags = folder->flags; 3951 crp->fileID = folder->folderID; 3952 crp->createDate = LocalToUTC(folder->createDate); 3953 crp->contentModDate = LocalToUTC(folder->modifyDate); 3954 crp->backupDate = LocalToUTC(folder->backupDate); 3955 crp->reserved1 = folder->valence; 3956 crp->reserved2 = 0; 3957 bcopy(&folder->userInfo, &crp->userInfo, 32); 3958 } else /* file */ { 3959 const struct HFSCatalogFile * file; 3960 3961 file = (const struct HFSCatalogFile *) dataPtr; 3962 crp->recordType = kHFSPlusFileRecord; 3963 crp->flags = file->flags; 3964 crp->fileID = file->fileID; 3965 crp->createDate = LocalToUTC(file->createDate); 3966 crp->contentModDate = LocalToUTC(file->modifyDate); 3967 crp->backupDate = LocalToUTC(file->backupDate); 3968 crp->reserved1 = 0; 3969 crp->reserved2 = 0; 3970 bcopy(&file->userInfo, &crp->userInfo, 16); 3971 bcopy(&file->finderInfo, &crp->finderInfo, 16); 3972 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize; 3973 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize; 3974 } 3975 crp->textEncoding = 0; 3976 crp->attributeModDate = crp->contentModDate; 3977 crp->accessDate = crp->contentModDate; 3978 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo)); 3979} 3980 3981/* 3982 * Build a catalog node thread record from a catalog key 3983 * and return the size of the record. 3984 */ 3985static int 3986buildthread(void *keyp, void *recp, int std_hfs, int directory) 3987{ 3988 int size = 0; 3989 3990 if (std_hfs) { 3991 HFSCatalogKey *key = (HFSCatalogKey *)keyp; 3992 HFSCatalogThread *rec = (HFSCatalogThread *)recp; 3993 3994 size = sizeof(HFSCatalogThread); 3995 bzero(rec, size); 3996 if (directory) 3997 rec->recordType = kHFSFolderThreadRecord; 3998 else 3999 rec->recordType = kHFSFileThreadRecord; 4000 rec->parentID = key->parentID; 4001 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1); 4002 4003 } else /* HFS+ */ { 4004 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp; 4005 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp; 4006 4007 size = sizeof(HFSPlusCatalogThread); 4008 if (directory) 4009 rec->recordType = kHFSPlusFolderThreadRecord; 4010 else 4011 rec->recordType = kHFSPlusFileThreadRecord; 4012 rec->reserved = 0; 4013 rec->parentID = key->parentID; 4014 bcopy(&key->nodeName, &rec->nodeName, 4015 sizeof(UniChar) * (key->nodeName.length + 1)); 4016 4017 /* HFS Plus has varaible sized thread records */ 4018 size -= (sizeof(rec->nodeName.unicode) - 4019 (rec->nodeName.length * sizeof(UniChar))); 4020 } 4021 4022 return (size); 4023} 4024 4025/* 4026 * Build a catalog node thread key. 4027 */ 4028static void 4029buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key) 4030{ 4031 if (std_hfs) { 4032 key->hfs.keyLength = kHFSCatalogKeyMinimumLength; 4033 key->hfs.reserved = 0; 4034 key->hfs.parentID = parentID; 4035 key->hfs.nodeName[0] = 0; 4036 } else { 4037 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength; 4038 key->hfsPlus.parentID = parentID; 4039 key->hfsPlus.nodeName.length = 0; 4040 } 4041} 4042 4043/* 4044 * Extract the text encoding from a catalog node record. 4045 */ 4046static u_long 4047getencoding(const CatalogRecord *crp) 4048{ 4049 u_long encoding; 4050 4051 if (crp->recordType == kHFSPlusFolderRecord) 4052 encoding = crp->hfsPlusFolder.textEncoding; 4053 else if (crp->recordType == kHFSPlusFileRecord) 4054 encoding = crp->hfsPlusFile.textEncoding; 4055 else 4056 encoding = 0; 4057 4058 return (encoding); 4059} 4060 4061/* 4062 * Extract the CNID from a catalog node record. 4063 */ 4064static cnid_t 4065getcnid(const CatalogRecord *crp) 4066{ 4067 cnid_t cnid = 0; 4068 4069 switch (crp->recordType) { 4070 case kHFSFolderRecord: 4071 cnid = crp->hfsFolder.folderID; 4072 break; 4073 case kHFSFileRecord: 4074 cnid = crp->hfsFile.fileID; 4075 break; 4076 case kHFSPlusFolderRecord: 4077 cnid = crp->hfsPlusFolder.folderID; 4078 break; 4079 case kHFSPlusFileRecord: 4080 cnid = crp->hfsPlusFile.fileID; 4081 break; 4082 default: 4083 panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp); 4084 break; 4085 } 4086 4087 return (cnid); 4088} 4089 4090/* 4091 * Extract the parent ID from a catalog node record. 4092 */ 4093static cnid_t 4094getparentcnid(const CatalogRecord *recp) 4095{ 4096 cnid_t cnid = 0; 4097 4098 switch (recp->recordType) { 4099 case kHFSFileThreadRecord: 4100 case kHFSFolderThreadRecord: 4101 cnid = recp->hfsThread.parentID; 4102 break; 4103 4104 case kHFSPlusFileThreadRecord: 4105 case kHFSPlusFolderThreadRecord: 4106 cnid = recp->hfsPlusThread.parentID; 4107 break; 4108 default: 4109 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp); 4110 break; 4111 } 4112 4113 return (cnid); 4114} 4115 4116/* 4117 * Determine if a catalog node record is a directory. 4118 */ 4119static int 4120isadir(const CatalogRecord *crp) 4121{ 4122 return (crp->recordType == kHFSFolderRecord || 4123 crp->recordType == kHFSPlusFolderRecord); 4124} 4125 4126