darwin-xnu/bsd/hfs/hfs_catalog.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
   3 *
   4 * @APPLE_LICENSE_HEADER_START@
   5 * 
   6 * The contents of this file constitute Original Code as defined in and
   7 * are subject to the Apple Public Source License Version 1.1 (the
   8 * "License").  You may not use this file except in compliance with the
   9 * License.  Please obtain a copy of the License at
  10 * http://www.apple.com/publicsource and read it before using this file.
  11 * 
  12 * This Original Code and all software distributed under the License are
  13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
  17 * License for the specific language governing rights and limitations
  18 * under the License.
  19 * 
  20 * @APPLE_LICENSE_HEADER_END@
  21 */
  22
  23#include <sys/systm.h>
  24#include <sys/kernel.h>
  25#include <sys/malloc.h>
  26#include <sys/stat.h>
  27#include <sys/mount.h>
  28#include <sys/vnode.h>
  29#include <sys/dirent.h>
  30#include <vfs/vfs_support.h>
  31#include <libkern/libkern.h>
  32
  33#include <sys/utfconv.h>
  34
  35#include "hfs.h"
  36#include "hfs_catalog.h"
  37#include "hfs_format.h"
  38#include "hfs_endian.h"
  39
  40#include "hfscommon/headers/BTreesInternal.h"
  41#include "hfscommon/headers/HFSUnicodeWrappers.h"
  42
  43
  44/*
  45 * Initialization of an FSBufferDescriptor structure.
  46 */
  47#define BDINIT(bd, addr) { \
  48        (bd).bufferAddress = (addr); \
  49        (bd).itemSize = sizeof(*(addr)); \
  50        (bd).itemCount = 1; \
  51}
  52
  53
  54struct btobj {
  55        BTreeIterator           iterator;
  56        HFSPlusCatalogKey       key;
  57        CatalogRecord           data;
  58};
  59
  60struct update_state {
  61        struct cat_desc *       s_desc; 
  62        struct cat_attr *       s_attr;
  63        struct cat_fork *       s_datafork;
  64        struct cat_fork *       s_rsrcfork;
  65        struct hfsmount *       s_hfsmp;
  66};
  67
  68struct position_state {
  69        int        error;
  70        u_int32_t  count;
  71        u_int32_t  index;
  72        u_int32_t  parentID;
  73        struct hfsmount *hfsmp;
  74};
  75
  76/* Map file mode type to directory entry types */
  77u_char modetodirtype[16] = {
  78        DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
  79        DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
  80        DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
  81        DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
  82};
  83#define MODE_TO_DT(mode)  (modetodirtype[((mode) & S_IFMT) >> 12])
  84
  85
  86static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
  87                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
  88
  89static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
  90                  struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
  91
  92extern int mac_roman_to_unicode(const Str31 hfs_str, UniChar *uni_str,
  93                                UInt32 maxCharLen, UInt32 *unicodeChars);
  94
  95extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
  96                          const u_int16_t* srcStr, Str31 dstStr, int retry);
  97
  98
  99/* Internal catalog support routines */
 100
 101static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
 102                            struct position_state *state);
 103
 104static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
 105
 106static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
 107
 108static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
 109                        HFSPlusCatalogKey *key, int retry);
 110
 111static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
 112
 113static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
 114
 115static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
 116
 117static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
 118                        int isdir, struct cat_desc *descp);
 119
 120static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
 121
 122static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
 123static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
 124static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
 125
 126static cnid_t getcnid(const CatalogRecord *crp);
 127static u_long getencoding(const CatalogRecord *crp);
 128static cnid_t getparentcnid(const CatalogRecord *recp);
 129
 130static int isadir(const CatalogRecord *crp);
 131
 132static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
 133
 134
 135__private_extern__
 136int
 137cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, struct proc *p)
 138{
 139        FCB *fcb;
 140        int lockflags;
 141        int result;
 142
 143        fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
 144
 145        lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
 146         
 147        result = BTReserveSpace(fcb, ops, (void*)cookie);
 148        
 149        hfs_systemfile_unlock(hfsmp, lockflags);
 150
 151        return MacToVFSError(result);
 152}
 153
 154__private_extern__
 155void
 156cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, struct proc *p)
 157{
 158        FCB *fcb;
 159        int lockflags;
 160
 161        fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
 162
 163        lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
 164
 165        (void) BTReleaseReserve(fcb, (void*)cookie);
 166
 167        hfs_systemfile_unlock(hfsmp, lockflags);
 168}
 169
 170 
 171__private_extern__
 172void
 173cat_convertattr(
 174        struct hfsmount *hfsmp,
 175        CatalogRecord * recp,
 176        struct cat_attr *attrp,
 177        struct cat_fork *datafp,
 178        struct cat_fork *rsrcfp)
 179{
 180        int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
 181
 182        if (std_hfs) {
 183                struct HFSPlusCatalogFile cnoderec;
 184
 185                promoteattr(hfsmp, recp, &cnoderec);
 186                getbsdattr(hfsmp, &cnoderec, attrp);
 187        } else {
 188                getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
 189        }
 190
 191        if (isadir(recp))
 192                bzero(datafp, sizeof(*datafp));
 193        else if (std_hfs) {
 194                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
 195                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
 196        } else {
 197                /* Convert the data fork. */
 198                datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
 199                datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
 200                if ((hfsmp->hfc_stage == HFC_RECORDING) &&
 201                    (attrp->ca_atime >= hfsmp->hfc_timebase)) {
 202                        datafp->cf_bytesread =
 203                                recp->hfsPlusFile.dataFork.clumpSize *
 204                                HFSTOVCB(hfsmp)->blockSize;
 205                } else {
 206                        datafp->cf_bytesread = 0;
 207                }
 208                datafp->cf_vblocks = 0;
 209                bcopy(&recp->hfsPlusFile.dataFork.extents[0],
 210                      &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
 211
 212                /* Convert the resource fork. */
 213                rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
 214                rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
 215                if ((hfsmp->hfc_stage == HFC_RECORDING) &&
 216                    (attrp->ca_atime >= hfsmp->hfc_timebase)) {
 217                        datafp->cf_bytesread =
 218                                recp->hfsPlusFile.resourceFork.clumpSize *
 219                                HFSTOVCB(hfsmp)->blockSize;
 220                } else {
 221                        datafp->cf_bytesread = 0;
 222                }
 223                rsrcfp->cf_vblocks = 0;
 224                bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
 225                      &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
 226        }
 227}
 228
 229__private_extern__
 230int
 231cat_convertkey(
 232        struct hfsmount *hfsmp,
 233        CatalogKey *key,
 234        CatalogRecord * recp,
 235        struct cat_desc *descp)
 236{
 237        int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
 238        HFSPlusCatalogKey * pluskey = NULL;
 239        u_long encoding;
 240
 241        if (std_hfs) {
 242                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
 243                promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
 244
 245        } else {
 246                pluskey = (HFSPlusCatalogKey *)key;
 247                encoding = getencoding(recp);
 248        }
 249
 250        builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
 251        if (std_hfs) {
 252                FREE(pluskey, M_TEMP);
 253        }
 254        return (0);
 255}
 256
 257
 258/*
 259 * cat_releasedesc
 260 */
 261__private_extern__
 262void
 263cat_releasedesc(struct cat_desc *descp)
 264{
 265        char * name;
 266
 267        if (descp == NULL)
 268                return;
 269
 270        if ((descp->cd_flags & CD_HASBUF) &&
 271            (descp->cd_nameptr != NULL)) {
 272                name = descp->cd_nameptr;
 273                descp->cd_nameptr = NULL;
 274                descp->cd_namelen = 0;
 275                descp->cd_flags &= ~CD_HASBUF;
 276                vfs_removename(name);
 277        }
 278        descp->cd_nameptr = NULL;
 279        descp->cd_namelen = 0;
 280}
 281
 282/*
 283 * These Catalog functions allow access to the HFS Catalog (database).
 284 * The catalog b-tree lock must be aquired before calling any of these routines.
 285 */
 286
 287/*
 288 * cat_lookup - lookup a catalog node using a cnode decriptor
 289 */
 290__private_extern__
 291int
 292cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
 293             struct cat_desc *outdescp, struct cat_attr *attrp,
 294             struct cat_fork *forkp, cnid_t *desc_cnid)
 295{
 296        CatalogKey * keyp;
 297        int std_hfs;
 298        int result;
 299
 300        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
 301
 302        MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
 303
 304        result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
 305        if (result)
 306                goto exit;
 307
 308        result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
 309        
 310        if (result == ENOENT) {
 311                if (!std_hfs) {
 312                        struct cat_desc temp_desc;
 313                        if (outdescp == NULL) {
 314                                bzero(&temp_desc, sizeof(temp_desc));
 315                                outdescp = &temp_desc;
 316                        }
 317                        result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
 318                        if (desc_cnid) {
 319                            *desc_cnid = outdescp->cd_cnid;
 320                        }
 321                        if (outdescp == &temp_desc) {
 322                                /* Release the local copy of desc */
 323                                cat_releasedesc(outdescp);
 324                        }
 325                } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
 326                //      make MacRoman key from utf-8
 327                //      result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
 328                //      update desc text encoding so that other catalog ops succeed
 329                }
 330        }
 331exit:   
 332        FREE(keyp, M_TEMP);
 333
 334        return (result);
 335}
 336
 337__private_extern__
 338int
 339cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
 340{
 341        struct BTreeIterator *iterator;
 342        struct FSBufferDescriptor file_data;
 343        struct HFSCatalogFile file_rec;
 344        UInt16 datasize;
 345        FCB *fcb;
 346        int result;
 347
 348        if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
 349                return (EINVAL);
 350
 351        fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
 352
 353        MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
 354        bzero(&iterator[0], 2* sizeof(*iterator));
 355        result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
 356        if (result)
 357                goto exit;
 358
 359        BDINIT(file_data, &file_rec);
 360        result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
 361        if (result) 
 362                goto exit;
 363
 364        if (file_rec.recordType != kHFSFileRecord) {
 365                result = EISDIR;
 366                goto exit;
 367        }
 368
 369        if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
 370                struct FSBufferDescriptor thread_data;
 371                struct HFSCatalogThread thread_rec;
 372
 373                file_rec.flags |= kHFSThreadExistsMask;
 374                BDINIT(thread_data, &thread_rec);
 375                thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
 376                buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
 377        
 378                result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
 379                if (result)
 380                        goto exit;
 381        
 382                (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
 383                (void) BTFlushPath(fcb);
 384        }       
 385exit:
 386        (void) BTFlushPath(fcb);
 387        FREE(iterator, M_TEMP);
 388
 389        return MacToVFSError(result);
 390}
 391
 392
 393/*
 394 * cat_findname - obtain a descriptor from cnid
 395 *
 396 * Only a thread lookup is performed.
 397 */
 398__private_extern__
 399int
 400cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
 401{
 402        struct BTreeIterator * iterator;
 403        FSBufferDescriptor btdata;
 404        CatalogKey * keyp;
 405        CatalogRecord * recp;
 406        int isdir;
 407        int result;
 408        int std_hfs;
 409
 410        isdir = 0;
 411        std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
 412
 413        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
 414        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
 415        iterator->hint.nodeNum = 0;
 416
 417        MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
 418        BDINIT(btdata, recp);
 419
 420        result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
 421        if (result)
 422                goto exit;
 423
 424        /* Turn thread record into a cnode key (in place). */
 425        switch (recp->recordType) {
 426        case kHFSFolderThreadRecord:
 427                isdir = 1;
 428                /* fall through */
 429        case kHFSFileThreadRecord:
 430                keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
 431                keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
 432                break;
 433
 434        case kHFSPlusFolderThreadRecord:
 435                isdir = 1;
 436                /* fall through */
 437        case kHFSPlusFileThreadRecord:
 438                keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
 439                keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
 440                                          (keyp->hfsPlus.nodeName.length * 2);
 441                break;
 442        default:
 443                result = ENOENT;
 444                goto exit;
 445        }
 446        if (std_hfs) {
 447                HFSPlusCatalogKey * pluskey = NULL;
 448                u_long encoding;
 449
 450                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
 451                promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
 452                builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
 453                FREE(pluskey, M_TEMP);
 454
 455        } else {
 456                builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
 457        }
 458exit:
 459        FREE(recp, M_TEMP);
 460        FREE(iterator, M_TEMP);
 461
 462        return MacToVFSError(result);
 463}
 464
 465/*
 466 * cat_idlookup - lookup a catalog node using a cnode id
 467 */
 468__private_extern__
 469int
 470cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
 471                 struct cat_attr *attrp, struct cat_fork *forkp)
 472{
 473        struct BTreeIterator * iterator;
 474        FSBufferDescriptor btdata;
 475        UInt16  datasize;
 476        CatalogKey * keyp;
 477        CatalogRecord * recp;
 478        int result;
 479        int std_hfs;
 480
 481        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
 482
 483        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
 484        bzero(iterator, sizeof(*iterator));
 485        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
 486
 487        MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
 488        BDINIT(btdata, recp);
 489
 490        result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
 491                                &btdata, &datasize, iterator);
 492        if (result)
 493                goto exit;
 494
 495        /* Turn thread record into a cnode key (in place) */
 496        switch (recp->recordType) {
 497        case kHFSFileThreadRecord:
 498        case kHFSFolderThreadRecord:
 499                keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
 500                keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
 501                break;
 502
 503        case kHFSPlusFileThreadRecord:
 504        case kHFSPlusFolderThreadRecord:
 505                keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
 506                keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
 507                                          (keyp->hfsPlus.nodeName.length * 2);
 508                break;
 509
 510        default:
 511                result = ENOENT;
 512                goto exit;
 513        }
 514
 515        result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp, NULL);
 516exit:
 517        FREE(recp, M_TEMP);
 518        FREE(iterator, M_TEMP);
 519
 520        return MacToVFSError(result);
 521}
 522
 523
 524/*
 525 * cat_lookupmangled - lookup a catalog node using a mangled name
 526 */
 527static int
 528cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
 529                  struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
 530{
 531        cnid_t fileID;
 532        int prefixlen;
 533        int result;
 534        
 535        if (wantrsrc)
 536                return (ENOENT);
 537
 538        fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
 539        if (fileID < kHFSFirstUserCatalogNodeID)
 540                return (ENOENT);
 541
 542        result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
 543        if (result)
 544                return (ENOENT);
 545
 546        /* It must be in the correct directory */
 547        if (descp->cd_parentcnid != outdescp->cd_parentcnid)
 548                goto falsematch;
 549
 550        if ((outdescp->cd_namelen < prefixlen) ||
 551            bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
 552                goto falsematch;
 553
 554        return (0);
 555
 556falsematch:
 557        cat_releasedesc(outdescp);
 558        return (ENOENT);
 559}
 560
 561
 562/*
 563 * cat_lookupbykey - lookup a catalog node using a cnode key
 564 */
 565static int
 566cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
 567                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
 568{
 569        struct BTreeIterator * iterator;
 570        FSBufferDescriptor btdata;
 571        CatalogRecord * recp;
 572        UInt16  datasize;
 573        int result;
 574        int std_hfs;
 575        u_long ilink = 0;
 576        cnid_t cnid = 0;
 577        u_long encoding = 0;
 578
 579        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
 580
 581        MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
 582        BDINIT(btdata, recp);
 583        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
 584        bzero(iterator, sizeof(*iterator));
 585        iterator->hint.nodeNum = hint;
 586        bcopy(keyp, &iterator->key, sizeof(CatalogKey));
 587
 588        result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
 589                                &btdata, &datasize, iterator);
 590        if (result) 
 591                goto exit;
 592
 593        /* Save the cnid now in case there's a hard link */
 594        cnid = getcnid(recp);
 595        encoding = getencoding(recp);
 596        hint = iterator->hint.nodeNum;
 597
 598        /* Hide the journal files (if any) */
 599        if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
 600                ((cnid == hfsmp->hfs_jnlfileid) ||
 601                 (cnid == hfsmp->hfs_jnlinfoblkid))) {
 602
 603                result = ENOENT;
 604                goto exit;
 605        }
 606
 607        /*
 608         * When a hardlink link is encountered, auto resolve it
 609         */
 610        if (!std_hfs
 611            && (attrp || forkp) 
 612            && (recp->recordType == kHFSPlusFileRecord)
 613            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
 614            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
 615            && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
 616                (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
 617
 618                ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
 619
 620                (void) resolvelink(hfsmp, ilink, (struct HFSPlusCatalogFile *)recp);
 621        }
 622
 623        if (attrp != NULL) {
 624                if (std_hfs) {
 625                        struct HFSPlusCatalogFile cnoderec;
 626
 627                        promoteattr(hfsmp, recp, &cnoderec);
 628                        getbsdattr(hfsmp, &cnoderec, attrp);
 629                } else {
 630                        getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
 631                        if (ilink)
 632                                attrp->ca_rdev = ilink;
 633                }
 634        }
 635        if (forkp != NULL) {
 636                if (isadir(recp)) {
 637                        bzero(forkp, sizeof(*forkp));
 638                } else if (std_hfs) {
 639                        promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
 640                } else if (wantrsrc) {
 641                        /* Convert the resource fork. */
 642                        forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
 643                        forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
 644                        if ((hfsmp->hfc_stage == HFC_RECORDING) &&
 645                            (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
 646                                forkp->cf_bytesread =
 647                                        recp->hfsPlusFile.resourceFork.clumpSize *
 648                                        HFSTOVCB(hfsmp)->blockSize;
 649                        } else {
 650                                forkp->cf_bytesread = 0;
 651                        }
 652                        forkp->cf_vblocks = 0;
 653                        bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
 654                              &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
 655                } else {
 656                        /* Convert the data fork. */
 657                        forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
 658                        forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
 659                        if ((hfsmp->hfc_stage == HFC_RECORDING) &&
 660                            (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
 661                                forkp->cf_bytesread =
 662                                        recp->hfsPlusFile.dataFork.clumpSize *
 663                                        HFSTOVCB(hfsmp)->blockSize;
 664                        } else {
 665                                forkp->cf_bytesread = 0;
 666                        }
 667                        forkp->cf_vblocks = 0;
 668                        bcopy(&recp->hfsPlusFile.dataFork.extents[0],
 669                              &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
 670                }
 671        }
 672        if (descp != NULL) {
 673                HFSPlusCatalogKey * pluskey = NULL;
 674
 675                if (std_hfs) {
 676                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
 677                        promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
 678        
 679                } else
 680                        pluskey = (HFSPlusCatalogKey *)&iterator->key;
 681
 682                builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
 683                if (std_hfs) {
 684                        FREE(pluskey, M_TEMP);
 685                }
 686        }
 687
 688        if (desc_cnid != NULL) {
 689            *desc_cnid = cnid;
 690        }
 691exit:
 692        FREE(iterator, M_TEMP);
 693        FREE(recp, M_TEMP);
 694
 695        return MacToVFSError(result);
 696}
 697
 698
 699/*
 700 * cat_create - create a node in the catalog
 701 */
 702__private_extern__
 703int
 704cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
 705        struct cat_desc *out_descp)
 706{
 707        ExtendedVCB * vcb;
 708        FCB * fcb;
 709        struct btobj * bto;
 710        FSBufferDescriptor btdata;
 711        u_int32_t nextCNID;
 712        u_int32_t datalen;
 713        int std_hfs;
 714        int result = 0;
 715        u_long encoding;
 716        int modeformat;
 717        int mntlock = 0;
 718
 719        modeformat = attrp->ca_mode & S_IFMT;
 720
 721        vcb = HFSTOVCB(hfsmp);
 722        fcb = GetFileControlBlock(vcb->catalogRefNum);
 723        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
 724
 725        /*
 726         * Atomically get the next CNID.  If we have wrapped the CNIDs
 727         * then keep the hfsmp lock held until we have found a CNID.
 728         */
 729        HFS_MOUNT_LOCK(hfsmp, TRUE);
 730        mntlock = 1;
 731        nextCNID = hfsmp->vcbNxtCNID;
 732        if (nextCNID == 0xFFFFFFFF) {
 733                if (std_hfs) {
 734                        result = ENOSPC;
 735                } else {
 736                        hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
 737                        hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
 738                }
 739        } else {
 740                hfsmp->vcbNxtCNID++;
 741        }
 742        hfsmp->vcbFlags |= 0xFF00;
 743        /* OK to drop lock if CNIDs are not wrapping */
 744        if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask) == 0) {
 745                HFS_MOUNT_UNLOCK(hfsmp, TRUE);
 746                mntlock = 0;
 747                if (result)
 748                        return (result);  /* HFS only exit */
 749        }
 750
 751        /* Get space for iterator, key and data */      
 752        MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
 753        bto->iterator.hint.nodeNum = 0;
 754
 755        result = buildkey(hfsmp, descp, &bto->key, 0);
 756        if (result)
 757                goto exit;
 758
 759        if (!std_hfs) {
 760                encoding = hfs_pickencoding(bto->key.nodeName.unicode,
 761                        bto->key.nodeName.length);
 762                hfs_setencodingbits(hfsmp, encoding);
 763        }
 764
 765        /*
 766         * Insert the thread record first
 767         */
 768        if (!std_hfs || (modeformat == S_IFDIR)) {
 769                datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
 770                                S_ISDIR(attrp->ca_mode));
 771                btdata.bufferAddress = &bto->data;
 772                btdata.itemSize = datalen;
 773                btdata.itemCount = 1;
 774                
 775                for (;;) {
 776                        buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
 777
 778                        result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
 779                        if ((result == btExists) && !std_hfs && mntlock) {
 780                                /*
 781                                 * Allow CNIDs on HFS Plus volumes to wrap around
 782                                 */
 783                                if (++nextCNID < kHFSFirstUserCatalogNodeID) {
 784                                        nextCNID = kHFSFirstUserCatalogNodeID;
 785                                }
 786                                continue;
 787                        }
 788                        break;
 789                }
 790                if (result) goto exit;
 791        }
 792        
 793        /*
 794         * CNID is now established. If we have wrapped then
 795         * update the vcbNxtCNID and drop the vcb lock.
 796         */
 797        if (mntlock) {
 798                hfsmp->vcbNxtCNID = nextCNID + 1;
 799                if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
 800                        hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
 801                }
 802                HFS_MOUNT_UNLOCK(hfsmp, TRUE);
 803                mntlock = 0;
 804        }
 805
 806        /*
 807         * Now insert the file/directory record
 808         */
 809        buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
 810        btdata.bufferAddress = &bto->data;
 811        btdata.itemSize = datalen;
 812        btdata.itemCount = 1;
 813        
 814        bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
 815
 816        result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
 817        if (result) {
 818                if (result == btExists)
 819                        result = EEXIST;
 820
 821                /* Back out the thread record */
 822                if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
 823                        buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
 824                        (void) BTDeleteRecord(fcb, &bto->iterator);
 825                }
 826                goto exit;
 827        }
 828
 829        /*
 830         * Insert was Successfull, update name, parent and volume
 831         */
 832
 833
 834        if (out_descp != NULL) {
 835                HFSPlusCatalogKey * pluskey = NULL;
 836
 837                if (std_hfs) {
 838                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
 839                        promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
 840        
 841                } else
 842                        pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
 843
 844                builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
 845                        encoding, S_ISDIR(attrp->ca_mode), out_descp);
 846                if (std_hfs) {
 847                        FREE(pluskey, M_TEMP);
 848                }
 849        }
 850        attrp->ca_fileid = nextCNID;
 851
 852exit:
 853        if (mntlock)
 854                HFS_MOUNT_UNLOCK(hfsmp, TRUE);
 855
 856        (void) BTFlushPath(fcb);
 857        FREE(bto, M_TEMP);
 858
 859        return MacToVFSError(result);
 860}
 861
 862
 863/*
 864 * cnode_rename - rename a catalog node
 865 *
 866 * Assumes that the target's directory exists.
 867 *
 868 * Order of B-tree operations:
 869 *      1. BTSearchRecord(from_cnode, &data);
 870 *      2. BTInsertRecord(to_cnode, &data);
 871 *      3. BTDeleteRecord(from_cnode);
 872 *      4. BTDeleteRecord(from_thread);
 873 *      5. BTInsertRecord(to_thread);
 874 */
 875__private_extern__
 876int 
 877cat_rename (
 878        struct hfsmount * hfsmp,
 879        struct cat_desc * from_cdp,
 880        struct cat_desc * todir_cdp,
 881        struct cat_desc * to_cdp,
 882        struct cat_desc * out_cdp )
 883{
 884        struct BTreeIterator * to_iterator = NULL;
 885        struct BTreeIterator * from_iterator = NULL;
 886        FSBufferDescriptor btdata;
 887        CatalogRecord * recp = NULL;
 888        HFSPlusCatalogKey * to_key;
 889        ExtendedVCB * vcb;
 890        FCB * fcb;
 891        UInt16  datasize;
 892        int result = 0;
 893        int sourcegone = 0;
 894        int skipthread = 0;
 895        int directory = from_cdp->cd_flags & CD_ISDIR;
 896        int std_hfs;
 897        u_long encoding = 0;
 898
 899        vcb = HFSTOVCB(hfsmp);
 900        fcb = GetFileControlBlock(vcb->catalogRefNum);
 901        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
 902
 903        if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
 904                return (EINVAL);
 905
 906        MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
 907        bzero(from_iterator, sizeof(*from_iterator));
 908        if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
 909                goto exit;      
 910
 911        MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
 912        bzero(to_iterator, sizeof(*to_iterator));
 913        if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
 914                goto exit;      
 915
 916        to_key = (HFSPlusCatalogKey *)&to_iterator->key;
 917        MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
 918        BDINIT(btdata, recp);
 919
 920        /*
 921         * When moving a directory, make sure its a valid move.
 922         */
 923        if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
 924                struct BTreeIterator iterator;
 925                cnid_t cnid = from_cdp->cd_cnid;
 926                cnid_t pathcnid = todir_cdp->cd_parentcnid;
 927
 928                /* First check the obvious ones */
 929                if (cnid == fsRtDirID  ||
 930                    cnid == to_cdp->cd_parentcnid  ||
 931                    cnid == pathcnid) {
 932                        result = EINVAL;
 933                        goto exit;
 934                }
 935                bzero(&iterator, sizeof(iterator));
 936                /*
 937                 * Traverese destination path all the way back to the root
 938                 * making sure that source directory is not encountered.
 939                 *
 940                 */
 941                while (pathcnid > fsRtDirID) {
 942                        buildthreadkey(pathcnid, std_hfs,
 943                                        (CatalogKey *)&iterator.key);
 944                        result = BTSearchRecord(fcb, &iterator, &btdata,
 945                                        &datasize, NULL);
 946                        if (result) goto exit;
 947                        
 948                        pathcnid = getparentcnid(recp);
 949                        if (pathcnid == cnid) {
 950                                result = EINVAL;
 951                                goto exit;
 952                        }
 953                }
 954        }
 955
 956        /*
 957         * Step 1: Find cnode data at old location
 958         */
 959        result = BTSearchRecord(fcb, from_iterator, &btdata,
 960                                &datasize, from_iterator);
 961        if (result) {
 962                if (std_hfs || (result != btNotFound)) 
 963                        goto exit;
 964        
 965                struct cat_desc temp_desc;
 966
 967                /* Probably the node has mangled name */
 968                result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL); 
 969                if (result) 
 970                        goto exit;
 971                        
 972                /* The file has mangled name.  Search the cnode data using full name */
 973                bzero(from_iterator, sizeof(*from_iterator));
 974                result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
 975                if (result) {
 976                        cat_releasedesc(&temp_desc);
 977                        goto exit;
 978                }
 979
 980                result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
 981                if (result) {
 982                        cat_releasedesc(&temp_desc);
 983                        goto exit;
 984                }
 985                
 986                cat_releasedesc(&temp_desc);
 987        }
 988
 989        /* Update the text encoding (on disk and in descriptor) */
 990        if (!std_hfs) {
 991                encoding = hfs_pickencoding(to_key->nodeName.unicode,
 992                                to_key->nodeName.length);
 993                hfs_setencodingbits(hfsmp, encoding);
 994                recp->hfsPlusFile.textEncoding = encoding;
 995                if (out_cdp)
 996                        out_cdp->cd_encoding = encoding;
 997        }
 998        
 999        if (std_hfs && !directory &&
1000            !(recp->hfsFile.flags & kHFSThreadExistsMask))
1001                skipthread = 1;
1002#if 0
1003        /*
1004         * If the keys are identical then there's nothing left to do!
1005         *
1006         * update the hint and exit
1007         *
1008         */
1009        if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
1010                goto exit;      
1011        if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
1012                goto exit;      
1013#endif
1014
1015        /* Step 2: Insert cnode at new location */
1016        result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1017        if (result == btExists) {
1018                int fromtype = recp->recordType;
1019
1020                if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
1021                        goto exit; /* EEXIST */
1022
1023                /* Find cnode data at new location */
1024                result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
1025                if (result)
1026                        goto exit;
1027                
1028                if ((fromtype != recp->recordType) ||
1029                    (from_cdp->cd_cnid != getcnid(recp))) {
1030                        result = EEXIST;
1031                        goto exit; /* EEXIST */
1032                }
1033                /* The old name is a case variant and must be removed */
1034                result = BTDeleteRecord(fcb, from_iterator);
1035                if (result)
1036                        goto exit;
1037
1038                /* Insert cnode (now that case duplicate is gone) */
1039                result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1040                if (result) {
1041                        /* Try and restore original before leaving */
1042                    // XXXdbg
1043                    #if 1
1044                       {
1045                        int err;
1046                        err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1047                        if (err)
1048                                panic("cat_create: could not undo (BTInsert = %d)", err);
1049                       }
1050                    #else
1051                        (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1052                    #endif
1053                        goto exit;
1054                }
1055                sourcegone = 1;
1056        }
1057        if (result)
1058                goto exit;
1059
1060        /* Step 3: Remove cnode from old location */
1061        if (!sourcegone) {
1062                result = BTDeleteRecord(fcb, from_iterator);
1063                if (result) {
1064                        /* Try and delete new record before leaving */
1065                  // XXXdbg
1066                  #if 1
1067                     {
1068                        int err;
1069                        err = BTDeleteRecord(fcb, to_iterator);
1070                        if (err)
1071                                panic("cat_create: could not undo (BTDelete = %d)", err);
1072                     }                  
1073                  #else
1074                        (void) BTDeleteRecord(fcb, to_iterator);
1075                  #endif
1076                        goto exit;
1077                }
1078        }
1079
1080        /* #### POINT OF NO RETURN #### */
1081
1082        /*
1083         * Step 4: Remove cnode's old thread record
1084         */
1085        buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1086        (void) BTDeleteRecord(fcb, from_iterator);
1087
1088        /*
1089         * Step 5: Insert cnode's new thread record
1090         * (optional for HFS files)
1091         */
1092        if (!skipthread) {
1093                datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
1094                btdata.itemSize = datasize;
1095                buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1096                result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1097        }
1098
1099        if (out_cdp) {
1100                HFSPlusCatalogKey * pluskey = NULL;
1101
1102                if (std_hfs) {
1103                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1104                        promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
1105
1106                        /* Save the real encoding hint in the Finder Info (field 4). */
1107                        if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
1108                                u_long realhint;
1109
1110                                realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
1111                                vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
1112                        }
1113        
1114                } else
1115                        pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
1116
1117                builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
1118                        encoding, directory, out_cdp);
1119                if (std_hfs) {
1120                        FREE(pluskey, M_TEMP);
1121                }
1122        }
1123exit:
1124        (void) BTFlushPath(fcb);
1125        if (from_iterator)
1126                FREE(from_iterator, M_TEMP);
1127        if (to_iterator)
1128                FREE(to_iterator, M_TEMP);
1129        if (recp)
1130                FREE(recp, M_TEMP);
1131        return MacToVFSError(result);
1132}
1133
1134
1135/*
1136 * cat_delete - delete a node from the catalog
1137 *
1138 * Order of B-tree operations:
1139 *      1. BTDeleteRecord(cnode);
1140 *      2. BTDeleteRecord(thread);
1141 *      3. BTUpdateRecord(parent);
1142 */
1143__private_extern__
1144int
1145cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
1146{
1147        ExtendedVCB * vcb;
1148        FCB * fcb;
1149        BTreeIterator *iterator;
1150        cnid_t cnid;
1151        int std_hfs;
1152        int result;
1153
1154        vcb = HFSTOVCB(hfsmp);
1155        fcb = GetFileControlBlock(vcb->catalogRefNum);
1156        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1157
1158        /* Preflight check:
1159         *
1160         * The root directory cannot be deleted
1161         * A directory must be empty
1162         * A file must be zero length (no blocks)
1163         */
1164        if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
1165            descp->cd_parentcnid == kHFSRootParentID)
1166                return (EINVAL);
1167
1168        /* XXX Preflight Missing */
1169        
1170        /* Get space for iterator */    
1171        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1172        iterator->hint.nodeNum = 0;
1173
1174        /*
1175         * Derive a key from either the file ID (for a virtual inode)
1176         * or the descriptor.
1177         */
1178        if (descp->cd_namelen == 0) {
1179            result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1180            cnid = attrp->ca_fileid;
1181        } else {
1182                result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1183                cnid = descp->cd_cnid;
1184        }
1185        if (result)
1186                goto exit;
1187
1188        /* Delete record */
1189        result = BTDeleteRecord(fcb, iterator);
1190        if (result) {
1191                if (std_hfs || (result != btNotFound))
1192                        goto exit;
1193
1194                struct cat_desc temp_desc;
1195                
1196                /* Probably the node has mangled name */
1197                result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL); 
1198                if (result) 
1199                        goto exit;
1200                
1201                /* The file has mangled name.  Delete the file using full name  */
1202                bzero(iterator, sizeof(*iterator));
1203                result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
1204                cnid = temp_desc.cd_cnid;
1205                if (result) {
1206                        cat_releasedesc(&temp_desc);
1207                        goto exit;
1208                }
1209
1210                result = BTDeleteRecord(fcb, iterator);
1211                if (result) { 
1212                        cat_releasedesc(&temp_desc);
1213                        goto exit;
1214                }
1215
1216                cat_releasedesc(&temp_desc);
1217        }
1218
1219        /* Delete thread record, ignore errors */
1220        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
1221        (void) BTDeleteRecord(fcb, iterator);
1222
1223exit:
1224        (void) BTFlushPath(fcb);
1225        FREE(iterator, M_TEMP);
1226
1227        return MacToVFSError(result);
1228}
1229
1230
1231/*
1232 * cnode_update - update the catalog node described by descp
1233 * using the data from attrp and forkp.
1234 */
1235__private_extern__
1236int
1237cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
1238        struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
1239{
1240        ExtendedVCB * vcb;
1241        FCB * fcb;
1242        BTreeIterator * iterator;
1243        struct update_state state;
1244        int std_hfs;
1245        int result;
1246
1247        vcb = HFSTOVCB(hfsmp);
1248        fcb = GetFileControlBlock(vcb->catalogRefNum);
1249        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1250
1251        state.s_desc = descp;
1252        state.s_attr = attrp;
1253        state.s_datafork = dataforkp;
1254        state.s_rsrcfork = rsrcforkp;
1255        state.s_hfsmp = hfsmp;
1256
1257        /* Get space for iterator */    
1258        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1259
1260        /*
1261         * For open-deleted files we need to do a lookup by cnid
1262         * (using thread rec).
1263         *
1264         * For hard links, the target of the update is the inode
1265         * itself (not the link record) so a lookup by fileid
1266         * (i.e. thread rec) is needed.
1267         */
1268        if ((descp->cd_cnid != attrp->ca_fileid) || (descp->cd_namelen == 0))
1269                result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1270        else
1271                result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1272        if (result)
1273                goto exit;
1274
1275        /* Pass a node hint */
1276        iterator->hint.nodeNum = descp->cd_hint;
1277
1278        result = BTUpdateRecord(fcb, iterator,
1279                                (IterateCallBackProcPtr)catrec_update, &state);
1280        if (result)
1281                goto exit;
1282
1283        /* Update the node hint. */
1284        descp->cd_hint = iterator->hint.nodeNum;
1285
1286exit:
1287        (void) BTFlushPath(fcb);
1288        FREE(iterator, M_TEMP);
1289
1290        return MacToVFSError(result);
1291}
1292
1293/*
1294 * catrec_update - Update the fields of a catalog record
1295 * This is called from within BTUpdateRecord.
1296 */
1297static int
1298catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
1299{
1300        struct cat_desc *descp;
1301        struct cat_attr *attrp;
1302        struct cat_fork *forkp;
1303        struct hfsmount *hfsmp;
1304        long blksize;
1305        int i;
1306
1307        descp   = state->s_desc;
1308        attrp   = state->s_attr;
1309        hfsmp   = state->s_hfsmp;
1310        blksize = HFSTOVCB(hfsmp)->blockSize;
1311
1312        switch (crp->recordType) {
1313        case kHFSFolderRecord: {
1314                HFSCatalogFolder *dir;
1315                
1316                dir = (struct HFSCatalogFolder *)crp;
1317                /* Do a quick sanity check */
1318                if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1319                    (dir->folderID != descp->cd_cnid))
1320                        return (btNotFound);
1321                dir->valence    = attrp->ca_entries;
1322                dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1323                dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1324                dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1325                bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
1326                bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
1327                break;
1328        }
1329        case kHFSFileRecord: {
1330                HFSCatalogFile *file;
1331                
1332                file = (struct HFSCatalogFile *)crp;
1333                /* Do a quick sanity check */
1334                if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1335                    (file->fileID != attrp->ca_fileid))
1336                        return (btNotFound);
1337                file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1338                file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1339                file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1340                bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
1341                bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
1342                if (state->s_rsrcfork) {
1343                        forkp = state->s_rsrcfork;
1344                        file->rsrcLogicalSize  = forkp->cf_size;
1345                        file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
1346                        for (i = 0; i < kHFSExtentDensity; ++i) {
1347                                file->rsrcExtents[i].startBlock =
1348                                        (u_int16_t)forkp->cf_extents[i].startBlock;
1349                                file->rsrcExtents[i].blockCount =
1350                                        (u_int16_t)forkp->cf_extents[i].blockCount;
1351                        }
1352                }
1353                if (state->s_datafork) {
1354                        forkp = state->s_datafork;
1355                        file->dataLogicalSize  = forkp->cf_size;
1356                        file->dataPhysicalSize = forkp->cf_blocks * blksize;
1357                        for (i = 0; i < kHFSExtentDensity; ++i) {
1358                                file->dataExtents[i].startBlock =
1359                                        (u_int16_t)forkp->cf_extents[i].startBlock;
1360                                file->dataExtents[i].blockCount =
1361                                        (u_int16_t)forkp->cf_extents[i].blockCount;
1362                        }
1363                }
1364                break;
1365        }
1366        case kHFSPlusFolderRecord: {
1367                HFSPlusCatalogFolder *dir;
1368                
1369                dir = (struct HFSPlusCatalogFolder *)crp;
1370                /* Do a quick sanity check */
1371                if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
1372                        (dir->folderID != descp->cd_cnid)) 
1373                        return (btNotFound);
1374                dir->flags            = attrp->ca_recflags;
1375                dir->valence          = attrp->ca_entries;
1376                dir->createDate       = to_hfs_time(attrp->ca_itime);
1377                dir->contentModDate   = to_hfs_time(attrp->ca_mtime);
1378                dir->backupDate       = to_hfs_time(attrp->ca_btime);
1379                dir->accessDate       = to_hfs_time(attrp->ca_atime);
1380                attrp->ca_atimeondisk = attrp->ca_atime;        
1381                dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
1382                dir->textEncoding     = descp->cd_encoding;
1383                dir->attrBlocks       = attrp->ca_attrblks;
1384                bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
1385                /*
1386                 * Update the BSD Info if it was already initialized on
1387                 * disk or if the runtime values have been modified.
1388                 *
1389                 * If the BSD info was already initialized, but
1390                 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1391                 * probably different than what was on disk.  We don't want
1392                 * to overwrite the on-disk values (so if we turn off
1393                 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1394                 * This way, we can still change fields like the mode or
1395                 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1396                 *
1397                 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1398                 * won't change the uid or gid from their defaults.  So, if
1399                 * the BSD info wasn't set, and the runtime values are not
1400                 * default, then what changed was the mode or flags.  We
1401                 * have to set the uid and gid to something, so use the
1402                 * supplied values (which will be default), which has the
1403                 * same effect as creating a new file while
1404                 * MNT_UNKNOWNPERMISSIONS is set.
1405                 */
1406                if ((dir->bsdInfo.fileMode != 0) ||
1407                    (attrp->ca_flags != 0) ||
1408                    (attrp->ca_uid != hfsmp->hfs_uid) ||
1409                    (attrp->ca_gid != hfsmp->hfs_gid) ||
1410                    ((attrp->ca_mode & ALLPERMS) !=
1411                     (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
1412                        if ((dir->bsdInfo.fileMode == 0) ||
1413                            (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1414                                dir->bsdInfo.ownerID = attrp->ca_uid;
1415                                dir->bsdInfo.groupID = attrp->ca_gid;
1416                        }
1417                        dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1418                        dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1419                        dir->bsdInfo.fileMode   = attrp->ca_mode;
1420                }
1421                break;
1422        }
1423        case kHFSPlusFileRecord: {
1424                HFSPlusCatalogFile *file;
1425                
1426                file = (struct HFSPlusCatalogFile *)crp;
1427                /* Do a quick sanity check */
1428                if (file->fileID != attrp->ca_fileid)
1429                        return (btNotFound);
1430                file->flags            = attrp->ca_recflags;
1431                file->createDate       = to_hfs_time(attrp->ca_itime);
1432                file->contentModDate   = to_hfs_time(attrp->ca_mtime);
1433                file->backupDate       = to_hfs_time(attrp->ca_btime);
1434                file->accessDate       = to_hfs_time(attrp->ca_atime);
1435                attrp->ca_atimeondisk  = attrp->ca_atime;       
1436                file->attributeModDate = to_hfs_time(attrp->ca_ctime);
1437                file->textEncoding     = descp->cd_encoding;
1438                file->attrBlocks       = attrp->ca_attrblks;
1439                bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
1440                /*
1441                 * Update the BSD Info if it was already initialized on
1442                 * disk or if the runtime values have been modified.
1443                 *
1444                 * If the BSD info was already initialized, but
1445                 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1446                 * probably different than what was on disk.  We don't want
1447                 * to overwrite the on-disk values (so if we turn off
1448                 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1449                 * This way, we can still change fields like the mode or
1450                 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1451                 *
1452                 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1453                 * won't change the uid or gid from their defaults.  So, if
1454                 * the BSD info wasn't set, and the runtime values are not
1455                 * default, then what changed was the mode or flags.  We
1456                 * have to set the uid and gid to something, so use the
1457                 * supplied values (which will be default), which has the
1458                 * same effect as creating a new file while
1459                 * MNT_UNKNOWNPERMISSIONS is set.
1460                 */
1461                if ((file->bsdInfo.fileMode != 0) ||
1462                    (attrp->ca_flags != 0) ||
1463                    (attrp->ca_uid != hfsmp->hfs_uid) ||
1464                    (attrp->ca_gid != hfsmp->hfs_gid) ||
1465                    ((attrp->ca_mode & ALLPERMS) !=
1466                     (hfsmp->hfs_file_mask & ACCESSPERMS))) {
1467                        if ((file->bsdInfo.fileMode == 0) ||
1468                            (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
1469                                file->bsdInfo.ownerID = attrp->ca_uid;
1470                                file->bsdInfo.groupID = attrp->ca_gid;
1471                        }
1472                        file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1473                        file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1474                        file->bsdInfo.fileMode   = attrp->ca_mode;
1475                }
1476                if (state->s_rsrcfork) {
1477                        forkp = state->s_rsrcfork;
1478                        file->resourceFork.logicalSize = forkp->cf_size;
1479                        file->resourceFork.totalBlocks = forkp->cf_blocks;
1480                        bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
1481                                sizeof(HFSPlusExtentRecord));
1482                        /* Push blocks read to disk */
1483                        file->resourceFork.clumpSize =
1484                                        howmany(forkp->cf_bytesread, blksize);
1485                }
1486                if (state->s_datafork) {
1487                        forkp = state->s_datafork;
1488                        file->dataFork.logicalSize = forkp->cf_size;
1489                        file->dataFork.totalBlocks = forkp->cf_blocks;
1490                        bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
1491                                sizeof(HFSPlusExtentRecord));
1492                        /* Push blocks read to disk */
1493                        file->dataFork.clumpSize =
1494                                        howmany(forkp->cf_bytesread, blksize);
1495                }
1496
1497                if ((file->resourceFork.extents[0].startBlock != 0) &&
1498                    (file->resourceFork.extents[0].startBlock ==
1499                     file->dataFork.extents[0].startBlock))
1500                        panic("catrec_update: rsrc fork == data fork");
1501
1502                /* Synchronize the lock state */
1503                if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1504                        file->flags |= kHFSFileLockedMask;
1505                else
1506                        file->flags &= ~kHFSFileLockedMask;
1507
1508                /* Push out special field if necessary */
1509                if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode))
1510                        file->bsdInfo.special.rawDevice = attrp->ca_rdev;
1511                else if (descp->cd_cnid != attrp->ca_fileid
1512                     ||  attrp->ca_nlink == 2)
1513                        file->bsdInfo.special.linkCount = attrp->ca_nlink;
1514                break;
1515        }
1516        default:
1517                return (btNotFound);
1518        }
1519        return (0);
1520}
1521
1522/*
1523 * Callback to collect directory entries.
1524 * Called with readattr_state for each item in a directory.
1525 */
1526struct readattr_state {
1527        struct hfsmount *hfsmp;
1528        struct cat_entrylist *list;
1529        cnid_t  dir_cnid;
1530        int stdhfs;
1531        int error;
1532};
1533
1534static int
1535cat_readattr(const CatalogKey *key, const CatalogRecord *rec,
1536             struct readattr_state *state)
1537{
1538        struct cat_entrylist *list = state->list;
1539        struct hfsmount *hfsmp = state->hfsmp;
1540        struct cat_entry *cep;
1541        cnid_t parentcnid;
1542
1543        if (list->realentries >= list->maxentries)
1544                return (0);  /* stop */
1545        
1546        parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
1547
1548        switch(rec->recordType) {
1549        case kHFSPlusFolderRecord:
1550        case kHFSPlusFileRecord:
1551        case kHFSFolderRecord:
1552        case kHFSFileRecord:
1553                if (parentcnid != state->dir_cnid) {
1554                        state->error = ENOENT;
1555                        return (0);     /* stop */
1556                }
1557                break;
1558        default:
1559                state->error = ENOENT;
1560                return (0);     /* stop */
1561        }
1562
1563        /* Hide the private meta data directory and journal files */
1564        if (parentcnid == kHFSRootFolderID) {
1565                if ((rec->recordType == kHFSPlusFolderRecord) &&
1566                    (rec->hfsPlusFolder.folderID == hfsmp->hfs_privdir_desc.cd_cnid)) {
1567                        return (1);     /* continue */
1568                }
1569                if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
1570                    (rec->recordType == kHFSPlusFileRecord) &&
1571                    ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
1572                     (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
1573
1574                        return (1);     /* continue */
1575                }
1576        }
1577
1578        cep = &list->entry[list->realentries++];
1579
1580        if (state->stdhfs) {
1581                struct HFSPlusCatalogFile cnoderec;
1582                HFSPlusCatalogKey * pluskey;
1583                long encoding;
1584
1585                promoteattr(hfsmp, rec, &cnoderec);
1586                getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
1587
1588                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1589                promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
1590                builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
1591                FREE(pluskey, M_TEMP);
1592
1593                if (rec->recordType == kHFSFileRecord) {
1594                        int blksize = HFSTOVCB(hfsmp)->blockSize;
1595
1596                        cep->ce_datasize = rec->hfsFile.dataLogicalSize;
1597                        cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
1598                        cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
1599                        cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
1600                }
1601        } else {
1602                getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
1603                builddesc((HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
1604                        isadir(rec), &cep->ce_desc);
1605                
1606                if (rec->recordType == kHFSPlusFileRecord) {
1607                        cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
1608                        cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
1609                        cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
1610                        cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
1611                        
1612                        /* Save link reference for later processing. */
1613                        if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
1614                        &&  (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator))
1615                                cep->ce_attr.ca_rdev = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
1616                }
1617        }
1618
1619        return (list->realentries < list->maxentries);
1620}
1621
1622/*
1623 * Pack a cat_entrylist buffer with attributes from the catalog
1624 *
1625 * Note: index is zero relative
1626 */
1627__private_extern__
1628int
1629cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
1630{
1631        FCB* fcb;
1632        CatalogKey * key;
1633        BTreeIterator * iterator;
1634        struct readattr_state state;
1635        cnid_t parentcnid;
1636        int i;
1637        int std_hfs;
1638        int index;
1639        int have_key;
1640        int result = 0;
1641
1642        ce_list->realentries = 0;
1643
1644        fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
1645        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
1646        parentcnid = dirhint->dh_desc.cd_parentcnid;
1647
1648        state.hfsmp = hfsmp;
1649        state.list = ce_list;
1650        state.dir_cnid = parentcnid;
1651        state.stdhfs = std_hfs;
1652        state.error = 0;
1653
1654        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
1655        bzero(iterator, sizeof(*iterator));
1656        key = (CatalogKey *)&iterator->key;
1657        have_key = 0;
1658        iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
1659        index = dirhint->dh_index + 1;
1660
1661        /*
1662         * Attempt to build a key from cached filename
1663         */
1664        if (dirhint->dh_desc.cd_namelen != 0) {
1665                if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
1666                        have_key = 1;
1667                }
1668        }
1669
1670        /*
1671         * If the last entry wasn't cached then position the btree iterator
1672         */
1673        if ((index == 0) || !have_key) {
1674                /*
1675                 * Position the iterator at the directory's thread record.
1676                 * (i.e. just before the first entry)
1677                 */
1678                buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
1679                result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
1680                if (result) {
1681                        result = MacToVFSError(result);
1682                        goto exit;
1683                }
1684        
1685                /*
1686                 * Iterate until we reach the entry just
1687                 * before the one we want to start with.
1688                 */
1689                if (index > 0) {
1690                        struct position_state ps;
1691
1692                        ps.error = 0;
1693                        ps.count = 0;
1694                        ps.index = index;
1695                        ps.parentID = dirhint->dh_desc.cd_parentcnid;
1696                        ps.hfsmp = hfsmp;
1697
1698                        result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1699                                                  (IterateCallBackProcPtr)cat_findposition, &ps);
1700                        if (ps.error)
1701                                result = ps.error;
1702                        else
1703                                result = MacToVFSError(result);
1704                        if (result) {
1705                                result = MacToVFSError(result);
1706                                goto exit;
1707                        }
1708                }
1709        }
1710
1711        /* Fill list with entries starting at iterator->key. */
1712        result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
1713                        (IterateCallBackProcPtr)cat_readattr, &state);
1714
1715        if (state.error)
1716                result = state.error;
1717        else if (ce_list->realentries == 0)
1718                result = ENOENT;
1719        else
1720                result = MacToVFSError(result);
1721
1722        if (std_hfs)
1723                goto exit;
1724
1725        /*
1726         *  Resolve any hard links.
1727         */
1728        for (i = 0; i < (int)ce_list->realentries; ++i) {
1729                struct FndrFileInfo *fip;
1730                struct cat_entry *cep;
1731                struct HFSPlusCatalogFile filerec;
1732
1733                cep = &ce_list->entry[i];
1734                if (!S_ISREG(cep->ce_attr.ca_mode))
1735                        continue;
1736        
1737                /* Note: Finder info is still in Big Endian */
1738                fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
1739
1740                /* Check for hard link signature. */
1741                if ((cep->ce_attr.ca_rdev != 0)
1742                &&  (SWAP_BE32(fip->fdType) == kHardLinkFileType)
1743                &&  (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
1744                &&  ((cep->ce_attr.ca_itime == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
1745                     (cep->ce_attr.ca_itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1746
1747                        if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
1748                                continue;
1749                        /* Repack entry from inode record. */
1750                        getbsdattr(hfsmp, &filerec, &cep->ce_attr);             
1751                        cep->ce_datasize = filerec.dataFork.logicalSize;
1752                        cep->ce_datablks = filerec.dataFork.totalBlocks;
1753                        cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
1754                        cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
1755                }
1756        }
1757exit:
1758        FREE(iterator, M_TEMP);
1759        
1760        return MacToVFSError(result);
1761}
1762
1763#define SMALL_DIRENTRY_SIZE  (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
1764
1765/*
1766 * Callback to pack directory entries.
1767 * Called with packdirentry_state for each item in a directory.
1768 */
1769
1770/* Hard link information collected during cat_getdirentries. */
1771struct linkinfo {
1772        u_long       link_ref;
1773        user_addr_t  dirent_addr;
1774};
1775typedef struct linkinfo linkinfo_t;
1776
1777/* State information for the cat_packdirentry callback function. */
1778struct packdirentry_state {
1779        int            cbs_extended;
1780        u_int32_t      cbs_parentID;
1781        u_int32_t      cbs_index;
1782        uio_t          cbs_uio;
1783        ExtendedVCB *  cbs_hfsmp;
1784        int            cbs_result;
1785        int32_t        cbs_nlinks;
1786        int32_t        cbs_maxlinks;
1787        linkinfo_t *   cbs_linkinfo;
1788        struct cat_desc * cbs_desc;
1789//      struct dirent  * cbs_stdentry;
1790        // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
1791        struct direntry * cbs_direntry;
1792        struct direntry * cbs_prevdirentry;
1793        u_int32_t      cbs_previlinkref;
1794        Boolean        cbs_hasprevdirentry;
1795        Boolean        cbs_eof;
1796};
1797
1798static int
1799cat_packdirentry(const CatalogKey *ckp, const CatalogRecord *crp,
1800                 struct packdirentry_state *state)
1801{
1802        struct hfsmount *hfsmp;
1803        CatalogName *cnp;
1804        cnid_t curID;
1805        OSErr result;
1806        struct dirent catent;
1807        struct direntry * entry = NULL;
1808        time_t itime;
1809        u_int32_t ilinkref = 0;
1810        u_int32_t curlinkref = 0;
1811        cnid_t  cnid;
1812        int hide = 0;
1813        u_int8_t type;
1814        u_int8_t is_mangled = 0;
1815        char *nameptr;
1816        user_addr_t uiobase;
1817        size_t namelen = 0;
1818        size_t maxnamelen;
1819        size_t uiosize = 0;
1820        caddr_t uioaddr;
1821        Boolean stop_after_pack = false;
1822        
1823        hfsmp = state->cbs_hfsmp;
1824
1825        if (hfsmp->hfs_flags & HFS_STANDARD)
1826                curID = ckp->hfs.parentID;
1827        else
1828                curID = ckp->hfsPlus.parentID;
1829
1830        /* We're done when parent directory changes */
1831        if (state->cbs_parentID != curID) {
1832                if (state->cbs_extended) {
1833                        if (state->cbs_hasprevdirentry) { /* the last record haven't been returned yet, so we want to stop after
1834                                                                                           * packing the last item */
1835                                stop_after_pack = true;
1836                        } else {
1837                                state->cbs_result = ENOENT;
1838                                return (0);     /* stop */
1839                        }                               
1840                } else {
1841                        state->cbs_result = ENOENT;
1842                        return (0);     /* stop */
1843                }
1844        }
1845
1846        if (state->cbs_extended) {
1847                entry = state->cbs_direntry;
1848                nameptr = &entry->d_name[0];
1849                maxnamelen = NAME_MAX;
1850        } else {
1851                nameptr = &catent.d_name[0];
1852                maxnamelen = NAME_MAX;
1853        }
1854
1855        if (state->cbs_extended && stop_after_pack) {
1856                cnid = INT_MAX;                 /* the last item returns a non-zero invalid cookie */
1857        } else {
1858                if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
1859                        switch(crp->recordType) {
1860                        case kHFSPlusFolderRecord:
1861                                type = DT_DIR;
1862                                cnid = crp->hfsPlusFolder.folderID;
1863                                /* Hide our private meta data directory */
1864                                if ((curID == kHFSRootFolderID) &&
1865                                        (cnid == hfsmp->hfs_privdir_desc.cd_cnid)) {
1866                                        hide = 1;
1867                                }
1868
1869                                break;
1870                        case kHFSPlusFileRecord:
1871                                itime = to_bsd_time(crp->hfsPlusFile.createDate);
1872                                /*
1873                                 * When a hardlink link is encountered save its link ref.
1874                                 */
1875                                if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
1876                                        (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
1877                                        ((itime == (time_t)hfsmp->hfs_itime) ||
1878                                         (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
1879                                        ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
1880                                }
1881                                type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
1882                                cnid = crp->hfsPlusFile.fileID;
1883                                /* Hide the journal files */
1884                                if ((curID == kHFSRootFolderID) &&
1885                                        ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
1886                                        ((cnid == hfsmp->hfs_jnlfileid) ||
1887                                         (cnid == hfsmp->hfs_jnlinfoblkid))) {
1888                                        hide = 1;
1889                                }
1890                                break;
1891                        default:
1892                                return (0);     /* stop */
1893                        };
1894
1895                        cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
1896                        result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
1897                                                                        nameptr, &namelen, maxnamelen + 1, ':', 0);
1898                        if (result == ENAMETOOLONG) {
1899                                result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
1900                                                                                                         cnp->ustr.unicode, maxnamelen + 1,
1901                                                                                                         (ByteCount*)&namelen, nameptr,
1902                                                                                                         cnid);         
1903                                is_mangled = 1;
1904                        }
1905                } else { /* hfs */
1906                        switch(crp->recordType) {
1907                        case kHFSFolderRecord:
1908                                type = DT_DIR;
1909                                cnid = crp->hfsFolder.folderID;
1910                                break;
1911                        case kHFSFileRecord:
1912                                type = DT_REG;
1913                                cnid = crp->hfsFile.fileID;
1914                                break;
1915                        default:
1916                                return (0);     /* stop */
1917                        };
1918
1919                        cnp = (CatalogName*) ckp->hfs.nodeName;
1920                        result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1,
1921                                                                 (ByteCount *)&namelen, nameptr);
1922                        /*
1923                         * When an HFS name cannot be encoded with the current
1924                         * volume encoding we use MacRoman as a fallback.
1925                         */
1926                        if (result)
1927                                result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1,
1928                                                                                   (ByteCount *)&namelen, nameptr);
1929                }
1930        }
1931
1932        if (state->cbs_extended) {
1933                /*
1934                 * The index is 1 relative and includes "." and ".."
1935                 *
1936                 * Also stuff the cnid in the upper 32 bits of the cookie.  The cookie is stored to the previous entry, which
1937                 * will be packed and copied this time
1938                 */
1939                state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
1940                uiosize = state->cbs_prevdirentry->d_reclen;
1941                uioaddr = (caddr_t) state->cbs_prevdirentry;
1942        } else {
1943                catent.d_type = type;
1944                catent.d_namlen = namelen;
1945                catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
1946                if (hide)
1947                        catent.d_fileno = 0;  /* file number = 0 means skip entry */
1948                else
1949                        catent.d_fileno = cnid;
1950                uioaddr = (caddr_t) &catent;
1951        }
1952
1953        /* Save current base address for post processing of hard-links. */
1954        uiobase = uio_curriovbase(state->cbs_uio);
1955
1956        /* If this entry won't fit then we're done */
1957        if ((uiosize > uio_resid(state->cbs_uio)) ||
1958            (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
1959                return (0);     /* stop */
1960        }
1961
1962        if (!state->cbs_extended || state->cbs_hasprevdirentry) {
1963                state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
1964                if (state->cbs_result == 0) {
1965                        ++state->cbs_index;
1966
1967                        /* Remember previous entry */
1968                        state->cbs_desc->cd_cnid = cnid;
1969                        if (type == DT_DIR) {
1970                                state->cbs_desc->cd_flags |= CD_ISDIR;
1971                        } else {
1972                                state->cbs_desc->cd_flags &= ~CD_ISDIR;
1973                        }
1974                        if (state->cbs_desc->cd_nameptr != NULL) {
1975                                vfs_removename(state->cbs_desc->cd_nameptr);
1976                        }
1977#if 0
1978                        state->cbs_desc->cd_encoding = xxxx;
1979#endif
1980                        if (!is_mangled) {
1981                                state->cbs_desc->cd_namelen = namelen;
1982                                state->cbs_desc->cd_nameptr = vfs_addname(nameptr, namelen, 0, 0);
1983                        } else {
1984                                /* Store unmangled name for the directory hint else it will 
1985                                 * restart readdir at the last location again 
1986                                 */
1987                                char *new_nameptr;
1988                                size_t bufsize;
1989                                size_t tmp_namelen = 0;
1990                        
1991                                cnp = (CatalogName *)&ckp->hfsPlus.nodeName;
1992                                bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
1993                                                                                         cnp->ustr.length * sizeof(UniChar),
1994                                                                                         ':', 0);
1995                                MALLOC(new_nameptr, char *, bufsize, M_TEMP, M_WAITOK);
1996                                result = utf8_encodestr(cnp->ustr.unicode,
1997                                                                                cnp->ustr.length * sizeof(UniChar),
1998                                                                                new_nameptr, &tmp_namelen,
1999                                                                                bufsize, ':', 0);
2000                        
2001                                state->cbs_desc->cd_namelen = tmp_namelen;
2002                                state->cbs_desc->cd_nameptr = vfs_addname(new_nameptr, tmp_namelen, 0, 0);
2003                        
2004                                FREE(new_nameptr, M_TEMP);
2005                        } 
2006                }
2007                if (state->cbs_hasprevdirentry) {
2008                        curlinkref = ilinkref;               /* save current */
2009                        ilinkref = state->cbs_previlinkref;  /* use previous */
2010                }
2011                /*
2012                 * Record any hard links for post processing.
2013                 */
2014                if ((ilinkref != 0) &&
2015                        (state->cbs_result == 0) &&
2016                        (state->cbs_nlinks < state->cbs_maxlinks)) {
2017                        state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
2018                        state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
2019                        state->cbs_nlinks++;
2020                }
2021                if (state->cbs_hasprevdirentry) {
2022                        ilinkref = curlinkref;   /* restore current */
2023                }
2024        }
2025
2026        if (state->cbs_extended) {      /* fill the direntry to be used the next time */
2027                if (stop_after_pack) {
2028                        state->cbs_eof = true;
2029                        return (0);     /* stop */
2030                }
2031                entry->d_type = type;
2032                entry->d_namlen = namelen;
2033                entry->d_reclen = EXT_DIRENT_LEN(namelen);
2034                if (hide)
2035                        entry->d_fileno = 0;  /* file number = 0 means skip entry */
2036                else
2037                        entry->d_fileno = cnid;
2038                /* swap the current and previous entry */
2039                struct direntry * tmp;
2040                tmp = state->cbs_direntry;
2041                state->cbs_direntry = state->cbs_prevdirentry;
2042                state->cbs_prevdirentry = tmp;
2043                state->cbs_hasprevdirentry = true;
2044                state->cbs_previlinkref = ilinkref;
2045        }
2046
2047        /* Continue iteration if there's room */
2048        return (state->cbs_result == 0  &&
2049                uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
2050}
2051
2052
2053/*
2054 * Pack a uio buffer with directory entries from the catalog
2055 */
2056__private_extern__
2057int
2058cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
2059                                  uio_t uio, int extended, int * items, int * eofflag)
2060{
2061        FCB* fcb;
2062        BTreeIterator * iterator;
2063        CatalogKey * key;
2064        struct packdirentry_state state;
2065        void * buffer;
2066        int bufsize;
2067        int maxlinks;
2068        int result;
2069        int index;
2070        int have_key;
2071        
2072        fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
2073
2074        /*
2075         * Get a buffer for link info array, btree iterator and a direntry:
2076         */
2077        maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
2078        bufsize = (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
2079        if (extended) {
2080                bufsize += 2*sizeof(struct direntry);
2081        }
2082        MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
2083        bzero(buffer, bufsize);
2084
2085        state.cbs_extended = extended;
2086        state.cbs_hasprevdirentry = false;
2087        state.cbs_previlinkref = 0;
2088        state.cbs_nlinks = 0;
2089        state.cbs_maxlinks = maxlinks;
2090        state.cbs_linkinfo = (linkinfo_t *) buffer;
2091
2092        iterator = (BTreeIterator *) ((char *)buffer + (maxlinks * sizeof(linkinfo_t)));
2093        key = (CatalogKey *)&iterator->key;
2094        have_key = 0;
2095        index = dirhint->dh_index + 1;
2096        if (extended) {
2097                state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
2098                state.cbs_prevdirentry = state.cbs_direntry + 1;
2099                state.cbs_eof = false;
2100        }
2101        /*
2102         * Attempt to build a key from cached filename
2103         */
2104        if (dirhint->dh_desc.cd_namelen != 0) {
2105                if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2106                        have_key = 1;
2107                }
2108        }
2109
2110        /*
2111         * If the last entry wasn't cached then position the btree iterator
2112         */
2113        if ((index == 0) || !have_key) {
2114                /*
2115                 * Position the iterator at the directory's thread record.
2116                 * (i.e. just before the first entry)
2117                 */
2118                buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
2119                result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
2120                if (result) {
2121                        result = MacToVFSError(result);
2122                        goto cleanup;
2123                }
2124        
2125                /*
2126                 * Iterate until we reach the entry just
2127                 * before the one we want to start with.
2128                 */
2129                if (index > 0) {
2130                        struct position_state ps;
2131
2132                        ps.error = 0;
2133                        ps.count = 0;
2134                        ps.index = index;
2135                        ps.parentID = dirhint->dh_desc.cd_parentcnid;
2136                        ps.hfsmp = hfsmp;
2137
2138                        result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2139                                                  (IterateCallBackProcPtr)cat_findposition, &ps);
2140                        if (ps.error)
2141                                result = ps.error;
2142                        else
2143                                result = MacToVFSError(result);
2144                        if (result) {
2145                                result = MacToVFSError(result);
2146                                goto cleanup;
2147                        }
2148                }
2149        }
2150
2151        state.cbs_index = index;
2152        state.cbs_hfsmp = hfsmp;
2153        state.cbs_uio = uio;
2154        state.cbs_desc = &dirhint->dh_desc;
2155        state.cbs_result = 0;
2156        state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
2157
2158        enum BTreeIterationOperations op;
2159        if (extended && index != 0 && have_key)
2160                op = kBTreeCurrentRecord;
2161        else
2162                op = kBTreeNextRecord;
2163
2164        /*
2165         * Process as many entries as possible starting at iterator->key.
2166         */
2167        result = BTIterateRecords(fcb, op, iterator,
2168                                  (IterateCallBackProcPtr)cat_packdirentry, &state);
2169
2170        /* Note that state.cbs_index is still valid on errors */
2171        *items = state.cbs_index - index;
2172        index = state.cbs_index;
2173
2174        if (state.cbs_eof) {
2175                *eofflag = 1;
2176        }
2177        
2178        /* Finish updating the catalog iterator. */
2179        dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
2180        dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
2181        dirhint->dh_index = index - 1;
2182        
2183        /*
2184         * Post process any hard links to get the real file id.
2185         */
2186        if (state.cbs_nlinks > 0) {
2187                u_int32_t fileid = 0;
2188                user_addr_t address;
2189                int i;
2190
2191                for (i = 0; i < state.cbs_nlinks; ++i) {
2192                        if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
2193                                continue;
2194                        /* This assumes that d_ino is always first field. */
2195                        address = state.cbs_linkinfo[i].dirent_addr;
2196                        if (address == (user_addr_t)0)
2197                                continue;
2198                        if (uio_isuserspace(uio)) {
2199                                (void) copyout(&fileid, address,
2200                                               extended ? sizeof(ino64_t) : sizeof(ino_t));
2201                        } else /* system space */ {
2202                                ino64_t *inoptr = (ino64_t *)CAST_DOWN(caddr_t, address);
2203                                *inoptr = fileid;
2204                        }
2205                }
2206        }
2207
2208        if (state.cbs_result)
2209                result = state.cbs_result;
2210        else
2211                result = MacToVFSError(result);
2212
2213        if (result == ENOENT) {
2214                result = 0;
2215        }
2216
2217cleanup:
2218        FREE(buffer, M_TEMP);
2219        
2220        return (result);
2221}
2222
2223
2224/*
2225 * Callback to establish directory position.
2226 * Called with position_state for each item in a directory.
2227 */
2228static int
2229cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
2230                 struct position_state *state)
2231{
2232        cnid_t curID;
2233
2234        if (state->hfsmp->hfs_flags & HFS_STANDARD)
2235                curID = ckp->hfs.parentID;
2236        else
2237                curID = ckp->hfsPlus.parentID;
2238
2239        /* Make sure parent directory didn't change */
2240        if (state->parentID != curID) {
2241                state->error = EINVAL;
2242                return (0);  /* stop */
2243        }
2244
2245        /* Count this entry */
2246        switch(crp->recordType) {
2247        case kHFSPlusFolderRecord:
2248        case kHFSPlusFileRecord:
2249        case kHFSFolderRecord:
2250        case kHFSFileRecord:
2251                ++state->count;
2252                break;
2253        default:
2254                printf("cat_findposition: invalid record type %d in dir %d\n",
2255                        crp->recordType, curID);
2256                state->error = EINVAL;
2257                return (0);  /* stop */
2258        };
2259
2260        return (state->count < state->index);
2261}
2262
2263
2264/*
2265 * cat_binarykeycompare - compare two HFS Plus catalog keys.
2266
2267 * The name portion of the key is compared using a 16-bit binary comparison. 
2268 * This is called from the b-tree code.
2269 */
2270__private_extern__
2271int
2272cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2273{
2274        u_int32_t searchParentID, trialParentID;
2275        int result;
2276
2277        searchParentID = searchKey->parentID;
2278        trialParentID = trialKey->parentID;
2279        result = 0;
2280        
2281        if (searchParentID > trialParentID) {
2282                ++result;
2283        } else if (searchParentID < trialParentID) {
2284                --result;
2285        } else {
2286                u_int16_t * str1 = &searchKey->nodeName.unicode[0];
2287                u_int16_t * str2 = &trialKey->nodeName.unicode[0];
2288                int length1 = searchKey->nodeName.length;
2289                int length2 = trialKey->nodeName.length;
2290                u_int16_t c1, c2;
2291                int length;
2292        
2293                if (length1 < length2) {
2294                        length = length1;
2295                        --result;
2296                } else if (length1 > length2) {
2297                        length = length2;
2298                        ++result;
2299                } else {
2300                        length = length1;
2301                }
2302        
2303                while (length--) {
2304                        c1 = *(str1++);
2305                        c2 = *(str2++);
2306        
2307                        if (c1 > c2) {
2308                                result = 1;
2309                                break;
2310                        }
2311                        if (c1 < c2) {
2312                                result = -1;
2313                                break;
2314                        }
2315                }
2316        }
2317
2318        return result;
2319}
2320
2321
2322/*
2323 * Compare two standard HFS catalog keys
2324 *
2325 * Result: +n  search key > trial key
2326 *          0  search key = trial key
2327 *         -n  search key < trial key
2328 */
2329int
2330CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
2331{
2332        cnid_t searchParentID, trialParentID;
2333        int result;
2334
2335        searchParentID = searchKey->parentID;
2336        trialParentID = trialKey->parentID;
2337
2338        if (searchParentID > trialParentID)
2339                result = 1;
2340        else if (searchParentID < trialParentID)
2341                result = -1;
2342        else /* parent dirID's are equal, compare names */
2343                result = FastRelString(searchKey->nodeName, trialKey->nodeName);
2344
2345        return result;
2346}
2347
2348
2349/*
2350 * Compare two HFS+ catalog keys
2351 *
2352 * Result: +n  search key > trial key
2353 *          0  search key = trial key
2354 *         -n  search key < trial key
2355 */
2356int
2357CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
2358{
2359        cnid_t searchParentID, trialParentID;
2360        int result;
2361
2362        searchParentID = searchKey->parentID;
2363        trialParentID = trialKey->parentID;
2364        
2365        if (searchParentID > trialParentID) {
2366                result = 1;
2367        }
2368        else if (searchParentID < trialParentID) {
2369                result = -1;
2370        } else {
2371                /* parent node ID's are equal, compare names */
2372                if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
2373                        result = searchKey->nodeName.length - trialKey->nodeName.length;
2374                else
2375                        result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
2376                                                    searchKey->nodeName.length,
2377                                                    &trialKey->nodeName.unicode[0],
2378                                                    trialKey->nodeName.length);
2379        }
2380
2381        return result;
2382}
2383
2384
2385/*
2386 * buildkey - build a Catalog b-tree key from a cnode descriptor
2387 */
2388static int
2389buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
2390        HFSPlusCatalogKey *key, int retry)
2391{
2392        int utf8_flags = 0;
2393        int result = 0;
2394        size_t unicodeBytes = 0;
2395
2396        if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
2397                return (EINVAL);  /* invalid name */
2398
2399        key->parentID = descp->cd_parentcnid;
2400        key->nodeName.length = 0;
2401        /*
2402         * Convert filename from UTF-8 into Unicode
2403         */
2404        
2405        if ((descp->cd_flags & CD_DECOMPOSED) == 0)
2406                utf8_flags |= UTF_DECOMPOSED;
2407        result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
2408                key->nodeName.unicode, &unicodeBytes,
2409                sizeof(key->nodeName.unicode), ':', utf8_flags);
2410        key->nodeName.length = unicodeBytes / sizeof(UniChar);
2411        key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
2412        if (result) {
2413                if (result != ENAMETOOLONG)
2414                        result = EINVAL;  /* name has invalid characters */
2415                return (result);
2416        }
2417
2418        /*
2419         * For HFS volumes convert to an HFS compatible key
2420         *
2421         * XXX need to save the encoding that succeeded
2422         */
2423        if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
2424                HFSCatalogKey hfskey;
2425
2426                bzero(&hfskey, sizeof(hfskey));
2427                hfskey.keyLength = kHFSCatalogKeyMinimumLength;
2428                hfskey.parentID = key->parentID;
2429                hfskey.nodeName[0] = 0;
2430                if (key->nodeName.length > 0) {
2431                        if (unicode_to_hfs(HFSTOVCB(hfsmp),
2432                                key->nodeName.length * 2,
2433                                key->nodeName.unicode,
2434                                &hfskey.nodeName[0], retry) != 0) {
2435                                return (EINVAL);
2436                        }
2437                        hfskey.keyLength += hfskey.nodeName[0];
2438                }
2439                bcopy(&hfskey, key, sizeof(hfskey));
2440        }
2441        return (0);
2442 }
2443
2444
2445/*
2446 * Resolve hard link reference to obtain the inode record.
2447 */
2448__private_extern__
2449int
2450resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp)
2451{
2452        FSBufferDescriptor btdata;
2453        struct BTreeIterator *iterator;
2454        struct cat_desc idesc;
2455        char inodename[32];
2456        int result = 0;
2457
2458        BDINIT(btdata, recp);
2459        MAKE_INODE_NAME(inodename, linkref);
2460
2461        /* Get space for iterator */    
2462        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2463        bzero(iterator, sizeof(*iterator));
2464
2465        /* Build a descriptor for private dir. */       
2466        idesc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
2467        idesc.cd_nameptr = inodename;
2468        idesc.cd_namelen = strlen(inodename);
2469        idesc.cd_flags = 0;
2470        idesc.cd_hint = 0;
2471        idesc.cd_encoding = 0;
2472        (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
2473
2474        result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2475                                &btdata, NULL, NULL);
2476
2477        if (result == 0) {
2478                /* Make sure there's a reference */
2479                if (recp->bsdInfo.special.linkCount == 0)
2480                        recp->bsdInfo.special.linkCount = 2;
2481        } else {
2482                printf("HFS resolvelink: can't find %s\n", inodename);
2483        }
2484
2485        FREE(iterator, M_TEMP);
2486
2487        return (result ? ENOENT : 0);
2488}
2489
2490/*
2491 * Resolve hard link reference to obtain the inode number.
2492 */
2493static int
2494resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
2495{
2496        struct HFSPlusCatalogFile record;
2497        int error;
2498        
2499        error = resolvelink(hfsmp, linkref, &record);
2500        if (error == 0) {
2501                if (record.fileID == 0)
2502                        error = ENOENT;
2503                else
2504                        *ino = record.fileID;
2505        }
2506        return (error);
2507}
2508
2509/*
2510 * getkey - get a key from id by doing a thread lookup
2511 */
2512static int
2513getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
2514{
2515        struct BTreeIterator * iterator;
2516        FSBufferDescriptor btdata;
2517        UInt16  datasize;
2518        CatalogKey * keyp;
2519        CatalogRecord * recp;
2520        int result;
2521        int std_hfs;
2522
2523        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2524
2525        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2526        bzero(iterator, sizeof(*iterator));
2527        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
2528
2529        MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
2530        BDINIT(btdata, recp);
2531
2532        result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
2533                                &btdata, &datasize, iterator);
2534        if (result)
2535                goto exit;
2536
2537        /* Turn thread record into a cnode key (in place) */
2538        switch (recp->recordType) {
2539        case kHFSFileThreadRecord:
2540        case kHFSFolderThreadRecord:
2541                keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
2542                keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
2543                bcopy(keyp, key, keyp->hfs.keyLength + 1);
2544                break;
2545
2546        case kHFSPlusFileThreadRecord:
2547        case kHFSPlusFolderThreadRecord:
2548                keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
2549                keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
2550                                          (keyp->hfsPlus.nodeName.length * 2);
2551                bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
2552                break;
2553
2554        default:
2555                result = ENOENT;
2556                break;
2557        }
2558
2559exit:
2560        FREE(iterator, M_TEMP);
2561        FREE(recp, M_TEMP);
2562
2563        return MacToVFSError(result);
2564}
2565
2566/*
2567 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
2568 * null arguments to cat_idlookup instead, but we save around 10% by not building the 
2569 * cat_desc here). Both key and attrp must point to real structures.
2570 */
2571__private_extern__
2572int
2573cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
2574{
2575        int result;
2576
2577        result = getkey(hfsmp, cnid, key);
2578       
2579        if (result == 0) {
2580                result = cat_lookupbykey(hfsmp, key, 0, 0, NULL, attrp, NULL, NULL);
2581        }
2582
2583        return MacToVFSError(result);
2584}
2585
2586
2587/*
2588 * buildrecord - build a default catalog directory or file record
2589 */
2590static void
2591buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
2592            CatalogRecord *crp, int *recordSize)
2593{
2594        int type = attrp->ca_mode & S_IFMT;
2595        u_int32_t createtime = to_hfs_time(attrp->ca_itime);
2596
2597        if (std_hfs) {
2598                createtime = UTCToLocal(createtime);
2599                if (type == S_IFDIR) {
2600                        bzero(crp, sizeof(HFSCatalogFolder));
2601                        crp->recordType = kHFSFolderRecord;
2602                        crp->hfsFolder.folderID = cnid;
2603                        crp->hfsFolder.createDate = createtime;
2604                        crp->hfsFolder.modifyDate = createtime;
2605                        bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
2606                        *recordSize = sizeof(HFSCatalogFolder);
2607                } else {
2608                        bzero(crp, sizeof(HFSCatalogFile));
2609                        crp->recordType = kHFSFileRecord;
2610                        crp->hfsFile.fileID = cnid;
2611                        crp->hfsFile.createDate = createtime;
2612                        crp->hfsFile.modifyDate = createtime;
2613                        bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
2614                        bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
2615                        *recordSize = sizeof(HFSCatalogFile);
2616                }
2617        } else {
2618                struct HFSPlusBSDInfo * bsdp = NULL;
2619                struct FndrFileInfo * fip = NULL;
2620
2621                if (type == S_IFDIR) {
2622                        crp->recordType = kHFSPlusFolderRecord;
2623                        crp->hfsPlusFolder.flags = 0;
2624                        crp->hfsPlusFolder.valence = 0;
2625                        crp->hfsPlusFolder.folderID = cnid;     
2626                        crp->hfsPlusFolder.createDate = createtime;
2627                        crp->hfsPlusFolder.contentModDate = createtime;
2628                        crp->hfsPlusFolder.attributeModDate = createtime;
2629                        crp->hfsPlusFolder.accessDate = createtime;
2630                        crp->hfsPlusFolder.backupDate = 0;
2631                        crp->hfsPlusFolder.textEncoding = encoding;
2632                        crp->hfsPlusFolder.attrBlocks = 0;
2633                        bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
2634                        bsdp = &crp->hfsPlusFolder.bsdInfo;
2635                        bsdp->special.rawDevice = 0;
2636                        *recordSize = sizeof(HFSPlusCatalogFolder);
2637                } else {
2638                        crp->recordType = kHFSPlusFileRecord;
2639                        crp->hfsPlusFile.flags = kHFSThreadExistsMask;
2640                        crp->hfsPlusFile.reserved1 = 0;
2641                        crp->hfsPlusFile.fileID = cnid;
2642                        crp->hfsPlusFile.createDate = createtime;
2643                        crp->hfsPlusFile.contentModDate = createtime;
2644                        crp->hfsPlusFile.accessDate = createtime;
2645                        crp->hfsPlusFile.attributeModDate = createtime;
2646                        crp->hfsPlusFile.backupDate = 0;
2647                        crp->hfsPlusFile.textEncoding = encoding;
2648                        crp->hfsPlusFile.attrBlocks = 0;
2649                        bsdp = &crp->hfsPlusFile.bsdInfo;
2650                        bsdp->special.rawDevice = 0;
2651                        switch(type) {
2652                        case S_IFBLK:
2653                        case S_IFCHR:
2654                                /* BLK/CHR need to save the device info */
2655                                bsdp->special.rawDevice = attrp->ca_rdev;
2656                                bzero(&crp->hfsPlusFile.userInfo, 32);
2657                                break;
2658                        case S_IFREG:
2659                                /* Hardlink links need to save the linkref */
2660                                fip = (FndrFileInfo *)&attrp->ca_finderinfo;
2661                                if ((SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
2662                                    (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
2663                                        bsdp->special.iNodeNum = attrp->ca_rdev;
2664                                }
2665                                bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2666                                break;
2667                        case S_IFLNK:
2668                                /* Symlinks also have a type and creator */
2669                                bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
2670                                break;
2671                        }
2672                        bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
2673                        *recordSize = sizeof(HFSPlusCatalogFile);
2674                }
2675                bsdp->ownerID    = attrp->ca_uid;
2676                bsdp->groupID    = attrp->ca_gid;
2677                bsdp->fileMode   = attrp->ca_mode;
2678                bsdp->adminFlags = attrp->ca_flags >> 16;
2679                bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
2680        }
2681}
2682
2683
2684/*
2685 * builddesc - build a cnode descriptor from an HFS+ key
2686 */
2687static int
2688builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
2689        int isdir, struct cat_desc *descp)
2690{
2691        int result = 0;
2692        char * nameptr;
2693        size_t bufsize;
2694        size_t utf8len;
2695        char tmpbuff[128];
2696
2697        /* guess a size... */
2698        bufsize = (3 * key->nodeName.length) + 1;
2699        if (bufsize >= sizeof(tmpbuff) - 1) {
2700            MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2701        } else {
2702            nameptr = &tmpbuff[0];
2703        }
2704
2705        result = utf8_encodestr(key->nodeName.unicode,
2706                        key->nodeName.length * sizeof(UniChar),
2707                        nameptr, (size_t *)&utf8len,
2708                        bufsize, ':', 0);
2709
2710        if (result == ENAMETOOLONG) {
2711                bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
2712                                             key->nodeName.length * sizeof(UniChar),
2713                                             ':', 0);
2714                FREE(nameptr, M_TEMP);
2715                MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
2716
2717                result = utf8_encodestr(key->nodeName.unicode,
2718                                        key->nodeName.length * sizeof(UniChar),
2719                                        nameptr, (size_t *)&utf8len,
2720                                        bufsize, ':', 0);
2721        }
2722        descp->cd_parentcnid = key->parentID;
2723        descp->cd_nameptr = vfs_addname(nameptr, utf8len, 0, 0);
2724        descp->cd_namelen = utf8len;
2725        descp->cd_cnid = cnid;
2726        descp->cd_hint = hint;
2727        descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
2728        if (isdir)
2729                descp->cd_flags |= CD_ISDIR;    
2730        descp->cd_encoding = encoding;
2731        if (nameptr != &tmpbuff[0]) {
2732            FREE(nameptr, M_TEMP);
2733        }
2734        return result;
2735}
2736
2737
2738/*
2739 * getbsdattr - get attributes in bsd format
2740 *
2741 */
2742static void
2743getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
2744{
2745        int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
2746        const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
2747
2748        attrp->ca_recflags = crp->flags;
2749        attrp->ca_nlink = 1;
2750        attrp->ca_atime = to_bsd_time(crp->accessDate);
2751        attrp->ca_atimeondisk = attrp->ca_atime;        
2752        attrp->ca_mtime = to_bsd_time(crp->contentModDate);
2753        attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
2754        attrp->ca_itime = to_bsd_time(crp->createDate);
2755        attrp->ca_btime = to_bsd_time(crp->backupDate);
2756
2757        if ((bsd->fileMode & S_IFMT) == 0) {
2758                attrp->ca_flags = 0;
2759                attrp->ca_uid = hfsmp->hfs_uid;
2760                attrp->ca_gid = hfsmp->hfs_gid;
2761                if (isDirectory)
2762                        attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
2763                else
2764                        attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
2765                attrp->ca_rdev = 0;
2766        } else {
2767                attrp->ca_rdev = 0;
2768                attrp->ca_uid = bsd->ownerID;
2769                attrp->ca_gid = bsd->groupID;
2770                attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
2771                attrp->ca_mode = (mode_t)bsd->fileMode;
2772                switch (attrp->ca_mode & S_IFMT) {
2773                case S_IFCHR: /* fall through */
2774                case S_IFBLK:
2775                        attrp->ca_rdev = bsd->special.rawDevice;
2776                        break;
2777                case S_IFREG:
2778                        /* Pick up the hard link count */
2779                        if (bsd->special.linkCount > 0)
2780                                attrp->ca_nlink = bsd->special.linkCount;
2781                        break;
2782                }
2783
2784                if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
2785                        /*
2786                         *  Override the permissions as determined by the mount auguments
2787                         *  in ALMOST the same way unset permissions are treated but keep
2788                         *  track of whether or not the file or folder is hfs locked
2789                         *  by leaving the h_pflags field unchanged from what was unpacked
2790                         *  out of the catalog.
2791                         */
2792                        attrp->ca_uid = hfsmp->hfs_uid;
2793                        attrp->ca_gid = hfsmp->hfs_gid;
2794                }
2795        }
2796
2797        if (isDirectory) {
2798                if (!S_ISDIR(attrp->ca_mode)) {
2799                        attrp->ca_mode &= ~S_IFMT;
2800                        attrp->ca_mode |= S_IFDIR;
2801                }
2802                attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
2803                attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
2804                attrp->ca_attrblks = ((HFSPlusCatalogFolder *)crp)->attrBlocks;
2805        } else {
2806                /* Keep IMMUTABLE bits in sync with HFS locked flag */
2807                if (crp->flags & kHFSFileLockedMask) {
2808                        /* The file's supposed to be locked:
2809                           Make sure at least one of the IMMUTABLE bits is set: */
2810                        if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
2811                                attrp->ca_flags |= UF_IMMUTABLE;
2812                } else {
2813                        /* The file's supposed to be unlocked: */
2814                        attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
2815                }
2816                /* get total blocks (both forks) */
2817                attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
2818                attrp->ca_attrblks = crp->attrBlocks;
2819                /* On HFS+ the ThreadExists flag must always be set. */
2820                if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
2821                        attrp->ca_recflags |= kHFSThreadExistsMask;
2822        }
2823        
2824        attrp->ca_fileid = crp->fileID;
2825
2826        bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
2827}
2828
2829/*
2830 * promotekey - promote hfs key to hfs plus key
2831 *
2832 */
2833static void
2834promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
2835           HFSPlusCatalogKey *keyp, u_long *encoding)
2836{
2837        hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
2838        UInt32 uniCount;
2839        int error;
2840
2841        *encoding = hfsmp->hfs_encoding;
2842
2843        error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
2844                                kHFSPlusMaxFileNameChars, &uniCount);
2845        /*
2846         * When an HFS name cannot be encoded with the current
2847         * encoding use MacRoman as a fallback.
2848         */
2849        if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
2850                *encoding = 0;
2851                (void) mac_roman_to_unicode(hfskey->nodeName,
2852                                            keyp->nodeName.unicode,
2853                                            kHFSPlusMaxFileNameChars,
2854                                            &uniCount);
2855        }
2856
2857        keyp->nodeName.length = uniCount;
2858        keyp->parentID = hfskey->parentID;
2859}
2860
2861/*
2862 * promotefork - promote hfs fork info to hfs plus
2863 *
2864 */
2865static void
2866promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
2867            int resource, struct cat_fork * forkp)
2868{
2869        struct HFSPlusExtentDescriptor *xp;
2870        u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2871
2872        bzero(forkp, sizeof(*forkp));
2873        xp = &forkp->cf_extents[0];
2874        if (resource) {
2875                forkp->cf_size = filep->rsrcLogicalSize;
2876                forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
2877                forkp->cf_bytesread = 0;
2878                forkp->cf_vblocks = 0;
2879                xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
2880                xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
2881                xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
2882                xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
2883                xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
2884                xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
2885        } else {
2886                forkp->cf_size = filep->dataLogicalSize;
2887                forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
2888                forkp->cf_bytesread = 0;
2889                forkp->cf_vblocks = 0;
2890                xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
2891                xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
2892                xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
2893                xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
2894                xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
2895                xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
2896        }
2897}
2898
2899/*
2900 * promoteattr - promote hfs catalog attributes to hfs plus
2901 *
2902 */
2903static void
2904promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
2905{
2906        u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
2907
2908        if (dataPtr->recordType == kHFSFolderRecord) {
2909                struct HFSCatalogFolder * folder;
2910
2911                folder = (struct HFSCatalogFolder *) dataPtr;
2912                crp->recordType       = kHFSPlusFolderRecord;
2913                crp->flags            = folder->flags;
2914                crp->fileID           = folder->folderID;
2915                crp->createDate       = LocalToUTC(folder->createDate);
2916                crp->contentModDate   = LocalToUTC(folder->modifyDate);
2917                crp->backupDate       = LocalToUTC(folder->backupDate);
2918                crp->reserved1        = folder->valence;
2919                bcopy(&folder->userInfo, &crp->userInfo, 32);
2920        } else /* file */ {
2921                struct HFSCatalogFile * file;
2922
2923                file = (struct HFSCatalogFile *) dataPtr;
2924                crp->recordType       = kHFSPlusFileRecord;
2925                crp->flags            = file->flags;
2926                crp->fileID           = file->fileID;
2927                crp->createDate       = LocalToUTC(file->createDate);
2928                crp->contentModDate   = LocalToUTC(file->modifyDate);
2929                crp->backupDate       = LocalToUTC(file->backupDate);
2930                crp->reserved1        = 0;
2931                bcopy(&file->userInfo, &crp->userInfo, 16);
2932                bcopy(&file->finderInfo, &crp->finderInfo, 16);
2933                crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
2934                crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
2935        }
2936        crp->textEncoding = 0;
2937        crp->attributeModDate = crp->contentModDate;
2938        crp->accessDate = crp->contentModDate;
2939        bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
2940        crp->attrBlocks = 0;
2941}
2942
2943/*
2944 * Build a catalog node thread record from a catalog key
2945 * and return the size of the record.
2946 */
2947static int
2948buildthread(void *keyp, void *recp, int std_hfs, int directory)
2949{
2950        int size = 0;
2951
2952        if (std_hfs) {
2953                HFSCatalogKey *key = (HFSCatalogKey *)keyp;
2954                HFSCatalogThread *rec = (HFSCatalogThread *)recp;
2955
2956                size = sizeof(HFSCatalogThread);
2957                bzero(rec, size);
2958                if (directory)
2959                        rec->recordType = kHFSFolderThreadRecord;
2960                else
2961                        rec->recordType = kHFSFileThreadRecord;
2962                rec->parentID = key->parentID;
2963                bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
2964
2965        } else /* HFS+ */ {
2966                HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
2967                HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
2968
2969                size = sizeof(HFSPlusCatalogThread);
2970                if (directory)
2971                        rec->recordType = kHFSPlusFolderThreadRecord;
2972                else
2973                        rec->recordType = kHFSPlusFileThreadRecord;
2974                rec->reserved = 0;
2975                rec->parentID = key->parentID;                  
2976                bcopy(&key->nodeName, &rec->nodeName,
2977                        sizeof(UniChar) * (key->nodeName.length + 1));
2978
2979                /* HFS Plus has varaible sized thread records */
2980                size -= (sizeof(rec->nodeName.unicode) -
2981                          (rec->nodeName.length * sizeof(UniChar)));
2982        }
2983        
2984        return (size);
2985}
2986
2987/*
2988 * Build a catalog node thread key.
2989 */
2990static void
2991buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
2992{
2993        if (std_hfs) {
2994                key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
2995                key->hfs.reserved = 0;
2996                key->hfs.parentID = parentID;
2997                key->hfs.nodeName[0] = 0;
2998        } else {
2999                key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
3000                key->hfsPlus.parentID = parentID;
3001                key->hfsPlus.nodeName.length = 0;
3002        }
3003}
3004
3005/*
3006 * Extract the text encoding from a catalog node record.
3007 */
3008static u_long 
3009getencoding(const CatalogRecord *crp)
3010{
3011        u_long encoding;
3012
3013        if (crp->recordType == kHFSPlusFolderRecord)
3014                encoding = crp->hfsPlusFolder.textEncoding;
3015        else if (crp->recordType == kHFSPlusFileRecord)
3016                encoding = crp->hfsPlusFile.textEncoding;
3017        else
3018                encoding = 0;
3019
3020        return (encoding);
3021}
3022
3023/*
3024 * Extract the CNID from a catalog node record.
3025 */
3026static cnid_t 
3027getcnid(const CatalogRecord *crp)
3028{
3029        cnid_t cnid = 0;
3030
3031        switch (crp->recordType) {
3032        case kHFSFolderRecord:
3033                cnid = crp->hfsFolder.folderID;
3034                break;
3035        case kHFSFileRecord:
3036                cnid = crp->hfsFile.fileID;
3037                break;
3038        case kHFSPlusFolderRecord:
3039                cnid = crp->hfsPlusFolder.folderID;
3040                break;
3041        case kHFSPlusFileRecord:
3042                cnid = crp->hfsPlusFile.fileID;
3043                break;
3044        default:
3045                printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp);
3046                break;
3047        }
3048
3049        return (cnid);
3050}
3051
3052/*
3053 * Extract the parent ID from a catalog node record.
3054 */
3055static cnid_t 
3056getparentcnid(const CatalogRecord *recp)
3057{
3058        cnid_t cnid = 0;
3059
3060        switch (recp->recordType) {
3061        case kHFSFileThreadRecord:
3062        case kHFSFolderThreadRecord:
3063                cnid = recp->hfsThread.parentID;
3064                break;
3065
3066        case kHFSPlusFileThreadRecord:
3067        case kHFSPlusFolderThreadRecord:
3068                cnid = recp->hfsPlusThread.parentID;
3069                break;
3070        default:
3071                panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp);
3072                break;
3073        }
3074
3075        return (cnid);
3076}
3077
3078/*
3079 * Determine if a catalog node record is a directory.
3080 */
3081static int 
3082isadir(const CatalogRecord *crp)
3083{
3084        return (crp->recordType == kHFSFolderRecord ||
3085                crp->recordType == kHFSPlusFolderRecord);
3086}
3087
3088
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.