]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_catalog.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
index 7d6999e6528b25563aa0e60a9452802e8c93e882..75c7701ee0ce7606e7e050f0d5ef5c789e7251bd 100644 (file)
@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
@@ -26,7 +26,6 @@
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/vnode.h>
 #include <sys/stat.h>
 #include <sys/mount.h>
 #include <sys/vnode.h>
-#include <sys/namei.h>
 #include <sys/dirent.h>
 #include <vfs/vfs_support.h>
 #include <libkern/libkern.h>
 #include <sys/dirent.h>
 #include <vfs/vfs_support.h>
 #include <libkern/libkern.h>
 #include "hfs_endian.h"
 
 #include "hfscommon/headers/BTreesInternal.h"
 #include "hfs_endian.h"
 
 #include "hfscommon/headers/BTreesInternal.h"
-#include "hfscommon/headers/CatalogPrivate.h"
 #include "hfscommon/headers/HFSUnicodeWrappers.h"
 
 #include "hfscommon/headers/HFSUnicodeWrappers.h"
 
-extern OSErr PositionIterator(CatalogIterator *cip, UInt32 offset, BTreeIterator *bip, UInt16 *op);
 
 /*
  * Initialization of an FSBufferDescriptor structure.
 
 /*
  * Initialization of an FSBufferDescriptor structure.
@@ -68,9 +65,26 @@ struct update_state {
        struct hfsmount *       s_hfsmp;
 };
 
        struct hfsmount *       s_hfsmp;
 };
 
+struct position_state {
+       int        error;
+       u_int32_t  count;
+       u_int32_t  index;
+       u_int32_t  parentID;
+       struct hfsmount *hfsmp;
+};
+
+/* Map file mode type to directory entry types */
+u_char modetodirtype[16] = {
+       DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
+       DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
+       DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
+       DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
+};
+#define MODE_TO_DT(mode)  (modetodirtype[((mode) & S_IFMT) >> 12])
+
 
 static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
 
 static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
-                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp);
+                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
 
 static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                   struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
 
 static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                   struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
@@ -84,7 +98,10 @@ extern int unicode_to_hfs(ExtendedVCB *vcb, ByteCount srcLen,
 
 /* Internal catalog support routines */
 
 
 /* Internal catalog support routines */
 
-int resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *recp);
+static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
+                            struct position_state *state);
+
+static int resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino);
 
 static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
 
 
 static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
 
@@ -95,7 +112,7 @@ static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *k
 
 static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
 
 
 static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, int *recordSize);
 
-static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen, struct update_state *state);
+static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
 
 static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
                        int isdir, struct cat_desc *descp);
 
 static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
                        int isdir, struct cat_desc *descp);
@@ -115,8 +132,43 @@ static int isadir(const CatalogRecord *crp);
 static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
 
 
 static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
 
 
+__private_extern__
+int
+cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, struct proc *p)
+{
+       FCB *fcb;
+       int lockflags;
+       int result;
+
+       fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
+
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+        
+       result = BTReserveSpace(fcb, ops, (void*)cookie);
+       
+       hfs_systemfile_unlock(hfsmp, lockflags);
+
+       return MacToVFSError(result);
+}
+
+__private_extern__
+void
+cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, struct proc *p)
+{
+       FCB *fcb;
+       int lockflags;
+
+       fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
+
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+
+       (void) BTReleaseReserve(fcb, (void*)cookie);
+
+       hfs_systemfile_unlock(hfsmp, lockflags);
+}
+
  
  
+__private_extern__
 void
 cat_convertattr(
        struct hfsmount *hfsmp,
 void
 cat_convertattr(
        struct hfsmount *hfsmp,
@@ -142,11 +194,39 @@ cat_convertattr(
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
        } else {
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
        } else {
-               bcopy(&recp->hfsPlusFile.dataFork, datafp, sizeof(*datafp));
-               bcopy(&recp->hfsPlusFile.resourceFork, rsrcfp, sizeof(*rsrcfp));
+               /* Convert the data fork. */
+               datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+               datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
+               if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+                   (attrp->ca_atime >= hfsmp->hfc_timebase)) {
+                       datafp->cf_bytesread =
+                               recp->hfsPlusFile.dataFork.clumpSize *
+                               HFSTOVCB(hfsmp)->blockSize;
+               } else {
+                       datafp->cf_bytesread = 0;
+               }
+               datafp->cf_vblocks = 0;
+               bcopy(&recp->hfsPlusFile.dataFork.extents[0],
+                     &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+
+               /* Convert the resource fork. */
+               rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+               rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
+               if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+                   (attrp->ca_atime >= hfsmp->hfc_timebase)) {
+                       datafp->cf_bytesread =
+                               recp->hfsPlusFile.resourceFork.clumpSize *
+                               HFSTOVCB(hfsmp)->blockSize;
+               } else {
+                       datafp->cf_bytesread = 0;
+               }
+               rsrcfp->cf_vblocks = 0;
+               bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
+                     &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
        }
 }
 
        }
 }
 
+__private_extern__
 int
 cat_convertkey(
        struct hfsmount *hfsmp,
 int
 cat_convertkey(
        struct hfsmount *hfsmp,
@@ -178,6 +258,7 @@ cat_convertkey(
 /*
  * cat_releasedesc
  */
 /*
  * cat_releasedesc
  */
+__private_extern__
 void
 cat_releasedesc(struct cat_desc *descp)
 {
 void
 cat_releasedesc(struct cat_desc *descp)
 {
@@ -192,7 +273,7 @@ cat_releasedesc(struct cat_desc *descp)
                descp->cd_nameptr = NULL;
                descp->cd_namelen = 0;
                descp->cd_flags &= ~CD_HASBUF;
                descp->cd_nameptr = NULL;
                descp->cd_namelen = 0;
                descp->cd_flags &= ~CD_HASBUF;
-               FREE(name, M_TEMP);
+               vfs_removename(name);
        }
        descp->cd_nameptr = NULL;
        descp->cd_namelen = 0;
        }
        descp->cd_nameptr = NULL;
        descp->cd_namelen = 0;
@@ -206,10 +287,11 @@ cat_releasedesc(struct cat_desc *descp)
 /*
  * cat_lookup - lookup a catalog node using a cnode decriptor
  */
 /*
  * cat_lookup - lookup a catalog node using a cnode decriptor
  */
+__private_extern__
 int
 cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
              struct cat_desc *outdescp, struct cat_attr *attrp,
 int
 cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
              struct cat_desc *outdescp, struct cat_attr *attrp,
-             struct cat_fork *forkp)
+             struct cat_fork *forkp, cnid_t *desc_cnid)
 {
        CatalogKey * keyp;
        int std_hfs;
 {
        CatalogKey * keyp;
        int std_hfs;
@@ -223,11 +305,23 @@ cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
        if (result)
                goto exit;
 
        if (result)
                goto exit;
 
-       result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, wantrsrc, outdescp, attrp, forkp);
+       result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
        
        if (result == ENOENT) {
                if (!std_hfs) {
        
        if (result == ENOENT) {
                if (!std_hfs) {
+                       struct cat_desc temp_desc;
+                       if (outdescp == NULL) {
+                               bzero(&temp_desc, sizeof(temp_desc));
+                               outdescp = &temp_desc;
+                       }
                        result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
                        result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
+                       if (desc_cnid) {
+                           *desc_cnid = outdescp->cd_cnid;
+                       }
+                       if (outdescp == &temp_desc) {
+                               /* Release the local copy of desc */
+                               cat_releasedesc(outdescp);
+                       }
                } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
                //      make MacRoman key from utf-8
                //      result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
                } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
                //      make MacRoman key from utf-8
                //      result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
@@ -240,6 +334,7 @@ exit:
        return (result);
 }
 
        return (result);
 }
 
+__private_extern__
 int
 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
 int
 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
@@ -288,15 +383,89 @@ cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
                (void) BTFlushPath(fcb);
        }       
 exit:
                (void) BTFlushPath(fcb);
        }       
 exit:
+       (void) BTFlushPath(fcb);
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
 }
 
 
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
 }
 
 
+/*
+ * cat_findname - obtain a descriptor from cnid
+ *
+ * Only a thread lookup is performed.
+ */
+__private_extern__
+int
+cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
+{
+       struct BTreeIterator * iterator;
+       FSBufferDescriptor btdata;
+       CatalogKey * keyp;
+       CatalogRecord * recp;
+       int isdir;
+       int result;
+       int std_hfs;
+
+       isdir = 0;
+       std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
+
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
+       iterator->hint.nodeNum = 0;
+
+       MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
+       BDINIT(btdata, recp);
+
+       result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
+       if (result)
+               goto exit;
+
+       /* Turn thread record into a cnode key (in place). */
+       switch (recp->recordType) {
+       case kHFSFolderThreadRecord:
+               isdir = 1;
+               /* fall through */
+       case kHFSFileThreadRecord:
+               keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
+               keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
+               break;
+
+       case kHFSPlusFolderThreadRecord:
+               isdir = 1;
+               /* fall through */
+       case kHFSPlusFileThreadRecord:
+               keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+               keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
+                                         (keyp->hfsPlus.nodeName.length * 2);
+               break;
+       default:
+               result = ENOENT;
+               goto exit;
+       }
+       if (std_hfs) {
+               HFSPlusCatalogKey * pluskey = NULL;
+               u_long encoding;
+
+               MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
+               promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
+               builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
+               FREE(pluskey, M_TEMP);
+
+       } else {
+               builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
+       }
+exit:
+       FREE(recp, M_TEMP);
+       FREE(iterator, M_TEMP);
+
+       return MacToVFSError(result);
+}
+
 /*
  * cat_idlookup - lookup a catalog node using a cnode id
  */
 /*
  * cat_idlookup - lookup a catalog node using a cnode id
  */
+__private_extern__
 int
 cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
                  struct cat_attr *attrp, struct cat_fork *forkp)
 int
 cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
                  struct cat_attr *attrp, struct cat_fork *forkp)
@@ -343,7 +512,19 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp,
                goto exit;
        }
 
                goto exit;
        }
 
-       result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp);
+       result = cat_lookupbykey(hfsmp, keyp, 0, 0, outdescp, attrp, forkp, NULL);
+       if (!result && outdescp) {
+               cnid_t dcnid = outdescp->cd_cnid;
+               /*
+                * Just for sanity's sake, let's make sure that
+                * the key in the thread matches the key in the record.
+                */
+               if (cnid != dcnid) {
+                       printf("Requested cnid (%d / 0x%08lx) != dcnid (%d / 0x%08lx)\n", cnid, cnid, dcnid, dcnid);                    
+                       result = ENOENT;
+               }
+       }
+
 exit:
        FREE(recp, M_TEMP);
        FREE(iterator, M_TEMP);
 exit:
        FREE(recp, M_TEMP);
        FREE(iterator, M_TEMP);
@@ -367,9 +548,16 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                return (ENOENT);
 
        fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
                return (ENOENT);
 
        fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
-       if (fileID < kHFSFirstUserCatalogNodeID)
+
+       if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID)
                return (ENOENT);
 
                return (ENOENT);
 
+       if(fileID == hfsmp->hfs_privdir_desc.cd_cnid ||
+               fileID == hfsmp->hfs_jnlfileid ||
+               fileID == hfsmp->hfs_jnlinfoblkid) {
+               return (ENOENT);
+       }
+
        result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
        if (result)
                return (ENOENT);
        result = cat_idlookup(hfsmp, fileID, outdescp, attrp, forkp);
        if (result)
                return (ENOENT);
@@ -395,7 +583,7 @@ falsematch:
  */
 static int
 cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
  */
 static int
 cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantrsrc,
-                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
+                  struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
 {
        struct BTreeIterator * iterator;
        FSBufferDescriptor btdata;
 {
        struct BTreeIterator * iterator;
        FSBufferDescriptor btdata;
@@ -426,6 +614,15 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantr
        encoding = getencoding(recp);
        hint = iterator->hint.nodeNum;
 
        encoding = getencoding(recp);
        hint = iterator->hint.nodeNum;
 
+       /* Hide the journal files (if any) */
+       if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
+               ((cnid == hfsmp->hfs_jnlfileid) ||
+                (cnid == hfsmp->hfs_jnlinfoblkid))) {
+
+               result = ENOENT;
+               goto exit;
+       }
+
        /*
         * When a hardlink link is encountered, auto resolve it
         */
        /*
         * When a hardlink link is encountered, auto resolve it
         */
@@ -434,8 +631,8 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantr
            && (recp->recordType == kHFSPlusFileRecord)
            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
            && (recp->recordType == kHFSPlusFileRecord)
            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType)
            && (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)
-           && ((to_bsd_time(recp->hfsPlusFile.createDate) == HFSTOVCB(hfsmp)->vcbCrDate) ||
-               (to_bsd_time(recp->hfsPlusFile.createDate) == hfsmp->hfs_metadata_createdate))) {
+           && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
+               (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
 
                ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
 
 
                ilink = recp->hfsPlusFile.bsdInfo.special.iNodeNum;
 
@@ -455,14 +652,74 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantr
                }
        }
        if (forkp != NULL) {
                }
        }
        if (forkp != NULL) {
-               if (isadir(recp))
+               if (isadir(recp)) {
                        bzero(forkp, sizeof(*forkp));
                        bzero(forkp, sizeof(*forkp));
-               else if (std_hfs)
+               } else if (std_hfs) {
                        promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
                        promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
-               else if (wantrsrc)
-                       bcopy(&recp->hfsPlusFile.resourceFork, forkp, sizeof(*forkp));
-               else
-                       bcopy(&recp->hfsPlusFile.dataFork, forkp, sizeof(*forkp));
+               } else if (wantrsrc) {
+                       /* Convert the resource fork. */
+                       forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+                       forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
+                       if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+                           (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
+                               forkp->cf_bytesread =
+                                       recp->hfsPlusFile.resourceFork.clumpSize *
+                                       HFSTOVCB(hfsmp)->blockSize;
+                       } else {
+                               forkp->cf_bytesread = 0;
+                       }
+                       forkp->cf_vblocks = 0;
+                       bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
+                             &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+               } else {
+                       int i;
+                       u_int32_t validblks;
+
+                       /* Convert the data fork. */
+                       forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+                       forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
+                       if ((hfsmp->hfc_stage == HFC_RECORDING) &&
+                           (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
+                               forkp->cf_bytesread =
+                                       recp->hfsPlusFile.dataFork.clumpSize *
+                                       HFSTOVCB(hfsmp)->blockSize;
+                       } else {
+                               forkp->cf_bytesread = 0;
+                       }
+                       forkp->cf_vblocks = 0;
+                       bcopy(&recp->hfsPlusFile.dataFork.extents[0],
+                             &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
+
+                       /* Validate the fork's resident extents. */
+                       validblks = 0;
+                       for (i = 0; i < kHFSPlusExtentDensity; ++i) {
+                               if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
+                                       /* Suppress any bad extents so a remove can succeed. */
+                                       forkp->cf_extents[i].startBlock = 0;
+                                       forkp->cf_extents[i].blockCount = 0;
+                                       /* Disable writes */
+                                       if (attrp != NULL) {
+                                               attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
+                                       }
+                               } else {
+                                       validblks += forkp->cf_extents[i].blockCount;
+                               }
+                       }
+                       /* Adjust for any missing blocks. */
+                       if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
+                               u_int64_t psize;
+
+                               forkp->cf_blocks = validblks;
+                               if (attrp != NULL) {
+                                       attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
+                               }
+                               psize = (u_int64_t)validblks * (u_int64_t)hfsmp->blockSize;
+                               if (psize < forkp->cf_size) {
+                                       forkp->cf_size = psize;
+                               }
+
+                       }
+               }
        }
        if (descp != NULL) {
                HFSPlusCatalogKey * pluskey = NULL;
        }
        if (descp != NULL) {
                HFSPlusCatalogKey * pluskey = NULL;
@@ -479,6 +736,10 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, u_long hint, int wantr
                        FREE(pluskey, M_TEMP);
                }
        }
                        FREE(pluskey, M_TEMP);
                }
        }
+
+       if (desc_cnid != NULL) {
+           *desc_cnid = cnid;
+       }
 exit:
        FREE(iterator, M_TEMP);
        FREE(recp, M_TEMP);
 exit:
        FREE(iterator, M_TEMP);
        FREE(recp, M_TEMP);
@@ -490,6 +751,7 @@ exit:
 /*
  * cat_create - create a node in the catalog
  */
 /*
  * cat_create - create a node in the catalog
  */
+__private_extern__
 int
 cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_desc *out_descp)
 int
 cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_desc *out_descp)
@@ -501,23 +763,46 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        u_int32_t nextCNID;
        u_int32_t datalen;
        int std_hfs;
        u_int32_t nextCNID;
        u_int32_t datalen;
        int std_hfs;
-       int result;
+       int result = 0;
        u_long encoding;
        int modeformat;
        u_long encoding;
        int modeformat;
+       int mntlock = 0;
 
        modeformat = attrp->ca_mode & S_IFMT;
 
        vcb = HFSTOVCB(hfsmp);
        fcb = GetFileControlBlock(vcb->catalogRefNum);
 
        modeformat = attrp->ca_mode & S_IFMT;
 
        vcb = HFSTOVCB(hfsmp);
        fcb = GetFileControlBlock(vcb->catalogRefNum);
-       nextCNID = vcb->vcbNxtCNID;
        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
 
        std_hfs = (vcb->vcbSigWord == kHFSSigWord);
 
-       if (std_hfs && nextCNID == 0xFFFFFFFF)
-               return (ENOSPC);
+       /*
+        * Atomically get the next CNID.  If we have wrapped the CNIDs
+        * then keep the hfsmp lock held until we have found a CNID.
+        */
+       HFS_MOUNT_LOCK(hfsmp, TRUE);
+       mntlock = 1;
+       nextCNID = hfsmp->vcbNxtCNID;
+       if (nextCNID == 0xFFFFFFFF) {
+               if (std_hfs) {
+                       result = ENOSPC;
+               } else {
+                       hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+                       hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
+               }
+       } else {
+               hfsmp->vcbNxtCNID++;
+       }
+       hfsmp->vcbFlags |= 0xFF00;
+       /* OK to drop lock if CNIDs are not wrapping */
+       if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask) == 0) {
+               HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+               mntlock = 0;
+               if (result)
+                       return (result);  /* HFS only exit */
+       }
 
        /* Get space for iterator, key and data */      
        MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
 
        /* Get space for iterator, key and data */      
        MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
-       bzero(bto, sizeof(struct btobj));
+       bto->iterator.hint.nodeNum = 0;
 
        result = buildkey(hfsmp, descp, &bto->key, 0);
        if (result)
 
        result = buildkey(hfsmp, descp, &bto->key, 0);
        if (result)
@@ -543,14 +828,11 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
                        buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
 
                        result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
                        buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
 
                        result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
-                       if (result == btExists && !std_hfs) {
+                       if ((result == btExists) && !std_hfs && mntlock) {
                                /*
                                 * Allow CNIDs on HFS Plus volumes to wrap around
                                 */
                                /*
                                 * Allow CNIDs on HFS Plus volumes to wrap around
                                 */
-                               ++nextCNID;
-                               if (nextCNID < kHFSFirstUserCatalogNodeID) {
-                                       vcb->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
-                                       vcb->vcbFlags |= 0xFF00;
+                               if (++nextCNID < kHFSFirstUserCatalogNodeID) {
                                        nextCNID = kHFSFirstUserCatalogNodeID;
                                }
                                continue;
                                        nextCNID = kHFSFirstUserCatalogNodeID;
                                }
                                continue;
@@ -559,6 +841,19 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
                }
                if (result) goto exit;
        }
                }
                if (result) goto exit;
        }
+       
+       /*
+        * CNID is now established. If we have wrapped then
+        * update the vcbNxtCNID and drop the vcb lock.
+        */
+       if (mntlock) {
+               hfsmp->vcbNxtCNID = nextCNID + 1;
+               if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
+                       hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+               }
+               HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+               mntlock = 0;
+       }
 
        /*
         * Now insert the file/directory record
 
        /*
         * Now insert the file/directory record
@@ -606,20 +901,11 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        }
        attrp->ca_fileid = nextCNID;
 
        }
        attrp->ca_fileid = nextCNID;
 
-       /* Update parent stats */
-       TrashCatalogIterator(vcb, descp->cd_parentcnid);
-       
-       /* Update volume stats */
-       if (++nextCNID < kHFSFirstUserCatalogNodeID) {
-               vcb->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
-               nextCNID = kHFSFirstUserCatalogNodeID;
-       }
-       vcb->vcbNxtCNID = nextCNID;
-       vcb->vcbFlags |= 0xFF00;
+exit:
+       if (mntlock)
+               HFS_MOUNT_UNLOCK(hfsmp, TRUE);
 
        (void) BTFlushPath(fcb);
 
        (void) BTFlushPath(fcb);
-
-exit:
        FREE(bto, M_TEMP);
 
        return MacToVFSError(result);
        FREE(bto, M_TEMP);
 
        return MacToVFSError(result);
@@ -638,6 +924,7 @@ exit:
  *     4. BTDeleteRecord(from_thread);
  *     5. BTInsertRecord(to_thread);
  */
  *     4. BTDeleteRecord(from_thread);
  *     5. BTInsertRecord(to_thread);
  */
+__private_extern__
 int 
 cat_rename (
        struct hfsmount * hfsmp,
 int 
 cat_rename (
        struct hfsmount * hfsmp,
@@ -686,7 +973,7 @@ cat_rename (
         * When moving a directory, make sure its a valid move.
         */
        if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
         * When moving a directory, make sure its a valid move.
         */
        if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
-               struct BTreeIterator iterator = {0};
+               struct BTreeIterator iterator;
                cnid_t cnid = from_cdp->cd_cnid;
                cnid_t pathcnid = todir_cdp->cd_parentcnid;
 
                cnid_t cnid = from_cdp->cd_cnid;
                cnid_t pathcnid = todir_cdp->cd_parentcnid;
 
@@ -697,7 +984,7 @@ cat_rename (
                        result = EINVAL;
                        goto exit;
                }
                        result = EINVAL;
                        goto exit;
                }
-
+               bzero(&iterator, sizeof(iterator));
                /*
                 * Traverese destination path all the way back to the root
                 * making sure that source directory is not encountered.
                /*
                 * Traverese destination path all the way back to the root
                 * making sure that source directory is not encountered.
@@ -723,10 +1010,35 @@ cat_rename (
         */
        result = BTSearchRecord(fcb, from_iterator, &btdata,
                                &datasize, from_iterator);
         */
        result = BTSearchRecord(fcb, from_iterator, &btdata,
                                &datasize, from_iterator);
-       if (result)
-               goto exit;
+       if (result) {
+               if (std_hfs || (result != btNotFound)) 
+                       goto exit;
+       
+               struct cat_desc temp_desc;
+
+               /* Probably the node has mangled name */
+               result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL); 
+               if (result) 
+                       goto exit;
+                       
+               /* The file has mangled name.  Search the cnode data using full name */
+               bzero(from_iterator, sizeof(*from_iterator));
+               result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
+               if (result) {
+                       cat_releasedesc(&temp_desc);
+                       goto exit;
+               }
+
+               result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
+               if (result) {
+                       cat_releasedesc(&temp_desc);
+                       goto exit;
+               }
+               
+               cat_releasedesc(&temp_desc);
+       }
 
 
-       /* Update the text encoding (on disk and in descriptor */
+       /* Update the text encoding (on disk and in descriptor) */
        if (!std_hfs) {
                encoding = hfs_pickencoding(to_key->nodeName.unicode,
                                to_key->nodeName.length);
        if (!std_hfs) {
                encoding = hfs_pickencoding(to_key->nodeName.unicode,
                                to_key->nodeName.length);
@@ -752,11 +1064,6 @@ cat_rename (
                goto exit;      
 #endif
 
                goto exit;      
 #endif
 
-       /* Trash the iterator caches */
-       TrashCatalogIterator(vcb, from_cdp->cd_parentcnid);
-       if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
-               TrashCatalogIterator(vcb, to_cdp->cd_parentcnid);
-
        /* Step 2: Insert cnode at new location */
        result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
        if (result == btExists) {
        /* Step 2: Insert cnode at new location */
        result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
        if (result == btExists) {
@@ -767,11 +1074,14 @@ cat_rename (
 
                /* Find cnode data at new location */
                result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
 
                /* Find cnode data at new location */
                result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
+               if (result)
+                       goto exit;
                
                if ((fromtype != recp->recordType) ||
                
                if ((fromtype != recp->recordType) ||
-                   (from_cdp->cd_cnid != getcnid(recp)))
+                   (from_cdp->cd_cnid != getcnid(recp))) {
+                       result = EEXIST;
                        goto exit; /* EEXIST */
                        goto exit; /* EEXIST */
-               
+               }
                /* The old name is a case variant and must be removed */
                result = BTDeleteRecord(fcb, from_iterator);
                if (result)
                /* The old name is a case variant and must be removed */
                result = BTDeleteRecord(fcb, from_iterator);
                if (result)
@@ -781,7 +1091,17 @@ cat_rename (
                result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
                if (result) {
                        /* Try and restore original before leaving */
                result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
                if (result) {
                        /* Try and restore original before leaving */
+                   // XXXdbg
+                   #if 1
+                      {
+                       int err;
+                       err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
+                       if (err)
+                               panic("cat_create: could not undo (BTInsert = %d)", err);
+                      }
+                   #else
                        (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
                        (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
+                   #endif
                        goto exit;
                }
                sourcegone = 1;
                        goto exit;
                }
                sourcegone = 1;
@@ -794,7 +1114,17 @@ cat_rename (
                result = BTDeleteRecord(fcb, from_iterator);
                if (result) {
                        /* Try and delete new record before leaving */
                result = BTDeleteRecord(fcb, from_iterator);
                if (result) {
                        /* Try and delete new record before leaving */
+                 // XXXdbg
+                 #if 1
+                    {
+                       int err;
+                       err = BTDeleteRecord(fcb, to_iterator);
+                       if (err)
+                               panic("cat_create: could not undo (BTDelete = %d)", err);
+                    }                  
+                 #else
                        (void) BTDeleteRecord(fcb, to_iterator);
                        (void) BTDeleteRecord(fcb, to_iterator);
+                 #endif
                        goto exit;
                }
        }
                        goto exit;
                }
        }
@@ -824,6 +1154,14 @@ cat_rename (
                if (std_hfs) {
                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                        promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
                if (std_hfs) {
                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                        promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
+
+                       /* Save the real encoding hint in the Finder Info (field 4). */
+                       if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
+                               u_long realhint;
+
+                               realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
+                               vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
+                       }
        
                } else
                        pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
        
                } else
                        pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
@@ -834,8 +1172,8 @@ cat_rename (
                        FREE(pluskey, M_TEMP);
                }
        }
                        FREE(pluskey, M_TEMP);
                }
        }
-       (void) BTFlushPath(fcb);
 exit:
 exit:
+       (void) BTFlushPath(fcb);
        if (from_iterator)
                FREE(from_iterator, M_TEMP);
        if (to_iterator)
        if (from_iterator)
                FREE(from_iterator, M_TEMP);
        if (to_iterator)
@@ -854,6 +1192,7 @@ exit:
  *     2. BTDeleteRecord(thread);
  *     3. BTUpdateRecord(parent);
  */
  *     2. BTDeleteRecord(thread);
  *     3. BTUpdateRecord(parent);
  */
+__private_extern__
 int
 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
 {
 int
 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
 {
@@ -874,24 +1213,23 @@ cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
         * A directory must be empty
         * A file must be zero length (no blocks)
         */
         * A directory must be empty
         * A file must be zero length (no blocks)
         */
-
        if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
        if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
-           descp->cd_parentcnid == kRootParID)
+           descp->cd_parentcnid == kHFSRootParentID)
                return (EINVAL);
 
        /* XXX Preflight Missing */
        
        /* Get space for iterator */    
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
                return (EINVAL);
 
        /* XXX Preflight Missing */
        
        /* Get space for iterator */    
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
-       bzero(iterator, sizeof(*iterator));
+       iterator->hint.nodeNum = 0;
 
        /*
         * Derive a key from either the file ID (for a virtual inode)
         * or the descriptor.
         */
        if (descp->cd_namelen == 0) {
 
        /*
         * Derive a key from either the file ID (for a virtual inode)
         * or the descriptor.
         */
        if (descp->cd_namelen == 0) {
-               result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
-               cnid = attrp->ca_fileid;
+           result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
+           cnid = attrp->ca_fileid;
        } else {
                result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
                cnid = descp->cd_cnid;
        } else {
                result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
                cnid = descp->cd_cnid;
@@ -901,17 +1239,41 @@ cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
 
        /* Delete record */
        result = BTDeleteRecord(fcb, iterator);
 
        /* Delete record */
        result = BTDeleteRecord(fcb, iterator);
-       if (result)
-               goto exit;
+       if (result) {
+               if (std_hfs || (result != btNotFound))
+                       goto exit;
+
+               struct cat_desc temp_desc;
+               
+               /* Probably the node has mangled name */
+               result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL); 
+               if (result) 
+                       goto exit;
+               
+               /* The file has mangled name.  Delete the file using full name  */
+               bzero(iterator, sizeof(*iterator));
+               result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
+               cnid = temp_desc.cd_cnid;
+               if (result) {
+                       cat_releasedesc(&temp_desc);
+                       goto exit;
+               }
+
+               result = BTDeleteRecord(fcb, iterator);
+               if (result) { 
+                       cat_releasedesc(&temp_desc);
+                       goto exit;
+               }
+
+               cat_releasedesc(&temp_desc);
+       }
 
        /* Delete thread record, ignore errors */
        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
        (void) BTDeleteRecord(fcb, iterator);
 
 
        /* Delete thread record, ignore errors */
        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
        (void) BTDeleteRecord(fcb, iterator);
 
-       TrashCatalogIterator(vcb, descp->cd_parentcnid);
-
-       (void) BTFlushPath(fcb);
 exit:
 exit:
+       (void) BTFlushPath(fcb);
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
@@ -922,6 +1284,7 @@ exit:
  * cnode_update - update the catalog node described by descp
  * using the data from attrp and forkp.
  */
  * cnode_update - update the catalog node described by descp
  * using the data from attrp and forkp.
  */
+__private_extern__
 int
 cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
 int
 cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
@@ -945,7 +1308,6 @@ cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
 
        /* Get space for iterator */    
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
 
        /* Get space for iterator */    
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
-       bzero(iterator, sizeof(*iterator));
 
        /*
         * For open-deleted files we need to do a lookup by cnid
 
        /*
         * For open-deleted files we need to do a lookup by cnid
@@ -973,9 +1335,8 @@ cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        /* Update the node hint. */
        descp->cd_hint = iterator->hint.nodeNum;
 
        /* Update the node hint. */
        descp->cd_hint = iterator->hint.nodeNum;
 
-       (void) BTFlushPath(fcb);
-
 exit:
 exit:
+       (void) BTFlushPath(fcb);
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
        FREE(iterator, M_TEMP);
 
        return MacToVFSError(result);
@@ -986,8 +1347,7 @@ exit:
  * This is called from within BTUpdateRecord.
  */
 static int
  * This is called from within BTUpdateRecord.
  */
 static int
-catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
-              struct update_state *state)
+catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
 {
        struct cat_desc *descp;
        struct cat_attr *attrp;
 {
        struct cat_desc *descp;
        struct cat_attr *attrp;
@@ -1061,15 +1421,18 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                dir = (struct HFSPlusCatalogFolder *)crp;
                /* Do a quick sanity check */
                if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
                dir = (struct HFSPlusCatalogFolder *)crp;
                /* Do a quick sanity check */
                if ((ckp->hfsPlus.parentID != descp->cd_parentcnid) ||
-                       (dir->folderID != descp->cd_cnid))
+                       (dir->folderID != descp->cd_cnid)) 
                        return (btNotFound);
                        return (btNotFound);
+               dir->flags            = attrp->ca_recflags;
                dir->valence          = attrp->ca_entries;
                dir->createDate       = to_hfs_time(attrp->ca_itime);
                dir->contentModDate   = to_hfs_time(attrp->ca_mtime);
                dir->backupDate       = to_hfs_time(attrp->ca_btime);
                dir->accessDate       = to_hfs_time(attrp->ca_atime);
                dir->valence          = attrp->ca_entries;
                dir->createDate       = to_hfs_time(attrp->ca_itime);
                dir->contentModDate   = to_hfs_time(attrp->ca_mtime);
                dir->backupDate       = to_hfs_time(attrp->ca_btime);
                dir->accessDate       = to_hfs_time(attrp->ca_atime);
+               attrp->ca_atimeondisk = attrp->ca_atime;        
                dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
                dir->textEncoding     = descp->cd_encoding;
                dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
                dir->textEncoding     = descp->cd_encoding;
+               dir->attrBlocks       = attrp->ca_attrblks;
                bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
                /*
                 * Update the BSD Info if it was already initialized on
                bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
                /*
                 * Update the BSD Info if it was already initialized on
@@ -1099,8 +1462,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                    ((attrp->ca_mode & ALLPERMS) !=
                     (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
                        if ((dir->bsdInfo.fileMode == 0) ||
                    ((attrp->ca_mode & ALLPERMS) !=
                     (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
                        if ((dir->bsdInfo.fileMode == 0) ||
-                           (HFSTOVFS(hfsmp)->mnt_flag &
-                            MNT_UNKNOWNPERMISSIONS) == 0) {
+                           (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
                                dir->bsdInfo.ownerID = attrp->ca_uid;
                                dir->bsdInfo.groupID = attrp->ca_gid;
                        }
                                dir->bsdInfo.ownerID = attrp->ca_uid;
                                dir->bsdInfo.groupID = attrp->ca_gid;
                        }
@@ -1117,12 +1479,15 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                /* Do a quick sanity check */
                if (file->fileID != attrp->ca_fileid)
                        return (btNotFound);
                /* Do a quick sanity check */
                if (file->fileID != attrp->ca_fileid)
                        return (btNotFound);
+               file->flags            = attrp->ca_recflags;
                file->createDate       = to_hfs_time(attrp->ca_itime);
                file->contentModDate   = to_hfs_time(attrp->ca_mtime);
                file->backupDate       = to_hfs_time(attrp->ca_btime);
                file->accessDate       = to_hfs_time(attrp->ca_atime);
                file->createDate       = to_hfs_time(attrp->ca_itime);
                file->contentModDate   = to_hfs_time(attrp->ca_mtime);
                file->backupDate       = to_hfs_time(attrp->ca_btime);
                file->accessDate       = to_hfs_time(attrp->ca_atime);
+               attrp->ca_atimeondisk  = attrp->ca_atime;       
                file->attributeModDate = to_hfs_time(attrp->ca_ctime);
                file->textEncoding     = descp->cd_encoding;
                file->attributeModDate = to_hfs_time(attrp->ca_ctime);
                file->textEncoding     = descp->cd_encoding;
+               file->attrBlocks       = attrp->ca_attrblks;
                bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
                /*
                 * Update the BSD Info if it was already initialized on
                bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
                /*
                 * Update the BSD Info if it was already initialized on
@@ -1152,8 +1517,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                    ((attrp->ca_mode & ALLPERMS) !=
                     (hfsmp->hfs_file_mask & ACCESSPERMS))) {
                        if ((file->bsdInfo.fileMode == 0) ||
                    ((attrp->ca_mode & ALLPERMS) !=
                     (hfsmp->hfs_file_mask & ACCESSPERMS))) {
                        if ((file->bsdInfo.fileMode == 0) ||
-                           (HFSTOVFS(hfsmp)->mnt_flag &
-                            MNT_UNKNOWNPERMISSIONS) == 0) {
+                           (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
                                file->bsdInfo.ownerID = attrp->ca_uid;
                                file->bsdInfo.groupID = attrp->ca_gid;
                        }
                                file->bsdInfo.ownerID = attrp->ca_uid;
                                file->bsdInfo.groupID = attrp->ca_gid;
                        }
@@ -1167,6 +1531,9 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                        file->resourceFork.totalBlocks = forkp->cf_blocks;
                        bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
                                sizeof(HFSPlusExtentRecord));
                        file->resourceFork.totalBlocks = forkp->cf_blocks;
                        bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
                                sizeof(HFSPlusExtentRecord));
+                       /* Push blocks read to disk */
+                       file->resourceFork.clumpSize =
+                                       howmany(forkp->cf_bytesread, blksize);
                }
                if (state->s_datafork) {
                        forkp = state->s_datafork;
                }
                if (state->s_datafork) {
                        forkp = state->s_datafork;
@@ -1174,6 +1541,9 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
                        file->dataFork.totalBlocks = forkp->cf_blocks;
                        bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
                                sizeof(HFSPlusExtentRecord));
                        file->dataFork.totalBlocks = forkp->cf_blocks;
                        bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
                                sizeof(HFSPlusExtentRecord));
+                       /* Push blocks read to disk */
+                       file->dataFork.clumpSize =
+                                       howmany(forkp->cf_bytesread, blksize);
                }
 
                if ((file->resourceFork.extents[0].startBlock != 0) &&
                }
 
                if ((file->resourceFork.extents[0].startBlock != 0) &&
@@ -1202,8 +1572,8 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, u_int16_t reclen,
 }
 
 /*
 }
 
 /*
- * catrec_readattr - 
- * This is called from within BTIterateRecords.
+ * Callback to collect directory entries.
+ * Called with readattr_state for each item in a directory.
  */
 struct readattr_state {
        struct hfsmount *hfsmp;
  */
 struct readattr_state {
        struct hfsmount *hfsmp;
@@ -1214,8 +1584,8 @@ struct readattr_state {
 };
 
 static int
 };
 
 static int
-catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
-               u_long node, struct readattr_state *state)
+cat_readattr(const CatalogKey *key, const CatalogRecord *rec,
+             struct readattr_state *state)
 {
        struct cat_entrylist *list = state->list;
        struct hfsmount *hfsmp = state->hfsmp;
 {
        struct cat_entrylist *list = state->list;
        struct hfsmount *hfsmp = state->hfsmp;
@@ -1242,11 +1612,19 @@ catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
                return (0);     /* stop */
        }
 
                return (0);     /* stop */
        }
 
-       /* Hide the private meta data directory. */
-       if (parentcnid == kRootDirID  &&
-           rec->recordType == kHFSPlusFolderRecord &&
-           rec->hfsPlusFolder.folderID == hfsmp->hfs_private_metadata_dir) {
-               return (1);     /* continue */
+       /* Hide the private meta data directory and journal files */
+       if (parentcnid == kHFSRootFolderID) {
+               if ((rec->recordType == kHFSPlusFolderRecord) &&
+                   (rec->hfsPlusFolder.folderID == hfsmp->hfs_privdir_desc.cd_cnid)) {
+                       return (1);     /* continue */
+               }
+               if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
+                   (rec->recordType == kHFSPlusFileRecord) &&
+                   ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
+                    (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
+
+                       return (1);     /* continue */
+               }
        }
 
        cep = &list->entry[list->realentries++];
        }
 
        cep = &list->entry[list->realentries++];
@@ -1261,7 +1639,7 @@ catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
 
                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
 
                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
-               builddesc(pluskey, getcnid(rec), node, encoding, isadir(rec), &cep->ce_desc);
+               builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
                FREE(pluskey, M_TEMP);
 
                if (rec->recordType == kHFSFileRecord) {
                FREE(pluskey, M_TEMP);
 
                if (rec->recordType == kHFSFileRecord) {
@@ -1274,7 +1652,7 @@ catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
                }
        } else {
                getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
                }
        } else {
                getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
-               builddesc((HFSPlusCatalogKey *)key, getcnid(rec), node, getencoding(rec),
+               builddesc((HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
                        isadir(rec), &cep->ce_desc);
                
                if (rec->recordType == kHFSPlusFileRecord) {
                        isadir(rec), &cep->ce_desc);
                
                if (rec->recordType == kHFSPlusFileRecord) {
@@ -1294,11 +1672,13 @@ catrec_readattr(const CatalogKey *key, const CatalogRecord *rec,
 }
 
 /*
 }
 
 /*
+ * Pack a cat_entrylist buffer with attributes from the catalog
+ *
  * Note: index is zero relative
  */
  * Note: index is zero relative
  */
+__private_extern__
 int
 int
-cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
-               struct cat_entrylist *ce_list)
+cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
 {
        FCB* fcb;
        CatalogKey * key;
 {
        FCB* fcb;
        CatalogKey * key;
@@ -1307,13 +1687,15 @@ cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
        cnid_t parentcnid;
        int i;
        int std_hfs;
        cnid_t parentcnid;
        int i;
        int std_hfs;
+       int index;
+       int have_key;
        int result = 0;
 
        ce_list->realentries = 0;
 
        fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
        int result = 0;
 
        ce_list->realentries = 0;
 
        fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
-       parentcnid = prevdesc->cd_parentcnid;
+       parentcnid = dirhint->dh_desc.cd_parentcnid;
 
        state.hfsmp = hfsmp;
        state.list = ce_list;
 
        state.hfsmp = hfsmp;
        state.list = ce_list;
@@ -1324,37 +1706,63 @@ cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
        bzero(iterator, sizeof(*iterator));
        key = (CatalogKey *)&iterator->key;
        MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
        bzero(iterator, sizeof(*iterator));
        key = (CatalogKey *)&iterator->key;
-       iterator->hint.nodeNum = prevdesc->cd_hint;
+       have_key = 0;
+       iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
+       index = dirhint->dh_index + 1;
 
        /*
 
        /*
-        * If the last entry wasn't cached then establish the iterator
+        * Attempt to build a key from cached filename
         */
         */
-       if ((index == 0) ||
-           (prevdesc->cd_namelen == 0) ||
-           (buildkey(hfsmp, prevdesc, (HFSPlusCatalogKey *)key, 0) != 0)) {
-               int i;
+       if (dirhint->dh_desc.cd_namelen != 0) {
+               if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
+                       have_key = 1;
+               }
+       }
+
+       /*
+        * If the last entry wasn't cached then position the btree iterator
+        */
+       if ((index == 0) || !have_key) {
                /*
                /*
-                * Position the iterator at the directory thread.
-                * (ie just before the first entry)
+                * Position the iterator at the directory's thread record.
+                * (i.e. just before the first entry)
                 */
                 */
-               buildthreadkey(parentcnid, std_hfs, key);
+               buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
                result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
                result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
-               if (result)
-                       goto exit;  /* bad news */
+               if (result) {
+                       result = MacToVFSError(result);
+                       goto exit;
+               }
+       
                /*
                 * Iterate until we reach the entry just
                 * before the one we want to start with.
                 */
                /*
                 * Iterate until we reach the entry just
                 * before the one we want to start with.
                 */
-               for (i = 0; i < index; ++i) {
-                       result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, NULL, NULL);
-                       if (result)
-                               goto exit;  /* bad news */
+               if (index > 0) {
+                       struct position_state ps;
+
+                       ps.error = 0;
+                       ps.count = 0;
+                       ps.index = index;
+                       ps.parentID = dirhint->dh_desc.cd_parentcnid;
+                       ps.hfsmp = hfsmp;
+
+                       result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
+                                                 (IterateCallBackProcPtr)cat_findposition, &ps);
+                       if (ps.error)
+                               result = ps.error;
+                       else
+                               result = MacToVFSError(result);
+                       if (result) {
+                               result = MacToVFSError(result);
+                               goto exit;
+                       }
                }
        }
 
                }
        }
 
-       /* Fill list with entries. */
+       /* Fill list with entries starting at iterator->key. */
        result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
        result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
-                       (IterateCallBackProcPtr)catrec_readattr, &state);
+                       (IterateCallBackProcPtr)cat_readattr, &state);
 
        if (state.error)
                result = state.error;
 
        if (state.error)
                result = state.error;
@@ -1369,7 +1777,7 @@ cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
        /*
         *  Resolve any hard links.
         */
        /*
         *  Resolve any hard links.
         */
-       for (i = 0; i < ce_list->realentries; ++i) {
+       for (i = 0; i < (int)ce_list->realentries; ++i) {
                struct FndrFileInfo *fip;
                struct cat_entry *cep;
                struct HFSPlusCatalogFile filerec;
                struct FndrFileInfo *fip;
                struct cat_entry *cep;
                struct HFSPlusCatalogFile filerec;
@@ -1385,8 +1793,8 @@ cat_getentriesattr(struct hfsmount *hfsmp, struct cat_desc *prevdesc, int index,
                if ((cep->ce_attr.ca_rdev != 0)
                &&  (SWAP_BE32(fip->fdType) == kHardLinkFileType)
                &&  (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
                if ((cep->ce_attr.ca_rdev != 0)
                &&  (SWAP_BE32(fip->fdType) == kHardLinkFileType)
                &&  (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)
-               &&  ((cep->ce_attr.ca_itime == HFSTOVCB(hfsmp)->vcbCrDate) ||
-                    (cep->ce_attr.ca_itime == hfsmp->hfs_metadata_createdate))) {
+               &&  ((cep->ce_attr.ca_itime == (time_t)HFSTOVCB(hfsmp)->vcbCrDate) ||
+                    (cep->ce_attr.ca_itime == (time_t)hfsmp->hfs_metadata_createdate))) {
 
                        if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
                                continue;
 
                        if (resolvelink(hfsmp, cep->ce_attr.ca_rdev, &filerec) != 0)
                                continue;
@@ -1404,181 +1812,464 @@ exit:
        return MacToVFSError(result);
 }
 
        return MacToVFSError(result);
 }
 
+#define SMALL_DIRENTRY_SIZE  (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
 
 
-struct read_state {
-       u_int32_t       cbs_parentID;
-       u_int32_t       cbs_hiddenDirID;
-       off_t           cbs_lastoffset;
-       struct uio *    cbs_uio;
-       ExtendedVCB *   cbs_vcb;
-       int16_t         cbs_hfsPlus;
-       int16_t         cbs_result;
-};
+/*
+ * Callback to pack directory entries.
+ * Called with packdirentry_state for each item in a directory.
+ */
 
 
+/* Hard link information collected during cat_getdirentries. */
+struct linkinfo {
+       u_long       link_ref;
+       user_addr_t  dirent_addr;
+};
+typedef struct linkinfo linkinfo_t;
+
+/* State information for the cat_packdirentry callback function. */
+struct packdirentry_state {
+       int            cbs_extended;
+       u_int32_t      cbs_parentID;
+       u_int32_t      cbs_index;
+       uio_t          cbs_uio;
+       ExtendedVCB *  cbs_hfsmp;
+       int            cbs_result;
+       int32_t        cbs_nlinks;
+       int32_t        cbs_maxlinks;
+       linkinfo_t *   cbs_linkinfo;
+       struct cat_desc * cbs_desc;
+//     struct dirent  * cbs_stdentry;
+       // followign fields are only used for NFS readdir, which uses the next file id as the seek offset of each entry
+       struct direntry * cbs_direntry;
+       struct direntry * cbs_prevdirentry;
+       u_int32_t      cbs_previlinkref;
+       Boolean        cbs_hasprevdirentry;
+       Boolean        cbs_eof;
+};
 
 static int
 
 static int
-catrec_read(const CatalogKey *ckp, const CatalogRecord *crp,
-                   u_int16_t recordLen, struct read_state *state)
+cat_packdirentry(const CatalogKey *ckp, const CatalogRecord *crp,
+                 struct packdirentry_state *state)
 {
 {
+       struct hfsmount *hfsmp;
        CatalogName *cnp;
        CatalogName *cnp;
-       size_t utf8chars;
-       u_int32_t curID;
+       cnid_t curID;
        OSErr result;
        struct dirent catent;
        OSErr result;
        struct dirent catent;
+       struct direntry * entry = NULL;
+       time_t itime;
+       u_int32_t ilinkref = 0;
+       u_int32_t curlinkref = 0;
+       cnid_t  cnid;
+       int hide = 0;
+       u_int8_t type;
+       u_int8_t is_mangled = 0;
+       char *nameptr;
+       user_addr_t uiobase;
+       size_t namelen = 0;
+       size_t maxnamelen;
+       size_t uiosize = 0;
+       caddr_t uioaddr;
+       Boolean stop_after_pack = false;
        
        
-       if (state->cbs_hfsPlus)
-               curID = ckp->hfsPlus.parentID;
-       else
+       hfsmp = state->cbs_hfsmp;
+
+       if (hfsmp->hfs_flags & HFS_STANDARD)
                curID = ckp->hfs.parentID;
                curID = ckp->hfs.parentID;
+       else
+               curID = ckp->hfsPlus.parentID;
 
        /* We're done when parent directory changes */
        if (state->cbs_parentID != curID) {
 
        /* We're done when parent directory changes */
        if (state->cbs_parentID != curID) {
-lastitem:
-/*
- * The NSDirectoryList class chokes on empty records (it doesnt check d_reclen!)
- * so remove padding for now...
- */
-#if 0
-               /*
-                * Pad the end of list with an empty record.
-                * This eliminates an extra call by readdir(3c).
-                */
-               catent.d_fileno = 0;
-               catent.d_reclen = 0;
-               catent.d_type = 0;
-               catent.d_namlen = 0;
-               *(int32_t*)&catent.d_name[0] = 0;
-
-               state->cbs_lastoffset = state->cbs_uio->uio_offset;
-
-               state->cbs_result = uiomove((caddr_t) &catent, 12, state->cbs_uio);
-               if (state->cbs_result == 0)
+               if (state->cbs_extended) {
+                       if (state->cbs_hasprevdirentry) { /* the last record haven't been returned yet, so we want to stop after
+                                                                                          * packing the last item */
+                               stop_after_pack = true;
+                       } else {
+                               state->cbs_result = ENOENT;
+                               return (0);     /* stop */
+                       }                               
+               } else {
                        state->cbs_result = ENOENT;
                        state->cbs_result = ENOENT;
-#else
-               state->cbs_lastoffset = state->cbs_uio->uio_offset;
-               state->cbs_result = ENOENT;
-#endif
-               return (0);     /* stop */
+                       return (0);     /* stop */
+               }
        }
 
        }
 
-       if (state->cbs_hfsPlus) {
-               switch(crp->recordType) {
-               case kHFSPlusFolderRecord:
-                       catent.d_type = DT_DIR;
-                       catent.d_fileno = crp->hfsPlusFolder.folderID;
-                       break;
-               case kHFSPlusFileRecord:
-                       catent.d_type = DT_REG;
-                       catent.d_fileno = crp->hfsPlusFile.fileID;
-                       break;
-               default:
-                       return (0);     /* stop */
-               };
-
-               cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
-               result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
-                               catent.d_name, &utf8chars, kdirentMaxNameBytes + 1, ':', 0);
-               if (result == ENAMETOOLONG) {
-                       result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
-                               cnp->ustr.unicode, kdirentMaxNameBytes + 1, (ByteCount*)&utf8chars, catent.d_name, catent.d_fileno);            
+       if (state->cbs_extended) {
+               entry = state->cbs_direntry;
+               nameptr = &entry->d_name[0];
+               maxnamelen = NAME_MAX;
+       } else {
+               nameptr = &catent.d_name[0];
+               maxnamelen = NAME_MAX;
+       }
+
+       if (state->cbs_extended && stop_after_pack) {
+               cnid = INT_MAX;                 /* the last item returns a non-zero invalid cookie */
+       } else {
+               if (!(hfsmp->hfs_flags & HFS_STANDARD)) {
+                       switch(crp->recordType) {
+                       case kHFSPlusFolderRecord:
+                               type = DT_DIR;
+                               cnid = crp->hfsPlusFolder.folderID;
+                               /* Hide our private meta data directory */
+                               if ((curID == kHFSRootFolderID) &&
+                                       (cnid == hfsmp->hfs_privdir_desc.cd_cnid)) {
+                                       hide = 1;
+                               }
+
+                               break;
+                       case kHFSPlusFileRecord:
+                               itime = to_bsd_time(crp->hfsPlusFile.createDate);
+                               /*
+                                * When a hardlink link is encountered save its link ref.
+                                */
+                               if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
+                                       (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
+                                       ((itime == (time_t)hfsmp->hfs_itime) ||
+                                        (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
+                                       ilinkref = crp->hfsPlusFile.bsdInfo.special.iNodeNum;
+                               }
+                               type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
+                               cnid = crp->hfsPlusFile.fileID;
+                               /* Hide the journal files */
+                               if ((curID == kHFSRootFolderID) &&
+                                       ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
+                                       ((cnid == hfsmp->hfs_jnlfileid) ||
+                                        (cnid == hfsmp->hfs_jnlinfoblkid))) {
+                                       hide = 1;
+                               }
+                               break;
+                       default:
+                               return (0);     /* stop */
+                       };
+
+                       cnp = (CatalogName*) &ckp->hfsPlus.nodeName;
+                       result = utf8_encodestr(cnp->ustr.unicode, cnp->ustr.length * sizeof(UniChar),
+                                                                       nameptr, &namelen, maxnamelen + 1, ':', 0);
+                       if (result == ENAMETOOLONG) {
+                               result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
+                                                                                                        cnp->ustr.unicode, maxnamelen + 1,
+                                                                                                        (ByteCount*)&namelen, nameptr,
+                                                                                                        cnid);         
+                               is_mangled = 1;
+                       }
+               } else { /* hfs */
+                       switch(crp->recordType) {
+                       case kHFSFolderRecord:
+                               type = DT_DIR;
+                               cnid = crp->hfsFolder.folderID;
+                               break;
+                       case kHFSFileRecord:
+                               type = DT_REG;
+                               cnid = crp->hfsFile.fileID;
+                               break;
+                       default:
+                               return (0);     /* stop */
+                       };
+
+                       cnp = (CatalogName*) ckp->hfs.nodeName;
+                       result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1,
+                                                                (ByteCount *)&namelen, nameptr);
+                       /*
+                        * When an HFS name cannot be encoded with the current
+                        * volume encoding we use MacRoman as a fallback.
+                        */
+                       if (result)
+                               result = mac_roman_to_utf8(cnp->pstr, maxnamelen + 1,
+                                                                                  (ByteCount *)&namelen, nameptr);
                }
                }
-       } else { /* hfs */
-               switch(crp->recordType) {
-               case kHFSFolderRecord:
-                       catent.d_type = DT_DIR;
-                       catent.d_fileno = crp->hfsFolder.folderID;
-                       break;
-               case kHFSFileRecord:
-                       catent.d_type = DT_REG;
-                       catent.d_fileno = crp->hfsFile.fileID;
-                       break;
-               default:
-                       return (0);     /* stop */
-               };
+       }
 
 
-               cnp = (CatalogName*) ckp->hfs.nodeName;
-               result = hfs_to_utf8(state->cbs_vcb, cnp->pstr, kdirentMaxNameBytes + 1,
-                                   (ByteCount *)&utf8chars, catent.d_name);
+       if (state->cbs_extended) {
                /*
                /*
-                * When an HFS name cannot be encoded with the current
-                * volume encoding we use MacRoman as a fallback.
+                * The index is 1 relative and includes "." and ".."
+                *
+                * Also stuff the cnid in the upper 32 bits of the cookie.  The cookie is stored to the previous entry, which
+                * will be packed and copied this time
                 */
                 */
-               if (result)
-                       result = mac_roman_to_utf8(cnp->pstr, kdirentMaxNameBytes + 1,
-                                   (ByteCount *)&utf8chars, catent.d_name);
+               state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
+               uiosize = state->cbs_prevdirentry->d_reclen;
+               uioaddr = (caddr_t) state->cbs_prevdirentry;
+       } else {
+               catent.d_type = type;
+               catent.d_namlen = namelen;
+               catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
+               if (hide)
+                       catent.d_fileno = 0;  /* file number = 0 means skip entry */
+               else
+                       catent.d_fileno = cnid;
+               uioaddr = (caddr_t) &catent;
        }
 
        }
 
-       catent.d_namlen = utf8chars;
-       catent.d_reclen = DIRENTRY_SIZE(utf8chars);
-       
-       /* hide our private meta data directory */
-       if (curID == kRootDirID                         &&
-           catent.d_fileno == state->cbs_hiddenDirID   &&
-           catent.d_type == DT_DIR)
-               goto lastitem;
-
-       state->cbs_lastoffset = state->cbs_uio->uio_offset;
+       /* Save current base address for post processing of hard-links. */
+       uiobase = uio_curriovbase(state->cbs_uio);
 
 
-       /* if this entry won't fit then we're done */
-       if (catent.d_reclen > state->cbs_uio->uio_resid)
+       /* If this entry won't fit then we're done */
+       if ((uiosize > uio_resid(state->cbs_uio)) ||
+           (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
                return (0);     /* stop */
                return (0);     /* stop */
+       }
 
 
-       state->cbs_result = uiomove((caddr_t) &catent, catent.d_reclen, state->cbs_uio);
+       if (!state->cbs_extended || state->cbs_hasprevdirentry) {
+               state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
+               if (state->cbs_result == 0) {
+                       ++state->cbs_index;
+
+                       /* Remember previous entry */
+                       state->cbs_desc->cd_cnid = cnid;
+                       if (type == DT_DIR) {
+                               state->cbs_desc->cd_flags |= CD_ISDIR;
+                       } else {
+                               state->cbs_desc->cd_flags &= ~CD_ISDIR;
+                       }
+                       if (state->cbs_desc->cd_nameptr != NULL) {
+                               vfs_removename(state->cbs_desc->cd_nameptr);
+                       }
+#if 0
+                       state->cbs_desc->cd_encoding = xxxx;
+#endif
+                       if (!is_mangled) {
+                               state->cbs_desc->cd_namelen = namelen;
+                               state->cbs_desc->cd_nameptr = vfs_addname(nameptr, namelen, 0, 0);
+                       } else {
+                               /* Store unmangled name for the directory hint else it will 
+                                * restart readdir at the last location again 
+                                */
+                               char *new_nameptr;
+                               size_t bufsize;
+                               size_t tmp_namelen = 0;
+                       
+                               cnp = (CatalogName *)&ckp->hfsPlus.nodeName;
+                               bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
+                                                                                        cnp->ustr.length * sizeof(UniChar),
+                                                                                        ':', 0);
+                               MALLOC(new_nameptr, char *, bufsize, M_TEMP, M_WAITOK);
+                               result = utf8_encodestr(cnp->ustr.unicode,
+                                                                               cnp->ustr.length * sizeof(UniChar),
+                                                                               new_nameptr, &tmp_namelen,
+                                                                               bufsize, ':', 0);
+                       
+                               state->cbs_desc->cd_namelen = tmp_namelen;
+                               state->cbs_desc->cd_nameptr = vfs_addname(new_nameptr, tmp_namelen, 0, 0);
+                       
+                               FREE(new_nameptr, M_TEMP);
+                       } 
+               }
+               if (state->cbs_hasprevdirentry) {
+                       curlinkref = ilinkref;               /* save current */
+                       ilinkref = state->cbs_previlinkref;  /* use previous */
+               }
+               /*
+                * Record any hard links for post processing.
+                */
+               if ((ilinkref != 0) &&
+                       (state->cbs_result == 0) &&
+                       (state->cbs_nlinks < state->cbs_maxlinks)) {
+                       state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
+                       state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
+                       state->cbs_nlinks++;
+               }
+               if (state->cbs_hasprevdirentry) {
+                       ilinkref = curlinkref;   /* restore current */
+               }
+       }
 
 
-       /* continue iteration if there's room */
+       if (state->cbs_extended) {      /* fill the direntry to be used the next time */
+               if (stop_after_pack) {
+                       state->cbs_eof = true;
+                       return (0);     /* stop */
+               }
+               entry->d_type = type;
+               entry->d_namlen = namelen;
+               entry->d_reclen = EXT_DIRENT_LEN(namelen);
+               if (hide)
+                       entry->d_fileno = 0;  /* file number = 0 means skip entry */
+               else
+                       entry->d_fileno = cnid;
+               /* swap the current and previous entry */
+               struct direntry * tmp;
+               tmp = state->cbs_direntry;
+               state->cbs_direntry = state->cbs_prevdirentry;
+               state->cbs_prevdirentry = tmp;
+               state->cbs_hasprevdirentry = true;
+               state->cbs_previlinkref = ilinkref;
+       }
+
+       /* Continue iteration if there's room */
        return (state->cbs_result == 0  &&
        return (state->cbs_result == 0  &&
-               state->cbs_uio->uio_resid >= AVERAGE_HFSDIRENTRY_SIZE);
+               uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
 }
 
 }
 
+
 /*
 /*
- *
+ * Pack a uio buffer with directory entries from the catalog
  */
  */
+__private_extern__
 int
 int
-cat_getdirentries(struct hfsmount *hfsmp, struct cat_desc *descp,
-               struct uio *uio, int *eofflag)
+cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
+                                 uio_t uio, int extended, int * items, int * eofflag)
 {
 {
-       ExtendedVCB *vcb = HFSTOVCB(hfsmp);
+       FCB* fcb;
        BTreeIterator * iterator;
        BTreeIterator * iterator;
-       CatalogIterator *cip;
-       u_int32_t diroffset;
-       u_int16_t op;
-       struct read_state state;
-       u_int32_t dirID = descp->cd_cnid;
+       CatalogKey * key;
+       struct packdirentry_state state;
+       void * buffer;
+       int bufsize;
+       int maxlinks;
        int result;
        int result;
+       int index;
+       int have_key;
+       
+       fcb = GetFileControlBlock(hfsmp->hfs_catalog_vp);
 
 
-       diroffset = uio->uio_offset;
-       *eofflag = 0;
+       /*
+        * Get a buffer for link info array, btree iterator and a direntry:
+        */
+       maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
+       bufsize = (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
+       if (extended) {
+               bufsize += 2*sizeof(struct direntry);
+       }
+       MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
+       bzero(buffer, bufsize);
 
 
-       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
-       bzero(iterator, sizeof(*iterator));
+       state.cbs_extended = extended;
+       state.cbs_hasprevdirentry = false;
+       state.cbs_previlinkref = 0;
+       state.cbs_nlinks = 0;
+       state.cbs_maxlinks = maxlinks;
+       state.cbs_linkinfo = (linkinfo_t *) buffer;
 
 
-       /* get an iterator and position it */
-       cip = GetCatalogIterator(vcb, dirID, diroffset);
+       iterator = (BTreeIterator *) ((char *)buffer + (maxlinks * sizeof(linkinfo_t)));
+       key = (CatalogKey *)&iterator->key;
+       have_key = 0;
+       index = dirhint->dh_index + 1;
+       if (extended) {
+               state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
+               state.cbs_prevdirentry = state.cbs_direntry + 1;
+               state.cbs_eof = false;
+       }
+       /*
+        * Attempt to build a key from cached filename
+        */
+       if (dirhint->dh_desc.cd_namelen != 0) {
+               if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
+                       have_key = 1;
+               }
+       }
 
 
-       result = PositionIterator(cip, diroffset, iterator, &op);
-       if (result == cmNotFound) {
-               *eofflag = 1;
-               result = 0;
-               AgeCatalogIterator(cip);
-               goto cleanup;
-       } else if ((result = MacToVFSError(result)))
-               goto cleanup;
-
-       state.cbs_hiddenDirID = hfsmp->hfs_private_metadata_dir;
-       state.cbs_lastoffset = cip->currentOffset;
-       state.cbs_vcb = vcb;
+       /*
+        * If the last entry wasn't cached then position the btree iterator
+        */
+       if ((index == 0) || !have_key) {
+               /*
+                * Position the iterator at the directory's thread record.
+                * (i.e. just before the first entry)
+                */
+               buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
+               result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
+               if (result) {
+                       result = MacToVFSError(result);
+                       goto cleanup;
+               }
+       
+               /*
+                * Iterate until we reach the entry just
+                * before the one we want to start with.
+                */
+               if (index > 0) {
+                       struct position_state ps;
+
+                       ps.error = 0;
+                       ps.count = 0;
+                       ps.index = index;
+                       ps.parentID = dirhint->dh_desc.cd_parentcnid;
+                       ps.hfsmp = hfsmp;
+
+                       result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
+                                                 (IterateCallBackProcPtr)cat_findposition, &ps);
+                       if (ps.error)
+                               result = ps.error;
+                       else
+                               result = MacToVFSError(result);
+                       if (result) {
+                               result = MacToVFSError(result);
+                               goto cleanup;
+                       }
+               }
+       }
+
+       state.cbs_index = index;
+       state.cbs_hfsmp = hfsmp;
        state.cbs_uio = uio;
        state.cbs_uio = uio;
+       state.cbs_desc = &dirhint->dh_desc;
        state.cbs_result = 0;
        state.cbs_result = 0;
-       state.cbs_parentID = dirID;
+       state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
 
 
-       if (vcb->vcbSigWord == kHFSPlusSigWord)
-               state.cbs_hfsPlus = 1;
+       enum BTreeIterationOperations op;
+       if (extended && index != 0 && have_key)
+               op = kBTreeCurrentRecord;
        else
        else
-               state.cbs_hfsPlus = 0;
+               op = kBTreeNextRecord;
 
 
-       /* process as many entries as possible... */
-       result = BTIterateRecords(GetFileControlBlock(vcb->catalogRefNum), op,
-                iterator, (IterateCallBackProcPtr)catrec_read, &state);
+       /*
+        * Process as many entries as possible starting at iterator->key.
+        */
+       result = BTIterateRecords(fcb, op, iterator,
+                                 (IterateCallBackProcPtr)cat_packdirentry, &state);
+
+       /* If readdir is called for NFS and BTIterateRecords reaches the end of the
+        * Catalog BTree, call cat_packdirentry() with dummy values to copy previous 
+        * direntry stored in state to the user buffer.
+        */
+       if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) {
+               CatalogKey ckp;
+               CatalogRecord crp;
+
+               bzero(&ckp, sizeof(ckp));
+               bzero(&crp, sizeof(crp));
+
+               result = cat_packdirentry(&ckp, &crp, &state);
+       }
+
+       /* Note that state.cbs_index is still valid on errors */
+       *items = state.cbs_index - index;
+       index = state.cbs_index;
+
+       if (state.cbs_eof) {
+               *eofflag = 1;
+       }
+       
+       /* Finish updating the catalog iterator. */
+       dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
+       dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
+       dirhint->dh_index = index - 1;
+       
+       /*
+        * Post process any hard links to get the real file id.
+        */
+       if (state.cbs_nlinks > 0) {
+               u_int32_t fileid = 0;
+               user_addr_t address;
+               int i;
+
+               for (i = 0; i < state.cbs_nlinks; ++i) {
+                       if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
+                               continue;
+                       /* This assumes that d_ino is always first field. */
+                       address = state.cbs_linkinfo[i].dirent_addr;
+                       if (address == (user_addr_t)0)
+                               continue;
+                       if (uio_isuserspace(uio)) {
+                               (void) copyout(&fileid, address,
+                                              extended ? sizeof(ino64_t) : sizeof(ino_t));
+                       } else /* system space */ {
+                               ino64_t *inoptr = (ino64_t *)CAST_DOWN(caddr_t, address);
+                               *inoptr = fileid;
+                       }
+               }
+       }
 
        if (state.cbs_result)
                result = state.cbs_result;
 
        if (state.cbs_result)
                result = state.cbs_result;
@@ -1586,27 +2277,174 @@ cat_getdirentries(struct hfsmount *hfsmp, struct cat_desc *descp,
                result = MacToVFSError(result);
 
        if (result == ENOENT) {
                result = MacToVFSError(result);
 
        if (result == ENOENT) {
-               *eofflag = 1;
                result = 0;
        }
 
                result = 0;
        }
 
-       if (result == 0) {
-               cip->currentOffset = state.cbs_lastoffset;
-               cip->nextOffset = uio->uio_offset;
-               UpdateCatalogIterator(iterator, cip);
+cleanup:
+       FREE(buffer, M_TEMP);
+       
+       return (result);
+}
+
+
+/*
+ * Callback to establish directory position.
+ * Called with position_state for each item in a directory.
+ */
+static int
+cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
+                 struct position_state *state)
+{
+       cnid_t curID;
+
+       if (state->hfsmp->hfs_flags & HFS_STANDARD)
+               curID = ckp->hfs.parentID;
+       else
+               curID = ckp->hfsPlus.parentID;
+
+       /* Make sure parent directory didn't change */
+       if (state->parentID != curID) {
+               state->error = EINVAL;
+               return (0);  /* stop */
        }
 
        }
 
-cleanup:
-       if (result) {
-               cip->volume = 0;
-               cip->folderID = 0;
-               AgeCatalogIterator(cip);
+       /* Count this entry */
+       switch(crp->recordType) {
+       case kHFSPlusFolderRecord:
+       case kHFSPlusFileRecord:
+       case kHFSFolderRecord:
+       case kHFSFileRecord:
+               ++state->count;
+               break;
+       default:
+               printf("cat_findposition: invalid record type %d in dir %d\n",
+                       crp->recordType, curID);
+               state->error = EINVAL;
+               return (0);  /* stop */
+       };
+
+       return (state->count < state->index);
+}
+
+
+/*
+ * cat_binarykeycompare - compare two HFS Plus catalog keys.
+
+ * The name portion of the key is compared using a 16-bit binary comparison. 
+ * This is called from the b-tree code.
+ */
+__private_extern__
+int
+cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
+{
+       u_int32_t searchParentID, trialParentID;
+       int result;
+
+       searchParentID = searchKey->parentID;
+       trialParentID = trialKey->parentID;
+       result = 0;
+       
+       if (searchParentID > trialParentID) {
+               ++result;
+       } else if (searchParentID < trialParentID) {
+               --result;
+       } else {
+               u_int16_t * str1 = &searchKey->nodeName.unicode[0];
+               u_int16_t * str2 = &trialKey->nodeName.unicode[0];
+               int length1 = searchKey->nodeName.length;
+               int length2 = trialKey->nodeName.length;
+               u_int16_t c1, c2;
+               int length;
+       
+               if (length1 < length2) {
+                       length = length1;
+                       --result;
+               } else if (length1 > length2) {
+                       length = length2;
+                       ++result;
+               } else {
+                       length = length1;
+               }
+       
+               while (length--) {
+                       c1 = *(str1++);
+                       c2 = *(str2++);
+       
+                       if (c1 > c2) {
+                               result = 1;
+                               break;
+                       }
+                       if (c1 < c2) {
+                               result = -1;
+                               break;
+                       }
+               }
        }
 
        }
 
-       (void) ReleaseCatalogIterator(cip);
-       FREE(iterator, M_TEMP);
+       return result;
+}
+
+
+/*
+ * Compare two standard HFS catalog keys
+ *
+ * Result: +n  search key > trial key
+ *          0  search key = trial key
+ *         -n  search key < trial key
+ */
+int
+CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
+{
+       cnid_t searchParentID, trialParentID;
+       int result;
+
+       searchParentID = searchKey->parentID;
+       trialParentID = trialKey->parentID;
+
+       if (searchParentID > trialParentID)
+               result = 1;
+       else if (searchParentID < trialParentID)
+               result = -1;
+       else /* parent dirID's are equal, compare names */
+               result = FastRelString(searchKey->nodeName, trialKey->nodeName);
+
+       return result;
+}
+
+
+/*
+ * Compare two HFS+ catalog keys
+ *
+ * Result: +n  search key > trial key
+ *          0  search key = trial key
+ *         -n  search key < trial key
+ */
+int
+CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
+{
+       cnid_t searchParentID, trialParentID;
+       int result;
+
+       searchParentID = searchKey->parentID;
+       trialParentID = trialKey->parentID;
        
        
-       return (result);
+       if (searchParentID > trialParentID) {
+               result = 1;
+       }
+       else if (searchParentID < trialParentID) {
+               result = -1;
+       } else {
+               /* parent node ID's are equal, compare names */
+               if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
+                       result = searchKey->nodeName.length - trialKey->nodeName.length;
+               else
+                       result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
+                                                   searchKey->nodeName.length,
+                                                   &trialKey->nodeName.unicode[0],
+                                                   trialKey->nodeName.length);
+       }
+
+       return result;
 }
 
 
 }
 
 
@@ -1691,7 +2529,7 @@ resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *r
        bzero(iterator, sizeof(*iterator));
 
        /* Build a descriptor for private dir. */       
        bzero(iterator, sizeof(*iterator));
 
        /* Build a descriptor for private dir. */       
-       idesc.cd_parentcnid = hfsmp->hfs_private_metadata_dir;
+       idesc.cd_parentcnid = hfsmp->hfs_privdir_desc.cd_cnid;
        idesc.cd_nameptr = inodename;
        idesc.cd_namelen = strlen(inodename);
        idesc.cd_flags = 0;
        idesc.cd_nameptr = inodename;
        idesc.cd_namelen = strlen(inodename);
        idesc.cd_flags = 0;
@@ -1715,6 +2553,25 @@ resolvelink(struct hfsmount *hfsmp, u_long linkref, struct HFSPlusCatalogFile *r
        return (result ? ENOENT : 0);
 }
 
        return (result ? ENOENT : 0);
 }
 
+/*
+ * Resolve hard link reference to obtain the inode number.
+ */
+static int
+resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
+{
+       struct HFSPlusCatalogFile record;
+       int error;
+       
+       error = resolvelink(hfsmp, linkref, &record);
+       if (error == 0) {
+               if (record.fileID == 0)
+                       error = ENOENT;
+               else
+                       *ino = record.fileID;
+       }
+       return (error);
+}
+
 /*
  * getkey - get a key from id by doing a thread lookup
  */
 /*
  * getkey - get a key from id by doing a thread lookup
  */
@@ -1772,6 +2629,26 @@ exit:
        return MacToVFSError(result);
 }
 
        return MacToVFSError(result);
 }
 
+/*
+ * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
+ * null arguments to cat_idlookup instead, but we save around 10% by not building the 
+ * cat_desc here). Both key and attrp must point to real structures.
+ */
+__private_extern__
+int
+cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
+{
+       int result;
+
+       result = getkey(hfsmp, cnid, key);
+       
+       if (result == 0) {
+               result = cat_lookupbykey(hfsmp, key, 0, 0, NULL, attrp, NULL, NULL);
+       }
+
+       return MacToVFSError(result);
+}
+
 
 /*
  * buildrecord - build a default catalog directory or file record
 
 /*
  * buildrecord - build a default catalog directory or file record
@@ -1808,33 +2685,41 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding
                struct FndrFileInfo * fip = NULL;
 
                if (type == S_IFDIR) {
                struct FndrFileInfo * fip = NULL;
 
                if (type == S_IFDIR) {
-                       bzero(crp, sizeof(HFSPlusCatalogFolder));
                        crp->recordType = kHFSPlusFolderRecord;
                        crp->recordType = kHFSPlusFolderRecord;
+                       crp->hfsPlusFolder.flags = 0;
+                       crp->hfsPlusFolder.valence = 0;
                        crp->hfsPlusFolder.folderID = cnid;     
                        crp->hfsPlusFolder.createDate = createtime;
                        crp->hfsPlusFolder.contentModDate = createtime;
                        crp->hfsPlusFolder.folderID = cnid;     
                        crp->hfsPlusFolder.createDate = createtime;
                        crp->hfsPlusFolder.contentModDate = createtime;
-                       crp->hfsPlusFolder.accessDate = createtime;
                        crp->hfsPlusFolder.attributeModDate = createtime;
                        crp->hfsPlusFolder.attributeModDate = createtime;
+                       crp->hfsPlusFolder.accessDate = createtime;
+                       crp->hfsPlusFolder.backupDate = 0;
                        crp->hfsPlusFolder.textEncoding = encoding;
                        crp->hfsPlusFolder.textEncoding = encoding;
+                       crp->hfsPlusFolder.attrBlocks = 0;
                        bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
                        bsdp = &crp->hfsPlusFolder.bsdInfo;
                        bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
                        bsdp = &crp->hfsPlusFolder.bsdInfo;
+                       bsdp->special.rawDevice = 0;
                        *recordSize = sizeof(HFSPlusCatalogFolder);
                } else {
                        *recordSize = sizeof(HFSPlusCatalogFolder);
                } else {
-                       bzero(crp, sizeof(HFSPlusCatalogFile));
                        crp->recordType = kHFSPlusFileRecord;
                        crp->recordType = kHFSPlusFileRecord;
+                       crp->hfsPlusFile.flags = kHFSThreadExistsMask;
+                       crp->hfsPlusFile.reserved1 = 0;
                        crp->hfsPlusFile.fileID = cnid;
                        crp->hfsPlusFile.createDate = createtime;
                        crp->hfsPlusFile.contentModDate = createtime;
                        crp->hfsPlusFile.accessDate = createtime;
                        crp->hfsPlusFile.attributeModDate = createtime;
                        crp->hfsPlusFile.fileID = cnid;
                        crp->hfsPlusFile.createDate = createtime;
                        crp->hfsPlusFile.contentModDate = createtime;
                        crp->hfsPlusFile.accessDate = createtime;
                        crp->hfsPlusFile.attributeModDate = createtime;
-                       crp->hfsPlusFile.flags |= kHFSThreadExistsMask;
+                       crp->hfsPlusFile.backupDate = 0;
                        crp->hfsPlusFile.textEncoding = encoding;
                        crp->hfsPlusFile.textEncoding = encoding;
+                       crp->hfsPlusFile.attrBlocks = 0;
                        bsdp = &crp->hfsPlusFile.bsdInfo;
                        bsdp = &crp->hfsPlusFile.bsdInfo;
+                       bsdp->special.rawDevice = 0;
                        switch(type) {
                        case S_IFBLK:
                        case S_IFCHR:
                                /* BLK/CHR need to save the device info */
                                bsdp->special.rawDevice = attrp->ca_rdev;
                        switch(type) {
                        case S_IFBLK:
                        case S_IFCHR:
                                /* BLK/CHR need to save the device info */
                                bsdp->special.rawDevice = attrp->ca_rdev;
+                               bzero(&crp->hfsPlusFile.userInfo, 32);
                                break;
                        case S_IFREG:
                                /* Hardlink links need to save the linkref */
                                break;
                        case S_IFREG:
                                /* Hardlink links need to save the linkref */
@@ -1850,6 +2735,7 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding
                                bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
                                break;
                        }
                                bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
                                break;
                        }
+                       bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
                        *recordSize = sizeof(HFSPlusCatalogFile);
                }
                bsdp->ownerID    = attrp->ca_uid;
                        *recordSize = sizeof(HFSPlusCatalogFile);
                }
                bsdp->ownerID    = attrp->ca_uid;
@@ -1870,12 +2756,17 @@ builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encodin
 {
        int result = 0;
        char * nameptr;
 {
        int result = 0;
        char * nameptr;
-       long bufsize;
+       size_t bufsize;
        size_t utf8len;
        size_t utf8len;
+       char tmpbuff[128];
 
        /* guess a size... */
        bufsize = (3 * key->nodeName.length) + 1;
 
        /* guess a size... */
        bufsize = (3 * key->nodeName.length) + 1;
-       MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
+       if (bufsize >= sizeof(tmpbuff) - 1) {
+           MALLOC(nameptr, char *, bufsize, M_TEMP, M_WAITOK);
+       } else {
+           nameptr = &tmpbuff[0];
+       }
 
        result = utf8_encodestr(key->nodeName.unicode,
                        key->nodeName.length * sizeof(UniChar),
 
        result = utf8_encodestr(key->nodeName.unicode,
                        key->nodeName.length * sizeof(UniChar),
@@ -1895,14 +2786,17 @@ builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encodin
                                        bufsize, ':', 0);
        }
        descp->cd_parentcnid = key->parentID;
                                        bufsize, ':', 0);
        }
        descp->cd_parentcnid = key->parentID;
-       descp->cd_nameptr = nameptr;
+       descp->cd_nameptr = vfs_addname(nameptr, utf8len, 0, 0);
        descp->cd_namelen = utf8len;
        descp->cd_cnid = cnid;
        descp->cd_hint = hint;
        descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
        if (isdir)
        descp->cd_namelen = utf8len;
        descp->cd_cnid = cnid;
        descp->cd_hint = hint;
        descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
        if (isdir)
-               descp->cd_flags |= CD_ISDIR;
+               descp->cd_flags |= CD_ISDIR;    
        descp->cd_encoding = encoding;
        descp->cd_encoding = encoding;
+       if (nameptr != &tmpbuff[0]) {
+           FREE(nameptr, M_TEMP);
+       }
        return result;
 }
 
        return result;
 }
 
@@ -1917,10 +2811,11 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
        int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
        const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
 
        int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
        const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
 
+       attrp->ca_recflags = crp->flags;
        attrp->ca_nlink = 1;
        attrp->ca_atime = to_bsd_time(crp->accessDate);
        attrp->ca_nlink = 1;
        attrp->ca_atime = to_bsd_time(crp->accessDate);
+       attrp->ca_atimeondisk = attrp->ca_atime;        
        attrp->ca_mtime = to_bsd_time(crp->contentModDate);
        attrp->ca_mtime = to_bsd_time(crp->contentModDate);
-       attrp->ca_mtime_nsec = 0;
        attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
        attrp->ca_itime = to_bsd_time(crp->createDate);
        attrp->ca_btime = to_bsd_time(crp->backupDate);
        attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
        attrp->ca_itime = to_bsd_time(crp->createDate);
        attrp->ca_btime = to_bsd_time(crp->backupDate);
@@ -1952,7 +2847,7 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
                        break;
                }
 
                        break;
                }
 
-               if (HFSTOVFS(hfsmp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
+               if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
                        /*
                         *  Override the permissions as determined by the mount auguments
                         *  in ALMOST the same way unset permissions are treated but keep
                        /*
                         *  Override the permissions as determined by the mount auguments
                         *  in ALMOST the same way unset permissions are treated but keep
@@ -1972,6 +2867,7 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
                }
                attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
                attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
                }
                attrp->ca_nlink = 2 + ((HFSPlusCatalogFolder *)crp)->valence;
                attrp->ca_entries = ((HFSPlusCatalogFolder *)crp)->valence;
+               attrp->ca_attrblks = ((HFSPlusCatalogFolder *)crp)->attrBlocks;
        } else {
                /* Keep IMMUTABLE bits in sync with HFS locked flag */
                if (crp->flags & kHFSFileLockedMask) {
        } else {
                /* Keep IMMUTABLE bits in sync with HFS locked flag */
                if (crp->flags & kHFSFileLockedMask) {
@@ -1985,6 +2881,10 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
                }
                /* get total blocks (both forks) */
                attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
                }
                /* get total blocks (both forks) */
                attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
+               attrp->ca_attrblks = crp->attrBlocks;
+               /* On HFS+ the ThreadExists flag must always be set. */
+               if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
+                       attrp->ca_recflags |= kHFSThreadExistsMask;
        }
        
        attrp->ca_fileid = crp->fileID;
        }
        
        attrp->ca_fileid = crp->fileID;
@@ -2040,6 +2940,8 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
        if (resource) {
                forkp->cf_size = filep->rsrcLogicalSize;
                forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
        if (resource) {
                forkp->cf_size = filep->rsrcLogicalSize;
                forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
+               forkp->cf_bytesread = 0;
+               forkp->cf_vblocks = 0;
                xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
                xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
                xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
                xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
                xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
                xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
@@ -2049,6 +2951,8 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
        } else {
                forkp->cf_size = filep->dataLogicalSize;
                forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
        } else {
                forkp->cf_size = filep->dataLogicalSize;
                forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
+               forkp->cf_bytesread = 0;
+               forkp->cf_vblocks = 0;
                xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
                xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
                xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
                xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
                xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
                xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
@@ -2099,7 +3003,7 @@ promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlus
        crp->attributeModDate = crp->contentModDate;
        crp->accessDate = crp->contentModDate;
        bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
        crp->attributeModDate = crp->contentModDate;
        crp->accessDate = crp->contentModDate;
        bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
-       crp->reserved2 = 0;
+       crp->attrBlocks = 0;
 }
 
 /*
 }
 
 /*
@@ -2203,7 +3107,11 @@ getcnid(const CatalogRecord *crp)
        case kHFSPlusFileRecord:
                cnid = crp->hfsPlusFile.fileID;
                break;
        case kHFSPlusFileRecord:
                cnid = crp->hfsPlusFile.fileID;
                break;
+       default:
+               printf("hfs: getcnid: unknown recordType (crp @ 0x%x)\n", crp);
+               break;
        }
        }
+
        return (cnid);
 }
 
        return (cnid);
 }
 
@@ -2225,7 +3133,11 @@ getparentcnid(const CatalogRecord *recp)
        case kHFSPlusFolderThreadRecord:
                cnid = recp->hfsPlusThread.parentID;
                break;
        case kHFSPlusFolderThreadRecord:
                cnid = recp->hfsPlusThread.parentID;
                break;
+       default:
+               panic("hfs: getparentcnid: unknown recordType (crp @ 0x%x)\n", recp);
+               break;
        }
        }
+
        return (cnid);
 }
 
        return (cnid);
 }
 
@@ -2239,4 +3151,3 @@ isadir(const CatalogRecord *crp)
                crp->recordType == kHFSPlusFolderRecord);
 }
 
                crp->recordType == kHFSPlusFolderRecord);
 }
 
-