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