]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_catalog.c
xnu-1699.22.81.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
index 04db5d2903b7b5ad836d0c00e270c4da423d79e7..0ab6f45853a447628d954a3ea0f60a0a03c8d9f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
@@ -90,10 +90,12 @@ 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 */
+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 +103,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 +116,17 @@ 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);
+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);
 
 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 +135,9 @@ 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);
 
-__private_extern__
 int
 cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
 {
@@ -152,7 +155,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 +169,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,
@@ -196,6 +197,7 @@ cat_convertattr(
        } 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 +213,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,7 +244,7 @@ cat_convertkey(
 {
        int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
        HFSPlusCatalogKey * pluskey = NULL;
-       u_long encoding;
+       u_int32_t encoding;
 
        if (std_hfs) {
                MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
@@ -295,7 +298,6 @@ cat_releasedesc(struct cat_desc *descp)
  * 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,
              struct cat_desc *outdescp, struct cat_attr *attrp,
@@ -342,7 +344,6 @@ exit:
        return (result);
 }
 
-__private_extern__
 int
 cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
@@ -407,7 +408,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)
 {
@@ -457,7 +457,7 @@ cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
        }
        if (std_hfs) {
                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);
@@ -480,7 +480,6 @@ 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,
     struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
@@ -512,12 +511,26 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
        case kHFSFileThreadRecord:
        case kHFSFolderThreadRecord:
                keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
+
+        /* check for NULL name */
+        if (keyp->hfs.nodeName[0] == 0) {
+            result = ENOENT;
+            goto exit;
+        }
+
                keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
                break;
 
        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;
@@ -527,7 +540,9 @@ cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files,
                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, 0, outdescp, attrp, forkp, NULL);
        /* No corresponding file/folder record found for a thread record,
         * mark the volume inconsistent.
         */
@@ -538,7 +553,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 +568,14 @@ 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;
+       int extlen1, extlen2;
        
        if (wantrsrc)
                return (ENOENT);
@@ -586,6 +602,16 @@ cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
                bcmp(outdescp->cd_nameptr, descp->cd_nameptr, prefixlen-6) != 0)
                goto falsematch;
 
+       extlen1 = CountFilenameExtensionChars(descp->cd_nameptr, descp->cd_namelen);
+       extlen2 = CountFilenameExtensionChars(outdescp->cd_nameptr, outdescp->cd_namelen);
+       if (extlen1 != extlen2)
+               goto falsematch;
+
+       if (bcmp(outdescp->cd_nameptr + (outdescp->cd_namelen - extlen2),
+                       descp->cd_nameptr + (descp->cd_namelen - extlen1),
+                       extlen1) != 0)
+               goto falsematch;
+
        return (0);
 
 falsematch:
@@ -598,7 +624,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 +633,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,16 +652,18 @@ 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 (!std_hfs) {
+               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) {
-
+                !(flags & HFS_LOOKUP_SYSFILE)) {
                result = ENOENT;
                goto exit;
        }
@@ -647,7 +676,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,7 +689,7 @@ 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);
                }
@@ -674,8 +703,50 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
                        getbsdattr(hfsmp, &cnoderec, attrp);
                } else {
                        getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
-                       if (ilink)
+                       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 or a valid hard link being that is not 
+                                * resolved for volume resize purposes.  We do not want to 
+                                * reset the hard link bit or reset link count on these records.
+                                */
+                               if (!(flags & HFS_LOOKUP_HARDLINK) && 
+                                   (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 (forkp != NULL) {
@@ -686,6 +757,7 @@ cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int allow_system_files
                } 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 +776,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 +809,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;
@@ -784,7 +873,6 @@ 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,
        struct cat_desc *out_descp)
@@ -796,7 +884,7 @@ cat_create(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attr
        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;
@@ -959,7 +1047,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 +1069,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);
@@ -1148,7 +1235,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)", err);
                                hfs_mark_volume_inconsistent(hfsmp);
                                result = err;
                                goto exit;
@@ -1175,7 +1262,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)", err);
                                hfs_mark_volume_inconsistent(hfsmp);
                                result = err;
                                goto exit;
@@ -1223,7 +1310,7 @@ cat_rename (
 
                        /* 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);
@@ -1258,7 +1345,6 @@ exit:
  *     2. BTDeleteRecord(thread);
  *     3. BTUpdateRecord(parent);
  */
-__private_extern__
 int
 cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
 {
@@ -1349,12 +1435,13 @@ 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;
@@ -1379,13 +1466,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 +1498,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.
@@ -1495,7 +1594,7 @@ catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *st
                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\n", dir->folderID, attrp->ca_fileid);
                        return (btNotFound);
                }
                dir->flags            = attrp->ca_recflags;
@@ -1556,6 +1655,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 +1698,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 +1747,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 +1759,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 +1801,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;
                }
@@ -1758,12 +1877,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 for %u failed\n", cnid);
                        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 %u\n", cnid);
                        invalid = 1;  /* On errors, assume an invalid parent */
                        break;
                }
@@ -1780,7 +1899,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 +1909,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 +1927,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 +1948,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\n", linkfileid);
        }
+
+       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 +1978,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 +2004,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 +2023,19 @@ 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);
+               printf("hfs: cat_lookup_siblinglinks: 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);
+               printf("hfs: cat_lookup_siblinglinks: cannot find %d\n", linkfileid);
                goto exit;
        }
        /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
@@ -1924,7 +2044,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 +2059,7 @@ cat_lookuplinkbyid(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid
                *nextlinkid = 0;
        }
 exit:
+       FREE(iterator, M_TEMP);         
        return MacToVFSError(result);
 }
 
@@ -1954,7 +2075,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,7 +2085,7 @@ 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;
@@ -1993,7 +2113,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;
        }
 
@@ -2072,7 +2192,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: err %d from BTInsertRecord\n", MacToVFSError(result));
 
                        buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
                        if (BTDeleteRecord(fcb, &bto->iterator)) {
@@ -2082,7 +2202,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,11 +2297,12 @@ 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);
        if (result) {
@@ -2193,7 +2314,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 +2357,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 +2369,6 @@ exit:
 /*
  * cat_deletelink - delete a link from the catalog
  */
-__private_extern__
 int
 cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
 {
@@ -2300,7 +2420,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;
@@ -2376,7 +2496,7 @@ getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
        if (state->stdhfs) {
                struct HFSPlusCatalogFile cnoderec;
                HFSPlusCatalogKey * pluskey;
-               u_long encoding;
+               u_int32_t encoding;
 
                promoteattr(hfsmp, rec, &cnoderec);
                getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
@@ -2425,7 +2545,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,7 +2696,7 @@ 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;
@@ -2626,6 +2745,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,6 +2759,27 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
 
        /* We're done when parent directory changes */
        if (state->cbs_parentID != curID) {
+               /*
+                * 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_extended) {
                        /* The last record has not been returned yet, so we 
                         * want to stop after packing the last item 
@@ -2646,10 +2787,12 @@ getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
                        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 */
                }
@@ -2697,6 +2840,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 +2851,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) &&
@@ -2752,9 +2897,22 @@ encodestr:
 
                /* 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);              
+                                                            (ByteCount*)&namelen, nameptr, linkid);            
                        is_mangled = 1;
                }
        }
@@ -2786,7 +2944,7 @@ 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 */
        }
@@ -2945,7 +3103,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 */
        }
 
@@ -2974,7 +3132,6 @@ getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
 /*
  * 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)
@@ -3012,7 +3169,13 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        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 +3183,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
@@ -3142,7 +3304,14 @@ cat_getdirentries(struct hfsmount *hfsmp, int entrycnt, directoryhint_t *dirhint
        *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 +3333,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;
 
@@ -3239,7 +3408,7 @@ cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
                ++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 +3424,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)
 {
@@ -3416,11 +3584,15 @@ 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];
                }
@@ -3433,9 +3605,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 +3646,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 %s\n", inodename);
        }
 
        FREE(iterator, M_TEMP);
@@ -3487,7 +3658,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;
@@ -3573,7 +3744,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 +3770,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;
@@ -3697,7 +3867,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;
@@ -3865,7 +4035,7 @@ getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct
  */
 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 +4070,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 +4106,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;
@@ -4039,10 +4209,10 @@ buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
 /*
  * 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;
@@ -4119,3 +4289,99 @@ isadir(const CatalogRecord *crp)
                crp->recordType == kHFSPlusFolderRecord);
 }
 
+/*
+ * 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);
+       } 
+}