]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_catalog.c
xnu-2422.1.72.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
index 59d20b4efe1e92ea40b23d352900c266ad489edd..c7aa7b38e325884238ccc6a1546d2e3c5ed3e159 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2013 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -90,10 +90,13 @@ u_char modetodirtype[16] = {
 #define MODE_TO_DT(mode)  (modetodirtype[((mode) & S_IFMT) >> 12])
 
 
-static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
+#define HFS_LOOKUP_SYSFILE     0x1     /* If set, allow lookup of system files */
+#define HFS_LOOKUP_HARDLINK    0x2     /* If set, allow lookup of hard link records and not resolve the hard links */
+#define HFS_LOOKUP_CASESENSITIVE       0x4     /* If set, verify results of a file/directory record match input case */
+static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
                   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,
+int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                   struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
 
 /* Internal catalog support routines */
@@ -101,7 +104,7 @@ static int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int
 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 resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino);
 
 static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
 
@@ -114,17 +117,19 @@ static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int3
 
 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,
+static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
                        int isdir, struct cat_desc *descp);
 
 static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
 
-static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_long *encoding);
+#if CONFIG_HFS_STD
+static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_int32_t *encoding);
 static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
 static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
+#endif
 
 static cnid_t getcnid(const CatalogRecord *crp);
-static u_long getencoding(const CatalogRecord *crp);
+static u_int32_t getencoding(const CatalogRecord *crp);
 static cnid_t getparentcnid(const CatalogRecord *recp);
 
 static int isadir(const CatalogRecord *crp);
@@ -133,8 +138,202 @@ static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
 
 static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
 
+static int cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
+       struct cat_fork *dataforkp, struct cat_fork *rsrcforkp);
+
+
+
+/* HFS ID Hashtable Functions */
+#define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash])
+
+/* Initialize the HFS ID hash table */
+void
+hfs_idhash_init (struct hfsmount *hfsmp) {
+       /* secured by catalog lock so no lock init needed */
+       hfsmp->hfs_idhashtbl = hashinit(HFS_IDHASH_DEFAULT, M_HFSMNT, &hfsmp->hfs_idhash);      
+}
+
+/* Free the HFS ID hash table */
+void
+hfs_idhash_destroy (struct hfsmount *hfsmp) {
+       /* during failed mounts & unmounts */
+       FREE(hfsmp->hfs_idhashtbl, M_HFSMNT);
+}
+
+/*
+from hfs_catalog.h:
+typedef struct cat_preflightid {
+       cnid_t fileid;
+       LIST_ENTRY(cat_preflightid) id_hash;
+} cat_preflightid_t;
+
+from hfs.h:
+  u_long hfs_idhash; / size of cnid/fileid hash table -1 /
+  LIST_HEAD(idhashhead, cat_preflightid) *hfs_idhashtbl; / base of ID hash /
+*/
+
+/* 
+ * Check the run-time ID hashtable.  
+ * 
+ * The catalog lock must be held (like other functions in this file).
+ *
+ * Returns: 
+ *             1 if the ID is in the hash table.
+ *             0 if the ID is not in the hash table
+ */ 
+int cat_check_idhash (struct hfsmount *hfsmp, cnid_t test_fileid) {
+
+       cat_preflightid_t *preflight;
+       int found = 0;
+
+       for (preflight = IDHASH(hfsmp, test_fileid)->lh_first; preflight ; preflight = preflight->id_hash.le_next) {
+               if (preflight->fileid == test_fileid) {
+                       found = 1;
+                       break;  
+               }
+       }
+
+       return found;           
+}
+
+/* Insert the supplied preflight into the ID hash table */ 
+int cat_insert_idhash (struct hfsmount *hfsmp, cat_preflightid_t *preflight) {
+
+       if (preflight) {
+               LIST_INSERT_HEAD(IDHASH(hfsmp, (preflight->fileid)), preflight, id_hash);
+               return 0;
+       }
+       return -1;
+}
+
+
+/* Remove the data structure with the specified ID from the hashtable */
+int cat_remove_idhash (cat_preflightid_t *preflight) {
+       
+       if ((preflight) && ((preflight->id_hash.le_next || preflight->id_hash.le_prev))) {
+               LIST_REMOVE (preflight, id_hash);
+               preflight->id_hash.le_next = NULL;
+               preflight->id_hash.le_prev = NULL;
+
+               return 0;
+       }
+
+       return -1;
+}
+
+/*
+ * Acquire a new CNID for use.  
+ * 
+ * This is slightly more complicated than just pulling the value from the
+ * hfsmount data structure.  We need to validate that the ID is not in-use
+ * even if we've not wrapped around and that there are not any lingering
+ * or orphaned fileIDs for this ID. 
+ *
+ * Also validate that there are not any pending insertions into the
+ * catalog by checking the ID hash table. 
+ */
+int 
+cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid) {
+
+       uint32_t nextCNID;
+       struct BTreeIterator *iterator;
+       FSBufferDescriptor btdata;
+       uint16_t datasize;
+       CatalogRecord *recp;
+       int result = 0;
+       int std_hfs;
+       int wrapped = 0;
+
+       std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
+       /*
+        * Get the next CNID. We can change it since we hold the catalog lock.
+        */
+nextid:
+       nextCNID = hfsmp->vcbNxtCNID;
+       if (nextCNID == 0xFFFFFFFF) {
+               if (std_hfs) {
+                       return (ENOSPC);
+               } else {
+                       wrapped++;
+                       if (wrapped > 1) {
+                               /* don't allow more than one wrap-around */
+                               return ENOSPC;
+                       }
+                       hfs_lock_mount (hfsmp);
+                       hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+                       hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
+                       hfs_unlock_mount (hfsmp);
+               }
+       } else {
+               hfsmp->vcbNxtCNID++;
+       }
+       MarkVCBDirty(hfsmp);
+
+       /* First check that there are not any entries pending in the hash table with this ID */
+       if (cat_check_idhash (hfsmp, nextCNID)) {
+               /* Someone wants to insert this into the catalog but hasn't done so yet. Skip it */
+               goto nextid;
+       }
+
+       /* Check to see if a thread record exists for the target ID we just got */
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
+       buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&iterator->key);
+
+       MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
+       BDINIT(btdata, recp);
+
+       result = BTSearchRecord(hfsmp->hfs_catalog_cp->c_datafork, iterator, &btdata, &datasize, iterator);
+       FREE (recp, M_TEMP);
+       FREE (iterator, M_TEMP);
+
+       if (result == btNotFound) {
+               /* Good.  File ID was not in use. Move on to checking EA B-Tree */
+               result = file_attribute_exist (hfsmp, nextCNID);
+               if (result == EEXIST) {
+                       /* This CNID has orphaned EAs.  Skip it and move on to the next one */
+                       result = 0;
+                       goto nextid;
+               }
+               if (result) {
+                       /* For any other error, return the result */
+                       return result;
+               }
+
+               /*
+                * Now validate that there are no lingering cnodes with this ID.  If a cnode
+                * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed,
+                * then it will still have an entry in the cnode hash table.  This means that
+                * a subsequent lookup will find THAT entry and believe this one has been deleted
+                * prematurely.  If there is a lingering cnode, then just skip this entry and move on.
+                * 
+                * Note that we pass (existence_only == 1) argument to hfs_chash_snoop.
+                */
+               if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+                       if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
+                               goto nextid;
+                       }
+               }
+
+               /* 
+                * If we get here, then we didn't see any thread records, orphaned EAs, 
+            * or stale cnodes. This ID is safe to vend out.
+                */
+               *new_cnid = nextCNID;
+       }
+       else if (result == noErr) {
+               /* move on to the next ID */
+               goto nextid;
+       }       
+       else {
+               /* For any other situation, just bail out */
+               return EIO; 
+       }
+
+       return 0;       
+
+}
 
-__private_extern__
 int
 cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
 {
@@ -152,7 +351,6 @@ cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unus
        return MacToVFSError(result);
 }
 
-__private_extern__
 void
 cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
 {
@@ -167,8 +365,7 @@ cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
                hfs_systemfile_unlock(hfsmp, lockflags);
 }
 
-__private_extern__
+__private_extern__ 
 void
 cat_convertattr(
        struct hfsmount *hfsmp,
@@ -179,23 +376,31 @@ cat_convertattr(
 {
        int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
 
-       if (std_hfs) {
+       if (std_hfs == 0) {
+               getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
+       }
+#if CONFIG_HFS_STD
+       else {
                struct HFSPlusCatalogFile cnoderec;
 
                promoteattr(hfsmp, recp, &cnoderec);
                getbsdattr(hfsmp, &cnoderec, attrp);
-       } else {
-               getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
-       }
+       } 
+#endif
 
-       if (isadir(recp))
+       if (isadir(recp)) {
                bzero(datafp, sizeof(*datafp));
+       }
+#if CONFIG_HFS_STD
        else if (std_hfs) {
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
                promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
-       } else {
+       } 
+#endif
+       else {
                /* Convert the data fork. */
                datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+               datafp->cf_new_size = 0;
                datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
                if ((hfsmp->hfc_stage == HFC_RECORDING) &&
                    (attrp->ca_atime >= hfsmp->hfc_timebase)) {
@@ -211,6 +416,7 @@ cat_convertattr(
 
                /* Convert the resource fork. */
                rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+               rsrcfp->cf_new_size = 0;
                rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
                if ((hfsmp->hfc_stage == HFC_RECORDING) &&
                    (attrp->ca_atime >= hfsmp->hfc_timebase)) {
@@ -241,22 +447,39 @@ cat_convertkey(
 {
        int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
        HFSPlusCatalogKey * pluskey = NULL;
-       u_long encoding;
-
-       if (std_hfs) {
-               MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
-               promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
+       u_int32_t encoding;
+       cnid_t cnid = 0;
+       int err = 0;
 
-       } else {
+       if (std_hfs == 0) {
                pluskey = (HFSPlusCatalogKey *)key;
                encoding = getencoding(recp);
        }
+#if CONFIG_HFS_STD
+       else {
+               MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
+               promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
+       }
+#endif
+  
+       /* Get the CNID before calling builddesc.  Need to error check it. */
+       cnid = getcnid(recp);
+       if (cnid == 0) {
+               /* If ths CNID == 0, it's invalid. Mark as corrupt */
+               hfs_mark_volume_inconsistent (hfsmp);
+               err = EINVAL;
+       }
+       else {
+               builddesc(pluskey, cnid, 0, encoding, isadir(recp), descp);
+       }
 
-       builddesc(pluskey, getcnid(recp), 0, encoding, isadir(recp), descp);
+#if CONFIG_HFS_STD
        if (std_hfs) {
                FREE(pluskey, M_TEMP);
        }
-       return (0);
+#endif
+
+       return err;
 }
 
 
@@ -286,26 +509,27 @@ cat_releasedesc(struct cat_desc *descp)
 
 /*
  * These Catalog functions allow access to the HFS Catalog (database).
- * The catalog b-tree lock must be aquired before calling any of these routines.
+ * The catalog b-tree lock must be acquired before calling any of these routines.
  */
 
 /*
- * cat_lookup - lookup a catalog node using a cnode decriptor
+ * cat_lookup - lookup a catalog node using a cnode descriptor
  *
  * Note: The caller is responsible for releasing the output
  * catalog descriptor (when supplied outdescp is non-null).
  */
-__private_extern__
 int
-cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
+cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, int force_casesensitive_lookup,
              struct cat_desc *outdescp, struct cat_attr *attrp,
              struct cat_fork *forkp, cnid_t *desc_cnid)
 {
        CatalogKey * keyp;
        int std_hfs;
        int result;
+       int flags;
 
        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
+       flags = force_casesensitive_lookup ? HFS_LOOKUP_CASESENSITIVE : 0;
 
        MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
 
@@ -313,7 +537,7 @@ cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
        if (result)
                goto exit;
 
-       result = cat_lookupbykey(hfsmp, keyp, 0, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
+       result = cat_lookupbykey(hfsmp, keyp, flags, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
        
        if (result == ENOENT) {
                if (!std_hfs) {
@@ -342,7 +566,6 @@ exit:
        return (result);
 }
 
-__private_extern__
 int
 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
@@ -407,7 +630,6 @@ exit:
  * catalog descriptor (when supplied outdescp is non-null).
 
  */
-__private_extern__
 int
 cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
 {
@@ -435,38 +657,45 @@ cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
 
        /* 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 CONFIG_HFS_STD
+               case kHFSFolderThreadRecord:
+                       isdir = 1;
+                       /* fall through */
+               case kHFSFileThreadRecord:
+                       keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
+                       keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
+                       break;
+#endif
+
+               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) {
+
+       if (std_hfs == 0) {
+               builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
+       }
+#if CONFIG_HFS_STD
+       else {
                HFSPlusCatalogKey * pluskey = NULL;
-               u_long encoding;
+               u_int32_t 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);
        }
+#endif 
+
 exit:
        FREE(recp, M_TEMP);
        FREE(iterator, M_TEMP);
@@ -480,9 +709,8 @@ exit:
  * Note: The caller is responsible for releasing the output
  * catalog descriptor (when supplied outdescp is non-null).
  */
-__private_extern__
 int
-cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
+cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, int wantrsrc,
     struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
 {
        struct BTreeIterator * iterator;
@@ -509,25 +737,44 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
 
        /* Turn thread record into a cnode key (in place) */
        switch (recp->recordType) {
-       case kHFSFileThreadRecord:
-       case kHFSFolderThreadRecord:
-               keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
-               keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
-               break;
 
-       case kHFSPlusFileThreadRecord:
-       case kHFSPlusFolderThreadRecord:
-               keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
-               keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
-                                         (keyp->hfsPlus.nodeName.length * 2);
-               break;
+#if CONFIG_HFS_STD
+               case kHFSFileThreadRecord:
+               case kHFSFolderThreadRecord:
+                       keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
 
-       default:
-               result = ENOENT;
-               goto exit;
+                       /* check for NULL name */
+                       if (keyp->hfs.nodeName[0] == 0) {
+                               result = ENOENT;
+                               goto exit;
+                       }
+
+                       keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
+                       break;
+#endif
+
+               case kHFSPlusFileThreadRecord:
+               case kHFSPlusFolderThreadRecord:
+                       keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+
+                       /* check for NULL name */
+                       if (keyp->hfsPlus.nodeName.length == 0) {
+                               result = ENOENT;
+                               goto exit;
+                       }
+
+                       keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
+                               (keyp->hfsPlus.nodeName.length * 2);
+                       break;
+
+               default:
+                       result = ENOENT;
+                       goto exit;
        }
 
-       result = cat_lookupbykey(hfsmp, keyp, allow_system_files, 0, 0, outdescp, attrp, forkp, NULL);
+       result = cat_lookupbykey(hfsmp, keyp, 
+                       ((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0), 
+                       0, wantrsrc, outdescp, attrp, forkp, NULL);
        /* No corresponding file/folder record found for a thread record,
         * mark the volume inconsistent.
         */
@@ -538,7 +785,7 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
                 * the key in the thread matches the key in the record.
                 */
                if (cnid != dcnid) {
-                       printf("Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
+                       printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
                        result = ENOENT;
                }
        }
@@ -553,13 +800,17 @@ exit:
 /*
  * cat_lookupmangled - lookup a catalog node using a mangled name
  */
-static int
+int
 cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                   struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
 {
        cnid_t fileID;
        u_int32_t prefixlen;
        int result;
+       u_int8_t utf8[NAME_MAX + 1];
+       u_int32_t utf8len;
+       u_int16_t unicode[kHFSPlusMaxFileNameChars + 1];
+       size_t unicodelen;
        
        if (wantrsrc)
                return (ENOENT);
@@ -575,16 +826,33 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                return (ENOENT);
        }
 
-       result = cat_idlookup(hfsmp, fileID, 0, outdescp, attrp, forkp);
+       result = cat_idlookup(hfsmp, fileID, 0, 0, outdescp, attrp, forkp);
        if (result)
                return (ENOENT);
        /* It must be in the correct directory */
        if (descp->cd_parentcnid != outdescp->cd_parentcnid)
                goto falsematch;
 
-       if (((u_int16_t)outdescp->cd_namelen < prefixlen) ||
-               bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
+       /*
+        * Compare the mangled version of file name looked up from the 
+        * disk with the mangled name provided by the user.  Note that 
+        * this comparison is case-sensitive, which should be fine
+        * since we're trying to prevent user space from constructing
+        * a mangled name that differs from the one they'd get from the
+        * file system.
+        */
+       result = utf8_decodestr(outdescp->cd_nameptr, outdescp->cd_namelen,
+                       unicode, &unicodelen, sizeof(unicode), ':', 0);
+       if (result) {
                goto falsematch;
+       }
+       result = ConvertUnicodeToUTF8Mangled(unicodelen, unicode, 
+                       sizeof(utf8), &utf8len, utf8, fileID);
+       if ((result != 0) || 
+           ((u_int16_t)descp->cd_namelen != utf8len) ||
+           (bcmp(descp->cd_nameptr, utf8, utf8len) != 0)) { 
+               goto falsematch;
+       }
 
        return (0);
 
@@ -598,7 +866,7 @@ falsematch:
  * cat_lookupbykey - lookup a catalog node using a cnode key
  */
 static int
-cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files, u_long hint, int wantrsrc,
+cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
                   struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
 {
        struct BTreeIterator * iterator;
@@ -607,9 +875,10 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
        u_int16_t  datasize;
        int result;
        int std_hfs;
-       u_long ilink = 0;
+       u_int32_t ilink = 0;
        cnid_t cnid = 0;
-       u_long encoding = 0;
+       u_int32_t encoding = 0;
+       cnid_t parentid = 0;
 
        std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
 
@@ -625,19 +894,39 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
        if (result) 
                goto exit;
 
-       /* Save the cnid and encoding now in case there's a hard link */
+       /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
        cnid = getcnid(recp);
+       if (cnid == 0) {
+               /* CNID of 0 is invalid.  Mark as corrupt */
+               hfs_mark_volume_inconsistent (hfsmp);
+               result = EINVAL;
+               goto exit;
+       }
+
+       if (std_hfs == 0) {
+               parentid = keyp->hfsPlus.parentID;
+       }
+       
        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)) &&
-                !allow_system_files) {
-
-               result = ENOENT;
+                !(flags & HFS_LOOKUP_SYSFILE)) {
+               result = ERESERVEDNAME;
                goto exit;
        }
+
+       if (!std_hfs && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
+               /* Make sure the case of the file was correct if requested */
+               if (flags & HFS_LOOKUP_CASESENSITIVE) {
+                       if (0 != cat_binarykeycompare(&keyp->hfsPlus, (HFSPlusCatalogKey *)&iterator->key)) {
+                               result = ERESERVEDNAME;
+                               goto exit;
+                       }
+               }
+       }
        
        /*
         * When a hardlink link is encountered, auto resolve it.
@@ -647,7 +936,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
        if (!std_hfs
            && (attrp || forkp) 
            && (recp->recordType == kHFSPlusFileRecord)
-           && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->vcbCrDate) ||
+           && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_itime) ||
                (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
                int isdirlink = 0;
                int isfilelink = 0;
@@ -660,32 +949,80 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
                           (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
                        isdirlink = 1;
                }
-               if (isfilelink || isdirlink) {
+               if ((isfilelink || isdirlink) && !(flags & HFS_LOOKUP_HARDLINK)) {
                        ilink = recp->hfsPlusFile.hl_linkReference;
                        (void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
                }
        }
 
-       if (attrp != NULL) {
-               if (std_hfs) {
+       if (attrp != NULL) { 
+               if (std_hfs == 0) {
+                       getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
+                       if (ilink) {
+                               /* Update the inode number for this hard link */
+                               attrp->ca_linkref = ilink;
+                       }
+                       
+                       /* 
+                        * Set kHFSHasLinkChainBit for hard links, and reset it for all 
+                        * other items.  Also set linkCount to 1 for regular files.
+                        *
+                        * Due to some bug (rdar://8505977), some regular files can have 
+                        * kHFSHasLinkChainBit set and linkCount more than 1 even if they 
+                        * are not really hard links.  The runtime code should not consider 
+                        * these files has hard links.  Therefore we reset the kHFSHasLinkChainBit 
+                        * and linkCount for regular file before we vend it out.  This might 
+                        * also result in repairing the bad files on disk, if the corresponding 
+                        * file is modified and updated on disk.  
+                        */
+                       if (ilink) {
+                               /* This is a hard link and the link count bit was not set */
+                               if (!(attrp->ca_recflags & kHFSHasLinkChainMask)) {
+                                       printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp->vcbVN, cnid, ilink);
+                                       attrp->ca_recflags |= kHFSHasLinkChainMask;
+                               }
+                       } else { 
+                               /* Make sure that this non-hard link (regular) record is not 
+                                * an inode record that was looked up and we do not end up 
+                                * reseting the hard link bit on it.
+                                */
+                               if ((parentid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) && 
+                                   (parentid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
+                                       /* This is not a hard link or inode and the link count bit was set */
+                                       if (attrp->ca_recflags & kHFSHasLinkChainMask) {
+                                               printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp->vcbVN, cnid);
+                                               attrp->ca_recflags &= ~kHFSHasLinkChainMask;
+                                       }
+                                       /* This is a regular file and the link count was more than 1 */
+                                       if (S_ISREG(attrp->ca_mode) && (attrp->ca_linkcount > 1)) {
+                                               printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp->vcbVN, cnid, attrp->ca_linkcount);
+                                               attrp->ca_linkcount = 1;
+                                       }
+                               }
+                       }
+               }
+#if CONFIG_HFS_STD
+               else {
                        struct HFSPlusCatalogFile cnoderec;
 
                        promoteattr(hfsmp, recp, &cnoderec);
-                       getbsdattr(hfsmp, &cnoderec, attrp);
-               } else {
-                       getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
-                       if (ilink)
-                               attrp->ca_linkref = ilink;
+                       getbsdattr(hfsmp, &cnoderec, attrp);    
                }
+#endif
        }
        if (forkp != NULL) {
                if (isadir(recp)) {
                        bzero(forkp, sizeof(*forkp));
-               } else if (std_hfs) {
+               } 
+#if CONFIG_HFS_STD
+               else if (std_hfs) {
                        promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
-               } else if (wantrsrc) {
+               } 
+#endif
+               else if (wantrsrc) {
                        /* Convert the resource fork. */
                        forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
+                       forkp->cf_new_size = 0;
                        forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
                        if ((hfsmp->hfc_stage == HFC_RECORDING) &&
                            (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
@@ -704,6 +1041,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
 
                        /* Convert the data fork. */
                        forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
+                       forkp->cf_new_size = 0;
                        forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
                        if ((hfsmp->hfc_stage == HFC_RECORDING) &&
                            (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
@@ -736,6 +1074,22 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
                        if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
                                off_t psize;
 
+                               /* 
+                                * This is technically a volume corruption. 
+                                * If the total number of blocks calculated by iterating + summing
+                                * the extents in the resident extent records, is less than that 
+                                * which is reported in the catalog entry, we should force a fsck.  
+                                * Only modifying ca_blocks here is not guaranteed to make it out 
+                                * to disk; it is a runtime-only field. 
+                                * 
+                                * Note that we could have gotten into this state if we had invalid ranges 
+                                * that existed in borrowed blocks that somehow made it out to disk. 
+                                * The cnode's on disk block count should never be greater 
+                                * than that which is in its extent records.
+                                */
+
+                               (void) hfs_mark_volume_inconsistent (hfsmp);
+
                                forkp->cf_blocks = validblks;
                                if (attrp != NULL) {
                                        attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
@@ -751,17 +1105,25 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
        if (descp != NULL) {
                HFSPlusCatalogKey * pluskey = NULL;
 
-               if (std_hfs) {
+               if (std_hfs == 0) {
+                       pluskey = (HFSPlusCatalogKey *)&iterator->key;
+               }
+#if CONFIG_HFS_STD
+               else {
                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                        promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
        
-               } else {
-                       pluskey = (HFSPlusCatalogKey *)&iterator->key;
                }
+#endif 
+       
                builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
+
+#if CONFIG_HFS_STD
                if (std_hfs) {
                        FREE(pluskey, M_TEMP);
                }
+#endif
+
        }
 
        if (desc_cnid != NULL) {
@@ -784,19 +1146,17 @@ exit:
  * The caller is responsible for releasing the output
  * catalog descriptor (when supplied outdescp is non-null).
  */
-__private_extern__
 int
-cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+cat_create(struct hfsmount *hfsmp, cnid_t new_fileid, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_desc *out_descp)
 {
        FCB * fcb;
        struct btobj * bto;
        FSBufferDescriptor btdata;
-       u_int32_t nextCNID;
        u_int32_t datalen;
        int std_hfs;
        int result = 0;
-       u_long encoding = kTextEncodingMacRoman;
+       u_int32_t encoding = kTextEncodingMacRoman;
        int modeformat;
 
        modeformat = attrp->ca_mode & S_IFMT;
@@ -804,24 +1164,8 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        fcb = hfsmp->hfs_catalog_cp->c_datafork;
        std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
 
-       /*
-        * Get the next CNID. We can change it since we hold the catalog lock.
-        */
-       nextCNID = hfsmp->vcbNxtCNID;
-       if (nextCNID == 0xFFFFFFFF) {
-               if (std_hfs) {
-                       return (ENOSPC);
-               } else {
-                       HFS_MOUNT_LOCK(hfsmp, TRUE)
-                       hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
-                       hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
-                       HFS_MOUNT_UNLOCK(hfsmp, TRUE);
-               }
-       } else {
-               hfsmp->vcbNxtCNID++;
-       }
-       MarkVCBDirty(hfsmp);
-
+       /* The caller is expected to reserve a CNID before calling this function! */
+       
        /* Get space for iterator, key and data */      
        MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
        bto->iterator.hint.nodeNum = 0;
@@ -846,50 +1190,22 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
                btdata.itemSize = datalen;
                btdata.itemCount = 1;
                
-               for (;;) {
-                       // this call requires the attribute file lock to be held
-                       result = file_attribute_exist(hfsmp, nextCNID);
-                       if (result == EEXIST) {
-                               // that cnid has orphaned attributes so just skip it.
-                               if (++nextCNID < kHFSFirstUserCatalogNodeID) {
-                                       nextCNID = kHFSFirstUserCatalogNodeID;
-                               }
-                               continue;
-                       }
-                       if (result) goto exit;
-                       
-                       buildthreadkey(nextCNID, std_hfs, (CatalogKey *) &bto->iterator.key);
-
-                       result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
-                       if ((result == btExists) && !std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
-                               /*
-                                * Allow CNIDs on HFS Plus volumes to wrap around
-                                */
-                               if (++nextCNID < kHFSFirstUserCatalogNodeID) {
-                                       nextCNID = kHFSFirstUserCatalogNodeID;
-                               }
-                               continue;
-                       }
-                       break;
-               }
-               if (result) goto exit;
-       }
-       
-       /*
-        * CNID is now established. If we have wrapped then
-        * update the vcbNxtCNID.
-        */
-       if ((hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
-               hfsmp->vcbNxtCNID = nextCNID + 1;
-               if (hfsmp->vcbNxtCNID < kHFSFirstUserCatalogNodeID) {
-                       hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
+               /* Caller asserts the following:
+                *      1) this CNID is not in use by any orphaned EAs 
+                *  2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
+                *  3) There are no thread or catalog records for this ID 
+                */              
+               buildthreadkey(new_fileid, std_hfs, (CatalogKey *) &bto->iterator.key);
+               result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+               if (result) {
+                       goto exit;
                }
        }
 
        /*
         * Now insert the file/directory record
         */
-       buildrecord(attrp, nextCNID, std_hfs, encoding, &bto->data, &datalen);
+       buildrecord(attrp, new_fileid, std_hfs, encoding, &bto->data, &datalen);
        btdata.bufferAddress = &bto->data;
        btdata.itemSize = datalen;
        btdata.itemCount = 1;
@@ -903,12 +1219,12 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
 
                /* Back out the thread record */
                if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
-                       buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&bto->iterator.key);
+                       buildthreadkey(new_fileid, std_hfs, (CatalogKey *)&bto->iterator.key);
                        if (BTDeleteRecord(fcb, &bto->iterator)) {
                                /* Error on deleting extra thread record, mark 
                                 * volume inconsistent 
                                 */
-                               printf ("hfs: cat_create() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
+                               printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
                                hfs_mark_volume_inconsistent(hfsmp);
                        }
                }
@@ -921,20 +1237,26 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        if (out_descp != NULL) {
                HFSPlusCatalogKey * pluskey = NULL;
 
-               if (std_hfs) {
+               if (std_hfs == 0) {
+                       pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
+               }
+#if CONFIG_HFS_STD
+               else {
                        MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
                        promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
-       
-               } else
-                       pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
+               } 
+#endif
 
-               builddesc(pluskey, nextCNID, bto->iterator.hint.nodeNum,
+               builddesc(pluskey, new_fileid, bto->iterator.hint.nodeNum,
                        encoding, S_ISDIR(attrp->ca_mode), out_descp);
+#if CONFIG_HFS_STD
                if (std_hfs) {
                        FREE(pluskey, M_TEMP);
                }
+#endif
+
        }
-       attrp->ca_fileid = nextCNID;
+       attrp->ca_fileid = new_fileid;
 
 exit:
        (void) BTFlushPath(fcb);
@@ -959,7 +1281,6 @@ exit:
  * Note: The caller is responsible for releasing the output
  * catalog descriptor (when supplied out_cdp is non-null).
  */
-__private_extern__
 int 
 cat_rename (
        struct hfsmount * hfsmp,
@@ -982,7 +1303,7 @@ cat_rename (
        int directory = from_cdp->cd_flags & CD_ISDIR;
        int is_dirlink = 0;
        int std_hfs;
-       u_long encoding = 0;
+       u_int32_t encoding = 0;
 
        vcb = HFSTOVCB(hfsmp);
        fcb = GetFileControlBlock(vcb->catalogRefNum);
@@ -1009,10 +1330,11 @@ cat_rename (
         * When moving a directory, make sure its a valid move.
         */
        if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
-               struct BTreeIterator iterator;
+               struct BTreeIterator *dir_iterator = NULL;
+
                cnid_t cnid = from_cdp->cd_cnid;
                cnid_t pathcnid = todir_cdp->cd_parentcnid;
-
+       
                /* First check the obvious ones */
                if (cnid == fsRtDirID  ||
                    cnid == to_cdp->cd_parentcnid  ||
@@ -1020,25 +1342,33 @@ cat_rename (
                        result = EINVAL;
                        goto exit;
                }
-               bzero(&iterator, sizeof(iterator));
+               /* now allocate the dir_iterator */
+               MALLOC (dir_iterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
+               if (dir_iterator == NULL) {
+                       return ENOMEM; 
+               }
+               bzero(dir_iterator, sizeof(*dir_iterator));
+                       
                /*
                 * Traverse destination path all the way back to the root
                 * making sure that source directory is not encountered.
                 *
                 */
                while (pathcnid > fsRtDirID) {
-                       buildthreadkey(pathcnid, std_hfs,
-                                       (CatalogKey *)&iterator.key);
-                       result = BTSearchRecord(fcb, &iterator, &btdata,
-                                       &datasize, NULL);
-                       if (result) goto exit;
-                       
+                       buildthreadkey(pathcnid, std_hfs, (CatalogKey *)&dir_iterator->key);
+                       result = BTSearchRecord(fcb, dir_iterator, &btdata, &datasize, NULL);
+                       if (result) {
+                               FREE(dir_iterator, M_TEMP);
+                               goto exit;
+                       }
                        pathcnid = getparentcnid(recp);
                        if (pathcnid == cnid || pathcnid == 0) {
                                result = EINVAL;
+                               FREE(dir_iterator, M_TEMP);
                                goto exit;
                        }
                }
+               FREE(dir_iterator, M_TEMP);
        }
 
        /*
@@ -1099,9 +1429,13 @@ cat_rename (
                        out_cdp->cd_encoding = encoding;
        }
 
+#if CONFIG_HFS_STD
        if (std_hfs && !directory &&
-           !(recp->hfsFile.flags & kHFSThreadExistsMask))
+           !(recp->hfsFile.flags & kHFSThreadExistsMask)) {
                skipthread = 1;
+       }
+#endif
+
 #if 0
        /*
         * If the keys are identical then there's nothing left to do!
@@ -1119,6 +1453,7 @@ cat_rename (
        result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
        if (result == btExists) {
                int fromtype = recp->recordType;
+               cnid_t cnid = 0;
 
                if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
                        goto exit; /* EEXIST */
@@ -1128,8 +1463,16 @@ cat_rename (
                if (result)
                        goto exit;
                
+               /* Get the CNID after calling searchrecord */
+               cnid  = getcnid (recp);
+               if (cnid == 0) {
+                       hfs_mark_volume_inconsistent(hfsmp);
+                       result = EINVAL;
+                       goto exit;
+               }
+
                if ((fromtype != recp->recordType) ||
-                   (from_cdp->cd_cnid != getcnid(recp))) {
+                   (from_cdp->cd_cnid != cnid)) {
                        result = EEXIST;
                        goto exit; /* EEXIST */
                }
@@ -1148,7 +1491,7 @@ cat_rename (
                        int err;
                        err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
                        if (err) {
-                               printf("cat_create: could not undo (BTInsert = %d)", err);
+                               printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err);
                                hfs_mark_volume_inconsistent(hfsmp);
                                result = err;
                                goto exit;
@@ -1175,7 +1518,7 @@ cat_rename (
                        int err;
                        err = BTDeleteRecord(fcb, to_iterator);
                        if (err) {
-                               printf("cat_create: could not undo (BTDelete = %d)", err);
+                               printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err);
                                hfs_mark_volume_inconsistent(hfsmp);
                                result = err;
                                goto exit;
@@ -1217,26 +1560,32 @@ cat_rename (
        if (out_cdp) {
                HFSPlusCatalogKey * pluskey = NULL;
 
-               if (std_hfs) {
+               if (std_hfs == 0) {     
+                       pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
+               }
+#if CONFIG_HFS_STD
+               else {
                        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;
+                               u_int32_t realhint;
 
                                realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
                                vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
                        }
-       
-               } else
-                       pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
+               }
+#endif
 
                builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
                        encoding, directory, out_cdp);
+#if CONFIG_HFS_STD
                if (std_hfs) {
                        FREE(pluskey, M_TEMP);
                }
+#endif
+
        }
 exit:
        (void) BTFlushPath(fcb);
@@ -1258,7 +1607,6 @@ exit:
  *     2. BTDeleteRecord(thread);
  *     3. BTUpdateRecord(parent);
  */
-__private_extern__
 int
 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
 {
@@ -1336,7 +1684,7 @@ cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
        if (BTDeleteRecord(fcb, iterator)) {
                if (!std_hfs) {
-                       printf ("hfs: cat_delete() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
+                       printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
                        hfs_mark_volume_inconsistent(hfsmp);
                }
        }
@@ -1349,22 +1697,21 @@ exit:
 
 
 /*
- * cnode_update - update the catalog node described by descp
- * using the data from attrp and forkp.
+ * cat_update_internal - update the catalog node described by descp
+ * using the data from attrp and forkp.  
+ * If update_hardlink is true, the hard link catalog record is updated
+ * and not the inode catalog record. 
  */
-__private_extern__
-int
-cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+static int
+cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
        struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
 {
        FCB * fcb;
        BTreeIterator * iterator;
        struct update_state state;
-       int std_hfs;
        int result;
 
        fcb = hfsmp->hfs_catalog_cp->c_datafork;
-       std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
 
        state.s_desc = descp;
        state.s_attr = attrp;
@@ -1379,13 +1726,14 @@ cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
         * For open-deleted files we need to do a lookup by cnid
         * (using thread rec).
         *
-        * For hard links, the target of the update is the inode
-        * itself (not the link record) so a lookup by fileid
-        * (i.e. thread rec) is needed.
+        * For hard links and if not requested by caller, the target 
+        * of the update is the inode itself (not the link record) 
+        * so a lookup by fileid (i.e. thread rec) is needed.
         */
-       if ((descp->cd_cnid != attrp->ca_fileid) ||
-           (descp->cd_namelen == 0) ||
-           (attrp->ca_recflags & kHFSHasLinkChainMask)) {
+       if ((update_hardlink == false) && 
+           ((descp->cd_cnid != attrp->ca_fileid) ||
+            (descp->cd_namelen == 0) ||
+            (attrp->ca_recflags & kHFSHasLinkChainMask))) {
                result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
        } else {
                result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
@@ -1410,6 +1758,17 @@ exit:
        return MacToVFSError(result);
 }
 
+/*
+ * cat_update - update the catalog node described by descp
+ * using the data from attrp and forkp. 
+ */
+int
+cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
+       struct cat_fork *dataforkp, struct cat_fork *rsrcforkp)
+{
+       return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
+}
+
 /*
  * catrec_update - Update the fields of a catalog record
  * This is called from within BTUpdateRecord.
@@ -1422,7 +1781,6 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
        struct cat_fork *forkp;
        struct hfsmount *hfsmp;
        long blksize;
-       int i;
 
        descp   = state->s_desc;
        attrp   = state->s_attr;
@@ -1430,6 +1788,8 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
        blksize = HFSTOVCB(hfsmp)->blockSize;
 
        switch (crp->recordType) {
+
+#if CONFIG_HFS_STD
        case kHFSFolderRecord: {
                HFSCatalogFolder *dir;
                
@@ -1448,7 +1808,8 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
        }
        case kHFSFileRecord: {
                HFSCatalogFile *file;
-               
+               int i;
+
                file = (struct HFSCatalogFile *)crp;
                /* Do a quick sanity check */
                if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
@@ -1489,13 +1850,15 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
                        file->flags &= ~kHFSFileLockedMask;
                break;
        }
+#endif
+       
        case kHFSPlusFolderRecord: {
                HFSPlusCatalogFolder *dir;
                
                dir = (struct HFSPlusCatalogFolder *)crp;
                /* Do a quick sanity check */
                if (dir->folderID != attrp->ca_fileid) {
-                       printf("catrec_update: id %d != %d\n", dir->folderID, attrp->ca_fileid);
+                       printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir->folderID, attrp->ca_fileid, hfsmp->vcbVN);
                        return (btNotFound);
                }
                dir->flags            = attrp->ca_recflags;
@@ -1556,6 +1919,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
        }
        case kHFSPlusFileRecord: {
                HFSPlusCatalogFile *file;
+               int is_dirlink; 
                
                file = (struct HFSPlusCatalogFile *)crp;
                /* Do a quick sanity check */
@@ -1598,13 +1962,22 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
                 * supplied values (which will be default), which has the
                 * same effect as creating a new file while
                 * MNT_UNKNOWNPERMISSIONS is set.
+                *
+                * Do not modify bsdInfo for directory hard link records.
+                * They are set during creation and are not modifiable, so just 
+                * leave them alone. 
                 */
-               if ((file->bsdInfo.fileMode != 0) ||
-                   (attrp->ca_flags != 0) ||
-                   (attrp->ca_uid != hfsmp->hfs_uid) ||
-                   (attrp->ca_gid != hfsmp->hfs_gid) ||
-                   ((attrp->ca_mode & ALLPERMS) !=
-                    (hfsmp->hfs_file_mask & ACCESSPERMS))) {
+               is_dirlink = (file->flags & kHFSHasLinkChainMask) &&     
+                            (SWAP_BE32(file->userInfo.fdType) == kHFSAliasType) && 
+                            (SWAP_BE32(file->userInfo.fdCreator) == kHFSAliasCreator);
+
+               if (!is_dirlink && 
+                   ((file->bsdInfo.fileMode != 0) ||
+                    (attrp->ca_flags != 0) ||
+                    (attrp->ca_uid != hfsmp->hfs_uid) ||
+                    (attrp->ca_gid != hfsmp->hfs_gid) ||
+                    ((attrp->ca_mode & ALLPERMS) !=
+                     (hfsmp->hfs_file_mask & ACCESSPERMS)))) {
                        if ((file->bsdInfo.fileMode == 0) ||
                            (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
                                file->bsdInfo.ownerID = attrp->ca_uid;
@@ -1638,7 +2011,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
                if ((file->resourceFork.extents[0].startBlock != 0) &&
                    (file->resourceFork.extents[0].startBlock ==
                     file->dataFork.extents[0].startBlock)) {
-                       panic("catrec_update: rsrc fork == data fork");
+                       panic("hfs: catrec_update: rsrc fork == data fork");
                }
 
                /* Synchronize the lock state */
@@ -1650,8 +2023,18 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
                /* Push out special field if necessary */
                if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) {
                        file->bsdInfo.special.rawDevice = attrp->ca_rdev;
-               } else if (descp->cd_cnid != attrp->ca_fileid || attrp->ca_linkcount == 2) {
-                       file->hl_linkCount = attrp->ca_linkcount;
+               } 
+               else {
+                       /* 
+                        * Protect against the degenerate case where the descriptor contains the
+                        * raw inode ID in its CNID field.  If the HFSPlusCatalogFile record indicates
+                        * the linkcount was greater than 1 (the default value), then it must have become
+                        * a hardlink.  In this case, update the linkcount from the cat_attr passed in.
+                        */
+                       if ((descp->cd_cnid != attrp->ca_fileid) || (attrp->ca_linkcount > 1 ) ||
+                               (file->hl_linkCount > 1)) {
+                               file->hl_linkCount = attrp->ca_linkcount;
+                       }
                }
                break;
        }
@@ -1682,7 +2065,7 @@ cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
                /* Update the bit in corresponding cnode, if any, in the hash.
                 * If the cnode has the bit already set, stop the traversal.
                 */
-               retval = hfs_chash_set_childlinkbit(hfsmp->hfs_raw_dev, cnid);
+               retval = hfs_chash_set_childlinkbit(hfsmp, cnid);
                if (retval == 0) {
                        break;
                }
@@ -1698,7 +2081,7 @@ cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
                lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
 
                /* Look up our catalog folder record */
-               retval = cat_idlookup(hfsmp, cnid, 0, &desc, &attr, NULL);
+               retval = cat_idlookup(hfsmp, cnid, 0, 0, &desc, &attr, NULL);
                if (retval) {
                        hfs_systemfile_unlock(hfsmp, lockflags);
                        hfs_end_transaction(hfsmp);
@@ -1758,12 +2141,12 @@ cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_c
                        break;
                }
                if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
-                       printf("cat_check_link_ancestry: getkey for %u failed\n", cnid);
+                       printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid, hfsmp->vcbVN);
                        invalid = 1;  /* On errors, assume an invalid parent */
                        break;
                }
                if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
-                       printf("cat_check_link_ancestry: cannot find %u\n", cnid);
+                       printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid, hfsmp->vcbVN);
                        invalid = 1;  /* On errors, assume an invalid parent */
                        break;
                }
@@ -1780,7 +2163,7 @@ cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_c
 
 
 /*
- * updatelink_callback - update a link's chain
+ * update_siblinglinks_callback - update a link's chain
  */
 
 struct linkupdate_state {
@@ -1790,12 +2173,12 @@ struct linkupdate_state {
 };
 
 static int
-updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
+update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
 {
        HFSPlusCatalogFile *file;
 
        if (crp->recordType != kHFSPlusFileRecord) {
-               printf("updatelink_callback: unexpected rec type %d\n", crp->recordType);
+               printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
                return (btNotFound);
        }
 
@@ -1808,17 +2191,16 @@ updatelink_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct l
                        file->hl_nextLinkID = state->nextlinkid;
                }
        } else {
-               printf("updatelink_callback: file %d isn't a chain\n", file->fileID);
+               printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
        }
        return (0);
 }
 
 /*
- * cat_updatelink - update a link's chain
+ * cat_update_siblinglinks - update a link's chain
  */
-__private_extern__
 int
-cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
+cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
 {
        FCB * fcb;
        BTreeIterator * iterator;
@@ -1830,24 +2212,25 @@ cat_updatelink(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cni
        state.prevlinkid = prevlinkid;
        state.nextlinkid = nextlinkid;
 
-       /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 
-       iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
-       iterator->hint.nodeNum = 0;
-
+       /* Create an iterator for use by us temporarily */
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
+       
        result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
        if (result == 0) {
-               result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)updatelink_callback, &state);
+               result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
                (void) BTFlushPath(fcb);
        } else {
-               printf("cat_updatelink: couldn't resolve cnid %d\n", linkfileid);
+               printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid, hfsmp->vcbVN);
        }
+       
+       FREE (iterator, M_TEMP);
        return MacToVFSError(result);
 }
 
 /*
  * cat_lookuplink - lookup a link by it's name
  */
-__private_extern__
 int
 cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid,  cnid_t *nextlinkid)
 {
@@ -1859,9 +2242,9 @@ cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfilei
 
        fcb = hfsmp->hfs_catalog_cp->c_datafork;
 
-       /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 
-       iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
-       iterator->hint.nodeNum = 0;
+       /* Create an iterator for use by us temporarily */
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
 
        if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
                goto exit;
@@ -1885,16 +2268,16 @@ cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfilei
                *nextlinkid = 0;
        }
 exit:
+       FREE(iterator, M_TEMP);
        return MacToVFSError(result);
 }
 
 
 /*
- * cat_lookuplink - lookup a link by its cnid
+ * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
  */
-__private_extern__
 int
-cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid,  cnid_t *nextlinkid)
+cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid,  cnid_t *nextlinkid)
 {
        FCB * fcb;
        BTreeIterator * iterator;
@@ -1904,18 +2287,16 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid
 
        fcb = hfsmp->hfs_catalog_cp->c_datafork;
 
-       /* Borrow the btcb iterator since we have an exclusive catalog lock. */ 
-       iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
-       iterator->hint.nodeNum = 0;
-
+       /* Create an iterator for use by us temporarily */
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
+       
        if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
-               printf("cat_lookuplinkbyid: getkey for %d failed %d\n", linkfileid, result);
                goto exit;
        }
        BDINIT(btdata, &file);
 
        if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
-               printf("cat_lookuplinkbyid: cannot find %d\n", linkfileid);
                goto exit;
        }
        /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
@@ -1924,7 +2305,7 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid
 
                parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
 
-               /* ADL inodes don't have a chain (its in an EA) */
+               /* directory inodes don't have a chain (its in an EA) */
                if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
                        result = ENOLINK;  /* signal to caller to get head of list */
                } else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
@@ -1939,6 +2320,110 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid
                *nextlinkid = 0;
        }
 exit:
+       FREE(iterator, M_TEMP);         
+       return MacToVFSError(result);
+}
+
+
+/*
+ * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
+ */
+int
+cat_lookup_lastlink(struct hfsmount *hfsmp, cnid_t linkfileid, 
+               cnid_t *lastlink, struct cat_desc *cdesc)
+{
+       FCB * fcb;
+       BTreeIterator * iterator;
+       struct FSBufferDescriptor btdata;
+       struct HFSPlusCatalogFile file;
+       int result;
+       int itercount = 0;
+       int foundlast = 0;
+       cnid_t currentlink = linkfileid;
+       
+       fcb = hfsmp->hfs_catalog_cp->c_datafork;
+       
+       /* Create an iterator for use by us temporarily */
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+
+       while ((foundlast == 0) && (itercount < HFS_LINK_MAX )) {
+               itercount++;
+               bzero(iterator, sizeof(*iterator));
+
+               if ((result = getkey(hfsmp, currentlink, (CatalogKey *)&iterator->key))) {
+                       goto exit;
+               }
+               BDINIT(btdata, &file);
+
+               if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
+                       goto exit;
+               }
+
+               /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
+               if (file.flags & kHFSHasLinkChainMask) {
+                       cnid_t parent;
+
+                       parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
+                       /* 
+                        * The raw inode for a directory hardlink doesn't have a chain.
+                        * Its link information lives in an EA. 
+                        */
+                       if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
+                               /* We don't iterate to find the oldest directory hardlink. */
+                               result = ENOLINK; 
+                               goto exit;
+                       } 
+                       else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
+                               /* Raw inode for file hardlink (the base inode) */
+                               currentlink = file.hl_firstLinkID;
+                       
+                               /* 
+                                * One minor special-casing here is necessary.
+                                * If our ID brought us to the raw hardlink inode, and it does
+                                * not have any siblings, then it's an open-unlinked file, and we
+                                * should not proceed any further.
+                                */ 
+                               if (currentlink == 0) {
+                                       result = ENOLINK;
+                                       goto exit;
+                               }
+                       } 
+                       else {
+                               /* Otherwise, this item's parent is a legitimate directory in the namespace */
+                               if (file.hl_nextLinkID == 0) {
+                                       /* If nextLinkID is 0, then we found the end; no more hardlinks */
+                                       foundlast = 1;
+                                       *lastlink = currentlink;
+                                       /* 
+                                        * Since we had to construct a catalog key to do this lookup
+                                        * we still hold it in-hand.  We might as well use it to build 
+                                        * the descriptor that the caller asked for.
+                                        */
+                                       builddesc ((HFSPlusCatalogKey*)&iterator->key, currentlink, 0, 0, 0, cdesc);
+                                       break;
+                               }
+
+                               currentlink = file.hl_nextLinkID;
+                       }
+               } 
+               else {
+                       /* Sorry, can't help you without a link chain */
+                       result = ENOLINK;
+                       goto exit;
+               }
+       }
+exit:
+       /* If we didn't find what we were looking for, zero out the args */
+       if (foundlast == 0) {
+               if (cdesc) {
+                       bzero (cdesc, sizeof(struct cat_desc));
+               }
+               if (lastlink) {
+                       *lastlink = 0;
+               }
+       }
+
+       FREE(iterator, M_TEMP);         
        return MacToVFSError(result);
 }
 
@@ -1954,7 +2439,6 @@ exit:
  *      ca_flags
  *      ca_finderinfo (type and creator)
  */
-__private_extern__
 int
 cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
                cnid_t nextlinkid, cnid_t *linkfileid)
@@ -1965,10 +2449,13 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *
        HFSPlusForkData *rsrcforkp;
        u_int32_t nextCNID;
        u_int32_t datalen;
-       u_long encoding;
+       u_int32_t encoding;
        int thread_inserted = 0;
        int alias_allocated = 0;
        int result = 0;
+       int std_hfs;
+
+       std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
 
        fcb = hfsmp->hfs_catalog_cp->c_datafork;
 
@@ -1977,10 +2464,10 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *
         */
        nextCNID = hfsmp->vcbNxtCNID;
        if (nextCNID == 0xFFFFFFFF) {
-               HFS_MOUNT_LOCK(hfsmp, TRUE)
+               hfs_lock_mount (hfsmp);
                hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
                hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
-               HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+               hfs_unlock_mount(hfsmp);
        } else {
                hfsmp->vcbNxtCNID++;
        }
@@ -1993,7 +2480,7 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *
 
        result = buildkey(hfsmp, descp, &bto->key, 0);
        if (result) {
-               printf("cat_createlink: err %d from buildkey\n", result);
+               printf("hfs: cat_createlink: err %d from buildkey\n", result);
                goto exit;
        }
 
@@ -2008,8 +2495,24 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *
        
        for (;;) {
                buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
+       
+               /*
+                * If the CNID wraparound bit is set, then we need to validate if there
+                * is a cnode in the hash already with this ID (even if it no longer exists
+                * on disk).  If so, then just skip this ID and move on to the next one. 
+                */
+               if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
+                       /* Verify that the CNID does not already exist in the cnode hash... */
+                       if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
+                               /* It was found in the cnode hash!*/
+                               result = btExists;
+                       }       
+               }
+
+               if (result == 0) {
+                       result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
+               }
 
-               result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
                if ((result == btExists) && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
                        /*
                         * Allow CNIDs on HFS Plus volumes to wrap around
@@ -2072,7 +2575,7 @@ cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *
 exit:  
        if (result) {
                if (thread_inserted) {
-                       printf("cat_createlink: err %d from BTInsertRecord\n", MacToVFSError(result));
+                       printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result), hfsmp->vcbVN);
 
                        buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
                        if (BTDeleteRecord(fcb, &bto->iterator)) {
@@ -2082,7 +2585,7 @@ exit:
                }
                if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
                        (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
-                                              rsrcforkp->extents[0].blockCount);
+                                              rsrcforkp->extents[0].blockCount, 0);
                        rsrcforkp->extents[0].startBlock = 0;
                        rsrcforkp->extents[0].blockCount = 0;
                }
@@ -2177,13 +2680,21 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog
 
        blksize = hfsmp->blockSize;
        blkcount = howmany(kHFSAliasSize, blksize);
-       sectorsize = hfsmp->hfs_phys_block_size;
+       sectorsize = hfsmp->hfs_logical_block_size;
        bzero(rsrcforkp, sizeof(HFSPlusForkData));
 
        /* Allocate some disk space for the alias content. */
-       result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 1, 1,
+       result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 
+                              HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE, 
+                              &rsrcforkp->extents[0].startBlock,
+                              &rsrcforkp->extents[0].blockCount);
+       /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
+       if (result == dskFulErr ) {     
+               result = BlockAllocate(hfsmp, 0, blkcount, blkcount, 
+                              HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE | HFS_ALLOC_FLUSHTXN, 
                               &rsrcforkp->extents[0].startBlock,
                               &rsrcforkp->extents[0].blockCount);
+       }
        if (result) {
                rsrcforkp->extents[0].startBlock = 0;
                goto exit;
@@ -2193,7 +2704,7 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog
        blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
        blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
 
-       bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_phys_block_size), 0, 0, BLK_META);
+       bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
        if (hfsmp->jnl) {
                journal_modify_block_start(hfsmp->jnl, bp);
        }
@@ -2236,7 +2747,7 @@ cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalog
 
 exit:
        if (result && rsrcforkp->extents[0].startBlock != 0) {
-               (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount);
+               (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
                rsrcforkp->extents[0].startBlock = 0;
                rsrcforkp->extents[0].blockCount = 0;
                rsrcforkp->logicalSize = 0;
@@ -2248,7 +2759,6 @@ exit:
 /*
  * cat_deletelink - delete a link from the catalog
  */
-__private_extern__
 int
 cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
@@ -2300,7 +2810,7 @@ cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
 
                        (void) BlockDeallocate(hfsmp, 
                                file.resourceFork.extents[i].startBlock, 
-                               file.resourceFork.extents[i].blockCount);
+                               file.resourceFork.extents[i].blockCount, 0);
 
                        totalBlocks -= file.resourceFork.extents[i].blockCount;
                        file.resourceFork.extents[i].startBlock = 0;
@@ -2341,8 +2851,10 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
        switch(rec->recordType) {
        case kHFSPlusFolderRecord:
        case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
        case kHFSFolderRecord:
        case kHFSFileRecord:
+#endif
                if (parentcnid != state->dir_cnid) {
                        state->error = ENOENT;
                        return (0);     /* stop */
@@ -2372,11 +2884,34 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
        }
 
        cep = &list->entry[list->realentries++];
+       if (state->stdhfs == 0) {
+               getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
+               builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
+                               isadir(rec), &cep->ce_desc);
+
+               if (rec->recordType == kHFSPlusFileRecord) {
+                       cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
+                       cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
+                       cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
+                       cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
 
-       if (state->stdhfs) {
+                       /* Save link reference for later processing. */
+                       if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
+                                       (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
+                               cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
+                       } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) && 
+                                       (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && 
+                                       (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
+                               cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
+                       }
+               }
+       }
+#if CONFIG_HFS_STD
+       else {
                struct HFSPlusCatalogFile cnoderec;
                HFSPlusCatalogKey * pluskey;
-               u_long encoding;
+               u_int32_t encoding;
 
                promoteattr(hfsmp, rec, &cnoderec);
                getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
@@ -2394,28 +2929,8 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
                        cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
                        cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
                }
-       } else {
-               getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
-               builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
-                       isadir(rec), &cep->ce_desc);
-               
-               if (rec->recordType == kHFSPlusFileRecord) {
-                       cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
-                       cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
-                       cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
-                       cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
-                       
-                       /* Save link reference for later processing. */
-                       if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
-                           (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
-                               cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
-                       } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) && 
-                                  (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) && 
-                                  (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
-                               cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
-                       }
-               }
        }
+#endif
 
        return (list->realentries < list->maxentries);
 }
@@ -2425,7 +2940,6 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
  *
  * Note: index is zero relative
  */
-__private_extern__
 int
 cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list)
 {
@@ -2577,14 +3091,14 @@ exit:
 
 /* Hard link information collected during cat_getdirentries. */
 struct linkinfo {
-       u_long       link_ref;
+       u_int32_t       link_ref;
        user_addr_t  dirent_addr;
 };
 typedef struct linkinfo linkinfo_t;
 
 /* State information for the getdirentries_callback function. */
 struct packdirentry_state {
-       int            cbs_extended;
+       int            cbs_flags;               /* VNODE_READDIR_* flags */
        u_int32_t      cbs_parentID;
        u_int32_t      cbs_index;
        uio_t          cbs_uio;
@@ -2626,6 +3140,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
        int hide = 0;
        u_int8_t type = DT_UNKNOWN;
        u_int8_t is_mangled = 0;
+       u_int8_t is_link = 0;
        u_int8_t *nameptr;
        user_addr_t uiobase = USER_ADDR_NULL;
        size_t namelen = 0;
@@ -2639,32 +3154,65 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
 
        /* We're done when parent directory changes */
        if (state->cbs_parentID != curID) {
-               if (state->cbs_extended) {
+               /*
+                * If the parent ID is different from curID this means we've hit
+                * the EOF for the directory.  To help future callers, we mark
+                * the cbs_eof boolean.  However, we should only mark the EOF 
+                * boolean if we're about to return from this function. 
+                *
+                * This is because this callback function does its own uiomove
+                * to get the data to userspace.  If we set the boolean before determining
+                * whether or not the current entry has enough room to write its
+                * data to userland, we could fool the callers of this catalog function
+                * into thinking they've hit EOF earlier than they really would have.
+                * In that case, we'd know that we have more entries to process and
+                * send to userland, but we didn't have enough room.  
+                * 
+                * To be safe, we mark cbs_eof here ONLY for the cases where we know we're 
+                * about to return and won't write any new data back
+                * to userland.  In the stop_after_pack case, we'll set this boolean
+                * regardless, so it's slightly safer to let that logic mark the boolean,
+                * especially since it's closer to the return of this function.
+                */              
+                       
+               if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
                        /* The last record has not been returned yet, so we 
                         * want to stop after packing the last item 
                         */
                        if (state->cbs_hasprevdirentry) { 
                                stop_after_pack = true;
                        } else {
+                               state->cbs_eof = true;
                                state->cbs_result = ENOENT;
                                return (0);     /* stop */
                        }                               
                } else {
+                       state->cbs_eof = true;
                        state->cbs_result = ENOENT;
                        return (0);     /* stop */
                }
        }
 
-       if (state->cbs_extended) {
+       if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
                entry = state->cbs_direntry;
                nameptr = (u_int8_t *)&entry->d_name[0];
-               maxnamelen = NAME_MAX;
+               if (state->cbs_flags & VNODE_READDIR_NAMEMAX) {
+                       /*
+                        * The NFS server sometimes needs to make filenames fit in
+                        * NAME_MAX bytes (since its client may not be able to
+                        * handle a longer name).  In that case, NFS will ask us
+                        * to mangle the name to keep it short enough.
+                        */
+                       maxnamelen = NAME_MAX + 1;
+               } else {
+                       maxnamelen = sizeof(entry->d_name);
+               }
        } else {
                nameptr = (u_int8_t *)&catent.d_name[0];
-               maxnamelen = NAME_MAX;
+               maxnamelen = sizeof(catent.d_name);
        }
 
-       if (state->cbs_extended && stop_after_pack) {
+       if ((state->cbs_flags & VNODE_READDIR_EXTENDED) && stop_after_pack) {
                /* The last item returns a non-zero invalid cookie */
                cnid = INT_MAX;         
        } else {
@@ -2697,6 +3245,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
                                } else {
                                        ilinkref = crp->hfsPlusFile.hl_linkReference;
                                }
+                               is_link =1;
                        } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
                                (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
                                (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
@@ -2707,6 +3256,7 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
                                type = DT_DIR;
                                /* A directory's link ref is always inode's file id. */
                                cnid = crp->hfsPlusFile.hl_linkReference;
+                               is_link = 1;
                        }
                        /* Hide the journal files */
                        if ((curID == kHFSRootFolderID) &&
@@ -2747,19 +3297,32 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
                } else {
 encodestr:
                        result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
-                                               nameptr, &namelen, maxnamelen + 1, ':', 0);
+                                               nameptr, &namelen, maxnamelen, ':', 0);
                }
 
                /* Check result returned from encoding the filename to utf8 */
                if (result == ENAMETOOLONG) {
+                       /* 
+                        * If we were looking at a catalog record for a hardlink (not the inode),
+                        * then we want to use its link ID as opposed to the inode ID for 
+                        * a mangled name.  For all other cases, they are the same.  Note that
+                        * due to the way directory hardlinks are implemented, the actual link
+                        * is going to be counted as a file record, so we can catch both
+                        * with is_link.
+                        */
+                       cnid_t linkid = cnid;
+                       if (is_link) {
+                               linkid =  crp->hfsPlusFile.fileID;
+                       }
+
                        result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
-                                                            cnp->ustr.unicode, maxnamelen + 1,
-                                                            (ByteCount*)&namelen, nameptr, cnid);              
+                                                            cnp->ustr.unicode, maxnamelen,
+                                                            (ByteCount*)&namelen, nameptr, linkid);            
                        is_mangled = 1;
                }
        }
 
-       if (state->cbs_extended) {
+       if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
                /*
                 * The index is 1 relative and includes "." and ".."
                 *
@@ -2786,12 +3349,12 @@ encodestr:
                uiobase = uio_curriovbase(state->cbs_uio);
        }
        /* If this entry won't fit then we're done */
-       if ((uiosize > uio_resid(state->cbs_uio)) ||
+       if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
            (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
                return (0);     /* stop */
        }
 
-       if (!state->cbs_extended || state->cbs_hasprevdirentry) {
+       if (!(state->cbs_flags & VNODE_READDIR_EXTENDED) || state->cbs_hasprevdirentry) {
                state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
                if (state->cbs_result == 0) {
                        ++state->cbs_index;
@@ -2855,7 +3418,7 @@ encodestr:
        }
 
        /* Fill the direntry to be used the next time */
-       if (state->cbs_extended) {      
+       if (state->cbs_flags & VNODE_READDIR_EXTENDED) {        
                if (stop_after_pack) {
                        state->cbs_eof = true;
                        return (0);     /* stop */
@@ -2883,6 +3446,7 @@ encodestr:
                uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
 }
 
+#if CONFIG_HFS_STD
 /*
  * getdirentries callback for standard HFS (non HFS+) directories.
  */
@@ -2914,7 +3478,7 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
        }
 
        nameptr = (u_int8_t *)&catent.d_name[0];
-       maxnamelen = NAME_MAX;
+       maxnamelen = sizeof(catent.d_name);
 
        switch(crp->recordType) {
        case kHFSFolderRecord:
@@ -2930,13 +3494,13 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
        };
 
        cnp = (const CatalogName*) ckp->hfs.nodeName;
-       result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen + 1, (ByteCount *)&namelen, nameptr);
+       result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen, (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);
+               result = mac_roman_to_utf8(cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr);
        }
        catent.d_type = type;
        catent.d_namlen = namelen;
@@ -2945,7 +3509,7 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
        uioaddr = (caddr_t) &catent;
 
        /* If this entry won't fit then we're done */
-       if (uiosize > uio_resid(state->cbs_uio)) {
+       if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
                return (0);     /* stop */
        }
 
@@ -2970,14 +3534,14 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
        /* Continue iteration if there's room */
        return (state->cbs_result == 0  && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
 }
+#endif
 
 /*
  * Pack a uio buffer with directory entries from the catalog
  */
-__private_extern__
 int
-cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint,
-                                 uio_t uio, int extended, int * items, int * eofflag)
+cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint,
+                                 uio_t uio, int flags, int * items, int * eofflag)
 {
        FCB* fcb;
        BTreeIterator * iterator;
@@ -2989,7 +3553,10 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        int result;
        int index;
        int have_key;
-
+       int extended;
+       
+       extended = flags & VNODE_READDIR_EXTENDED;
+       
        if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
                return (ENOTSUP);
        }
@@ -2998,7 +3565,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        /*
         * Get a buffer for link info array, btree iterator and a direntry:
         */
-       maxlinks = MIN(entrycnt, uio_resid(uio) / SMALL_DIRENTRY_SIZE);
+       maxlinks = MIN(entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE));
        bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
        if (extended) {
                bufsize += 2*sizeof(struct direntry);
@@ -3006,13 +3573,19 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
        bzero(buffer, bufsize);
 
-       state.cbs_extended = extended;
+       state.cbs_flags = flags;
        state.cbs_hasprevdirentry = false;
        state.cbs_previlinkref = 0;
        state.cbs_nlinks = 0;
        state.cbs_maxlinks = maxlinks;
        state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
-
+       /*
+        * We need to set cbs_eof to false regardless of whether or not the
+        * control flow is actually in the extended case, since we use this
+        * field to track whether or not we've returned EOF from the iterator function.
+        */
+       state.cbs_eof = false;
+       
        iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
        key = (CatalogKey *)&iterator->key;
        have_key = 0;
@@ -3020,7 +3593,6 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        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
@@ -3113,10 +3685,8 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        /*
         * Process as many entries as possible starting at iterator->key.
         */
-       if (hfsmp->hfs_flags & HFS_STANDARD)
-               result = BTIterateRecords(fcb, op, iterator,
-                                 (IterateCallBackProcPtr)getdirentries_std_callback, &state);
-       else {
+       if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
+               /* HFS+ */
                result = BTIterateRecords(fcb, op, iterator,
                                  (IterateCallBackProcPtr)getdirentries_callback, &state);
        
@@ -3127,7 +3697,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
                 * dummy values to copy the last directory entry stored in 
                 * packdirentry_state 
                 */
-               if (state.cbs_extended && (result == fsBTRecordNotFoundErr)) {
+               if (extended && (result == fsBTRecordNotFoundErr)) {
                        CatalogKey ckp;
                        CatalogRecord crp;
 
@@ -3137,12 +3707,26 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
                        result = getdirentries_callback(&ckp, &crp, &state);
                }
        }
+#if CONFIG_HFS_STD
+       else {
+               /* HFS (standard) */
+               result = BTIterateRecords(fcb, op, iterator,
+                                 (IterateCallBackProcPtr)getdirentries_std_callback, &state);
+       }
+#endif
 
        /* Note that state.cbs_index is still valid on errors */
        *items = state.cbs_index - index;
        index = state.cbs_index;
 
+       /*
+        * Also note that cbs_eof is set in all cases if we ever hit EOF
+        * during the enumeration by the catalog callback.  Mark the directory's hint
+        * descriptor as having hit EOF.
+        */
+
        if (state.cbs_eof) {
+               dirhint->dh_desc.cd_flags |= CD_EOF;
                *eofflag = 1;
        }
        
@@ -3164,7 +3748,7 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
         * Post process any hard links to get the real file id.
         */
        if (state.cbs_nlinks > 0) {
-               u_int32_t fileid = 0;
+               ino_t fileid = 0;
                user_addr_t address;
                int i;
 
@@ -3217,12 +3801,16 @@ static int
 cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
                  struct position_state *state)
 {
-       cnid_t curID;
+       cnid_t curID = 0;
 
-       if (state->hfsmp->hfs_flags & HFS_STANDARD)
-               curID = ckp->hfs.parentID;
-       else
+       if ((state->hfsmp->hfs_flags & HFS_STANDARD) == 0) {
                curID = ckp->hfsPlus.parentID;
+       }
+#if CONFIG_HFS_STD
+       else {
+               curID = ckp->hfs.parentID;
+       }
+#endif
 
        /* Make sure parent directory didn't change */
        if (state->parentID != curID) {
@@ -3234,12 +3822,14 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
        switch(crp->recordType) {
        case kHFSPlusFolderRecord:
        case kHFSPlusFileRecord:
+#if CONFIG_HFS_STD
        case kHFSFolderRecord:
        case kHFSFileRecord:
+#endif
                ++state->count;
                break;
        default:
-               printf("cat_findposition: invalid record type %d in dir %d\n",
+               printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
                        crp->recordType, curID);
                state->error = EINVAL;
                return (0);  /* stop */
@@ -3255,7 +3845,6 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
  * 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)
 {
@@ -3275,38 +3864,15 @@ cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
                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;
-                       }
-               }
+
+               result = UnicodeBinaryCompare (str1, length1, str2, length2);
        }
 
        return result;
 }
 
 
+#if CONFIG_HFS_STD
 /*
  * Compare two standard HFS catalog keys
  *
@@ -3332,6 +3898,7 @@ CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
 
        return result;
 }
+#endif
 
 
 /*
@@ -3377,9 +3944,14 @@ static int
 buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
        HFSPlusCatalogKey *key, int retry)
 {
+       int std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
        int utf8_flags = UTF_ESCAPE_ILLEGAL;
        int result = 0;
        size_t unicodeBytes = 0;
+       
+       if (std_hfs == 0) {
+               retry = 0;
+       }
 
        if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
                return (EINVAL);  /* invalid name */
@@ -3403,12 +3975,13 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
                return (result);
        }
 
+#if CONFIG_HFS_STD
        /*
         * For HFS volumes convert to an HFS compatible key
         *
         * XXX need to save the encoding that succeeded
         */
-       if (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord) {
+       if (std_hfs) {
                HFSCatalogKey hfskey;
 
                bzero(&hfskey, sizeof(hfskey));
@@ -3416,16 +3989,22 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
                hfskey.parentID = key->parentID;
                hfskey.nodeName[0] = 0;
                if (key->nodeName.length > 0) {
-                       if (unicode_to_hfs(HFSTOVCB(hfsmp),
+                       int res;
+                       if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
                                key->nodeName.length * 2,
                                key->nodeName.unicode,
-                               &hfskey.nodeName[0], retry) != 0) {
-                               return (EINVAL);
+                               &hfskey.nodeName[0], retry)) != 0) {
+                               if (res != ENAMETOOLONG)
+                                       res = EINVAL;
+
+                               return res;
                        }
                        hfskey.keyLength += hfskey.nodeName[0];
                }
                bcopy(&hfskey, key, sizeof(hfskey));
        }
+#endif
+
        return (0);
  }
 
@@ -3433,9 +4012,8 @@ buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
 /*
  * Resolve hard link reference to obtain the inode record.
  */
-__private_extern__
 int
-cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
+cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
 {
        FSBufferDescriptor btdata;
        struct BTreeIterator *iterator;
@@ -3475,7 +4053,7 @@ cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HF
                if (recp->hl_linkCount == 0)
                        recp->hl_linkCount = 2;
        } else {
-               printf("HFS resolvelink: can't find %s\n", inodename);
+               printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN);
        }
 
        FREE(iterator, M_TEMP);
@@ -3487,7 +4065,7 @@ cat_resolvelink(struct hfsmount *hfsmp, u_long linkref, int isdirlink, struct HF
  * Resolve hard link reference to obtain the inode number.
  */
 static int
-resolvelinkid(struct hfsmount *hfsmp, u_long linkref, ino_t *ino)
+resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
 {
        struct HFSPlusCatalogFile record;
        int error;
@@ -3538,12 +4116,15 @@ getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
 
        /* Turn thread record into a cnode key (in place) */
        switch (recp->recordType) {
+
+#if CONFIG_HFS_STD
        case kHFSFileThreadRecord:
        case kHFSFolderThreadRecord:
                keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
                keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
                bcopy(keyp, key, keyp->hfs.keyLength + 1);
                break;
+#endif
 
        case kHFSPlusFileThreadRecord:
        case kHFSPlusFolderThreadRecord:
@@ -3573,7 +4154,6 @@ exit:
  * The key's parent id is the only part of the key expected to be used by the caller.
  * The name portion of the key may not always be valid (ie in the case of a hard link).
  */
-__private_extern__
 int
 cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
 {
@@ -3600,7 +4180,7 @@ cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct
                 * Pick up the first link in the chain and get a descriptor for it.
                 * This allows blind bulk access checks to work for hardlinks.
                 */
-               if ((cat_lookuplinkbyid(hfsmp, cnid, &prevlinkid,  &nextlinkid) == 0) &&
+               if ((cat_lookup_siblinglinks(hfsmp, cnid, &prevlinkid,  &nextlinkid) == 0) &&
                    (nextlinkid != 0)) {
                        if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
                                key->hfsPlus.parentID = linkdesc.cd_parentcnid;
@@ -3622,27 +4202,7 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding
        int type = attrp->ca_mode & S_IFMT;
        u_int32_t createtime = to_hfs_time(attrp->ca_itime);
 
-       if (std_hfs) {
-               createtime = UTCToLocal(createtime);
-               if (type == S_IFDIR) {
-                       bzero(crp, sizeof(HFSCatalogFolder));
-                       crp->recordType = kHFSFolderRecord;
-                       crp->hfsFolder.folderID = cnid;
-                       crp->hfsFolder.createDate = createtime;
-                       crp->hfsFolder.modifyDate = createtime;
-                       bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
-                       *recordSize = sizeof(HFSCatalogFolder);
-               } else {
-                       bzero(crp, sizeof(HFSCatalogFile));
-                       crp->recordType = kHFSFileRecord;
-                       crp->hfsFile.fileID = cnid;
-                       crp->hfsFile.createDate = createtime;
-                       crp->hfsFile.modifyDate = createtime;
-                       bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
-                       bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
-                       *recordSize = sizeof(HFSCatalogFile);
-               }
-       } else {
+       if (std_hfs == 0) {
                struct HFSPlusBSDInfo * bsdp = NULL;
 
                if (type == S_IFDIR) {
@@ -3690,6 +4250,30 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding
                bsdp->adminFlags = attrp->ca_flags >> 16;
                bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
        }
+#if CONFIG_HFS_STD
+       else {
+               createtime = UTCToLocal(createtime);
+               if (type == S_IFDIR) {
+                       bzero(crp, sizeof(HFSCatalogFolder));
+                       crp->recordType = kHFSFolderRecord;
+                       crp->hfsFolder.folderID = cnid;
+                       crp->hfsFolder.createDate = createtime;
+                       crp->hfsFolder.modifyDate = createtime;
+                       bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
+                       *recordSize = sizeof(HFSCatalogFolder);
+               } else {
+                       bzero(crp, sizeof(HFSCatalogFile));
+                       crp->recordType = kHFSFileRecord;
+                       crp->hfsFile.fileID = cnid;
+                       crp->hfsFile.createDate = createtime;
+                       crp->hfsFile.modifyDate = createtime;
+                       bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
+                       bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
+                       *recordSize = sizeof(HFSCatalogFile);
+               }
+       }
+#endif
+
 }
 
 
@@ -3697,7 +4281,7 @@ buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding
  * builddesc - build a cnode descriptor from an HFS+ key
  */
 static int
-builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_long hint, u_long encoding,
+builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
        int isdir, struct cat_desc *descp)
 {
        int result = 0;
@@ -3859,13 +4443,14 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
        bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
 }
 
+#if CONFIG_HFS_STD
 /*
  * promotekey - promote hfs key to hfs plus key
  *
  */
 static void
 promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
-           HFSPlusCatalogKey *keyp, u_long *encoding)
+           HFSPlusCatalogKey *keyp, u_int32_t *encoding)
 {
        hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
        u_int32_t uniCount;
@@ -3900,7 +4485,7 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
             int resource, struct cat_fork * forkp)
 {
        struct HFSPlusExtentDescriptor *xp;
-       u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+       u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
 
        bzero(forkp, sizeof(*forkp));
        xp = &forkp->cf_extents[0];
@@ -3936,7 +4521,7 @@ promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
 static void
 promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
 {
-       u_long blocksize = HFSTOVCB(hfsmp)->blockSize;
+       u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
 
        if (dataPtr->recordType == kHFSFolderRecord) {
                const struct HFSCatalogFolder * folder;
@@ -3973,6 +4558,7 @@ promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlus
        crp->accessDate = crp->contentModDate;
        bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
 }
+#endif
 
 /*
  * Build a catalog node thread record from a catalog key
@@ -3983,20 +4569,7 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory)
 {
        int size = 0;
 
-       if (std_hfs) {
-               HFSCatalogKey *key = (HFSCatalogKey *)keyp;
-               HFSCatalogThread *rec = (HFSCatalogThread *)recp;
-
-               size = sizeof(HFSCatalogThread);
-               bzero(rec, size);
-               if (directory)
-                       rec->recordType = kHFSFolderThreadRecord;
-               else
-                       rec->recordType = kHFSFileThreadRecord;
-               rec->parentID = key->parentID;
-               bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
-
-       } else /* HFS+ */ {
+       if (std_hfs == 0) {
                HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
                HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
 
@@ -4010,11 +4583,28 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory)
                bcopy(&key->nodeName, &rec->nodeName,
                        sizeof(UniChar) * (key->nodeName.length + 1));
 
-               /* HFS Plus has varaible sized thread records */
+               /* HFS Plus has variable sized thread records */
                size -= (sizeof(rec->nodeName.unicode) -
                          (rec->nodeName.length * sizeof(UniChar)));
+
        }
-       
+#if CONFIG_HFS_STD
+       else {
+               HFSCatalogKey *key = (HFSCatalogKey *)keyp;
+               HFSCatalogThread *rec = (HFSCatalogThread *)recp;
+
+               size = sizeof(HFSCatalogThread);
+               bzero(rec, size);
+               if (directory)
+                       rec->recordType = kHFSFolderThreadRecord;
+               else
+                       rec->recordType = kHFSFileThreadRecord;
+               rec->parentID = key->parentID;
+               bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
+
+       } 
+#endif
+
        return (size);
 }
 
@@ -4024,25 +4614,29 @@ buildthread(void *keyp, void *recp, int std_hfs, int directory)
 static void
 buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
 {
-       if (std_hfs) {
+       if (std_hfs == 0) {
+               key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
+               key->hfsPlus.parentID = parentID;
+               key->hfsPlus.nodeName.length = 0;
+       }
+#if CONFIG_HFS_STD
+       else {
                key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
                key->hfs.reserved = 0;
                key->hfs.parentID = parentID;
                key->hfs.nodeName[0] = 0;
-       } else {
-               key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
-               key->hfsPlus.parentID = parentID;
-               key->hfsPlus.nodeName.length = 0;
        }
+#endif
+
 }
 
 /*
  * Extract the text encoding from a catalog node record.
  */
-static u_long 
+static u_int32_t 
 getencoding(const CatalogRecord *crp)
 {
-       u_long encoding;
+       u_int32_t encoding;
 
        if (crp->recordType == kHFSPlusFolderRecord)
                encoding = crp->hfsPlusFolder.textEncoding;
@@ -4063,12 +4657,16 @@ getcnid(const CatalogRecord *crp)
        cnid_t cnid = 0;
 
        switch (crp->recordType) {
+
+#if CONFIG_HFS_STD
        case kHFSFolderRecord:
                cnid = crp->hfsFolder.folderID;
                break;
        case kHFSFileRecord:
                cnid = crp->hfsFile.fileID;
                break;
+#endif
+
        case kHFSPlusFolderRecord:
                cnid = crp->hfsPlusFolder.folderID;
                break;
@@ -4076,7 +4674,7 @@ getcnid(const CatalogRecord *crp)
                cnid = crp->hfsPlusFile.fileID;
                break;
        default:
-               panic("hfs: getcnid: unknown recordType (crp @ %p)\n", crp);
+               printf("hfs: getcnid: unknown recordType=%d\n", crp->recordType);
                break;
        }
 
@@ -4092,10 +4690,13 @@ getparentcnid(const CatalogRecord *recp)
        cnid_t cnid = 0;
 
        switch (recp->recordType) {
+
+#if CONFIG_HFS_STD
        case kHFSFileThreadRecord:
        case kHFSFolderThreadRecord:
                cnid = recp->hfsThread.parentID;
                break;
+#endif
 
        case kHFSPlusFileThreadRecord:
        case kHFSPlusFolderThreadRecord:
@@ -4115,7 +4716,112 @@ getparentcnid(const CatalogRecord *recp)
 static int 
 isadir(const CatalogRecord *crp)
 {
-       return (crp->recordType == kHFSFolderRecord ||
-               crp->recordType == kHFSPlusFolderRecord);
+       if (crp->recordType == kHFSPlusFolderRecord) {
+               return 1;
+       }
+#if CONFIG_HFS_STD
+       if (crp->recordType == kHFSFolderRecord) {
+               return 1;
+       }
+#endif
+
+       return 0;
+}
+
+/*
+ * cat_lookup_dirlink - lookup a catalog record for directory hard link 
+ * (not inode) using catalog record id.  Note that this function does 
+ * NOT resolve directory hard link to its directory inode and return 
+ * the link record.
+ *
+ * Note: The caller is responsible for releasing the output catalog 
+ * descriptor (when supplied outdescp is non-null).
+ */
+int
+cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id, 
+               u_int8_t forktype, struct cat_desc *outdescp, 
+               struct cat_attr *attrp, struct cat_fork *forkp)
+{
+       struct BTreeIterator *iterator = NULL;
+       FSBufferDescriptor btdata;
+       u_int16_t datasize;
+       CatalogKey *keyp;
+       CatalogRecord *recp = NULL;
+       int error;
+
+       /* No directory hard links on standard HFS */
+       if (hfsmp->vcbSigWord == kHFSSigWord) {
+               return ENOTSUP;
+       }
+
+       MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       if (iterator == NULL) {
+               return ENOMEM;
+       }
+       bzero(iterator, sizeof(*iterator));
+       buildthreadkey(dirlink_id, 1, (CatalogKey *)&iterator->key);
+
+       MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
+       if (recp == NULL) {
+               error = ENOMEM;
+               goto out;
+       }
+       BDINIT(btdata, recp);
+
+       error = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
+                               &btdata, &datasize, iterator);
+       if (error) {
+               goto out;
+       }
+       /* Directory hard links are catalog file record */
+       if (recp->recordType != kHFSPlusFileThreadRecord) {
+               error = ENOENT;
+               goto out;
+       }
+
+       keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
+       keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
+                                 (keyp->hfsPlus.nodeName.length * 2);
+       if (forktype == kHFSResourceForkType) {
+               /* Lookup resource fork for directory hard link */
+               error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, true, outdescp, attrp, forkp, NULL);
+       } else {
+               /* Lookup data fork, if any, for directory hard link */
+               error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, false, outdescp, attrp, forkp, NULL);
+       }
+       if (error) {
+               printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id, error);
+               hfs_mark_volume_inconsistent(hfsmp);
+               goto out;
+       }
+       /* Just for sanity, make sure that id in catalog record and thread record match */
+       if ((outdescp != NULL) && (dirlink_id != outdescp->cd_cnid)) {
+               printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id, outdescp->cd_cnid);
+               hfs_mark_volume_inconsistent(hfsmp);
+               error = ENOENT;
+       }
+
+out:
+       if (recp) {
+               FREE(recp, M_TEMP);
+       }
+       FREE(iterator, M_TEMP);
+
+       return MacToVFSError(error);
+}
+
+/*
+ * cnode_update_dirlink - update the catalog node for directory hard link 
+ * described by descp using the data from attrp and forkp.
+ */
+int
+cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype, 
+               struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
+{
+       if (forktype == kHFSResourceForkType) {
+               return cat_update_internal(hfsmp, true, descp, attrp, NULL, forkp);
+       } else {
+               return cat_update_internal(hfsmp, true, descp, attrp, forkp, NULL);
+       } 
 }