]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfs_vfsutils.c
xnu-1504.9.17.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_vfsutils.c
index 36c3e550e3c2f37dbc268a5c672eebd3008dc32f..eb721483c93907c9b45ee493f45a33739f402b78 100644 (file)
 /*
- * Copyright (c) 2000-2001 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
  *
- * @APPLE_LICENSE_HEADER_START@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
- * @APPLE_LICENSE_HEADER_END@
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
  */
 /*     @(#)hfs_vfsutils.c      4.0
 *
-*      (c) 1997-2000 Apple Computer, Inc.  All Rights Reserved
+*      (c) 1997-2002 Apple Computer, Inc.  All Rights Reserved
 *
 *      hfs_vfsutils.c -- Routines that go between the HFS layer and the VFS.
 *
-*      Change History (most recent first):
-*
-*      22-Jan-2000     Don Brady       Remove calls to MountCheck.
-*       7-Sep-1999     Don Brady       Add HFS Plus hard-link support.
-*      25-Aug-1999     Don Brady       Dont't use vcbAlBlSt for HFS plus volumes (2350009).
-*       9-Aug-1999 Pat Dirks           Added support for ATTR_VOL_ENCODINGSUSED [#2357367].
-*      16-Jul-1999     Pat Dirks               Fixed PackCommonCatalogInfoAttributeBlock to return full range of possible vnode types [#2317604]
-*      15-Jun-1999     Pat Dirks               Added support for return of mounted device in hfs_getattrlist [#2345297].
-*       9-Jun-1999     Don Brady               Cleanup vcb accesses in hfs_MountHFSVolume.
-*       3-Jun-1999     Don Brady               Remove references to unused/legacy vcb fields (eg vcbXTClpSiz).
-*      21-May-1999     Don Brady               Add call to hfs_vinit in hfsGet to support mknod.
-*       6-Apr-1999     Don Brady               Fixed de-reference of NULL dvp in hfsGet.
-*      22-Mar-1999     Don Brady               Add support for UFS delete semantics.
-*       1-Mar-1999     Scott Roberts   Dont double MALLOC on long names.
-*      23-Feb-1999     Pat Dirks               Change incrementing of meta refcount to be done BEFORE lock is acquired.
-*       2-Feb-1999     Pat Dirks               For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
-*      10-Mar-1999     Don Brady               Removing obsolete code.
-*       2-Feb-1999     Don Brady               For volume ATTR_CMN_SCRIPT use vcb->volumeNameEncodingHint instead of 0.
-*      18-Jan-1999     Pat Dirks               Changed CopyCatalogToHFSNode to start with ACCESSPERMS instead of adding
-*                                                              write access only for unlocked files (now handled via IMMUTABLE setting)
-*       7-Dec-1998 Pat Dirks           Changed PackCatalogInfoFileAttributeBlock to return proper I/O block size.
-*       7-Dec-1998     Don Brady               Pack the real text encoding instead of zero.
-*      16-Dec-1998     Don Brady               Use the root's crtime intead of vcb create time for getattrlist.
-*      16-Dec-1998     Don Brady               Use the root's crtime intead of vcb create time for getattrlist.
-*       2-Dec-1998     Scott Roberts   Copy the mdbVN correctly into the vcb.
-*    3-Dec-1998 Pat Dirks              Added support for ATTR_VOL_MOUNTFLAGS.
-*      20-Nov-1998     Don Brady               Add support for UTF-8 names.
-*   18-Nov-1998        Pat Dirks               Changed UnpackCommonAttributeBlock to call wait for hfs_chflags to update catalog entry when changing flags
-*   13-Nov-1998 Pat Dirks       Changed BestBlockSizeFit to try PAGE_SIZE only and skip check for MAXBSIZE.
-*      10-Nov-1998     Pat Dirks               Changed CopyCatalogToHFSNode to ensure consistency between lock flag and IMMUTABLE bits.
-*   10-Nov-1998        Pat Dirks               Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
-*      18-Nov-1998     Pat Dirks               Changed PackVolAttributeBlock to return proper logical block size
-*                               for ATTR_VOL_IOBLOCKSIZE attribute.
-*       3-Nov-1998     Umesh Vaishampayan      Changes to deal with "struct timespec"
-*                                                              change in the kernel.   
-*      23-Sep-1998     Don Brady               In UnpackCommonAttributeBlock simplified setting of gid, uid and mode.
-*   10-Nov-1998        Pat Dirks               Added MapFileOffset(), LogicalBlockSize() and UpdateBlockMappingTable() routines.
-*      17-Sep-1998     Pat Dirks               Changed BestBlockSizeFit to try MAXBSIZE and PAGE_SIZE first.
-*       8-Sep-1998     Don Brady               Fix CopyVNodeToCatalogNode to use h_mtime for contentModDate (instead of h_ctime).
-*       4-Sep-1998     Pat Dirks               Added BestBlockSizeFit routine.
-*      18-Aug-1998     Don Brady               Change DEBUG_BREAK_MSG to a DBG_UTILS in MacToVFSError (radar #2262802).
-*      30-Jun-1998     Don Brady               Add calls to MacToVFSError to hfs/hfsplus mount routines (for radar #2249539).
-*      22-Jun-1998     Don Brady               Add more error cases to MacToVFSError; all HFS Common errors are negative.
-*                                                              Changed hfsDelete to call DeleteFile for files.
-*       4-Jun-1998     Pat Dirks               Changed incorrect references to 'vcbAlBlkSize' to 'blockSize';
-*                                                              Added hfsCreateFileID.
-*       4-Jun-1998     Don Brady               Add hfsMoveRename to replace hfsMove and hfsRename. Use VPUT/VRELE macros
-*                                                              instead of vput/vrele to catch bad ref counts.
-*      28-May-1998     Pat Dirks               Adjusted for change in definition of ATTR_CMN_NAME and removed ATTR_CMN_RAWDEVICE.
-*       7-May-1998     Don Brady               Added check for NULL vp to hfs_metafilelocking (radar #2233832).
-*      24-Apr-1998     Pat Dirks               Fixed AttributeBlockSize to return only length of variable attribute block.
-*      4/21/1998       Don Brady               Add SUPPORTS_MAC_ALIASES conditional (for radar #2225419).
-*      4/21/1998       Don Brady               Map cmNotEmpty errors to ENOTEMPTY (radar #2229259).
-*      4/21/1998       Don Brady               Fix up time/date conversions.
-*      4/20/1998       Don Brady               Remove course-grained hfs metadata locking.
-*      4/18/1998       Don Brady               Add VCB locking.
-*      4/17/1998       Pat Dirks               Fixed PackFileAttributeBlock to return more up-to-date EOF/PEOF info from vnode.
-*      4/15/1998       Don Brady               Add hasOverflowExtents and hfs_metafilelocking. Use ExtendBTreeFile instead
-*                                                              of SetEndOfForkProc. Set forktype for system files.
-*      4/14/1998       Deric Horn              PackCatalogInfoAttributeBlock(), and related packing routines to
-*                                                              pack attribute data given hfsCatalogInfo, without the objects vnode;
-*      4/14/1998       Scott Roberts   Add execute priviledges to all hfs objects.
-*       4/9/1998       Don Brady               Add MDB/VolumeHeader flushing to hfsUnmount;
-*       4/8/1998       Don Brady               Make sure vcbVRefNum field gets initialized (use MAKE_VREFNUM).
-*       4/6/1998       Don Brady               Removed calls to CreateVolumeCatalogCache (obsolete).
-*      4/06/1998       Scott Roberts   Added complex file support.
-*      4/02/1998       Don Brady               UpdateCatalogNode now takes parID and name as input.
-*      3/31/1998       Don Brady               Sync up with final HFSVolumes.h header file.
-*      3/31/1998       Don Brady               Check result from UFSToHFSStr to make sure hfs/hfs+ names are not greater
-*                                                              than 31 characters.
-*      3/30/1998       Don Brady               In InitMetaFileVNode set VSYSTEM bit in vnode's v_flag.
-*      3/26/1998       Don Brady               Cleaned up hfs_MountXXX routines. Removed CloseBtreeFile and OpenBTreeFile.
-*                                                              Simplified hfsUnmount (removed MacOS specific code).
-*      3/17/1998       Don Brady               AttributeBlockSize calculation did not account for the size field (4bytes).
-*                                                              PackVolCommonAttributes and PackCommonAttributeBlock for ATTR_CMN_NAME
-*                                                              were not setting up the name correctly.
-*      3/17/1998       Don Brady               Changed CreateCatalogNode interface to take kCatalogFolderNode and
-*                                                              kCatalogFileNode as type input. Also, force MountCheck to always run.
-*      12-nov-1997     Scott Roberts   Initially created file.
-*      17-Mar-98       ser                             Broke out and created CopyCatalogToHFSNode()
-*
 */
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/stat.h>
-#include <sys/attr.h>
 #include <sys/mount.h>
-#include <sys/lock.h>
+#include <sys/mount_internal.h>
 #include <sys/buf.h>
+#include <sys/buf_internal.h>
 #include <sys/ubc.h>
 #include <sys/unistd.h>
+#include <sys/utfconv.h>
+#include <sys/kauth.h>
+#include <sys/fcntl.h>
+#include <sys/vnode_internal.h>
+#include <kern/clock.h>
+
+#include <libkern/OSAtomic.h>
 
 #include "hfs.h"
+#include "hfs_catalog.h"
 #include "hfs_dbg.h"
 #include "hfs_mount.h"
 #include "hfs_endian.h"
+#include "hfs_cnode.h"
+#include "hfs_fsctl.h"
 
 #include "hfscommon/headers/FileMgrInternal.h"
 #include "hfscommon/headers/BTreesInternal.h"
 #include "hfscommon/headers/HFSUnicodeWrappers.h"
 
-#define                SUPPORTS_MAC_ALIASES    0
-#define                kMaxSecsForFsync        5
-
-#define BYPASSBLOCKINGOPTIMIZATION 0
-
-#define kMaxLockedMetaBuffers          32              /* number of locked buffer caches to hold for meta data */
-
-extern int (**hfs_vnodeop_p)(void *);
-extern int (**hfs_specop_p)(void *);
-extern int (**hfs_fifoop_p)(void *);
-extern int count_lock_queue __P((void));
-extern uid_t console_user;
-
-OSErr  ValidMasterDirectoryBlock( HFSMasterDirectoryBlock *mdb );
-UInt16 DivUp( UInt32 byteRun, UInt32 blockSize );
-
-/* Externs from vhash */
-extern void hfs_vhashins_sibling(dev_t dev, UInt32 nodeID, struct hfsnode *hp, struct hfsfilemeta **fm);
-extern void hfs_vhashins(dev_t dev, UInt32 nodeID,struct hfsnode *hp);
-extern struct vnode *hfs_vhashget(dev_t dev, UInt32 nodeID, UInt8 forkType);
-
-extern int hfs_vinit( struct mount *mntp, int (**specops)(void *), int (**fifoops)(), struct vnode **vpp);
-
-extern UInt16 CountRootFiles(ExtendedVCB *vcb);
-extern OSErr GetVolumeNameFromCatalog(ExtendedVCB *vcb);
-
-static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents,
-                                                        HFSCatalogNodeID fileID, void * keyCompareProc);
-
 static void ReleaseMetaFileVNode(struct vnode *vp);
+static int  hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args);
 
-static void RemovedMetaDataDirectory(ExtendedVCB *vcb);
-
-void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm);
-void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp);
-void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm);
-u_int32_t GetLogicalBlockSize(struct vnode *vp);
+static u_int32_t hfs_hotfile_freeblocks(struct hfsmount *);
 
-/* BTree accessor routines */
-extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
-extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
-extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
-extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
 
 //*******************************************************************************
 // Note: Finder information in the HFS/HFS+ metadata are considered opaque and
@@ -183,148 +80,199 @@ extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, Relea
 //
 //
 //*******************************************************************************
+unsigned char hfs_catname[] = "Catalog B-tree";
+unsigned char hfs_extname[] = "Extents B-tree";
+unsigned char hfs_vbmname[] = "Volume Bitmap";
+unsigned char hfs_attrname[] = "Attribute B-tree";
+unsigned char hfs_startupname[] = "Startup File";
 
+
+__private_extern__
 OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
-               u_long sectors, struct proc *p)
+               __unused struct proc *p)
 {
-    ExtendedVCB                        *vcb = HFSTOVCB(hfsmp);
-    struct vnode                       *tmpvnode;
-    OSErr                                      err;
-    HFSPlusExtentRecord                extents;
+       ExtendedVCB *vcb = HFSTOVCB(hfsmp);
+       int error;
        ByteCount utf8chars;
-    DBG_FUNC_NAME("hfs_MountHFSVolume");
-    DBG_PRINT_FUNC_NAME();
+       struct cat_desc cndesc;
+       struct cat_attr cnattr;
+       struct cat_fork fork;
 
-    if (hfsmp == nil || mdb == nil)                            /* exit if bad paramater */
+       /* Block size must be a multiple of 512 */
+       if (SWAP_BE32(mdb->drAlBlkSiz) == 0 ||
+           (SWAP_BE32(mdb->drAlBlkSiz) & 0x01FF) != 0)
                return (EINVAL);
 
-    err = ValidMasterDirectoryBlock( mdb );            /* make sure this is an HFS disk */
-    if (err)
-       return MacToVFSError(err);
-
        /* don't mount a writeable volume if its dirty, it must be cleaned by fsck_hfs */
-       if ((hfsmp->hfs_fs_ronly == 0) && ((SWAP_BE16 (mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0))
+       if (((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
+           ((SWAP_BE16(mdb->drAtrb) & kHFSVolumeUnmountedMask) == 0)) {
                return (EINVAL);
-               
+       }
+       hfsmp->hfs_flags |= HFS_STANDARD;
        /*
         * The MDB seems OK: transfer info from it into VCB
         * Note - the VCB starts out clear (all zeros)
         *
         */
-       vcb->vcbVRefNum                 = MAKE_VREFNUM(hfsmp->hfs_raw_dev);
-
-       vcb->vcbSigWord                 = SWAP_BE16 (mdb->drSigWord);
-       vcb->vcbCrDate                  = LocalToUTC (SWAP_BE32 (mdb->drCrDate));
-       vcb->vcbLsMod                   = LocalToUTC (SWAP_BE32 (mdb->drLsMod));
-       vcb->vcbAtrb                    = SWAP_BE16 (mdb->drAtrb);
-       vcb->vcbNmFls                   = SWAP_BE16 (mdb->drNmFls);
-       vcb->vcbVBMSt                   = SWAP_BE16 (mdb->drVBMSt);
-       vcb->nextAllocation             = SWAP_BE16 (mdb->drAllocPtr);
-       vcb->totalBlocks                = SWAP_BE16 (mdb->drNmAlBlks);
-       vcb->blockSize                  = SWAP_BE32 (mdb->drAlBlkSiz);
-       vcb->vcbClpSiz                  = SWAP_BE32 (mdb->drClpSiz);
-       vcb->vcbAlBlSt                  = SWAP_BE16 (mdb->drAlBlSt);
-       vcb->vcbNxtCNID                 = SWAP_BE32 (mdb->drNxtCNID);
-       vcb->freeBlocks                 = SWAP_BE16 (mdb->drFreeBks);
-       vcb->vcbVolBkUp                 = LocalToUTC (SWAP_BE32 (mdb->drVolBkUp));
-       vcb->vcbVSeqNum                 = SWAP_BE16 (mdb->drVSeqNum);
-       vcb->vcbWrCnt                   = SWAP_BE32 (mdb->drWrCnt);
-       vcb->vcbNmRtDirs                = SWAP_BE16 (mdb->drNmRtDirs);
-       vcb->vcbFilCnt                  = SWAP_BE32 (mdb->drFilCnt);
-       vcb->vcbDirCnt                  = SWAP_BE32 (mdb->drDirCnt);
+       vcb->vcbSigWord         = SWAP_BE16 (mdb->drSigWord);
+       vcb->vcbCrDate          = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drCrDate)));
+       vcb->localCreateDate    = SWAP_BE32 (mdb->drCrDate);
+       vcb->vcbLsMod           = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drLsMod)));
+       vcb->vcbAtrb            = SWAP_BE16 (mdb->drAtrb);
+       vcb->vcbNmFls           = SWAP_BE16 (mdb->drNmFls);
+       vcb->vcbVBMSt           = SWAP_BE16 (mdb->drVBMSt);
+       vcb->nextAllocation     = SWAP_BE16 (mdb->drAllocPtr);
+       vcb->totalBlocks        = SWAP_BE16 (mdb->drNmAlBlks);
+       vcb->allocLimit         = vcb->totalBlocks;
+       vcb->blockSize          = SWAP_BE32 (mdb->drAlBlkSiz);
+       vcb->vcbClpSiz          = SWAP_BE32 (mdb->drClpSiz);
+       vcb->vcbAlBlSt          = SWAP_BE16 (mdb->drAlBlSt);
+       vcb->vcbNxtCNID         = SWAP_BE32 (mdb->drNxtCNID);
+       vcb->freeBlocks         = SWAP_BE16 (mdb->drFreeBks);
+       vcb->vcbVolBkUp         = to_bsd_time(LocalToUTC(SWAP_BE32(mdb->drVolBkUp)));
+       vcb->vcbWrCnt           = SWAP_BE32 (mdb->drWrCnt);
+       vcb->vcbNmRtDirs        = SWAP_BE16 (mdb->drNmRtDirs);
+       vcb->vcbFilCnt          = SWAP_BE32 (mdb->drFilCnt);
+       vcb->vcbDirCnt          = SWAP_BE32 (mdb->drDirCnt);
        bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
-       vcb->nextAllocation             = SWAP_BE16 ( mdb->drAllocPtr); /* Duplicate?!?!?! */
-       vcb->encodingsBitmap = 0;
-       vcb->vcbWrCnt++;        /* Compensate for write of MDB on last flush */
-       /*
-        * Copy the drVN field, which is a Pascal String to the vcb, which is a cstring
-        */
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+               vcb->vcbWrCnt++;        /* Compensate for write of MDB on last flush */
 
        /* convert hfs encoded name into UTF-8 string */
-       err = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
+       error = hfs_to_utf8(vcb, mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
        /*
         * When an HFS name cannot be encoded with the current
         * volume encoding we use MacRoman as a fallback.
         */
-       if (err || (utf8chars == 0))
+       if (error || (utf8chars == 0))
                (void) mac_roman_to_utf8(mdb->drVN, NAME_MAX, &utf8chars, vcb->vcbVN);
 
-       vcb->altIDSector = sectors - 2;
+       hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
+       vcb->vcbVBMIOSize = kHFSBlockSize;
 
-    // Initialize our dirID/nodePtr cache associated with this volume.
-    err = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
-    ReturnIfError( err );
+       hfsmp->hfs_alt_id_sector = HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
+                                                 hfsmp->hfs_logical_block_count);
 
-    hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
-
-    // XXX PPD: Should check here for hardware lock flag and set flags in VCB/MP appropriately
-       VCB_LOCK_INIT(vcb);
+       bzero(&cndesc, sizeof(cndesc));
+       cndesc.cd_parentcnid = kHFSRootParentID;
+       cndesc.cd_flags |= CD_ISMETA;
+       bzero(&cnattr, sizeof(cnattr));
+       cnattr.ca_linkcount = 1;
+       cnattr.ca_mode = S_IFREG;
+       bzero(&fork, sizeof(fork));
 
        /*
-        * Set up Extents B-tree vnode...
-        */ 
-       err = GetInitializedVNode(hfsmp, &tmpvnode, 0);
-       if (err) goto MtVolErr;
-    /* HFSToHFSPlusExtents(mdb->drXTExtRec, extents); */ /* ASDFADSFSD */
-       extents[0].startBlock = SWAP_BE16 (mdb->drXTExtRec[0].startBlock);
-       extents[0].blockCount = SWAP_BE16 (mdb->drXTExtRec[0].blockCount);
-       extents[1].startBlock = SWAP_BE16 (mdb->drXTExtRec[1].startBlock);
-       extents[1].blockCount = SWAP_BE16 (mdb->drXTExtRec[1].blockCount);
-       extents[2].startBlock = SWAP_BE16 (mdb->drXTExtRec[2].startBlock);
-       extents[2].blockCount = SWAP_BE16 (mdb->drXTExtRec[2].blockCount);
-    
-    err = InitMetaFileVNode(tmpvnode, SWAP_BE32 (mdb->drXTFlSize), SWAP_BE32 (mdb->drXTClpSiz), extents,
-                                                       kHFSExtentsFileID, CompareExtentKeys);
-    if (err) goto MtVolErr;
+        * Set up Extents B-tree vnode
+        */
+       cndesc.cd_nameptr = hfs_extname;
+       cndesc.cd_namelen = strlen((char *)hfs_extname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
+       fork.cf_size = SWAP_BE32(mdb->drXTFlSize);
+       fork.cf_blocks = fork.cf_size / vcb->blockSize;
+       fork.cf_clump = SWAP_BE32(mdb->drXTClpSiz);
+       fork.cf_vblocks = 0;
+       fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drXTExtRec[0].startBlock);
+       fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drXTExtRec[0].blockCount);
+       fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drXTExtRec[1].startBlock);
+       fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drXTExtRec[1].blockCount);
+       fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drXTExtRec[2].startBlock);
+       fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drXTExtRec[2].blockCount);
+       cnattr.ca_blocks = fork.cf_blocks;
+
+       error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+                               &hfsmp->hfs_extents_vp);
+       if (error) goto MtVolErr;
+       error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
+                                        (KeyCompareProcPtr)CompareExtentKeys));
+       if (error) {
+               hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+               goto MtVolErr;
+       }
+       hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
 
        /*
         * Set up Catalog B-tree vnode...
         */ 
-       err = GetInitializedVNode(hfsmp, &tmpvnode, 0);
-       if (err) goto MtVolErr;
-    /* HFSToHFSPlusExtents(mdb->drCTExtRec, extents); */
-       extents[0].startBlock = SWAP_BE16 (mdb->drCTExtRec[0].startBlock);
-       extents[0].blockCount = SWAP_BE16 (mdb->drCTExtRec[0].blockCount);
-       extents[1].startBlock = SWAP_BE16 (mdb->drCTExtRec[1].startBlock);
-       extents[1].blockCount = SWAP_BE16 (mdb->drCTExtRec[1].blockCount);
-       extents[2].startBlock = SWAP_BE16 (mdb->drCTExtRec[2].startBlock);
-       extents[2].blockCount = SWAP_BE16 (mdb->drCTExtRec[2].blockCount);
-    
-    err = InitMetaFileVNode(tmpvnode, SWAP_BE32 (mdb->drCTFlSize), SWAP_BE32 (mdb->drCTClpSiz), extents,
-                                                       kHFSCatalogFileID, CompareCatalogKeys);
-       if (err) goto MtVolErr;
+       cndesc.cd_nameptr = hfs_catname;
+       cndesc.cd_namelen = strlen((char *)hfs_catname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
+       fork.cf_size = SWAP_BE32(mdb->drCTFlSize);
+       fork.cf_blocks = fork.cf_size / vcb->blockSize;
+       fork.cf_clump = SWAP_BE32(mdb->drCTClpSiz);
+       fork.cf_vblocks = 0;
+       fork.cf_extents[0].startBlock = SWAP_BE16(mdb->drCTExtRec[0].startBlock);
+       fork.cf_extents[0].blockCount = SWAP_BE16(mdb->drCTExtRec[0].blockCount);
+       fork.cf_extents[1].startBlock = SWAP_BE16(mdb->drCTExtRec[1].startBlock);
+       fork.cf_extents[1].blockCount = SWAP_BE16(mdb->drCTExtRec[1].blockCount);
+       fork.cf_extents[2].startBlock = SWAP_BE16(mdb->drCTExtRec[2].startBlock);
+       fork.cf_extents[2].blockCount = SWAP_BE16(mdb->drCTExtRec[2].blockCount);
+       cnattr.ca_blocks = fork.cf_blocks;
+
+       error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+                               &hfsmp->hfs_catalog_vp);
+       if (error) {
+               hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+               goto MtVolErr;
+       }
+       error = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+                                        (KeyCompareProcPtr)CompareCatalogKeys));
+       if (error) {
+               hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+               hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+               goto MtVolErr;
+       }
+       hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
+
+       /*
+        * Set up dummy Allocation file vnode (used only for locking bitmap)
+        */  
+       cndesc.cd_nameptr = hfs_vbmname;
+       cndesc.cd_namelen = strlen((char *)hfs_vbmname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
+       bzero(&fork, sizeof(fork));
+       cnattr.ca_blocks = 0;
+
+       error = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &fork,
+                                &hfsmp->hfs_allocation_vp);
+       if (error) {
+               hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+               hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+               goto MtVolErr;
+       }
+       hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
 
        /* mark the volume dirty (clear clean unmount bit) */
        vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
 
-       /* Remove any MetaDataDirectory from hfs disks */
-       if (hfsmp->hfs_fs_ronly == 0)
-               RemovedMetaDataDirectory(vcb);
-
-       /*
-        * all done with b-trees so we can unlock now...
-        */
-    VOP_UNLOCK(vcb->catalogRefNum, 0, p);
-    VOP_UNLOCK(vcb->extentsRefNum, 0, p);
-
-    err = noErr;
+    if (error == noErr)
+      {
+               error = cat_idlookup(hfsmp, kHFSRootFolderID, 0, NULL, NULL, NULL);
+      }
 
-    if ( err == noErr )
+    if ( error == noErr )
       {
         if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )            //      if the disk is not write protected
           {
             MarkVCBDirty( vcb );                                                               //      mark VCB dirty so it will be written
           }
       }
+
+       /*
+        * all done with system files so we can unlock now...
+        */
+       hfs_unlock(VTOC(hfsmp->hfs_allocation_vp));
+       hfs_unlock(VTOC(hfsmp->hfs_catalog_vp));
+       hfs_unlock(VTOC(hfsmp->hfs_extents_vp));
+
     goto       CmdDone;
 
     //--       Release any resources allocated so far before exiting with an error:
-MtVolErr:;
-       ReleaseMetaFileVNode(vcb->catalogRefNum);
-       ReleaseMetaFileVNode(vcb->extentsRefNum);
+MtVolErr:
+       ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
+       ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
 
-CmdDone:;
-    return( err );
+CmdDone:
+    return (error);
 }
 
 //*******************************************************************************
@@ -333,158 +281,480 @@ CmdDone:;
 //
 //*******************************************************************************
 
+__private_extern__
 OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
-       u_long embBlkOffset, u_long sectors, struct proc *p)
+       off_t embeddedOffset, u_int64_t disksize, __unused struct proc *p, void *args, kauth_cred_t cred)
 {
-    register ExtendedVCB       *vcb;
-    HFSPlusForkData                    *fdp;
-    struct vnode                       *tmpvnode;
-    OSErr                                      retval;
-
-    if (hfsmp == nil || vhp == nil)            /*      exit if bad paramater */
+       register ExtendedVCB *vcb;
+       struct cat_desc cndesc;
+       struct cat_attr cnattr;
+       struct cat_fork cfork;
+       u_int32_t blockSize;
+       daddr64_t spare_sectors;
+       struct BTreeInfoRec btinfo;
+       u_int16_t  signature;
+       u_int16_t  hfs_version;
+       int  i;
+       OSErr retval;
+
+       signature = SWAP_BE16(vhp->signature);
+       hfs_version = SWAP_BE16(vhp->version);
+
+       if (signature == kHFSPlusSigWord) {
+               if (hfs_version != kHFSPlusVersion) {
+                       printf("hfs_mount: invalid HFS+ version: %d\n", hfs_version);
+                       return (EINVAL);
+               }
+       } else if (signature == kHFSXSigWord) {
+               if (hfs_version != kHFSXVersion) {
+                       printf("hfs_mount: invalid HFSX version: %d\n", hfs_version);
+                       return (EINVAL);
+               }
+               /* The in-memory signature is always 'H+'. */
+               signature = kHFSPlusSigWord;
+               hfsmp->hfs_flags |= HFS_X;
+       } else {
+               /* Removed printf for invalid HFS+ signature because it gives
+                * false error for UFS root volume 
+                */
                return (EINVAL);
+       }
 
-       DBG_VFS(("hfs_MountHFSPlusVolume: signature=0x%x, version=%d, blockSize=%ld\n",
-            SWAP_BE16 (vhp->signature),
-            SWAP_BE16 (vhp->version),
-            SWAP_BE32 (vhp->blockSize)));
-
-    retval = ValidVolumeHeader(vhp);   /*      make sure this is an HFS Plus disk */
-    if (retval)
-       return MacToVFSError(retval);
+       /* Block size must be at least 512 and a power of 2 */
+       blockSize = SWAP_BE32(vhp->blockSize);
+       if (blockSize < 512 || !powerof2(blockSize))
+               return (EINVAL);
    
        /* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
-       if (hfsmp->hfs_fs_ronly == 0 && (SWAP_BE32 (vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0 && hfsmp->jnl == NULL &&
+           (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) == 0)
                return (EINVAL);
+
+       /* Make sure we can live with the physical block size. */
+       if ((disksize & (hfsmp->hfs_logical_block_size - 1)) ||
+           (embeddedOffset & (hfsmp->hfs_logical_block_size - 1)) ||
+           (blockSize < hfsmp->hfs_logical_block_size)) {
+               return (ENXIO);
+       }
+
+       /* If allocation block size is less than the physical 
+        * block size, we assume that the physical block size 
+        * is same as logical block size.  The physical block 
+        * size value is used to round down the offsets for 
+        * reading and writing the primary and alternate volume 
+        * headers at physical block boundary and will cause 
+        * problems if it is less than the block size.
+        */
+       if (blockSize < hfsmp->hfs_physical_block_size) {
+               hfsmp->hfs_physical_block_size = hfsmp->hfs_logical_block_size;
+               hfsmp->hfs_log_per_phys = 1;
+       }
+
        /*
         * The VolumeHeader seems OK: transfer info from it into VCB
         * Note - the VCB starts out clear (all zeros)
         */
        vcb = HFSTOVCB(hfsmp);
 
-       //DBG_ASSERT((hfsmp->hfs_raw_dev & 0xFFFF0000) == 0);
-       vcb->vcbVRefNum         =       MAKE_VREFNUM(hfsmp->hfs_raw_dev);
-       vcb->vcbSigWord         =       SWAP_BE16 (vhp->signature);
-       vcb->vcbLsMod           =       SWAP_BE32 (vhp->modifyDate);
-       vcb->vcbAtrb            =       (UInt16) SWAP_BE32 (vhp->attributes);           // VCB only uses lower 16 bits
-       vcb->vcbClpSiz          =       SWAP_BE32 (vhp->rsrcClumpSize);
-       vcb->vcbNxtCNID         =       SWAP_BE32 (vhp->nextCatalogID);
-       vcb->vcbVolBkUp         =       SWAP_BE32 (vhp->backupDate);
-       vcb->vcbWrCnt           =       SWAP_BE32 (vhp->writeCount);
-       vcb->vcbFilCnt          =       SWAP_BE32 (vhp->fileCount);
-       vcb->vcbDirCnt          =       SWAP_BE32 (vhp->folderCount);
+       vcb->vcbSigWord = signature;
+       vcb->vcbJinfoBlock = SWAP_BE32(vhp->journalInfoBlock);
+       vcb->vcbLsMod   = to_bsd_time(SWAP_BE32(vhp->modifyDate));
+       vcb->vcbAtrb    = SWAP_BE32(vhp->attributes);
+       vcb->vcbClpSiz  = SWAP_BE32(vhp->rsrcClumpSize);
+       vcb->vcbNxtCNID = SWAP_BE32(vhp->nextCatalogID);
+       vcb->vcbVolBkUp = to_bsd_time(SWAP_BE32(vhp->backupDate));
+       vcb->vcbWrCnt   = SWAP_BE32(vhp->writeCount);
+       vcb->vcbFilCnt  = SWAP_BE32(vhp->fileCount);
+       vcb->vcbDirCnt  = SWAP_BE32(vhp->folderCount);
        
        /* copy 32 bytes of Finder info */
        bcopy(vhp->finderInfo, vcb->vcbFndrInfo, sizeof(vhp->finderInfo));    
 
        vcb->vcbAlBlSt = 0;             /* hfs+ allocation blocks start at first block of volume */
-       vcb->vcbWrCnt++;                /* compensate for write of Volume Header on last flush */
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
+               vcb->vcbWrCnt++;        /* compensate for write of Volume Header on last flush */
 
-       VCB_LOCK_INIT(vcb);
-
-       /*      Now fill in the Extended VCB info */
-       vcb->nextAllocation                     =       SWAP_BE32 (vhp->nextAllocation);
-       vcb->totalBlocks                        =       SWAP_BE32 (vhp->totalBlocks);
-       vcb->freeBlocks                         =       SWAP_BE32 (vhp->freeBlocks);
-       vcb->blockSize                          =       SWAP_BE32 (vhp->blockSize);
-       vcb->checkedDate                        =       SWAP_BE32 (vhp->checkedDate);
-       vcb->encodingsBitmap            =       SWAP_BE64 (vhp->encodingsBitmap);
+       /* Now fill in the Extended VCB info */
+       vcb->nextAllocation     = SWAP_BE32(vhp->nextAllocation);
+       vcb->totalBlocks        = SWAP_BE32(vhp->totalBlocks);
+       vcb->allocLimit         = vcb->totalBlocks;
+       vcb->freeBlocks         = SWAP_BE32(vhp->freeBlocks);
+       vcb->blockSize          = blockSize;
+       vcb->encodingsBitmap    = SWAP_BE64(vhp->encodingsBitmap);
+       vcb->localCreateDate    = SWAP_BE32(vhp->createDate);
        
-       vcb->hfsPlusIOPosOffset         =       embBlkOffset * 512;
+       vcb->hfsPlusIOPosOffset = embeddedOffset;
 
-       vcb->altIDSector = embBlkOffset + sectors - 2;
+       /* Default to no free block reserve */
+       vcb->reserveBlocks = 0;
 
-       vcb->localCreateDate            =       SWAP_BE32 (vhp->createDate); /* in local time, not GMT! */
+       /*
+        * Update the logical block size in the mount struct
+        * (currently set up from the wrapper MDB) using the
+        * new blocksize value:
+        */
+       hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_logical_block_size);
+       vcb->vcbVBMIOSize = min(vcb->blockSize, MAXPHYSIO);
 
-    /* Update the logical block size in the mount struct (currently set up from the wrapper MDB)
-       using the new blocksize value: */
-    hfsmp->hfs_logBlockSize = BestBlockSizeFit(vcb->blockSize, MAXBSIZE, hfsmp->hfs_phys_block_size);
+       /*
+        * Validate and initialize the location of the alternate volume header.
+        */
+       spare_sectors = hfsmp->hfs_logical_block_count -
+                       (((daddr64_t)vcb->totalBlocks * blockSize) /
+                          hfsmp->hfs_logical_block_size);
 
-    // XXX PPD: Should check here for hardware lock flag and set flags in VCB/MP appropriately
-    // vcb->vcbAtrb |= kVolumeHardwareLockMask;        // XXX this line for debugging only!!!!
+       if (spare_sectors > (daddr64_t)(blockSize / hfsmp->hfs_logical_block_size)) {
+               hfsmp->hfs_alt_id_sector = 0;  /* partition has grown! */
+       } else {
+               hfsmp->hfs_alt_id_sector = (hfsmp->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size) +
+                                          HFS_ALT_SECTOR(hfsmp->hfs_logical_block_size,
+                                                         hfsmp->hfs_logical_block_count);
+       }
 
-    // Initialize our dirID/nodePtr cache associated with this volume.
-    retval = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
-    if (retval != noErr) goto ErrorExit;
+       bzero(&cndesc, sizeof(cndesc));
+       cndesc.cd_parentcnid = kHFSRootParentID;
+       cndesc.cd_flags |= CD_ISMETA;
+       bzero(&cnattr, sizeof(cnattr));
+       cnattr.ca_linkcount = 1;
+       cnattr.ca_mode = S_IFREG;
 
        /*
-        * Set up Extents B-tree vnode...
-        */ 
-       retval = GetInitializedVNode(hfsmp, &tmpvnode, 0);
-       if (retval) goto ErrorExit;
-    fdp = &vhp->extentsFile;
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-       retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
-                                                               kHFSExtentsFileID, CompareExtentKeysPlus);
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-    if (retval) goto ErrorExit;
+        * Set up Extents B-tree vnode
+        */
+       cndesc.cd_nameptr = hfs_extname;
+       cndesc.cd_namelen = strlen((char *)hfs_extname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSExtentsFileID;
+
+       cfork.cf_size    = SWAP_BE64 (vhp->extentsFile.logicalSize);
+       cfork.cf_new_size= 0;
+       cfork.cf_clump   = SWAP_BE32 (vhp->extentsFile.clumpSize);
+       cfork.cf_blocks  = SWAP_BE32 (vhp->extentsFile.totalBlocks);
+       cfork.cf_vblocks = 0;
+       cnattr.ca_blocks = cfork.cf_blocks;
+       for (i = 0; i < kHFSPlusExtentDensity; i++) {
+               cfork.cf_extents[i].startBlock =
+                               SWAP_BE32 (vhp->extentsFile.extents[i].startBlock);
+               cfork.cf_extents[i].blockCount =
+                               SWAP_BE32 (vhp->extentsFile.extents[i].blockCount);
+       }
+       retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+                                &hfsmp->hfs_extents_vp);
+       if (retval)
+       {
+               goto ErrorExit;
+       }
+       hfsmp->hfs_extents_cp = VTOC(hfsmp->hfs_extents_vp);
+       hfs_unlock(hfsmp->hfs_extents_cp);
 
+       retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_extents_vp),
+                                         (KeyCompareProcPtr) CompareExtentKeysPlus));
+       if (retval)
+       {
+               goto ErrorExit;
+       }
        /*
-        * Set up Catalog B-tree vnode...
+        * Set up Catalog B-tree vnode
         */ 
-       retval = GetInitializedVNode(hfsmp, &tmpvnode, 0);
-       if (retval) goto ErrorExit;
-       fdp = &vhp->catalogFile;
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-       retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
-                                                               kHFSCatalogFileID, CompareExtendedCatalogKeys);
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-       if (retval) goto ErrorExit;
+       cndesc.cd_nameptr = hfs_catname;
+       cndesc.cd_namelen = strlen((char *)hfs_catname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSCatalogFileID;
+
+       cfork.cf_size    = SWAP_BE64 (vhp->catalogFile.logicalSize);
+       cfork.cf_clump   = SWAP_BE32 (vhp->catalogFile.clumpSize);
+       cfork.cf_blocks  = SWAP_BE32 (vhp->catalogFile.totalBlocks);
+       cfork.cf_vblocks = 0;
+       cnattr.ca_blocks = cfork.cf_blocks;
+       for (i = 0; i < kHFSPlusExtentDensity; i++) {
+               cfork.cf_extents[i].startBlock =
+                               SWAP_BE32 (vhp->catalogFile.extents[i].startBlock);
+               cfork.cf_extents[i].blockCount =
+                               SWAP_BE32 (vhp->catalogFile.extents[i].blockCount);
+       }
+       retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+                                &hfsmp->hfs_catalog_vp);
+       if (retval) {
+               goto ErrorExit;
+       }
+       hfsmp->hfs_catalog_cp = VTOC(hfsmp->hfs_catalog_vp);
+       hfs_unlock(hfsmp->hfs_catalog_cp);
+
+       retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+                                         (KeyCompareProcPtr) CompareExtendedCatalogKeys));
+       if (retval) {
+               goto ErrorExit;
+       }
+       if ((hfsmp->hfs_flags & HFS_X) &&
+           BTGetInformation(VTOF(hfsmp->hfs_catalog_vp), 0, &btinfo) == 0) {
+               if (btinfo.keyCompareType == kHFSBinaryCompare) {
+                       hfsmp->hfs_flags |= HFS_CASE_SENSITIVE;
+                       /* Install a case-sensitive key compare */
+                       (void) BTOpenPath(VTOF(hfsmp->hfs_catalog_vp),
+                                         (KeyCompareProcPtr)cat_binarykeycompare);
+               }
+       }
 
        /*
-        * Set up Allocation file vnode...
+        * Set up Allocation file vnode
         */  
-       retval = GetInitializedVNode(hfsmp, &tmpvnode, 0);
-       if (retval) goto ErrorExit;
-       fdp = &vhp->allocationFile;
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-       retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
-                                                               kHFSAllocationFileID, NULL);
-    SWAP_HFS_PLUS_FORK_DATA (fdp);
-       if (retval) goto ErrorExit;
+       cndesc.cd_nameptr = hfs_vbmname;
+       cndesc.cd_namelen = strlen((char *)hfs_vbmname);
+       cndesc.cd_cnid = cnattr.ca_fileid = kHFSAllocationFileID;
+
+       cfork.cf_size    = SWAP_BE64 (vhp->allocationFile.logicalSize);
+       cfork.cf_clump   = SWAP_BE32 (vhp->allocationFile.clumpSize);
+       cfork.cf_blocks  = SWAP_BE32 (vhp->allocationFile.totalBlocks);
+       cfork.cf_vblocks = 0;
+       cnattr.ca_blocks = cfork.cf_blocks;
+       for (i = 0; i < kHFSPlusExtentDensity; i++) {
+               cfork.cf_extents[i].startBlock =
+                               SWAP_BE32 (vhp->allocationFile.extents[i].startBlock);
+               cfork.cf_extents[i].blockCount =
+                               SWAP_BE32 (vhp->allocationFile.extents[i].blockCount);
+       }
+       retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+                                &hfsmp->hfs_allocation_vp);
+       if (retval) {
+               goto ErrorExit;
+       }
+       hfsmp->hfs_allocation_cp = VTOC(hfsmp->hfs_allocation_vp);
+       hfs_unlock(hfsmp->hfs_allocation_cp);
+
+       /*
+        * Set up Attribute B-tree vnode
+        */
+       if (vhp->attributesFile.totalBlocks != 0) {
+               cndesc.cd_nameptr = hfs_attrname;
+               cndesc.cd_namelen = strlen((char *)hfs_attrname);
+               cndesc.cd_cnid = cnattr.ca_fileid = kHFSAttributesFileID;
+       
+               cfork.cf_size    = SWAP_BE64 (vhp->attributesFile.logicalSize);
+               cfork.cf_clump   = SWAP_BE32 (vhp->attributesFile.clumpSize);
+               cfork.cf_blocks  = SWAP_BE32 (vhp->attributesFile.totalBlocks);
+               cfork.cf_vblocks = 0;
+               cnattr.ca_blocks = cfork.cf_blocks;
+               for (i = 0; i < kHFSPlusExtentDensity; i++) {
+                       cfork.cf_extents[i].startBlock =
+                                       SWAP_BE32 (vhp->attributesFile.extents[i].startBlock);
+                       cfork.cf_extents[i].blockCount =
+                                       SWAP_BE32 (vhp->attributesFile.extents[i].blockCount);
+               }
+               retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+                                        &hfsmp->hfs_attribute_vp);
+               if (retval) {
+                       goto ErrorExit;
+               }
+               hfsmp->hfs_attribute_cp = VTOC(hfsmp->hfs_attribute_vp);
+               hfs_unlock(hfsmp->hfs_attribute_cp);
+               retval = MacToVFSError(BTOpenPath(VTOF(hfsmp->hfs_attribute_vp),
+                                                 (KeyCompareProcPtr) hfs_attrkeycompare));
+               if (retval) {
+                       goto ErrorExit;
+               }
+       }
+
        /*
-        * Now that Catalog file is open get the volume name from the catalog
+        * Set up Startup file vnode
         */
-       retval = MacToVFSError( GetVolumeNameFromCatalog(vcb) );        
-       if (retval != noErr) goto ErrorExit;
+       if (vhp->startupFile.totalBlocks != 0) {
+               cndesc.cd_nameptr = hfs_startupname;
+               cndesc.cd_namelen = strlen((char *)hfs_startupname);
+               cndesc.cd_cnid = cnattr.ca_fileid = kHFSStartupFileID;
+       
+               cfork.cf_size    = SWAP_BE64 (vhp->startupFile.logicalSize);
+               cfork.cf_clump   = SWAP_BE32 (vhp->startupFile.clumpSize);
+               cfork.cf_blocks  = SWAP_BE32 (vhp->startupFile.totalBlocks);
+               cfork.cf_vblocks = 0;
+               cnattr.ca_blocks = cfork.cf_blocks;
+               for (i = 0; i < kHFSPlusExtentDensity; i++) {
+                       cfork.cf_extents[i].startBlock =
+                                       SWAP_BE32 (vhp->startupFile.extents[i].startBlock);
+                       cfork.cf_extents[i].blockCount =
+                                       SWAP_BE32 (vhp->startupFile.extents[i].blockCount);
+               }
+               retval = hfs_getnewvnode(hfsmp, NULL, NULL, &cndesc, 0, &cnattr, &cfork,
+                                        &hfsmp->hfs_startup_vp);
+               if (retval) {
+                       goto ErrorExit;
+               }
+               hfsmp->hfs_startup_cp = VTOC(hfsmp->hfs_startup_vp);
+               hfs_unlock(hfsmp->hfs_startup_cp);
+       }
+       
+       /* Pick up volume name and create date */
+       retval = cat_idlookup(hfsmp, kHFSRootFolderID, 0, &cndesc, &cnattr, NULL);
+       if (retval) {
+               goto ErrorExit;
+       }
+       vcb->vcbCrDate = cnattr.ca_itime;
+       vcb->volumeNameEncodingHint = cndesc.cd_encoding;
+       bcopy(cndesc.cd_nameptr, vcb->vcbVN, min(255, cndesc.cd_namelen));
+       cat_releasedesc(&cndesc);
 
        /* mark the volume dirty (clear clean unmount bit) */
        vcb->vcbAtrb &= ~kHFSVolumeUnmountedMask;
+       if (hfsmp->jnl && (hfsmp->hfs_flags & HFS_READ_ONLY) == 0) {
+               hfs_flushvolumeheader(hfsmp, TRUE, 0);
+       }
+
+       /* kHFSHasFolderCount is only supported/updated on HFSX volumes */
+       if ((hfsmp->hfs_flags & HFS_X) != 0) {
+               hfsmp->hfs_flags |= HFS_FOLDERCOUNT;
+       }
+
+       //
+       // Check if we need to do late journal initialization.  This only
+       // happens if a previous version of MacOS X (or 9) touched the disk.
+       // In that case hfs_late_journal_init() will go re-locate the journal 
+       // and journal_info_block files and validate that they're still kosher.
+       //
+       if (   (vcb->vcbAtrb & kHFSVolumeJournaledMask)
+               && (SWAP_BE32(vhp->lastMountedVersion) != kHFSJMountVersion)
+               && (hfsmp->jnl == NULL)) {
+
+               retval = hfs_late_journal_init(hfsmp, vhp, args);
+               if (retval != 0) {
+                       if (retval == EROFS) {
+                               // EROFS is a special error code that means the volume has an external
+                               // journal which we couldn't find.  in that case we do not want to
+                               // rewrite the volume header - we'll just refuse to mount the volume.
+                               retval = EINVAL;
+                               goto ErrorExit;
+                       }
+
+                       hfsmp->jnl = NULL;
+                       
+                       // if the journal failed to open, then set the lastMountedVersion
+                       // to be "FSK!" which fsck_hfs will see and force the fsck instead
+                       // of just bailing out because the volume is journaled.
+                       if (!(hfsmp->hfs_flags & HFS_READ_ONLY)) {
+                               HFSPlusVolumeHeader *jvhp;
+                               daddr64_t mdb_offset;
+                               struct buf *bp = NULL;
+                               
+                               hfsmp->hfs_flags |= HFS_NEED_JNL_RESET;
+                                   
+                               mdb_offset = (daddr64_t)((embeddedOffset / blockSize) + HFS_PRI_SECTOR(blockSize));
+
+                               bp = NULL;
+                               retval = (int)buf_meta_bread(hfsmp->hfs_devvp, 
+                                               HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
+                                               hfsmp->hfs_physical_block_size, cred, &bp);
+                               if (retval == 0) {
+                                       jvhp = (HFSPlusVolumeHeader *)(buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size));
+                                           
+                                       if (SWAP_BE16(jvhp->signature) == kHFSPlusSigWord || SWAP_BE16(jvhp->signature) == kHFSXSigWord) {
+                                               printf ("hfs(3): Journal replay fail.  Writing lastMountVersion as FSK!\n");
+                                               jvhp->lastMountedVersion = SWAP_BE32(kFSKMountVersion);
+                                               buf_bwrite(bp);
+                                       } else {
+                                               buf_brelse(bp);
+                                       }
+                                       bp = NULL;
+                               } else if (bp) {
+                                       buf_brelse(bp);
+                                       // clear this so the error exit path won't try to use it
+                                       bp = NULL;
+                           }
+                       }
+
+                       retval = EINVAL;
+                       goto ErrorExit;
+               } else if (hfsmp->jnl) {
+                       vfs_setflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
+               }
+       } else if (hfsmp->jnl || ((vcb->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) {
+               struct cat_attr jinfo_attr, jnl_attr;
+               
+               if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+                   vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               }
+
+               // if we're here we need to fill in the fileid's for the
+               // journal and journal_info_block.
+               hfsmp->hfs_jnlinfoblkid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jinfo_attr, NULL);
+               hfsmp->hfs_jnlfileid    = GetFileInfo(vcb, kRootDirID, ".journal", &jnl_attr, NULL);
+               if (hfsmp->hfs_jnlinfoblkid == 0 || hfsmp->hfs_jnlfileid == 0) {
+                       printf("hfs: danger! couldn't find the file-id's for the journal or journal_info_block\n");
+                       printf("hfs: jnlfileid %d, jnlinfoblkid %d\n", hfsmp->hfs_jnlfileid, hfsmp->hfs_jnlinfoblkid);
+               }
+
+               if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+                   vcb->vcbAtrb |= kHFSVolumeJournaledMask;
+               }
+
+               if (hfsmp->jnl == NULL) {
+                   vfs_clearflags(hfsmp->hfs_mp, (u_int64_t)((unsigned int)MNT_JOURNALED));
+               }
+       }
 
-       /* setup private/hidden directory for unlinked files */
-       hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);
+       /*
+        * Establish a metadata allocation zone.
+        */
+       hfs_metadatazone_init(hfsmp);
 
        /*
-        * all done with metadata files so we can unlock now...
+        * Make any metadata zone adjustments.
         */
-       VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
-       VOP_UNLOCK(vcb->catalogRefNum, 0, p);
-       VOP_UNLOCK(vcb->extentsRefNum, 0, p);
+       if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
+               /* Keep the roving allocator out of the metadata zone. */
+               if (vcb->nextAllocation >= hfsmp->hfs_metazone_start &&
+                   vcb->nextAllocation <= hfsmp->hfs_metazone_end) {       
+                       HFS_UPDATE_NEXT_ALLOCATION(hfsmp, hfsmp->hfs_metazone_end + 1);
+               }
+       } else {
+               if (vcb->nextAllocation <= 1) {
+                       vcb->nextAllocation = hfsmp->hfs_min_alloc_start;
+               }
+       }
+       vcb->sparseAllocation = hfsmp->hfs_min_alloc_start;
 
-       if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )             //      if the disk is not write protected
+       /* Setup private/hidden directories for hardlinks. */
+       hfs_privatedir_init(hfsmp, FILE_HARDLINKS);
+       hfs_privatedir_init(hfsmp, DIR_HARDLINKS);
+
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) 
+               hfs_remove_orphans(hfsmp);
+
+       /* See if we need to erase unused Catalog nodes due to <rdar://problem/6947811>. */
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0)
        {
-               MarkVCBDirty( vcb );                                                            //      mark VCB dirty so it will be written
+               retval = hfs_erase_unused_nodes(hfsmp);
+               if (retval)
+                       goto ErrorExit;
        }
        
-       DBG_VFS(("hfs_MountHFSPlusVolume: returning (%d)\n", retval));
+       if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )     // if the disk is not write protected
+       {
+               MarkVCBDirty( vcb );    // mark VCB dirty so it will be written
+       }
 
-       return (0);
+       /*
+        * Allow hot file clustering if conditions allow.
+        */
+       if ((hfsmp->hfs_flags & HFS_METADATA_ZONE)  &&
+           ((hfsmp->hfs_flags & HFS_READ_ONLY) == 0) &&
+           ((hfsmp->hfs_mp->mnt_kern_flag & MNTK_SSD) == 0)) {
+               (void) hfs_recording_init(hfsmp);
+       }
 
+       /* Force ACLs on HFS+ file systems. */
+       vfs_setextendedsecurity(HFSTOVFS(hfsmp));
+
+       /* Check if volume supports writing of extent-based extended attributes */
+       hfs_check_volxattr(hfsmp, HFS_SET_XATTREXTENTS_STATE);
+
+       return (0);
 
 ErrorExit:
        /*
-        * A fatal error occured and the volume cannot be mounted
+        * A fatal error occurred and the volume cannot be mounted
         * release any resources that we aquired...
         */
-
-       DBG_VFS(("hfs_MountHFSPlusVolume: fatal error (%d)\n", retval));
-
-       InvalidateCatalogCache(vcb);
-    
-       ReleaseMetaFileVNode(vcb->allocationsRefNum);
-       ReleaseMetaFileVNode(vcb->catalogRefNum);
-       ReleaseMetaFileVNode(vcb->extentsRefNum);
+       if (hfsmp->hfs_attribute_vp)
+               ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
+       ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
+       ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
+       ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
 
        return (retval);
 }
@@ -497,90 +767,19 @@ ErrorExit:
  */
 static void ReleaseMetaFileVNode(struct vnode *vp)
 {
-       if (vp)
-       {
-               FCB *fcb = VTOFCB(vp);
+       struct filefork *fp;
 
-               if (fcb->fcbBTCBPtr != NULL)
-                       (void) BTClosePath(fcb);        /* ignore errors since there is only one path open */
+       if (vp && (fp = VTOF(vp))) {
+               if (fp->fcbBTCBPtr != NULL) {
+                       (void)hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK);
+                       (void) BTClosePath(fp);
+                       hfs_unlock(VTOC(vp));
+               }
 
                /* release the node even if BTClosePath fails */
-               if (VOP_ISLOCKED(vp))
-                       vput(vp);
-               else
-                       vrele(vp);
-       }
-}
-
-
-/*
- * InitMetaFileVNode
- *
- * vp  U L L
- */
-static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, const HFSPlusExtentRecord extents,
-                                                        HFSCatalogNodeID fileID, void * keyCompareProc)
-{
-       FCB                             *fcb;
-       ExtendedVCB             *vcb;
-       int                             result = 0;
-
-       DBG_ASSERT(vp != NULL);
-       DBG_ASSERT(vp->v_data != NULL);
-
-       vcb = VTOVCB(vp);
-       fcb = VTOFCB(vp);
-
-       switch (fileID)
-       {
-               case kHFSExtentsFileID:
-                       vcb->extentsRefNum = vp;
-                       break;
-
-               case kHFSCatalogFileID:
-                       vcb->catalogRefNum = vp;
-                       break;
-
-               case kHFSAllocationFileID:
-                       vcb->allocationsRefNum = vp;
-                       break;
-
-               default:
-                       panic("InitMetaFileVNode: invalid fileID!");
+               vnode_recycle(vp);
+               vnode_put(vp);
        }
-
-       fcb->fcbEOF = eof;
-       fcb->fcbPLen = eof;
-       fcb->fcbClmpSize = clumpSize;
-       H_FILEID(VTOH(vp)) = fileID;
-       H_DIRID(VTOH(vp)) = kHFSRootParentID;
-       H_FORKTYPE(VTOH(vp)) = kSysFile;
-
-    bcopy(extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
-
-       /*
-        * Lock the hfsnode and insert the hfsnode into the hash queue:
-        */
-       hfs_vhashins(H_DEV(VTOH(vp)), fileID, VTOH(vp));
-       vp->v_flag |= VSYSTEM;  /* tag our metadata files (used by vflush call) */
-
-       /* As the vnode is a system vnode we don't need UBC */
-       if(UBCINFOEXISTS(vp)) {
-               /* So something is wrong if the it exists */
-               panic("ubc exists for system vnode");
-       }
-    
-    if (keyCompareProc != NULL) {
-               result = BTOpenPath(fcb,
-                                                       (KeyCompareProcPtr) keyCompareProc,
-                                                       GetBTreeBlock,
-                                                       ReleaseBTreeBlock,
-                                                       ExtendBTreeFile,
-                                                       SetBTreeBlockSize);
-               result = MacToVFSError(result);
-       }
-
-    return (result);
 }
 
 
@@ -591,3126 +790,2083 @@ static int InitMetaFileVNode(struct vnode *vp, off_t eof, u_long clumpSize, cons
 *
 *************************************************************/
 
-short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
+__private_extern__
+int
+hfsUnmount( register struct hfsmount *hfsmp, __unused struct proc *p)
 {
-    ExtendedVCB        *vcb = HFSTOVCB(hfsmp);
-    int                        retval = E_NONE;
+       /* Get rid of our attribute data vnode (if any). */
+       if (hfsmp->hfs_attrdata_vp) {
+               vnode_t advp = hfsmp->hfs_attrdata_vp;
+       
+               if (vnode_get(advp) == 0) {
+                       vnode_rele_ext(advp, O_EVTONLY, 0);
+                       vnode_put(advp);
+               }
+               hfsmp->hfs_attrdata_vp = NULLVP;
+       }
 
-       (void) DisposeMRUCache(vcb->hintCachePtr);
-       InvalidateCatalogCache( vcb );
-       // XXX PPD: Should dispose of any allocated volume cache here: call DisposeVolumeCacheBlocks( vcb )?
+       if (hfsmp->hfs_startup_vp)
+               ReleaseMetaFileVNode(hfsmp->hfs_startup_vp);
 
-       (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
-       (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
+       if (hfsmp->hfs_allocation_vp)
+               ReleaseMetaFileVNode(hfsmp->hfs_allocation_vp);
 
-       if (vcb->vcbSigWord == kHFSPlusSigWord)
-               ReleaseMetaFileVNode(vcb->allocationsRefNum);
+       if (hfsmp->hfs_attribute_vp)
+               ReleaseMetaFileVNode(hfsmp->hfs_attribute_vp);
 
-       ReleaseMetaFileVNode(vcb->catalogRefNum);
-       ReleaseMetaFileVNode(vcb->extentsRefNum);
+       ReleaseMetaFileVNode(hfsmp->hfs_catalog_vp);
+       ReleaseMetaFileVNode(hfsmp->hfs_extents_vp);
 
-       return (retval);
+       /*
+        * Setting these pointers to NULL so that any references
+        * past this point will fail, and tell us the point of failure.
+        * Also, facilitates a check in hfs_update for a null catalog
+        * vp
+        */
+       hfsmp->hfs_allocation_vp = NULL;
+       hfsmp->hfs_attribute_vp = NULL;
+       hfsmp->hfs_catalog_vp = NULL;
+       hfsmp->hfs_extents_vp = NULL;
+       hfsmp->hfs_startup_vp = NULL;
+
+       return (0);
 }
 
 
 /*
- * Performs a lookup on the given dirID, name. Returns the catalog info
- *
- * If len is -1, then it is a null terminated string, pass it along to MacOS as kUndefinedStrLen
+ * Test if fork has overflow extents.
  */
-
-short hfs_getcatalog (ExtendedVCB *vcb, UInt32 parentDirID, char *name, short len, hfsCatalogInfo *catInfo)
+__private_extern__
+int
+overflow_extents(struct filefork *fp)
 {
-       OSErr result;
-    UInt32             length;
-       struct FInfo *fip;
-       
-    if (len == -1 )    {               /* Convert it to MacOS terms */
-        if (name)
-            length = strlen(name);
-        else
-            length = kUndefinedStrLen;
-    }
-       else
-        length = len;
-
-       result = GetCatalogNode(vcb, parentDirID, name, length, catInfo->hint, &catInfo->nodeData,      &catInfo->hint);
-
-#if HFS_DIAGNOSTICS
-       if (catInfo->nodeData.cnm_nameptr) {
-               DBG_ASSERT(strlen(catInfo->nodeData.cnm_nameptr) == catInfo->nodeData.cnm_length);
-       }
-#endif
+       u_int32_t blocks;
+
+       //
+       // If the vnode pointer is NULL then we're being called
+       // from hfs_remove_orphans() with a faked-up filefork
+       // and therefore it has to be an HFS+ volume.  Otherwise
+       // we check through the volume header to see what type
+       // of volume we're on.
+        //
+       if (FTOV(fp) == NULL || VTOVCB(FTOV(fp))->vcbSigWord == kHFSPlusSigWord) {
+               if (fp->ff_extents[7].blockCount == 0)
+                       return (0);
+
+               blocks = fp->ff_extents[0].blockCount +
+                        fp->ff_extents[1].blockCount +
+                        fp->ff_extents[2].blockCount +
+                        fp->ff_extents[3].blockCount +
+                        fp->ff_extents[4].blockCount +
+                        fp->ff_extents[5].blockCount +
+                        fp->ff_extents[6].blockCount +
+                        fp->ff_extents[7].blockCount;  
+       } else {
+               if (fp->ff_extents[2].blockCount == 0)
+                       return false;
+               
+               blocks = fp->ff_extents[0].blockCount +
+                        fp->ff_extents[1].blockCount +
+                        fp->ff_extents[2].blockCount;  
+         }
 
-#if HFS_HARDLINKS
-       if (result)
-               goto exit;
+       return (fp->ff_blocks > blocks);
+}
 
-       fip = (struct FInfo *) &catInfo->nodeData.cnd_finderInfo;
 
+/*
+ * Lock HFS system file(s).
+ */
+__private_extern__
+int
+hfs_systemfile_lock(struct hfsmount *hfsmp, int flags, enum hfslocktype locktype)
+{
        /*
-        * if we encounter an indirect link (hardlink) then auto resolve it...
+        * Locking order is Catalog file, Attributes file, Startup file, Bitmap file, Extents file
         */
-       if ((catInfo->nodeData.cnd_type == kCatalogFileNode)   &&
-           (fip->fdType == kHardLinkFileType)                 &&
-           (fip->fdCreator == kHFSPlusCreator)                &&
-           ((catInfo->nodeData.cnd_createDate == vcb->vcbCrDate) ||
-           (catInfo->nodeData.cnd_createDate == VCBTOHFS(vcb)->hfs_metadata_createdate))) {
-
-               u_int32_t indlinkno;
-               char iNodeName[32];
-               UInt32 privDir = VCBTOHFS(vcb)->hfs_private_metadata_dir;
-               
-               indlinkno = catInfo->nodeData.cnd_iNodeNum;
-               MAKE_INODE_NAME(iNodeName, indlinkno);
+       if (flags & SFL_CATALOG) {
+
+#ifdef HFS_CHECK_LOCK_ORDER
+               if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Attributes before Catalog)");
+               }
+               if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Startup before Catalog)");
+               }
+               if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Extents before Catalog)");
+               }
+#endif /* HFS_CHECK_LOCK_ORDER */
 
+               (void) hfs_lock(hfsmp->hfs_catalog_cp, locktype);
                /*
-                * Get nodeData from the data node file. 
-                * Flag the node data to NOT copy the name, perserver the original
+                * When the catalog file has overflow extents then
+                * also acquire the extents b-tree lock if its not
+                * already requested.
                 */
-               catInfo->nodeData.cnm_flags |= kCatNameNoCopyName;
-               result = GetCatalogNode(vcb, privDir, iNodeName, 0, 0, &catInfo->nodeData, &catInfo->hint);
-               catInfo->nodeData.cnm_flags &= ~kCatNameNoCopyName;             /* Just to keep things like they should be */
-
-               /* make sure there's at lease 1 reference */
-               if (result == 0) {
-                       if (catInfo->nodeData.cnd_linkCount == 0)
-                               catInfo->nodeData.cnd_linkCount = 2;
-                       /* keep a copy of iNodeNum to put into h_indnodeno */
-                       catInfo->nodeData.cnd_iNodeNumCopy = indlinkno;
+               if ((flags & SFL_EXTENTS) == 0 &&
+                   overflow_extents(VTOF(hfsmp->hfs_catalog_vp))) {
+                       flags |= SFL_EXTENTS;
                }
-               
-               /* if we can not resolve the link, allow the link to be
-                * exposed (as an empty file) so it can be deleted
-                */
-               if (result == cmNotFound)
-                       result = 0;
        }
+       if (flags & SFL_ATTRIBUTE) {
 
-exit:
-#endif
-
-       if (result)
-               DBG_ERR(("on Lookup, GetCatalogNode returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));
-
-       return MacToVFSError(result);
-}
-
-
+#ifdef HFS_CHECK_LOCK_ORDER
+               if (hfsmp->hfs_startup_cp && hfsmp->hfs_startup_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Startup before Attributes)");
+               }
+               if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Extents before Attributes)");
+               }
+#endif /* HFS_CHECK_LOCK_ORDER */
 
-short hfsDelete (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, short isfile, UInt32 catalogHint)
-{
-    OSErr result = noErr;
-    
-    /* XXX have all the file's blocks been flushed/trashed? */
+               if (hfsmp->hfs_attribute_cp) {
+                       (void) hfs_lock(hfsmp->hfs_attribute_cp, locktype);
+                       /*
+                        * When the attribute file has overflow extents then
+                        * also acquire the extents b-tree lock if its not
+                        * already requested.
+                        */
+                       if ((flags & SFL_EXTENTS) == 0 &&
+                           overflow_extents(VTOF(hfsmp->hfs_attribute_vp))) {
+                               flags |= SFL_EXTENTS;
+                       }
+               } else {
+                       flags &= ~SFL_ATTRIBUTE;
+               }
+       }
+       if (flags & SFL_STARTUP) {
+#ifdef HFS_CHECK_LOCK_ORDER
+               if (hfsmp-> hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == current_thread()) {
+                       panic("hfs_systemfile_lock: bad lock order (Extents before Startup)");
+               }
+#endif /* HFS_CHECK_LOCK_ORDER */
 
-       /*
-        * DeleteFile will delete the catalog node and then
-        * free up any disk space used by the file.
+               (void) hfs_lock(hfsmp->hfs_startup_cp, locktype);
+               /*
+                * When the startup file has overflow extents then
+                * also acquire the extents b-tree lock if its not
+                * already requested.
+                */
+               if ((flags & SFL_EXTENTS) == 0 &&
+                   overflow_extents(VTOF(hfsmp->hfs_startup_vp))) {
+                       flags |= SFL_EXTENTS;
+               }
+       }
+       /* 
+        * To prevent locks being taken in the wrong order, the extent lock
+        * gets a bitmap lock as well.
         */
-       if (isfile)
-               result = DeleteFile(vcb, parentDirID, name, catalogHint);
-       else /* is a directory */
-               result = DeleteCatalogNode(vcb, parentDirID, name, catalogHint);
-
-    if (result)
-        DBG_ERR(("on Delete, DeleteFile returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));
-               
-       return MacToVFSError(result);
+       if (flags & (SFL_BITMAP | SFL_EXTENTS)) {
+               /*
+                * Since the only bitmap operations are clearing and
+                * setting bits we always need exclusive access. And
+                * when we have a journal, we can "hide" behind that
+                * lock since we can only change the bitmap from
+                * within a transaction.
+                */
+               if (hfsmp->jnl || (hfsmp->hfs_allocation_cp == NULL)) {
+                       flags &= ~SFL_BITMAP;
+               } else {
+                       (void) hfs_lock(hfsmp->hfs_allocation_cp, HFS_EXCLUSIVE_LOCK);
+                       /* The bitmap lock is also grabbed when only extent lock 
+                        * was requested. Set the bitmap lock bit in the lock
+                        * flags which callers will use during unlock.
+                        */
+                       flags |= SFL_BITMAP;
+               }
+       }
+       if (flags & SFL_EXTENTS) {
+               /*
+                * Since the extents btree lock is recursive we always
+                * need exclusive access.
+                */
+               (void) hfs_lock(hfsmp->hfs_extents_cp, HFS_EXCLUSIVE_LOCK);
+       }
+       return (flags);
 }
 
-
-short hfsMoveRename (ExtendedVCB *vcb, UInt32 oldDirID, char *oldName, UInt32 newDirID, char *newName, UInt32 *hint)
+/*
+ * unlock HFS system file(s).
+ */
+__private_extern__
+void
+hfs_systemfile_unlock(struct hfsmount *hfsmp, int flags)
 {
-    OSErr result = noErr;
+       struct timeval tv;
+       u_int32_t lastfsync;
+       int numOfLockedBuffs;
 
-    result = MoveRenameCatalogNode(vcb, oldDirID,oldName, *hint, newDirID, newName, hint);
-
-    if (result)
-        DBG_ERR(("on hfsMoveRename, MoveRenameCatalogNode returned: %d: newdirid: %ld newname: %s\n", result, newDirID, newName));
-        
-
-    return MacToVFSError(result);
+       if (hfsmp->jnl == NULL) {
+               microuptime(&tv);
+               lastfsync = tv.tv_sec;
+       }
+       if (flags & SFL_STARTUP && hfsmp->hfs_startup_cp) {
+               hfs_unlock(hfsmp->hfs_startup_cp);
+       }
+       if (flags & SFL_ATTRIBUTE && hfsmp->hfs_attribute_cp) {
+               if (hfsmp->jnl == NULL) {
+                       BTGetLastSync((FCB*)VTOF(hfsmp->hfs_attribute_vp), &lastfsync);
+                       numOfLockedBuffs = count_lock_queue();
+                       if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+                           ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+                             kMaxSecsForFsync))) {
+                               hfs_btsync(hfsmp->hfs_attribute_vp, HFS_SYNCTRANS);
+                       }
+               }
+               hfs_unlock(hfsmp->hfs_attribute_cp);
+       }
+       if (flags & SFL_CATALOG) {
+               if (hfsmp->jnl == NULL) {
+                       BTGetLastSync((FCB*)VTOF(hfsmp->hfs_catalog_vp), &lastfsync);
+                       numOfLockedBuffs = count_lock_queue();
+                       if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+                           ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+                             kMaxSecsForFsync))) {
+                               hfs_btsync(hfsmp->hfs_catalog_vp, HFS_SYNCTRANS);
+                       }
+               }
+               hfs_unlock(hfsmp->hfs_catalog_cp);
+       }
+       if (flags & SFL_BITMAP) {
+               hfs_unlock(hfsmp->hfs_allocation_cp);
+       }
+       if (flags & SFL_EXTENTS) {
+               if (hfsmp->jnl == NULL) {
+                       BTGetLastSync((FCB*)VTOF(hfsmp->hfs_extents_vp), &lastfsync);
+                       numOfLockedBuffs = count_lock_queue();
+                       if ((numOfLockedBuffs > kMaxLockedMetaBuffers) ||
+                           ((numOfLockedBuffs > 1) && ((tv.tv_sec - lastfsync) >
+                             kMaxSecsForFsync))) {
+                               hfs_btsync(hfsmp->hfs_extents_vp, HFS_SYNCTRANS);
+                       }
+               }
+               hfs_unlock(hfsmp->hfs_extents_cp);
+       }
 }
 
-/* XXX SER pass back the hint so other people can use it */
-
 
-short hfsCreate(ExtendedVCB *vcb, UInt32 dirID, char *name, int        mode)
+/*
+ * RequireFileLock
+ *
+ * Check to see if a vnode is locked in the current context
+ * This is to be used for debugging purposes only!!
+ */
+#if HFS_DIAGNOSTIC
+void RequireFileLock(FileReference vp, int shareable)
 {
-    OSErr                              result = noErr;
-    HFSCatalogNodeID   catalogNodeID;
-    UInt32                             catalogHint;
-    UInt32                             type;
-
-       /* just test for directories, the default is to create a file (like symlinks) */
-       if ((mode & IFMT) == IFDIR)
-               type = kCatalogFolderNode;
-       else
-               type = kCatalogFileNode;
-
-    result = CreateCatalogNode (vcb, dirID, name, type, &catalogNodeID, &catalogHint);
-    return MacToVFSError(result);
-}
-
-
-short hfsCreateFileID (ExtendedVCB *vcb, UInt32 parentDirID, StringPtr name, UInt32 catalogHint, UInt32 *fileIDPtr)
-{
-       return MacToVFSError(CreateFileIDRef(vcb, parentDirID, name, catalogHint, fileIDPtr));
-}
+       int locked;
 
+       /* The extents btree and allocation bitmap are always exclusive. */
+       if (VTOC(vp)->c_fileid == kHFSExtentsFileID ||
+           VTOC(vp)->c_fileid == kHFSAllocationFileID) {
+               shareable = 0;
+       }
+       
+       locked = VTOC(vp)->c_lockowner == (void *)current_thread();
+       
+       if (!locked && !shareable) {
+               switch (VTOC(vp)->c_fileid) {
+               case kHFSExtentsFileID:
+                       panic("hfs: extents btree not locked! v: 0x%08X\n #\n", (u_int)vp);
+                       break;
+               case kHFSCatalogFileID:
+                       panic("hfs: catalog btree not locked! v: 0x%08X\n #\n", (u_int)vp);
+                       break;
+               case kHFSAllocationFileID:
+                       /* The allocation file can hide behind the jornal lock. */
+                       if (VTOHFS(vp)->jnl == NULL)
+                               panic("hfs: allocation file not locked! v: 0x%08X\n #\n", (u_int)vp);
+                       break;
+               case kHFSStartupFileID:
+                       panic("hfs: startup file not locked! v: 0x%08X\n #\n", (u_int)vp);
+               case kHFSAttributesFileID:
+                       panic("hfs: attributes btree not locked! v: 0x%08X\n #\n", (u_int)vp);
+                       break;
+               }
+       }
+}
+#endif
 
-/********************************************************************************/
-/*                                                                                                                                                             */
-/*     hfs_vget_catinfo - Returns a vnode derived from a hfs catInfo struct    */
-/*                                                                                                                                                             */
-/********************************************************************************/
 
-int hfs_vget_catinfo(struct vnode *parent_vp, struct hfsCatalogInfo *catInfo, u_int32_t forkType, struct vnode **target_vp)
+/*
+ * There are three ways to qualify for ownership rights on an object:
+ *
+ * 1. (a) Your UID matches the cnode's UID.
+ *    (b) The object in question is owned by "unknown"
+ * 2. (a) Permissions on the filesystem are being ignored and
+ *        your UID matches the replacement UID.
+ *    (b) Permissions on the filesystem are being ignored and
+ *        the replacement UID is "unknown".
+ * 3. You are root.
+ *
+ */
+int
+hfs_owner_rights(struct hfsmount *hfsmp, uid_t cnode_uid, kauth_cred_t cred,
+               __unused struct proc *p, int invokesuperuserstatus)
 {
-       int             retval = E_NONE;
-
-
-       *target_vp = hfs_vhashget(H_DEV(VTOH(parent_vp)), catInfo->nodeData.cnd_nodeID, forkType);
-
-       if (*target_vp == NULL) {
-               if (forkType == kAnyFork)
-                       if (catInfo->nodeData.cnd_type == kCatalogFolderNode) 
-                               forkType = kDirectory;
-                       else
-                               forkType = kDataFork;
-               
-               retval = hfs_vcreate( VTOVCB(parent_vp), catInfo, forkType, target_vp);
-       };
-
-       return (retval);
+       if ((kauth_cred_getuid(cred) == cnode_uid) ||                                    /* [1a] */
+           (cnode_uid == UNKNOWNUID) ||                                                                          /* [1b] */
+           ((((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) &&          /* [2] */
+             ((kauth_cred_getuid(cred) == hfsmp->hfs_uid) ||                            /* [2a] */
+               (hfsmp->hfs_uid == UNKNOWNUID))) ||                           /* [2b] */
+           (invokesuperuserstatus && (suser(cred, 0) == 0))) {    /* [3] */
+               return (0);
+       } else {        
+               return (EPERM);
+       }
 }
 
 
+u_int32_t BestBlockSizeFit(u_int32_t allocationBlockSize,
+                               u_int32_t blockSizeLimit,
+                               u_int32_t baseMultiple) {
+    /*
+       Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
+       specified limit but still an even multiple of the baseMultiple.
+     */
+    int baseBlockCount, blockCount;
+    u_int32_t trialBlockSize;
 
-/********************************************************************************/
-/*                                                                                                                                                             */
-/*     hfs_vget_fork - Returns a vnode derived from a sibling                                          */
-/*             vp is locked                                                                                                                    */
-/*                                                                                                                                                             */
-/********************************************************************************/
+    if (allocationBlockSize % baseMultiple != 0) {
+        /*
+           Whoops: the allocation blocks aren't even multiples of the specified base:
+           no amount of dividing them into even parts will be a multiple, either then!
+        */
+        return 512;            /* Hope for the best */
+    };
 
-int hfs_vget_sibling(struct vnode *vp, u_int16_t forkType, struct vnode **vpp)
-{
-    struct vnode       * target_vp = NULL;
-    int                                retval = E_NONE;
+    /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
+       from being handled as two 6K logical blocks instead of 3 4K logical blocks.
+       Even though the former (the result of the loop below) is the larger allocation
+       block size, the latter is more efficient: */
+    if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
 
+    /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
+    baseBlockCount = allocationBlockSize / baseMultiple;                               /* Now guaranteed to be an even multiple */
 
-    DBG_ASSERT(vp != NULL);
-    DBG_ASSERT(VTOH(vp) != NULL);
-    DBG_ASSERT(VTOH(vp)->h_meta != NULL);
-    DBG_ASSERT(forkType==kDataFork || forkType==kRsrcFork);
+    for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
+        trialBlockSize = blockCount * baseMultiple;
+        if (allocationBlockSize % trialBlockSize == 0) {                               /* An even multiple? */
+            if ((trialBlockSize <= blockSizeLimit) &&
+                (trialBlockSize % baseMultiple == 0)) {
+                return trialBlockSize;
+            };
+        };
+    };
 
-    target_vp = hfs_vhashget(H_DEV(VTOH(vp)), H_FILEID(VTOH(vp)), forkType);
-    
-       /* 
-        * If not in the hash, then we have to create it 
-        */
-       if (target_vp == NULL) {
-               struct proc *p = current_proc();
-               hfsCatalogInfo catInfo;
+    /* Note: we should never get here, since blockCount = 1 should always work,
+       but this is nice and safe and makes the compiler happy, too ... */
+    return 512;
+}
 
-               INIT_CATALOGDATA(&catInfo.nodeData, 0);
-               catInfo.hint = H_HINT(VTOH(vp));
 
-               /* lock catalog b-tree */
-               retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, p);
-               if (retval) goto GetCatErr_Exit;
-               
-               retval = hfs_getcatalog (VTOVCB(vp), H_DIRID(VTOH(vp)), H_NAME(VTOH(vp)), VTOH(vp)->h_meta->h_namelen, &catInfo);
-               
-               /* unlock catalog b-tree */
-               (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);
-               if (retval)  goto GetCatErr_Exit;
+__private_extern__
+u_int32_t
+GetFileInfo(ExtendedVCB *vcb, __unused u_int32_t dirid, const char *name,
+                       struct cat_attr *fattr, struct cat_fork *forkinfo)
+{
+       struct hfsmount * hfsmp;
+       struct cat_desc jdesc;
+       int lockflags;
+       int error;
        
-               retval = hfs_vcreate( VTOVCB(vp), &catInfo, forkType, &target_vp);
+       if (vcb->vcbSigWord != kHFSPlusSigWord)
+               return (0);
 
-GetCatErr_Exit:
-               CLEAN_CATALOGDATA(&catInfo.nodeData);
-       };
+       hfsmp = VCBTOHFS(vcb);
 
-Err_Exit:
+       memset(&jdesc, 0, sizeof(struct cat_desc));
+       jdesc.cd_parentcnid = kRootDirID;
+       jdesc.cd_nameptr = (const u_int8_t *)name;
+       jdesc.cd_namelen = strlen(name);
 
-       if (!retval) {
-               DBG_ASSERT(target_vp!=NULL);
-       } else {
-               DBG_ASSERT(target_vp==NULL);
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
+       error = cat_lookup(hfsmp, &jdesc, 0, NULL, fattr, forkinfo, NULL);
+       hfs_systemfile_unlock(hfsmp, lockflags);
+
+       if (error == 0) {
+               return (fattr->ca_fileid);
+       } else if (hfsmp->hfs_flags & HFS_READ_ONLY) {
+               return (0);
        }
-       
-       *vpp = target_vp;
-    return (retval);
-}
 
+       return (0);     /* XXX what callers expect on an error */
+}
 
-/************************************************************************/
-/*     hfs_vcreate - Returns a vnode derived from hfs                                                  */
-/*                                                                                                                                             */
-/*     When creating the vnode, care must be made to set the                           */
-/*     correct fields in the correct order. Calls to malloc()                          */
-/*     and other subroutines, can cause a context switch,                                      */
-/*     and the fields must be ready for the possibility                                        */
-/*                                                                                                                                             */
-/*                                                                                                                                             */
-/************************************************************************/
 
-short hfs_vcreate(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt8 forkType, struct vnode **vpp)
+/*
+ * On HFS Plus Volumes, there can be orphaned files or directories
+ * These are files or directories that were unlinked while busy. 
+ * If the volume was not cleanly unmounted then some of these may
+ * have persisted and need to be removed.
+ */
+__private_extern__
+void
+hfs_remove_orphans(struct hfsmount * hfsmp)
 {
-       struct hfsnode          *hp;
-       struct vnode            *vp;
-       struct hfsmount         *hfsmp;
-       struct hfsfilemeta      *fm;
-       struct mount            *mp;
-       struct vfsFCB           *xfcb;
-       dev_t                           dev;
-       short                           retval;
-
-#if HFS_DIAGNOSTIC
-       DBG_ASSERT(vcb != NULL);
-       DBG_ASSERT(catInfo != NULL);
-       DBG_ASSERT(vpp != NULL);
-       DBG_ASSERT((forkType == kDirectory) || (forkType == kDataFork) || (forkType == kRsrcFork));
-       if (catInfo->nodeData.cnd_type == kCatalogFolderNode) {
-                       DBG_ASSERT(forkType == kDirectory);
-       } else {
-                       DBG_ASSERT(forkType != kDirectory);
-       }
-#endif
-
-       hfsmp   = VCBTOHFS(vcb);
-       mp              = HFSTOVFS(hfsmp);
-       dev             = hfsmp->hfs_raw_dev;
-
-       /* Check if unmount in progress */
-       if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
-               *vpp = NULL;
-               return (EPERM);
-       }
-       DBG_UTILS(("\thfs_vcreate: On '%s' with forktype of %d, nodeType of 0x%08lX\n", catInfo->nodeData.cnm_nameptr, forkType, (unsigned long)catInfo->nodeData.cnd_type));
-       
-       /* Must malloc() here, since getnewvnode() can sleep */
-       MALLOC_ZONE(hp, struct hfsnode *, sizeof(struct hfsnode), M_HFSNODE, M_WAITOK);
-       bzero((caddr_t)hp, sizeof(struct hfsnode));
-       
-       /*
-        * Set that this node is in the process of being allocated
-        * Set it as soon as possible, so context switches well always hit upon it.
-        * if this is set then wakeup() MUST be called on hp after the flag is cleared
-        * DO NOT exit without clearing and waking up !!!!
-        */
-       hp->h_nodeflags |= IN_ALLOCATING;                               /* Mark this as being allocating */
-       lockinit(&hp->h_lock, PINOD, "hfsnode", 0, 0);
+       struct BTreeIterator * iterator = NULL;
+       struct FSBufferDescriptor btdata;
+       struct HFSPlusCatalogFile filerec;
+       struct HFSPlusCatalogKey * keyp;
+       struct proc *p = current_proc();
+       FCB *fcb;
+       ExtendedVCB *vcb;
+       char filename[32];
+       char tempname[32];
+       size_t namelen;
+       cat_cookie_t cookie;
+       int catlock = 0;
+       int catreserve = 0;
+       int started_tr = 0;
+       int lockflags;
+       int result;
+       int orphaned_files = 0;
+       int orphaned_dirs = 0;
+
+       bzero(&cookie, sizeof(cookie));
+
+       if (hfsmp->hfs_flags & HFS_CLEANED_ORPHANS)
+               return;
 
+       vcb = HFSTOVCB(hfsmp);
+       fcb = VTOF(hfsmp->hfs_catalog_vp);
 
-       /* getnewvnode() does a VREF() on the vnode */
-       /* Allocate a new vnode. If unsuccesful, leave after freeing memory */
-       if ((retval = getnewvnode(VT_HFS, mp, hfs_vnodeop_p, &vp))) {
-               wakeup(hp);                             /* Shouldnt happen, but just to make sure */
-               FREE_ZONE(hp, sizeof(struct hfsnode), M_HFSNODE);
-               *vpp = NULL;
-               return (retval);
-       };
+       btdata.bufferAddress = &filerec;
+       btdata.itemSize = sizeof(filerec);
+       btdata.itemCount = 1;
 
-       /*
-        * Set the essentials before locking it down
-        */
-       hp->h_vp = vp;                                                                  /* Make HFSTOV work */
-       vp->v_data = hp;                                                                /* Make VTOH work */
-       H_FORKTYPE(hp) = forkType;
-       fm = NULL;
+       MALLOC(iterator, struct BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
+       bzero(iterator, sizeof(*iterator));
        
-       /*
-        * Lock the hfsnode and insert the hfsnode into the hash queue, also if meta exists
-        * add to sibling list and return the meta address
-        */
-       if  (SIBLING_FORKTYPE(forkType))
-               hfs_vhashins_sibling(dev, catInfo->nodeData.cnd_nodeID, hp, &fm);
-       else
-               hfs_vhashins(dev, catInfo->nodeData.cnd_nodeID, hp);
+       /* Build a key to "temp" */
+       keyp = (HFSPlusCatalogKey*)&iterator->key;
+       keyp->parentID = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
+       keyp->nodeName.length = 4;  /* "temp" */
+       keyp->keyLength = kHFSPlusCatalogKeyMinimumLength + keyp->nodeName.length * 2;
+       keyp->nodeName.unicode[0] = 't';
+       keyp->nodeName.unicode[1] = 'e';
+       keyp->nodeName.unicode[2] = 'm';
+       keyp->nodeName.unicode[3] = 'p';
 
        /*
-        * If needed allocate and init the object meta data:
+        * Position the iterator just before the first real temp file/dir.
         */
-       if (fm == NULL) {
-               /* Allocate it....remember we can do a context switch here */
-               MALLOC_ZONE(fm, struct hfsfilemeta *, sizeof(struct hfsfilemeta), M_HFSFMETA, M_WAITOK);
-               bzero(fm, sizeof(struct hfsfilemeta));
-
-               /* Fill it in */
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+       (void) BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
+       hfs_systemfile_unlock(hfsmp, lockflags);
+
+       /* Visit all the temp files/dirs in the HFS+ private directory. */
+       for (;;) {
+               lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+               result = BTIterateRecord(fcb, kBTreeNextRecord, iterator, &btdata, NULL);
+               hfs_systemfile_unlock(hfsmp, lockflags);
+               if (result)
+                       break;
+               if (keyp->parentID != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid)
+                       break;
+               
+               (void) utf8_encodestr(keyp->nodeName.unicode, keyp->nodeName.length * 2,
+                                     (u_int8_t *)filename, &namelen, sizeof(filename), 0, 0);
+               
+               (void) snprintf(tempname, sizeof(tempname), "%s%d",
+                               HFS_DELETE_PREFIX, filerec.fileID);
+               
                /*
-                * NOTICE: XXX Even though we have added the vnode to the hash so it is alive on TWO
-                * accessable lists, we  do not assign it until later,
-                * this helps to make sure we do not use a half initiated meta
+                * Delete all files (and directories) named "tempxxx", 
+                * where xxx is the file's cnid in decimal.
+                *
                 */
+               if (bcmp(tempname, filename, namelen) == 0) {
+                       struct filefork dfork;
+                       struct filefork rfork;
+                       struct cnode cnode;
+
+                       bzero(&dfork, sizeof(dfork));
+                       bzero(&rfork, sizeof(rfork));
+                       bzero(&cnode, sizeof(cnode));
+                       
+                       /* Delete any attributes, ignore errors */
+                       (void) hfs_removeallattr(hfsmp, filerec.fileID);
+                       
+                       if (hfs_start_transaction(hfsmp) != 0) {
+                           printf("hfs_remove_orphans: failed to start transaction\n");
+                           goto exit;
+                       }
+                       started_tr = 1;
+               
+                       /*
+                        * Reserve some space in the Catalog file.
+                        */
+                       if (cat_preflight(hfsmp, CAT_DELETE, &cookie, p) != 0) {
+                           printf("hfs_remove_orphans: cat_preflight failed\n");
+                               goto exit;
+                       }
+                       catreserve = 1;
+
+                       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+                       catlock = 1;
+
+                       /* Build a fake cnode */
+                       cat_convertattr(hfsmp, (CatalogRecord *)&filerec, &cnode.c_attr,
+                                       &dfork.ff_data, &rfork.ff_data);
+                       cnode.c_desc.cd_parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
+                       cnode.c_desc.cd_nameptr = (const u_int8_t *)filename;
+                       cnode.c_desc.cd_namelen = namelen;
+                       cnode.c_desc.cd_cnid = cnode.c_attr.ca_fileid;
+                       cnode.c_blocks = dfork.ff_blocks + rfork.ff_blocks;
+
+                       /* Position iterator at previous entry */
+                       if (BTIterateRecord(fcb, kBTreePrevRecord, iterator,
+                           NULL, NULL) != 0) {
+                               break;
+                       }
 
-               /* Init the sibling list if needed */
-               if (SIBLING_FORKTYPE(forkType)) {
-                       simple_lock_init(&fm->h_siblinglock);
-                       CIRCLEQ_INIT(&fm->h_siblinghead);
-                       CIRCLEQ_INSERT_HEAD(&fm->h_siblinghead, hp, h_sibling);
-               };
-
-               fm->h_dev = dev;
-               CopyCatalogToObjectMeta(catInfo, vp, fm);
-
-               /*
-                * the vnode is finally alive, with the exception of the FCB below,
-                * It is finally locked and ready for its debutante ball
-                */
-               hp->h_meta = fm;
-       };
-       fm->h_usecount++;
+                       /* Truncate the file to zero (both forks) */
+                       if (dfork.ff_blocks > 0) {
+                               u_int64_t fsize;
+                               
+                               dfork.ff_cp = &cnode;
+                               cnode.c_datafork = &dfork;
+                               cnode.c_rsrcfork = NULL;
+                               fsize = (u_int64_t)dfork.ff_blocks * (u_int64_t)HFSTOVCB(hfsmp)->blockSize;
+                               while (fsize > 0) {
+                                   if (fsize > HFS_BIGFILE_SIZE && overflow_extents(&dfork)) {
+                                               fsize -= HFS_BIGFILE_SIZE;
+                                       } else {
+                                               fsize = 0;
+                                       }
+
+                                       if (TruncateFileC(vcb, (FCB*)&dfork, fsize, false) != 0) {
+                                               printf("hfs: error truncting data fork!\n");
+                                               break;
+                                       }
+
+                                       //
+                                       // if we're iteratively truncating this file down,
+                                       // then end the transaction and start a new one so
+                                       // that no one transaction gets too big.
+                                       //
+                                       if (fsize > 0 && started_tr) {
+                                               /* Drop system file locks before starting 
+                                                * another transaction to preserve lock order.
+                                                */
+                                               hfs_systemfile_unlock(hfsmp, lockflags);
+                                               catlock = 0;
+                                               hfs_end_transaction(hfsmp);
+
+                                               if (hfs_start_transaction(hfsmp) != 0) {
+                                                       started_tr = 0;
+                                                       break;
+                                               }
+                                               lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG | SFL_ATTRIBUTE | SFL_EXTENTS | SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+                                               catlock = 1;
+                                       }
+                               }
+                       }
 
+                       if (rfork.ff_blocks > 0) {
+                               rfork.ff_cp = &cnode;
+                               cnode.c_datafork = NULL;
+                               cnode.c_rsrcfork = &rfork;
+                               if (TruncateFileC(vcb, (FCB*)&rfork, 0, false) != 0) {
+                                       printf("hfs: error truncting rsrc fork!\n");
+                                       break;
+                               }
+                       }
 
-       /*
-        * Init the File Control Block.
-        */
-       CopyCatalogToFCB(catInfo, vp);
+                       /* Remove the file or folder record from the Catalog */ 
+                       if (cat_delete(hfsmp, &cnode.c_desc, &cnode.c_attr) != 0) {
+                               printf("hfs_remove_orphans: error deleting cat rec for id %d!\n", cnode.c_desc.cd_cnid);
+                               hfs_systemfile_unlock(hfsmp, lockflags);
+                               catlock = 0;
+                               hfs_volupdate(hfsmp, VOL_UPDATE, 0);
+                               break;
+                       }
+                       
+                       if (cnode.c_attr.ca_mode & S_IFDIR) {
+                               orphaned_dirs++;
+                       }
+                       else {
+                               orphaned_files++;
+                       }
 
-       /*
-        * Finish vnode initialization.
-        * Setting the v_type 'stamps' the vnode as 'complete', so should be done almost last. 
-        * 
-        * At this point the vnode should be locked and fully allocated. And ready to be used
-        * or accessed. (though having it locked prevents most of this, it
-        * can still be accessed through lists and hashs).
-        */
-       vp->v_type = IFTOVT(hp->h_meta->h_mode);
-       if ((vp->v_type == VREG) 
-               && (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp))) {
-               ubc_info_init(vp);
-       }
+                       /* Update parent and volume counts */   
+                       hfsmp->hfs_private_attr[FILE_HARDLINKS].ca_entries--;
+                       if (cnode.c_attr.ca_mode & S_IFDIR) {
+                               DEC_FOLDERCOUNT(hfsmp, hfsmp->hfs_private_attr[FILE_HARDLINKS]);
+                       }
 
-    /*
-        * Initialize the vnode from the inode, check for aliases, sets the VROOT flag.
-        * Note that the underlying vnode may have changed.
-        */
-       if ((retval = hfs_vinit(mp, hfs_specop_p, hfs_fifoop_p, &vp))) {
-               wakeup((caddr_t)hp);
-               vput(vp);
-               *vpp = NULL;
-               return (retval);
-       }
+                       (void)cat_update(hfsmp, &hfsmp->hfs_private_desc[FILE_HARDLINKS],
+                                        &hfsmp->hfs_private_attr[FILE_HARDLINKS], NULL, NULL);
 
-    /*
-     * Finish inode initialization now that aliasing has been resolved.
-     */
-    hp->h_meta->h_devvp = hfsmp->hfs_devvp;
-    VREF(hp->h_meta->h_devvp);
-    
-#if HFS_DIAGNOSTIC
-       hp->h_valid = HFS_VNODE_MAGIC;
-#endif
-       hp->h_nodeflags &= ~IN_ALLOCATING;                              /* vnode is completely initialized */
+                       /* Drop locks and end the transaction */
+                       hfs_systemfile_unlock(hfsmp, lockflags);
+                       cat_postflight(hfsmp, &cookie, p);
+                       catlock = catreserve = 0;
 
-       /* Wake up anybody waiting for us to finish..see hfs_vhash.c */
-       wakeup((caddr_t)hp);
+                       /* 
+                          Now that Catalog is unlocked, update the volume info, making
+                          sure to differentiate between files and directories
+                       */
+                       if (cnode.c_attr.ca_mode & S_IFDIR) {
+                               hfs_volupdate(hfsmp, VOL_RMDIR, 0);
+                       }
+                       else{
+                               hfs_volupdate(hfsmp, VOL_RMFILE, 0);
+                       }
 
-#if HFS_DIAGNOSTIC
+                       if (started_tr) {
+                               hfs_end_transaction(hfsmp);
+                               started_tr = 0;
+                       }
 
-       /* Lets do some testing here */
-       DBG_ASSERT(hp->h_meta);
-       DBG_ASSERT(VTOH(vp)==hp);
-       DBG_ASSERT(HTOV(hp)==vp);
-       DBG_ASSERT(hp->h_meta->h_usecount>=1 && hp->h_meta->h_usecount<=2);
-       if (catInfo->nodeData.cnd_type == kCatalogFolderNode) {
-               DBG_ASSERT(vp->v_type == VDIR);
-               DBG_ASSERT(H_FORKTYPE(VTOH(vp)) == kDirectory);
+               } /* end if */
+       } /* end for */
+       if (orphaned_files > 0 || orphaned_dirs > 0)
+               printf("hfs: Removed %d orphaned / unlinked files and %d directories \n", orphaned_files, orphaned_dirs);
+exit:
+       if (catlock) {
+               hfs_systemfile_unlock(hfsmp, lockflags);
+       }
+       if (catreserve) {
+               cat_postflight(hfsmp, &cookie, p);
+       }
+       if (started_tr) {
+               hfs_end_transaction(hfsmp);
        }
-#endif // HFS_DIAGNOSTIC
-
-
-       *vpp = vp;
-       return 0;
 
+       FREE(iterator, M_TEMP);
+       hfsmp->hfs_flags |= HFS_CLEANED_ORPHANS;
 }
 
-void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm)
+
+/*
+ * This will return the correct logical block size for a given vnode.
+ * For most files, it is the allocation block size, for meta data like
+ * BTrees, this is kept as part of the BTree private nodeSize
+ */
+u_int32_t
+GetLogicalBlockSize(struct vnode *vp)
 {
-       ExtendedVCB                             *vcb = VTOVCB(vp);
-       struct mount                    *mp = VTOVFS(vp);
-       Boolean                                 isHFSPlus, isDirectory;
-       ushort                                  finderFlags;
-       ushort filetype;
-
-       DBG_ASSERT (fm != NULL);
-       DBG_ASSERT (fm->h_namelen == 0);
-       DBG_ASSERT (fm->h_namePtr == 0);
+u_int32_t logBlockSize;
        
-       DBG_UTILS(("\tCopying to file's meta data: name:%s, nodeid:%ld\n", catalogInfo->nodeData.cnm_nameptr, catalogInfo->nodeData.cnd_nodeID));
-
-       isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-       isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
-       finderFlags = SWAP_BE16 (((struct FInfo *)(&catalogInfo->nodeData.cnd_finderInfo))->fdFlags);
-
-       /* Copy over the dirid, and hint */
-       fm->h_nodeID = catalogInfo->nodeData.cnd_nodeID;
-       fm->h_dirID = catalogInfo->nodeData.cnm_parID;
-       fm->h_hint = catalogInfo->hint;
-
-       /* Copy over the name */
-       hfs_name_CatToMeta(&catalogInfo->nodeData, fm);
-
-
-       /* get dates in BSD format */
-       fm->h_mtime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
-       fm->h_crtime = to_bsd_time(catalogInfo->nodeData.cnd_createDate);
-       fm->h_butime = to_bsd_time(catalogInfo->nodeData.cnd_backupDate);
-       if (isHFSPlus) {
-               fm->h_atime = to_bsd_time(catalogInfo->nodeData.cnd_accessDate);
-               fm->h_ctime = to_bsd_time(catalogInfo->nodeData.cnd_attributeModDate);
-       }
-       else {
-               fm->h_atime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
-               fm->h_ctime = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
-       }
-
-       /* Now the rest */
-       if (isHFSPlus && (catalogInfo->nodeData.cnd_mode & IFMT)) {
-               fm->h_uid = catalogInfo->nodeData.cnd_ownerID;
-               fm->h_gid = catalogInfo->nodeData.cnd_groupID;
-               fm->h_pflags = catalogInfo->nodeData.cnd_ownerFlags |
-                              (catalogInfo->nodeData.cnd_adminFlags << 16);
-               fm->h_mode = (mode_t)catalogInfo->nodeData.cnd_mode;
-#if 1
-               if (fm->h_uid == 0xFFFFFFFD) {  /* 0xfffffffd = 4294967293, the old "unknown" */
-                       fm->h_uid = UNKNOWNUID;
-                       fm->h_metaflags |= IN_CHANGE;
-                       vcb->vcbFlags |= kHFS_DamagedVolume;    /* Trigger fsck on next mount */
-               };
-               if (fm->h_gid == 0xFFFFFFFD) {  /* 0xfffffffd = 4294967293, the old "unknown" */
-                       fm->h_gid = UNKNOWNGID;
-                       fm->h_metaflags |= IN_CHANGE;
-                       vcb->vcbFlags |= kHFS_DamagedVolume;    /* Trigger fsck on next mount */
-               };
-#endif
-               filetype = fm->h_mode & IFMT;
-               if (filetype == IFCHR || filetype == IFBLK)
-                       fm->h_rdev = catalogInfo->nodeData.cnd_rawDevice;
-               else {
-                       fm->h_rdev = 0;
-#if HFS_HARDLINKS
-                       if (catalogInfo->nodeData.cnd_type == kCatalogFileNode  &&
-                           catalogInfo->nodeData.cnd_linkCount > 0) {
-                               fm->h_nlink = catalogInfo->nodeData.cnd_linkCount;
-                               fm->h_indnodeno = catalogInfo->nodeData.cnd_iNodeNumCopy;
-                               fm->h_metaflags |= IN_DATANODE;
-                       }
-#endif
-               }
+       DBG_ASSERT(vp != NULL);
 
-               if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
+       /* start with default */
+       logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
+
+       if (vnode_issystem(vp)) {
+               if (VTOF(vp)->fcbBTCBPtr != NULL) {
+                       BTreeInfoRec                    bTreeInfo;
+       
                        /*
-                        *      Override the permissions as determined by the mount auguments
-                        *  in ALMOST the same way unset permissions are treated but keep
-                        *      track of whether or not the file or folder is hfs locked
-                        *  by leaving the h_pflags field unchanged from what was unpacked
-                        *  out of the catalog.
-                        */ 
-                       fm->h_metaflags |= IN_UNSETACCESS;                      
-                       fm->h_uid = VTOHFS(vp)->hfs_uid;
-                       fm->h_gid = VTOHFS(vp)->hfs_gid;
-#if OVERRIDE_UNKNOWN_PERMISSIONS
-                       /* Default access is full read/write/execute: */
-                       /* XXX won't this smash IFCHR, IFBLK and IFLNK (for no-follow lookups)? */
-                       fm->h_mode = ACCESSPERMS;       /* 0777: rwxrwxrwx */
-                       fm->h_rdev = 0;
-                       
-                       /* ... but no more than that permitted by the mount point's: */
-                       if (isDirectory) {
-                               fm->h_mode &= VTOHFS(vp)->hfs_dir_mask;
-                       }
-                       else {
-                               fm->h_mode &= VTOHFS(vp)->hfs_file_mask;
-                       }
+                        * We do not lock the BTrees, because if we are getting block..then the tree
+                        * should be locked in the first place.
+                        * We just want the nodeSize wich will NEVER change..so even if the world
+                        * is changing..the nodeSize should remain the same. Which argues why lock
+                        * it in the first place??
+                        */
                        
-                       if(isDirectory)
-                               fm->h_mode |= IFDIR;
-                       else if (SUPPORTS_MAC_ALIASES && (finderFlags & kIsAlias))      /* aliases will be symlinks in the future */
-                               fm->h_mode |= IFLNK;
-                       else
-                               fm->h_mode |= IFREG;
-#endif
-               };
-       } else {
-               /*
-                *      Set the permissions as determined by the mount auguments
-                *      but keep in account if the file or folder is hfs locked
-                */ 
-               fm->h_metaflags |= IN_UNSETACCESS;                      
-               fm->h_uid = VTOHFS(vp)->hfs_uid;
-               fm->h_gid = VTOHFS(vp)->hfs_gid;
-               fm->h_pflags = 0;       /* No valid pflags on disk (IMMUTABLE is synced from lock flag later) */
-               fm->h_rdev = 0;         /* No valid rdev on disk */
-               /* Default access is full read/write/execute: */
-               fm->h_mode = ACCESSPERMS;       /* 0777: rwxrwxrwx */
-               
-               /* ... but no more than that permitted by the mount point's: */
-               if (isDirectory) {
-                       fm->h_mode &= VTOHFS(vp)->hfs_dir_mask;
-               }
-               else {
-                       fm->h_mode &= VTOHFS(vp)->hfs_file_mask;
+                       (void) BTGetInformation (VTOF(vp), kBTreeInfoVersion, &bTreeInfo);
+                                       
+                       logBlockSize = bTreeInfo.nodeSize;
+
+               } else if (VTOC(vp)->c_fileid == kHFSAllocationFileID) {
+                               logBlockSize = VTOVCB(vp)->vcbVBMIOSize;
                }
-               
-               if(isDirectory)
-                       fm->h_mode |= IFDIR;
-               else if (SUPPORTS_MAC_ALIASES && (finderFlags & kIsAlias))      /* aliases will be symlinks in the future */
-                       fm->h_mode |= IFLNK;
-               else
-                       fm->h_mode |= IFREG;
-       };
-
-       /* Make sure that there is no nodeType/mode mismatch */
-       if (isDirectory && ((fm->h_mode & IFMT) != IFDIR)) {
-               fm->h_mode &= ~IFMT;            /* Clear the bad bits */
-               fm->h_mode |= IFDIR;            /* Set the proper one */
-       };
-
-       /* Make sure the IMMUTABLE bits are in sync with the locked flag in the catalog: */
-       if (!isDirectory) {
-               if (catalogInfo->nodeData.cnd_flags & kHFSFileLockedMask) {
-                       /* The file's supposed to be locked:
-                          Make sure at least one of the IMMUTABLE bits is set: */
-                       if ((fm->h_pflags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0) {
-                               fm->h_pflags |= UF_IMMUTABLE;                           /* Set the user-changable IMMUTABLE bit */
-                       };
-               } else {
-                       /* The file's supposed to be unlocked: */
-                       fm->h_pflags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
-               };
-       };
-
-       if (isDirectory) {
-               fm->h_nlink = 2 + catalogInfo->nodeData.cnd_valence;
-               fm->h_size = (2 * sizeof(hfsdotentry)) + 
-                       (catalogInfo->nodeData.cnd_valence * AVERAGE_HFSDIRENTRY_SIZE);
-               if (fm->h_size < MAX_HFSDIRENTRY_SIZE)
-                       fm->h_size = MAX_HFSDIRENTRY_SIZE;
-       } else {
-               fm->h_size = (off_t)vcb->blockSize *
-                       (off_t)(catalogInfo->nodeData.cnd_rsrcfork.totalBlocks +
-                        catalogInfo->nodeData.cnd_datafork.totalBlocks);
        }
-}
 
+       DBG_ASSERT(logBlockSize > 0);
+       
+       return logBlockSize;    
+}
 
-void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp)
+__private_extern__
+u_int32_t
+hfs_freeblks(struct hfsmount * hfsmp, int wantreserve)
 {
-       FCB                                     *fcb = VTOFCB(vp);
-       ExtendedVCB                             *vcb = VTOVCB(vp);
-       Boolean                                 isHFSPlus, isDirectory, isResource;
-       HFSPlusExtentDescriptor *extents;
-       UInt8                                   forkType;
-
-       DBG_ASSERT (vp != NULL);
-       DBG_ASSERT (fcb != NULL);
-       DBG_ASSERT (vcb != NULL);
-       DBG_ASSERT (VTOH(vp) != NULL);
-
-       forkType = H_FORKTYPE(VTOH(vp));
-       isResource = (forkType == kRsrcFork);
-       isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
-       isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-
-       /* Init the fcb */
-       fcb->fcbFlags = catalogInfo->nodeData.cnd_flags;
-
-       if (forkType != kDirectory) {
-               fcb->fcbFlags &= kHFSFileLockedMask;            /* Clear resource, dirty bits */
-               if (fcb->fcbFlags != 0)                                         /* if clear, its not locked, then.. */
-                       fcb->fcbFlags = fcbFileLockedMask;              /* duplicate the bit for later use */
-
-               fcb->fcbClmpSize = vcb->vcbClpSiz;      /*XXX why not use the one in catalogInfo? */
-
-               if (isResource) 
-                       extents = catalogInfo->nodeData.cnd_rsrcfork.extents;
+       u_int32_t freeblks;
+       u_int32_t rsrvblks;
+       u_int32_t loanblks;
+
+       /*
+        * We don't bother taking the mount lock
+        * to look at these values since the values
+        * themselves are each updated atomically
+        * on aligned addresses.
+        */
+       freeblks = hfsmp->freeBlocks;
+       rsrvblks = hfsmp->reserveBlocks;
+       loanblks = hfsmp->loanedBlocks;
+       if (wantreserve) {
+               if (freeblks > rsrvblks)
+                       freeblks -= rsrvblks;
                else
-                       extents = catalogInfo->nodeData.cnd_datafork.extents;
+                       freeblks = 0;
+       }
+       if (freeblks > loanblks)
+               freeblks -= loanblks;
+       else
+               freeblks = 0;
 
-               /* Copy the extents to their correct location: */
-               bcopy (extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));
+#ifdef HFS_SPARSE_DEV
+       /* 
+        * When the underlying device is sparse, check the
+        * available space on the backing store volume.
+        */
+       if ((hfsmp->hfs_flags & HFS_HAS_SPARSE_DEVICE) && hfsmp->hfs_backingfs_rootvp) {
+               struct vfsstatfs *vfsp;  /* 272 bytes */
+               u_int64_t vfreeblks;
+               u_int32_t loanedblks;
+               struct mount * backingfs_mp;
+               struct timeval now;
+
+               backingfs_mp = vnode_mount(hfsmp->hfs_backingfs_rootvp);
+
+               microtime(&now);
+               if ((now.tv_sec - hfsmp->hfs_last_backingstatfs) >= 1) {
+                   vfs_update_vfsstat(backingfs_mp, vfs_context_kernel(), VFS_KERNEL_EVENT);
+                   hfsmp->hfs_last_backingstatfs = now.tv_sec;
+               }
 
-               if (isResource) {       
-                       fcb->fcbEOF = catalogInfo->nodeData.cnd_rsrcfork.logicalSize;
-                       fcb->fcbPLen = (off_t)((off_t)catalogInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-                       fcb->fcbFlags |= fcbResourceMask;
-               }  else {
-                       fcb->fcbEOF = catalogInfo->nodeData.cnd_datafork.logicalSize;
-                       fcb->fcbPLen = (off_t)((off_t)catalogInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
-               };
-       };
+               if ((vfsp = vfs_statfs(backingfs_mp))) {
+                       HFS_MOUNT_LOCK(hfsmp, TRUE);
+                       vfreeblks = vfsp->f_bavail;
+                       /* Normalize block count if needed. */
+                       if (vfsp->f_bsize != hfsmp->blockSize) {
+                               vfreeblks = ((u_int64_t)vfreeblks * (u_int64_t)(vfsp->f_bsize)) / hfsmp->blockSize;
+                       }
+                       if (vfreeblks > (unsigned int)hfsmp->hfs_sparsebandblks)
+                               vfreeblks -= hfsmp->hfs_sparsebandblks;
+                       else
+                               vfreeblks = 0;
+                       
+                       /* Take into account any delayed allocations. */
+                       loanedblks = 2 * hfsmp->loanedBlocks;
+                       if (vfreeblks > loanedblks)
+                               vfreeblks -= loanedblks;
+                       else
+                               vfreeblks = 0;
 
+                       if (hfsmp->hfs_backingfs_maxblocks) {
+                               vfreeblks = MIN(vfreeblks, hfsmp->hfs_backingfs_maxblocks);
+                       }
+                       freeblks = MIN(vfreeblks, freeblks);
+                       HFS_MOUNT_UNLOCK(hfsmp, TRUE);
+               }
+       }
+#endif /* HFS_SPARSE_DEV */
 
+       return (freeblks);
 }
 
-int hasOverflowExtents(struct hfsnode *hp)
+/*
+ * Map HFS Common errors (negative) to BSD error codes (positive).
+ * Positive errors (ie BSD errors) are passed through unchanged.
+ */
+short MacToVFSError(OSErr err)
 {
-       ExtendedVCB             *vcb = HTOVCB(hp);
-       FCB                             *fcb = HTOFCB(hp);
-       u_long                  blocks;
+       if (err >= 0)
+               return err;
 
-       if (vcb->vcbSigWord == kHFSPlusSigWord)
-         {
-
-               if (fcb->fcbExtents[7].blockCount == 0)
-                       return false;
-               
-               blocks = fcb->fcbExtents[0].blockCount +
-                                fcb->fcbExtents[1].blockCount +
-                                fcb->fcbExtents[2].blockCount +
-                                fcb->fcbExtents[3].blockCount +
-                                fcb->fcbExtents[4].blockCount +
-                                fcb->fcbExtents[5].blockCount +
-                                fcb->fcbExtents[6].blockCount +
-                                fcb->fcbExtents[7].blockCount; 
-         }
-       else
-         {
-               if (fcb->fcbExtents[2].blockCount == 0)
-                       return false;
-               
-               blocks = fcb->fcbExtents[0].blockCount +
-                                fcb->fcbExtents[1].blockCount +
-                                fcb->fcbExtents[2].blockCount; 
-         }
+       switch (err) {
+       case dskFulErr:                 /*    -34 */
+       case btNoSpaceAvail:            /* -32733 */
+               return ENOSPC;
+       case fxOvFlErr:                 /* -32750 */
+               return EOVERFLOW;
+       
+       case btBadNode:                 /* -32731 */
+               return EIO;
+       
+       case memFullErr:                /*  -108 */
+               return ENOMEM;          /*   +12 */
+       
+       case cmExists:                  /* -32718 */
+       case btExists:                  /* -32734 */
+               return EEXIST;          /*    +17 */
+       
+       case cmNotFound:                /* -32719 */
+       case btNotFound:                /* -32735 */    
+               return ENOENT;          /*     28 */
+       
+       case cmNotEmpty:                /* -32717 */
+               return ENOTEMPTY;       /*     66 */
+       
+       case cmFThdDirErr:              /* -32714 */
+               return EISDIR;          /*     21 */
+       
+       case fxRangeErr:                /* -32751 */
+               return ERANGE;
+       
+       case bdNamErr:                  /*   -37 */
+               return ENAMETOOLONG;    /*    63 */
+       
+       case paramErr:                  /*   -50 */
+       case fileBoundsErr:             /* -1309 */
+               return EINVAL;          /*   +22 */
+       
+       case fsBTBadNodeSize:
+               return ENXIO;
 
-       return ((fcb->fcbPLen / vcb->blockSize) > blocks);
+       default:
+               return EIO;             /*   +5 */
+       }
 }
 
 
-int hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
+/*
+ * Find the current thread's directory hint for a given index.
+ *
+ * Requires an exclusive lock on directory cnode.
+ *
+ * Use detach if the cnode lock must be dropped while the hint is still active.
+ */
+__private_extern__
+directoryhint_t *
+hfs_getdirhint(struct cnode *dcp, int index, int detach)
 {
-       ExtendedVCB             *vcb;
-       struct vnode    *vp = NULL;
-       int                             numOfLockedBuffs;
-       int     retval = 0;
+       struct timeval tv;
+       directoryhint_t *hint;
+       boolean_t need_remove, need_init;
+       const u_int8_t * name;
 
-       vcb = HFSTOVCB(hfsmp);
-
-       DBG_UTILS(("hfs_metafilelocking: vol: %d, file: %d %s%s%s\n", vcb->vcbVRefNum, fileID,
-                       ((flags & LK_TYPE_MASK) == LK_RELEASE ? "RELEASE" : ""),
-                       ((flags & LK_TYPE_MASK) == LK_EXCLUSIVE ? "EXCLUSIVE" : ""),
-                       ((flags & LK_TYPE_MASK) == LK_SHARED ? "SHARED" : "") ));
-
-
-       switch (fileID)
-       {
-               case kHFSExtentsFileID:
-                       vp = vcb->extentsRefNum;
-                       break;
+       microuptime(&tv);
 
-               case kHFSCatalogFileID:
-                       vp = vcb->catalogRefNum;
+       /*
+        *  Look for an existing hint first.  If not found, create a new one (when
+        *  the list is not full) or recycle the oldest hint.  Since new hints are
+        *  always added to the head of the list, the last hint is always the
+        *  oldest.
+        */
+       TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
+               if (hint->dh_index == index)
                        break;
-
-               case kHFSAllocationFileID:
-                       /* bitmap is covered by Extents B-tree locking */
-                       /* FALL THROUGH */
-               default:
-                       panic("hfs_lockmetafile: invalid fileID");
+       }
+       if (hint != NULL) { /* found an existing hint */
+               need_init = false;
+               need_remove = true;
+       } else { /* cannot find an existing hint */
+               need_init = true;
+               if (dcp->c_dirhintcnt < HFS_MAXDIRHINTS) { /* we don't need recycling */
+                       /* Create a default directory hint */
+                       MALLOC_ZONE(hint, directoryhint_t *, sizeof(directoryhint_t), M_HFSDIRHINT, M_WAITOK);
+                       ++dcp->c_dirhintcnt;
+                       need_remove = false;
+               } else {                                /* recycle the last (i.e., the oldest) hint */
+                       hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead);
+                       if ((hint->dh_desc.cd_flags & CD_HASBUF) &&
+                           (name = hint->dh_desc.cd_nameptr)) {
+                               hint->dh_desc.cd_nameptr = NULL;
+                               hint->dh_desc.cd_namelen = 0;
+                               hint->dh_desc.cd_flags &= ~CD_HASBUF;                           
+                               vfs_removename((const char *)name);
+                       }
+                       need_remove = true;
+               }
        }
 
-       if (vp != NULL) {
-
-               /* Release, if necesary any locked buffer caches */
-       if ((flags & LK_TYPE_MASK) == LK_RELEASE) {
-                       struct timeval tv = time;
-                       u_int32_t               lastfsync = tv.tv_sec; 
-                       
-                       (void) BTGetLastSync(VTOFCB(vp), &lastfsync);
-                       
-                       numOfLockedBuffs = count_lock_queue();
-                       if ((numOfLockedBuffs > kMaxLockedMetaBuffers) || ((numOfLockedBuffs>1) && ((tv.tv_sec - lastfsync) > kMaxSecsForFsync))) {
-                               DBG_UTILS(("Synching meta deta: %d... # locked buffers = %d, fsync gap = %ld\n", H_FILEID(VTOH(vp)),
-                                               numOfLockedBuffs, (tv.tv_sec - lastfsync)));
-                               hfs_fsync_transaction(vp);
-                       };
-               };
-               
-               retval = lockmgr(&VTOH(vp)->h_lock, flags, &vp->v_interlock, p);
-       };
+       if (need_remove)
+               TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
 
-       return retval;
+       if (detach)
+               --dcp->c_dirhintcnt;
+       else
+               TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
+
+       if (need_init) {
+               hint->dh_index = index;
+               hint->dh_desc.cd_flags = 0;
+               hint->dh_desc.cd_encoding = 0;
+               hint->dh_desc.cd_namelen = 0;
+               hint->dh_desc.cd_nameptr = NULL;
+               hint->dh_desc.cd_parentcnid = dcp->c_fileid;
+               hint->dh_desc.cd_hint = dcp->c_childhint;
+               hint->dh_desc.cd_cnid = 0;
+       }
+       hint->dh_time = tv.tv_sec;
+       return (hint);
 }
 
-
 /*
- * There are three ways to qualify for ownership rights on an object:
- *
- * 1. (a) Your UID matches the UID of the vnode
- *    (b) The object in question is owned by "unknown" and your UID matches the console user's UID
- * 2. Permissions on the filesystem are being ignored and your UID matches the replacement UID
- * 3. You are root
+ * Release a single directory hint.
  *
+ * Requires an exclusive lock on directory cnode.
  */
-int hfs_owner_rights(struct vnode *vp, struct ucred *cred, struct proc *p, Boolean invokesuperuserstatus) {
-    return ((cred->cr_uid == VTOH(vp)->h_meta->h_uid) ||                                                                               /* [1a] */
-               ((VTOH(vp)->h_meta->h_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) ||          /* [1b] */
-               ((VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) &&                                                                     /* [2] */
-                (cred->cr_uid == VTOHFS(vp)->hfs_uid)) ||                                                                                      /* [2] */
-               (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) ? 0 : EPERM;
-}
-
-
-
-int hfs_catalogentry_owner_rights(uid_t obj_uid, struct mount *mp, struct ucred *cred, struct proc *p, Boolean invokesuperuserstatus) {
-    return ((cred->cr_uid == obj_uid) ||                                                                                                               /* [1a] */
-               ((VFSTOHFS(mp)->hfs_uid == UNKNOWNUID) && (cred->cr_uid == console_user)) ||            /* [1b] */
-               ((mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) &&                                                                                     /* [2] */
-                (cred->cr_uid == VFSTOHFS(mp)->hfs_uid)) ||                                                                            /* [2] */
-               (invokesuperuserstatus && (suser(cred, &p->p_acflag) == 0))) ? 0 : EPERM;
-}
-
-
-
-void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData)
+__private_extern__
+void
+hfs_reldirhint(struct cnode *dcp, directoryhint_t * relhint)
 {
-    ExtendedVCB                        *vcb;
-    FCB                                                *fcb;
-    struct hfsnode                     *hp;
-    Boolean                                    isHFSPlus, isResource;
-    HFSPlusExtentDescriptor    *extents;
-
-    hp = VTOH(vp);
-    vcb = HTOVCB(hp);
-    fcb = HTOFCB(hp);
-    isResource = (H_FORKTYPE(hp) == kRsrcFork);
-    isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-
-    /* date and time of last fork modification */
-    if (hp->h_meta->h_mtime != 0)
-               nodeData->cnd_contentModDate = to_hfs_time(hp->h_meta->h_mtime);
-
-       if (isHFSPlus) {
-               /* Make sure that there is no nodeType/mode mismatch */
-               if ((nodeData->cnd_type == kCatalogFolderNode) 
-                               && ((hp->h_meta->h_mode & IFMT) != IFDIR)) {
-
-                       DBG_ASSERT((hp->h_meta->h_mode & IFMT) == IFDIR);
-                       hp->h_meta->h_mode &= ~IFMT;            /* Clear the bad bits */
-                       hp->h_meta->h_mode |= IFDIR;            /* Set the proper one */
-               };
-               /* date and time of last modification (any kind) */
-               if (hp->h_meta->h_ctime != 0)
-                       nodeData->cnd_attributeModDate = to_hfs_time(hp->h_meta->h_ctime);
-               /* date and time of last access (MacOS X only) */
-               if (hp->h_meta->h_atime != 0)
-                       nodeData->cnd_accessDate = to_hfs_time(hp->h_meta->h_atime);
-               /* hfs_setattr can change the create date */
-               if (hp->h_meta->h_crtime != 0)
-                       nodeData->cnd_createDate = to_hfs_time(hp->h_meta->h_crtime);
-               if (! (hp->h_meta->h_metaflags & IN_UNSETACCESS)) {
-                       nodeData->cnd_adminFlags = hp->h_meta->h_pflags >> 16;
-                       nodeData->cnd_ownerFlags = hp->h_meta->h_pflags & 0x000000FF;
-                       nodeData->cnd_mode = hp->h_meta->h_mode;
-                       nodeData->cnd_ownerID = hp->h_meta->h_uid;
-                       nodeData->cnd_groupID = hp->h_meta->h_gid;
-               }
-       };
-
-       /* the rest only applies to files */
-       if (nodeData->cnd_type == kCatalogFileNode) {
-               if (hp->h_meta->h_pflags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
-                       /* The file is locked: set the locked bit in the catalog. */
-                       nodeData->cnd_flags |= kHFSFileLockedMask;
-               } else {
-                       /* The file is unlocked: make sure the locked bit in the catalog is clear. */
-                       nodeData->cnd_flags &= ~kHFSFileLockedMask;
-               };
-               if (isResource) {
-                       extents = nodeData->cnd_rsrcfork.extents;
-                       nodeData->cnd_rsrcfork.logicalSize = fcb->fcbEOF;
-                       nodeData->cnd_rsrcfork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
-               } else {
-                       extents = nodeData->cnd_datafork.extents;
-                       nodeData->cnd_datafork.logicalSize = fcb->fcbEOF;
-                       nodeData->cnd_datafork.totalBlocks = fcb->fcbPLen / vcb->blockSize;
-               };
-
-               bcopy ( fcb->fcbExtents, extents, sizeof(HFSPlusExtentRecord));
-
-               if ((vp->v_type == VBLK) || (vp->v_type == VCHR))
-                       nodeData->cnd_rawDevice = hp->h_meta->h_rdev;
-               else if (hp->h_meta->h_metaflags & IN_DATANODE)
-                       nodeData->cnd_linkCount = hp->h_meta->h_nlink;
-       
-           if (vp->v_type == VLNK) {
-               ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdType = SWAP_BE32 (kSymLinkFileType);
-               ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdCreator = SWAP_BE32 (kSymLinkCreator);
-       
-                       /* Set this up as an alias */
-                       #if SUPPORTS_MAC_ALIASES
-                               ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdFlags |= SWAP_BE16 (kIsAlias);
-                       #endif
+       const u_int8_t * name;
+       directoryhint_t *hint;
+
+       /* Check if item is on list (could be detached) */
+       TAILQ_FOREACH(hint, &dcp->c_hintlist, dh_link) {
+               if (hint == relhint) {
+                       TAILQ_REMOVE(&dcp->c_hintlist, relhint, dh_link);
+                       --dcp->c_dirhintcnt;
+                       break;
                }
        }
- }
-
-
-/*********************************************************************
-
-       Sets the name in the filemeta structure
-
-       XXX Does not preflight if changing from one size to another
-       XXX Currently not protected from context switching
-
-*********************************************************************/
-
-void hfs_set_metaname(char *name, struct hfsfilemeta *fm, struct hfsmount *hfsmp)
-{
-int                    namelen = strlen(name);
-char           *tname, *fname;
-
-#if HFS_DIAGNOSTIC
-       DBG_ASSERT(name != NULL);
-       DBG_ASSERT(fm != NULL);
-    if (fm->h_namePtr) {
-        DBG_ASSERT(fm->h_namelen == strlen(fm->h_namePtr));
-        if (strlen(fm->h_namePtr) > MAXHFSVNODELEN)
-            DBG_ASSERT(fm->h_metaflags & IN_LONGNAME);
-       };
-       if (fm->h_metaflags & IN_LONGNAME) {
-               DBG_ASSERT(fm->h_namePtr != (char *)fm->h_fileName);
-               DBG_ASSERT(fm->h_namePtr != NULL);
-       };
-#endif //HFS_DIAGNOSTIC
-       
-       /*
-        * Details that have to be dealt with:
-        * 1. No name is allocated. fm->h_namePtr should be NULL
-        * 2. A name is being changed and:
-        *      a. it was in static space and now cannot fit
-        *      b. It was malloc'd and now will fit in the static
-        *      c. It did and will fit in the static
-        * This could be a little smarter:
-        * - Dont re'malloc if the new name is smaller (but then wasting memory)
-        * - If its a longname but the same size, we still free and malloc
-        * - 
-        */
-
-       
-       /* Allocate the new memory */
-       if (namelen > MAXHFSVNODELEN) {
-               /*
-                * Notice the we ALWAYS allocate, even if the new is less then the old,
-                * or even if they are the SAME
-                */
-               MALLOC(tname, char *, namelen+1, M_TEMP, M_WAITOK);
+       name = relhint->dh_desc.cd_nameptr;
+       if ((relhint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
+               relhint->dh_desc.cd_nameptr = NULL;
+               relhint->dh_desc.cd_namelen = 0;
+               relhint->dh_desc.cd_flags &= ~CD_HASBUF;
+               vfs_removename((const char *)name);
        }
-       else
-               tname = fm->h_fileName;
-
-       simple_lock(&hfsmp->hfs_renamelock);
-       
-       /* Check to see if there is something to free, if yes, remember it */ 
-       if (fm->h_metaflags & IN_LONGNAME)
-               fname = fm->h_namePtr;
-       else
-               fname = NULL;
-       
-       /* Set the flag */
-       if (namelen > MAXHFSVNODELEN) {
-               fm->h_metaflags |= IN_LONGNAME;
-               }
-               else {
-               fm->h_metaflags &= ~IN_LONGNAME;
-       };
-
-       /* Now copy it over */
-       bcopy(name, tname, namelen+1);
-
-       fm->h_namePtr = tname;
-       fm->h_namelen = namelen;
-
-       simple_unlock(&hfsmp->hfs_renamelock);
-
-       /* Lastly, free the old, if set */
-       if (fname != NULL)
-               FREE(fname, M_TEMP);
-
+       FREE_ZONE(relhint, sizeof(directoryhint_t), M_HFSDIRHINT);
 }
 
-void hfs_name_CatToMeta(CatalogNodeData *nodeData, struct hfsfilemeta *fm)
+/*
+ * Release directory hints for given directory
+ *
+ * Requires an exclusive lock on directory cnode.
+ */
+__private_extern__
+void
+hfs_reldirhints(struct cnode *dcp, int stale_hints_only)
 {
-char           *fname;
-
-#if HFS_DIAGNOSTIC
-       DBG_ASSERT(nodeData != NULL);
-       DBG_ASSERT(fm != NULL);
-    if (fm->h_namePtr) {
-        DBG_ASSERT(fm->h_namelen == strlen(fm->h_namePtr));
-        if (strlen(fm->h_namePtr) > MAXHFSVNODELEN)
-            DBG_ASSERT(fm->h_metaflags & IN_LONGNAME);
-       };
-       if (fm->h_metaflags & IN_LONGNAME) {
-               DBG_ASSERT(fm->h_namePtr != (char *)fm->h_fileName);
-               DBG_ASSERT(fm->h_namePtr != NULL);
-       };
-       
-       DBG_ASSERT(nodeData->cnm_nameptr != NULL);
-       
-       if (nodeData->cnm_length) {
-               DBG_ASSERT(strlen(nodeData->cnm_nameptr) == nodeData->cnm_length);
+       struct timeval tv;
+       directoryhint_t *hint, *prev;
+       const u_int8_t * name;
+
+       if (stale_hints_only)
+               microuptime(&tv);
+
+       /* searching from the oldest to the newest, so we can stop early when releasing stale hints only */
+       for (hint = TAILQ_LAST(&dcp->c_hintlist, hfs_hinthead); hint != NULL; hint = prev) {
+               if (stale_hints_only && (tv.tv_sec - hint->dh_time) < HFS_DIRHINT_TTL)
+                       break;  /* stop here if this entry is too new */
+               name = hint->dh_desc.cd_nameptr;
+               if ((hint->dh_desc.cd_flags & CD_HASBUF) && (name != NULL)) {
+                       hint->dh_desc.cd_nameptr = NULL;
+                       hint->dh_desc.cd_namelen = 0;
+                       hint->dh_desc.cd_flags &= ~CD_HASBUF;
+                       vfs_removename((const char *)name);
                }
-       
-       if (nodeData->cnm_length > MAXHFSVNODELEN) 
-               { DBG_ASSERT(nodeData->cnm_nameptr != nodeData->cnm_namespace); }
-       else if (nodeData->cnm_nameptr) 
-               { DBG_ASSERT(nodeData->cnm_nameptr == nodeData->cnm_namespace); }
-
-#endif //HFS_DIAGNOSTIC
-       
-
-       /* Check to see if there is something to free, if yes, remember it */ 
-       if (fm->h_metaflags & IN_LONGNAME)
-               fname = fm->h_namePtr;
-       else
-               fname = NULL;
-
-       /* Set the flag */
-       if (nodeData->cnm_length > MAXHFSVNODELEN) {
-               fm->h_metaflags |= IN_LONGNAME;
-       } else {
-               fm->h_metaflags &= ~IN_LONGNAME;
-       };
-
-       /* Copy over the name */
-       if (nodeData->cnm_nameptr == nodeData->cnm_namespace) {
-               bcopy(nodeData->cnm_namespace, fm->h_fileName, nodeData->cnm_length+1);
-               fm->h_namePtr = fm->h_fileName;
-       } 
-       else {
-               fm->h_namePtr = nodeData->cnm_nameptr;
+               prev = TAILQ_PREV(hint, hfs_hinthead, dh_link); /* must save this pointer before calling FREE_ZONE on this node */
+               TAILQ_REMOVE(&dcp->c_hintlist, hint, dh_link);
+               FREE_ZONE(hint, sizeof(directoryhint_t), M_HFSDIRHINT);
+               --dcp->c_dirhintcnt;
        }
-
-       fm->h_namelen = nodeData->cnm_length;
-
-       nodeData->cnm_flags |= kCatNameIsConsumed;
-       nodeData->cnm_flags &= ~kCatNameIsAllocated;
-       nodeData->cnm_length =  0;
-       nodeData->cnm_nameptr =  (char *)0;
-       nodeData->cnm_namespace[0] = 0;
-
-       /* Lastly, free the old, if set */
-       if (fname != NULL)
-               FREE(fname, M_TEMP);
-}
-
-
-
-unsigned long DerivePermissionSummary(uid_t obj_uid, gid_t obj_gid, mode_t obj_mode, struct mount *mp, struct ucred *cred, struct proc *p) {
-    register gid_t *gp;
-    unsigned long permissions;
-    int i;
-
-     /* User id 0 (root) always gets access. */
-     if (cred->cr_uid == 0) {
-         permissions = R_OK | W_OK | X_OK;
-         goto Exit;
-     };
-
-    /* Otherwise, check the owner. */
-    if (hfs_catalogentry_owner_rights(obj_uid, mp, cred, p, false) == 0) {
-        permissions = ((unsigned long)obj_mode & S_IRWXU) >> 6;
-        goto Exit;
-    }
-
-    /* Otherwise, check the groups. */
-    if (! (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS)) {
-           for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
-               if (obj_gid == *gp) {
-                   permissions = ((unsigned long)obj_mode & S_IRWXG) >> 3;
-                               goto Exit;
-               }
-           };
-       };
-
-    /* Otherwise, settle for 'others' access. */
-    permissions = (unsigned long)obj_mode & S_IRWXO;
-
-Exit:
-       return permissions;    
-}
-
-
-
-int AttributeBlockSize(struct attrlist *attrlist) {
-       int size;
-       attrgroup_t a;
-       
-#if ((ATTR_CMN_NAME                    | ATTR_CMN_DEVID                        | ATTR_CMN_FSID                         | ATTR_CMN_OBJTYPE              | \
-      ATTR_CMN_OBJTAG          | ATTR_CMN_OBJID                        | ATTR_CMN_OBJPERMANENTID       | ATTR_CMN_PAROBJID             | \
-      ATTR_CMN_SCRIPT          | ATTR_CMN_CRTIME                       | ATTR_CMN_MODTIME                      | ATTR_CMN_CHGTIME              | \
-      ATTR_CMN_ACCTIME         | ATTR_CMN_BKUPTIME                     | ATTR_CMN_FNDRINFO                     | ATTR_CMN_OWNERID              | \
-      ATTR_CMN_GRPID           | ATTR_CMN_ACCESSMASK           | ATTR_CMN_NAMEDATTRCOUNT       | ATTR_CMN_NAMEDATTRLIST| \
-      ATTR_CMN_FLAGS           | ATTR_CMN_USERACCESS) != ATTR_CMN_VALIDMASK)
-#error AttributeBlockSize: Missing bits in common mask computation!
-#endif
-          DBG_ASSERT((attrlist->commonattr & ~ATTR_CMN_VALIDMASK) == 0);
-
-#if ((ATTR_VOL_FSTYPE          | ATTR_VOL_SIGNATURE            | ATTR_VOL_SIZE                         | ATTR_VOL_SPACEFREE    | \
-      ATTR_VOL_SPACEAVAIL      | ATTR_VOL_MINALLOCATION        | ATTR_VOL_ALLOCATIONCLUMP      | ATTR_VOL_IOBLOCKSIZE  | \
-      ATTR_VOL_OBJCOUNT                | ATTR_VOL_FILECOUNT            | ATTR_VOL_DIRCOUNT                     | ATTR_VOL_MAXOBJCOUNT  | \
-      ATTR_VOL_MOUNTPOINT      | ATTR_VOL_NAME                         | ATTR_VOL_MOUNTFLAGS       | ATTR_VOL_INFO                     | \
-      ATTR_VOL_MOUNTEDDEVICE| ATTR_VOL_ENCODINGSUSED   | ATTR_VOL_CAPABILITIES         | ATTR_VOL_ATTRIBUTES) != ATTR_VOL_VALIDMASK)
-#error AttributeBlockSize: Missing bits in volume mask computation!
-#endif
-          DBG_ASSERT((attrlist->volattr & ~ATTR_VOL_VALIDMASK) == 0);
-
-#if ((ATTR_DIR_LINKCOUNT | ATTR_DIR_ENTRYCOUNT | ATTR_DIR_MOUNTSTATUS) != ATTR_DIR_VALIDMASK)
-#error AttributeBlockSize: Missing bits in directory mask computation!
-#endif
-      DBG_ASSERT((attrlist->dirattr & ~ATTR_DIR_VALIDMASK) == 0);
-#if ((ATTR_FILE_LINKCOUNT      | ATTR_FILE_TOTALSIZE           | ATTR_FILE_ALLOCSIZE           | ATTR_FILE_IOBLOCKSIZE         | \
-      ATTR_FILE_CLUMPSIZE      | ATTR_FILE_DEVTYPE                     | ATTR_FILE_FILETYPE            | ATTR_FILE_FORKCOUNT           | \
-      ATTR_FILE_FORKLIST       | ATTR_FILE_DATALENGTH          | ATTR_FILE_DATAALLOCSIZE       | ATTR_FILE_DATAEXTENTS         | \
-      ATTR_FILE_RSRCLENGTH     | ATTR_FILE_RSRCALLOCSIZE       | ATTR_FILE_RSRCEXTENTS) != ATTR_FILE_VALIDMASK)
-#error AttributeBlockSize: Missing bits in file mask computation!
-#endif
-          DBG_ASSERT((attrlist->fileattr & ~ATTR_FILE_VALIDMASK) == 0);
-
-#if ((ATTR_FORK_TOTALSIZE | ATTR_FORK_ALLOCSIZE) != ATTR_FORK_VALIDMASK)
-#error AttributeBlockSize: Missing bits in fork mask computation!
-#endif
-      DBG_ASSERT((attrlist->forkattr & ~ATTR_FORK_VALIDMASK) == 0);
-
-       size = 0;
-       
-       if ((a = attrlist->commonattr) != 0) {
-        if (a & ATTR_CMN_NAME) size += sizeof(struct attrreference);
-               if (a & ATTR_CMN_DEVID) size += sizeof(dev_t);
-               if (a & ATTR_CMN_FSID) size += sizeof(fsid_t);
-               if (a & ATTR_CMN_OBJTYPE) size += sizeof(fsobj_type_t);
-               if (a & ATTR_CMN_OBJTAG) size += sizeof(fsobj_tag_t);
-               if (a & ATTR_CMN_OBJID) size += sizeof(fsobj_id_t);
-        if (a & ATTR_CMN_OBJPERMANENTID) size += sizeof(fsobj_id_t);
-               if (a & ATTR_CMN_PAROBJID) size += sizeof(fsobj_id_t);
-               if (a & ATTR_CMN_SCRIPT) size += sizeof(text_encoding_t);
-               if (a & ATTR_CMN_CRTIME) size += sizeof(struct timespec);
-               if (a & ATTR_CMN_MODTIME) size += sizeof(struct timespec);
-               if (a & ATTR_CMN_CHGTIME) size += sizeof(struct timespec);
-               if (a & ATTR_CMN_ACCTIME) size += sizeof(struct timespec);
-               if (a & ATTR_CMN_BKUPTIME) size += sizeof(struct timespec);
-               if (a & ATTR_CMN_FNDRINFO) size += 32 * sizeof(UInt8);
-               if (a & ATTR_CMN_OWNERID) size += sizeof(uid_t);
-               if (a & ATTR_CMN_GRPID) size += sizeof(gid_t);
-               if (a & ATTR_CMN_ACCESSMASK) size += sizeof(u_long);
-               if (a & ATTR_CMN_NAMEDATTRCOUNT) size += sizeof(u_long);
-               if (a & ATTR_CMN_NAMEDATTRLIST) size += sizeof(struct attrreference);
-               if (a & ATTR_CMN_FLAGS) size += sizeof(u_long);
-               if (a & ATTR_CMN_USERACCESS) size += sizeof(u_long);
-       };
-       if ((a = attrlist->volattr) != 0) {
-               if (a & ATTR_VOL_FSTYPE) size += sizeof(u_long);
-               if (a & ATTR_VOL_SIGNATURE) size += sizeof(u_long);
-               if (a & ATTR_VOL_SIZE) size += sizeof(off_t);
-               if (a & ATTR_VOL_SPACEFREE) size += sizeof(off_t);
-               if (a & ATTR_VOL_SPACEAVAIL) size += sizeof(off_t);
-               if (a & ATTR_VOL_MINALLOCATION) size += sizeof(off_t);
-               if (a & ATTR_VOL_ALLOCATIONCLUMP) size += sizeof(off_t);
-               if (a & ATTR_VOL_IOBLOCKSIZE) size += sizeof(u_long);
-               if (a & ATTR_VOL_OBJCOUNT) size += sizeof(u_long);
-               if (a & ATTR_VOL_FILECOUNT) size += sizeof(u_long);
-               if (a & ATTR_VOL_DIRCOUNT) size += sizeof(u_long);
-               if (a & ATTR_VOL_MAXOBJCOUNT) size += sizeof(u_long);
-               if (a & ATTR_VOL_MOUNTPOINT) size += sizeof(struct attrreference);
-        if (a & ATTR_VOL_NAME) size += sizeof(struct attrreference);
-        if (a & ATTR_VOL_MOUNTFLAGS) size += sizeof(u_long);
-        if (a & ATTR_VOL_MOUNTEDDEVICE) size += sizeof(struct attrreference);
-        if (a & ATTR_VOL_ENCODINGSUSED) size += sizeof(unsigned long long);
-        if (a & ATTR_VOL_CAPABILITIES) size += sizeof(vol_capabilities_attr_t);
-        if (a & ATTR_VOL_ATTRIBUTES) size += sizeof(vol_attributes_attr_t);
-       };
-       if ((a = attrlist->dirattr) != 0) {
-               if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_long);
-               if (a & ATTR_DIR_ENTRYCOUNT) size += sizeof(u_long);
-               if (a & ATTR_DIR_MOUNTSTATUS) size += sizeof(u_long);
-       };
-       if ((a = attrlist->fileattr) != 0) {
-               if (a & ATTR_FILE_LINKCOUNT) size += sizeof(u_long);
-               if (a & ATTR_FILE_TOTALSIZE) size += sizeof(off_t);
-               if (a & ATTR_FILE_ALLOCSIZE) size += sizeof(off_t);
-               if (a & ATTR_FILE_IOBLOCKSIZE) size += sizeof(size_t);
-               if (a & ATTR_FILE_CLUMPSIZE) size += sizeof(off_t);
-               if (a & ATTR_FILE_DEVTYPE) size += sizeof(u_long);
-               if (a & ATTR_FILE_FILETYPE) size += sizeof(u_long);
-               if (a & ATTR_FILE_FORKCOUNT) size += sizeof(u_long);
-               if (a & ATTR_FILE_FORKLIST) size += sizeof(struct attrreference);
-               if (a & ATTR_FILE_DATALENGTH) size += sizeof(off_t);
-               if (a & ATTR_FILE_DATAALLOCSIZE) size += sizeof(off_t);
-               if (a & ATTR_FILE_DATAEXTENTS) size += sizeof(extentrecord);
-               if (a & ATTR_FILE_RSRCLENGTH) size += sizeof(off_t);
-               if (a & ATTR_FILE_RSRCALLOCSIZE) size += sizeof(off_t);
-               if (a & ATTR_FILE_RSRCEXTENTS) size += sizeof(extentrecord);
-       };
-       if ((a = attrlist->forkattr) != 0) {
-               if (a & ATTR_FORK_TOTALSIZE) size += sizeof(off_t);
-               if (a & ATTR_FORK_ALLOCSIZE) size += sizeof(off_t);
-       };
-
-    return size;
-}
-
-
-
-char* FindMountpointName(struct mount *mp) {
-       size_t namelength = strlen(mp->mnt_stat.f_mntonname);
-       int foundchars = 0;
-       char *c;
-       
-       if (namelength == 0) return NULL;
-       
-       /* Look backwards through the name string, looking for the first slash
-          encountered (which must precede the last part of the pathname)
-        */
-       for (c = mp->mnt_stat.f_mntonname + namelength - 1; namelength > 0; --c, --namelength) {
-               if (*c != '/') {
-                       foundchars = 1;
-               } else if (foundchars) {
-                       return (c + 1);
-               };
-       };
-       
-       return mp->mnt_stat.f_mntonname;
-}
-
-
-
-void PackObjectName(struct vnode *vp,
-                                       char *name,
-                                       size_t namelen,
-                                       void **attrbufptrptr,
-                                       void **varbufptrptr) {
-       char *mpname;
-       size_t mpnamelen;
-       u_long attrlength;
-       
-       /* The name of an object may be incorrect for the root of a mounted filesystem
-          because it may be mounted on a different directory name than the name of the
-          volume (such as "blah-1".  For the root directory, it's best to return the
-          last element of the location where the volume's mounted:
-        */
-       if ((vp->v_flag & VROOT) && (mpname = FindMountpointName(vp->v_mount))) {
-               mpnamelen = strlen(mpname);
-               
-               /* Trim off any trailing slashes: */
-               while ((mpnamelen > 0) && (mpname[mpnamelen-1] == '/')) {
-                       --mpnamelen;
-               };
-               
-               /* If there's anything left, use it instead of the volume's name */
-               if (mpnamelen > 0) {
-                       name = mpname;
-                       namelen = mpnamelen;
-               };
-       };
-       
-       attrlength = namelen + 1;
-    ((struct attrreference *)(*attrbufptrptr))->attr_dataoffset = (char *)(*varbufptrptr) - (char *)(*attrbufptrptr);
-    ((struct attrreference *)(*attrbufptrptr))->attr_length = attrlength;
-    (void) strncpy((unsigned char *)(*varbufptrptr), name, attrlength);
-
-    /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-    (char *)(*varbufptrptr) += attrlength + ((4 - (attrlength & 3)) & 3);
-    ++((struct attrreference *)(*attrbufptrptr));
-}
-
-
-
-void PackVolCommonAttributes(struct attrlist *alist,
-                                                        struct vnode *root_vp,
-                                                        struct hfsCatalogInfo *root_catInfo,
-                                                        void **attrbufptrptr,
-                                                        void **varbufptrptr) {
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-    struct hfsnode *root_hp = VTOH(root_vp);
-    struct mount *mp = VTOVFS(root_vp);
-    struct hfsmount *hfsmp = VTOHFS(root_vp);
-    ExtendedVCB *vcb = HFSTOVCB(hfsmp);
-       u_long attrlength;
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-
-    if ((a = alist->commonattr) != 0) {
-        if (a & ATTR_CMN_NAME) {
-               PackObjectName(root_vp, H_NAME(root_hp), root_hp->h_meta->h_namelen, &attrbufptr, &varbufptr);
-        };
-               if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++ = hfsmp->hfs_raw_dev;
-               if (a & ATTR_CMN_FSID) {
-                       *((fsid_t *)attrbufptr) = mp->mnt_stat.f_fsid;
-                       ++((fsid_t *)attrbufptr);
-               };
-               if (a & ATTR_CMN_OBJTYPE) *((fsobj_type_t *)attrbufptr)++ = 0;
-               if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = VT_HFS;
-               if (a & ATTR_CMN_OBJID) {
-                       ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
-                       ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-                       ++((fsobj_id_t *)attrbufptr);
-               };
-        if (a & ATTR_CMN_OBJPERMANENTID) {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
-            ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-            ++((fsobj_id_t *)attrbufptr);
-        };
-               if (a & ATTR_CMN_PAROBJID) {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = 0;
-                       ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-                       ++((fsobj_id_t *)attrbufptr);
-               };
-               VCB_LOCK(vcb);
-        if (a & ATTR_CMN_SCRIPT) *((text_encoding_t *)attrbufptr)++ = vcb->volumeNameEncodingHint;
-               /* NOTE: all VCB dates are in Mac OS time */
-               if (a & ATTR_CMN_CRTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(LocalToUTC(vcb->localCreateDate));
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_MODTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_CHGTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_ACCTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbLsMod);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_BKUPTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbVolBkUp);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_FNDRINFO) {
-            bcopy (&vcb->vcbFndrInfo, attrbufptr, sizeof(vcb->vcbFndrInfo));
-            (char *)attrbufptr += sizeof(vcb->vcbFndrInfo);
-               };
-               VCB_UNLOCK(vcb);
-               if (a & ATTR_CMN_OWNERID) {
-                       if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((uid_t *)attrbufptr)++ =
-                                       (VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid;
-                       } else {
-                               *((uid_t *)attrbufptr)++ =
-                                       (root_hp->h_meta->h_uid == UNKNOWNUID) ? console_user : root_hp->h_meta->h_uid;
-                       };
-               };
-               if (a & ATTR_CMN_GRPID) {
-                       if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((gid_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_gid;
-                       } else {
-                               *((gid_t *)attrbufptr)++ = root_hp->h_meta->h_gid;
-                       };
-               };
-               if (a & ATTR_CMN_ACCESSMASK) *((u_long *)attrbufptr)++ = (u_long)root_hp->h_meta->h_mode;
-               if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0;                 /* XXX PPD TBC */
-               if (a & ATTR_CMN_NAMEDATTRLIST) {
-                       attrlength = 0;
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
-            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-                       
-                       /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-            (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-            ++((struct attrreference *)attrbufptr);
-               };
-               if (a & ATTR_CMN_FLAGS) *((u_long *)attrbufptr)++ = root_hp->h_meta->h_pflags;
-               if (a & ATTR_CMN_USERACCESS) {
-                       if (mp->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid,
-                                                                                       VTOHFS(root_vp)->hfs_gid,
-                                                                                       root_hp->h_meta->h_mode,
-                                                                                       VTOVFS(root_vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       } else {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((root_hp->h_meta->h_uid == UNKNOWNUID) ? console_user : root_hp->h_meta->h_uid,
-                                                                                       root_hp->h_meta->h_gid,
-                                                                                       root_hp->h_meta->h_mode,
-                                                                                       VTOVFS(root_vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       };
-               };
-       };
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
-}
-
-
-
-void PackVolAttributeBlock(struct attrlist *alist,
-                                                  struct vnode *root_vp,
-                                                  struct hfsCatalogInfo *root_catInfo,
-                                                  void **attrbufptrptr,
-                                                  void **varbufptrptr) {
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-    struct mount *mp = VTOVFS(root_vp);
-    struct hfsmount *hfsmp = VTOHFS(root_vp);
-    ExtendedVCB *vcb = HFSTOVCB(hfsmp);
-       u_long attrlength;
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-       
-       if ((a = alist->volattr) != 0) {
-               VCB_LOCK(vcb);
-               if (a & ATTR_VOL_FSTYPE) *((u_long *)attrbufptr)++ = (u_long)mp->mnt_vfc->vfc_typenum;
-               if (a & ATTR_VOL_SIGNATURE) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbSigWord;
-        if (a & ATTR_VOL_SIZE) *((off_t *)attrbufptr)++ = (off_t)vcb->totalBlocks * (off_t)vcb->blockSize;
-        if (a & ATTR_VOL_SPACEFREE) *((off_t *)attrbufptr)++ = (off_t)vcb->freeBlocks * (off_t)vcb->blockSize;
-        if (a & ATTR_VOL_SPACEAVAIL) *((off_t *)attrbufptr)++ = (off_t)vcb->freeBlocks * (off_t)vcb->blockSize;
-        if (a & ATTR_VOL_MINALLOCATION) *((off_t *)attrbufptr)++ = (off_t)vcb->blockSize;
-        if (a & ATTR_VOL_ALLOCATIONCLUMP) *((off_t *)attrbufptr)++ = (off_t)(vcb->vcbClpSiz);
-        if (a & ATTR_VOL_IOBLOCKSIZE) *((u_long *)attrbufptr)++ = (u_long)hfsmp->hfs_logBlockSize;
-               if (a & ATTR_VOL_OBJCOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbFilCnt + (u_long)vcb->vcbDirCnt;
-               if (a & ATTR_VOL_FILECOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbFilCnt;
-               if (a & ATTR_VOL_DIRCOUNT) *((u_long *)attrbufptr)++ = (u_long)vcb->vcbDirCnt;
-               if (a & ATTR_VOL_MAXOBJCOUNT) *((u_long *)attrbufptr)++ = 0xFFFFFFFF;
-               if (a & ATTR_VOL_MOUNTPOINT) {
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
-            ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_stat.f_mntonname) + 1;
-                       attrlength = ((struct attrreference *)attrbufptr)->attr_length;
-                       attrlength = attrlength + ((4 - (attrlength & 3)) & 3);         /* round up to the next 4-byte boundary: */
-                       (void) bcopy(mp->mnt_stat.f_mntonname, varbufptr, attrlength);
-                       
-                       /* Advance beyond the space just allocated: */
-            (char *)varbufptr += attrlength;
-            ++((struct attrreference *)attrbufptr);
-               };
-        if (a & ATTR_VOL_NAME) {
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
-            ((struct attrreference *)attrbufptr)->attr_length = VTOH(root_vp)->h_meta->h_namelen + 1;
-                       attrlength = ((struct attrreference *)attrbufptr)->attr_length;
-                       attrlength = attrlength + ((4 - (attrlength & 3)) & 3);         /* round up to the next 4-byte boundary: */
-            bcopy(H_NAME(VTOH(root_vp)), varbufptr, attrlength);
-
-                       /* Advance beyond the space just allocated: */
-            (char *)varbufptr += attrlength;
-            ++((struct attrreference *)attrbufptr);
-        };
-        if (a & ATTR_VOL_MOUNTFLAGS) *((u_long *)attrbufptr)++ = (u_long)mp->mnt_flag;
-        if (a & ATTR_VOL_MOUNTEDDEVICE) {
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
-            ((struct attrreference *)attrbufptr)->attr_length = strlen(mp->mnt_stat.f_mntfromname) + 1;
-                       attrlength = ((struct attrreference *)attrbufptr)->attr_length;
-                       attrlength = attrlength + ((4 - (attrlength & 3)) & 3);         /* round up to the next 4-byte boundary: */
-                       (void) bcopy(mp->mnt_stat.f_mntfromname, varbufptr, attrlength);
-                       
-                       /* Advance beyond the space just allocated: */
-            (char *)varbufptr += attrlength;
-            ++((struct attrreference *)attrbufptr);
-        };
-        if (a & ATTR_VOL_ENCODINGSUSED) *((unsigned long long *)attrbufptr)++ = (unsigned long long)vcb->encodingsBitmap;
-        if (a & ATTR_VOL_CAPABILITIES) {
-               ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_FORMAT] =
-                               VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS;
-               ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_INTERFACES] =
-                               VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT;
-               ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
-               ((vol_capabilities_attr_t *)attrbufptr)->capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
-
-               ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_FORMAT] =
-                               VOL_CAP_FMT_PERSISTENTOBJECTIDS | VOL_CAP_FMT_SYMBOLICLINKS | VOL_CAP_FMT_HARDLINKS;
-               ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_INTERFACES] =
-                               VOL_CAP_INT_SEARCHFS | VOL_CAP_INT_ATTRLIST | VOL_CAP_INT_NFSEXPORT;
-               ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_RESERVED1] = 0;
-               ((vol_capabilities_attr_t *)attrbufptr)->valid[VOL_CAPABILITIES_RESERVED2] = 0;
-
-            ++((vol_capabilities_attr_t *)attrbufptr);
-        };
-        if (a & ATTR_VOL_ATTRIBUTES) {
-               ((vol_attributes_attr_t *)attrbufptr)->validattr.commonattr = ATTR_CMN_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->validattr.volattr = ATTR_VOL_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->validattr.dirattr = ATTR_DIR_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->validattr.fileattr = ATTR_FILE_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->validattr.forkattr = ATTR_FORK_VALIDMASK;
-
-               ((vol_attributes_attr_t *)attrbufptr)->nativeattr.commonattr = ATTR_CMN_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->nativeattr.volattr = ATTR_VOL_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->nativeattr.dirattr = ATTR_DIR_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->nativeattr.fileattr = ATTR_FILE_VALIDMASK;
-               ((vol_attributes_attr_t *)attrbufptr)->nativeattr.forkattr = ATTR_FORK_VALIDMASK;
-
-            ++((vol_attributes_attr_t *)attrbufptr);
-        };
-               VCB_UNLOCK(vcb);
-       };
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
 }
 
-
-
-
-void PackVolumeInfo(struct attrlist *alist,
-                    struct vnode *root_vp,
-                    struct hfsCatalogInfo *root_catinfo,
-                    void **attrbufptrptr,
-                    void **varbufptrptr) {
-
-    PackVolCommonAttributes(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
-    PackVolAttributeBlock(alist, root_vp, root_catinfo, attrbufptrptr, varbufptrptr);
-};
-
-// Pack the common attribute contents of an objects hfsCatalogInfo
-void PackCommonCatalogInfoAttributeBlock(struct attrlist               *alist,
-                                                                               struct vnode                    *root_vp,
-                                                                               struct hfsCatalogInfo   *catalogInfo,
-                                                                               void                                    **attrbufptrptr,
-                                                                               void                                    **varbufptrptr )
+/*
+ * Insert a detached directory hint back into the list of dirhints.
+ *
+ * Requires an exclusive lock on directory cnode.
+ */
+__private_extern__
+void
+hfs_insertdirhint(struct cnode *dcp, directoryhint_t * hint)
 {
-       struct hfsnode  *hp;
-       void                    *attrbufptr;
-       void                    *varbufptr;
-       attrgroup_t             a;
-       u_long                  attrlength;
-       
-       hp                      = VTOH(root_vp);
-       attrbufptr      = *attrbufptrptr;
-       varbufptr       = *varbufptrptr;
-       
-       if ((a = alist->commonattr) != 0)
-       {
-               if (a & ATTR_CMN_NAME)                                                                          
-            {
-            attrlength = strlen(catalogInfo->nodeData.cnm_nameptr) + 1;
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
-            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-            (void) strncpy((unsigned char *)varbufptr,
-                           catalogInfo->nodeData.cnm_nameptr, attrlength);
-            /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-            (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-            ++((struct attrreference *)attrbufptr);
-            };
-               if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++                        = H_DEV(hp);
-               if (a & ATTR_CMN_FSID) {
-                       *((fsid_t *)attrbufptr) = VTOVFS(root_vp)->mnt_stat.f_fsid;
-                       ++((fsid_t *)attrbufptr);
-               };
-               if (a & ATTR_CMN_OBJTYPE)
-               {
-                       switch (catalogInfo->nodeData.cnd_type) {
-                         case kCatalogFolderNode:
-                               *((fsobj_type_t *)attrbufptr)++ = VDIR;
-                               break;
-
-                         case kCatalogFileNode:
-                               /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */
-                       if ((HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) &&
-                               (catalogInfo->nodeData.cnd_mode & IFMT)) {
-                                       *((fsobj_type_t *)attrbufptr)++ =
-                                           IFTOVT((mode_t)catalogInfo->nodeData.cnd_mode);
-                               } else {
-                                       *((fsobj_type_t *)attrbufptr)++ = VREG;
-                               };
-                               break;
-                               
-                         default:
-                               *((fsobj_type_t *)attrbufptr)++ = VNON;
-                               break;
-                       };
-               }
-               if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = root_vp->v_tag;
-        if (a & ATTR_CMN_OBJID)
-        {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnd_nodeID;
-            ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-            ++((fsobj_id_t *)attrbufptr);
-        };
-        if (a & ATTR_CMN_OBJPERMANENTID)
-        {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnd_nodeID;
-            ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-            ++((fsobj_id_t *)attrbufptr);
-        };
-               if (a & ATTR_CMN_PAROBJID)
-               {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = catalogInfo->nodeData.cnm_parID;
-                       ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-                       ++((fsobj_id_t *)attrbufptr);
-               };
-        if (a & ATTR_CMN_SCRIPT)
-         {
-           if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) {
-                       *((text_encoding_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_textEncoding;
-           } else {
-                       *((text_encoding_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_encoding;
-           }
-         };
-               if (a & ATTR_CMN_CRTIME)
-               {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_createDate);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_MODTIME)
-               {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_contentModDate);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_CHGTIME)
-               {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_attributeModDate);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_ACCTIME)
-               {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_accessDate);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_BKUPTIME)
-               {
-                       ((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(catalogInfo->nodeData.cnd_backupDate);
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_FNDRINFO)
-               {
-                       bcopy (&catalogInfo->nodeData.cnd_finderInfo, attrbufptr, sizeof(catalogInfo->nodeData.cnd_finderInfo));
-                       (char *)attrbufptr += sizeof(catalogInfo->nodeData.cnd_finderInfo);
-               };
-               if (a & ATTR_CMN_OWNERID) {
-                       if (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((uid_t *)attrbufptr)++ =
-                                       (VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid;
-                       } else {
-                               *((uid_t *)attrbufptr)++ =
-                                       (catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID;
-                       };
-               }
-               if (a & ATTR_CMN_GRPID) {
-                       if (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((gid_t *)attrbufptr)++ = VTOHFS(root_vp)->hfs_gid;
-                       } else {
-                               *((gid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_groupID;
-                       };
-               }
-               if (a & ATTR_CMN_ACCESSMASK) {
-#if OVERRIDE_UNKNOWN_PERMISSIONS
-                       if (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               switch (catalogInfo->nodeData.cnd_type) {
-                                 case kCatalogFileNode:
-                                       /* Files in an HFS+ catalog can represent many things (regular files, symlinks, block/character devices, ...) */
-                                       *((fsobj_type_t *)attrbufptr)++ = (u_long)(VTOHFS(root_vp)->hfs_file_mask);
-                                       break;
-                                       
-                                 case kCatalogFolderNode:
-                                       /* Fall through to default case */
-                                 
-                                 default:
-                                       *((u_long *)attrbufptr)++ = (u_long)(VTOHFS(root_vp)->hfs_dir_mask);
-                               };
-                       } else {
-#endif
-                               *((u_long *)attrbufptr)++ =
-                                   (u_long)catalogInfo->nodeData.cnd_mode;
-#if OVERRIDE_UNKNOWN_PERMISSIONS
-                       };
-#endif
-               }
-               if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0;                 /* XXX PPD TBC */
-               if (a & ATTR_CMN_NAMEDATTRLIST)
-               {
-                       attrlength = 0;
-                       ((struct attrreference *)attrbufptr)->attr_dataoffset   = 0;
-                       ((struct attrreference *)attrbufptr)->attr_length               = attrlength;
-                       
-                       /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-                       (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-                       ++((struct attrreference *)attrbufptr);
-               };
-        if (a & ATTR_CMN_FLAGS)
-               *((u_long *)attrbufptr)++ =
-                       (u_long) (catalogInfo->nodeData.cnd_ownerFlags |
-                                (catalogInfo->nodeData.cnd_adminFlags << 16));
-               if (a & ATTR_CMN_USERACCESS) {
-                       if (VTOVFS(root_vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((VTOHFS(root_vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(root_vp)->hfs_uid,
-                                                                                       VTOHFS(root_vp)->hfs_gid,
-                                                                                       (catalogInfo->nodeData.cnd_type == kCatalogFileNode) ? VTOHFS(root_vp)->hfs_file_mask : VTOHFS(root_vp)->hfs_dir_mask,
-                                                                                       VTOVFS(root_vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       } else {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((catalogInfo->nodeData.cnd_ownerID == UNKNOWNUID) ? console_user : catalogInfo->nodeData.cnd_ownerID,
-                                                                                       catalogInfo->nodeData.cnd_groupID,
-                                                                                       (mode_t)catalogInfo->nodeData.cnd_mode,
-                                                                                       VTOVFS(root_vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       };
-               };
-       };
-       
-       *attrbufptrptr  = attrbufptr;
-       *varbufptrptr   = varbufptr;
-}
+       directoryhint_t *test;
 
+       TAILQ_FOREACH(test, &dcp->c_hintlist, dh_link) {
+               if (test == hint)
+                       panic("hfs_insertdirhint: hint %p already on list!", hint);
+       }
 
-void PackCommonAttributeBlock(struct attrlist *alist,
-                                                         struct vnode *vp,
-                                                         struct hfsCatalogInfo *catInfo,
-                                                         void **attrbufptrptr,
-                                                         void **varbufptrptr) {
-       struct hfsnode *hp;
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-       u_long attrlength;
-       
-       hp = VTOH(vp);
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-       
-    if ((a = alist->commonattr) != 0) {
-        if (a & ATTR_CMN_NAME) {
-                       PackObjectName(vp, H_NAME(hp), hp->h_meta->h_namelen, &attrbufptr, &varbufptr);
-        };
-               if (a & ATTR_CMN_DEVID) *((dev_t *)attrbufptr)++ = H_DEV(hp);
-               if (a & ATTR_CMN_FSID) {
-                       *((fsid_t *)attrbufptr) = VTOVFS(vp)->mnt_stat.f_fsid;
-                       ++((fsid_t *)attrbufptr);
-               };
-               if (a & ATTR_CMN_OBJTYPE) *((fsobj_type_t *)attrbufptr)++ = vp->v_type;
-               if (a & ATTR_CMN_OBJTAG) *((fsobj_tag_t *)attrbufptr)++ = vp->v_tag;
-        if (a & ATTR_CMN_OBJID)        {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = H_FILEID(hp);
-                       ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-                       ++((fsobj_id_t *)attrbufptr);
-               };
-        if (a & ATTR_CMN_OBJPERMANENTID)       {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = H_FILEID(hp);
-            ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-            ++((fsobj_id_t *)attrbufptr);
-        };
-               if (a & ATTR_CMN_PAROBJID) {
-            ((fsobj_id_t *)attrbufptr)->fid_objno = H_DIRID(hp);
-                       ((fsobj_id_t *)attrbufptr)->fid_generation = 0;
-                       ++((fsobj_id_t *)attrbufptr);
-               };
-        if (a & ATTR_CMN_SCRIPT)
-         {
-           if (HTOVCB(hp)->vcbSigWord == kHFSPlusSigWord) {
-                       *((text_encoding_t *)attrbufptr)++ = catInfo->nodeData.cnd_textEncoding;
-           } else {
-                       *((text_encoding_t *)attrbufptr)++ = VTOHFS(vp)->hfs_encoding;
-           }
-         };
-               if (a & ATTR_CMN_CRTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_crtime;
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_MODTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_mtime;
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_CHGTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_ctime;
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_ACCTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_atime;
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_BKUPTIME) {
-                       ((struct timespec *)attrbufptr)->tv_sec = hp->h_meta->h_butime;
-                       ((struct timespec *)attrbufptr)->tv_nsec = 0;
-                       ++((struct timespec *)attrbufptr);
-               };
-               if (a & ATTR_CMN_FNDRINFO) {
-                       bcopy (&catInfo->nodeData.cnd_finderInfo, attrbufptr, sizeof(catInfo->nodeData.cnd_finderInfo));
-                       (char *)attrbufptr += sizeof(catInfo->nodeData.cnd_finderInfo);
-               };
-               if (a & ATTR_CMN_OWNERID) {
-                       if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((uid_t *)attrbufptr)++ =
-                                       (VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid;
-                       } else {
-                               *((uid_t *)attrbufptr)++ =
-                                       (hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid;
-                       }
-               };
-               if (a & ATTR_CMN_GRPID) {
-                       if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((gid_t *)attrbufptr)++ = VTOHFS(vp)->hfs_gid;
-                       } else {
-                               *((gid_t *)attrbufptr)++ = hp->h_meta->h_gid;
-                       };
-               };
-               if (a & ATTR_CMN_ACCESSMASK) *((u_long *)attrbufptr)++ = (u_long)hp->h_meta->h_mode;
-               if (a & ATTR_CMN_NAMEDATTRCOUNT) *((u_long *)attrbufptr)++ = 0;                 /* XXX PPD TBC */
-               if (a & ATTR_CMN_NAMEDATTRLIST) {
-                       attrlength = 0;
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
-            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-                       
-                       /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-            (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-            ++((struct attrreference *)attrbufptr);
-               };
-               if (a & ATTR_CMN_FLAGS) *((u_long *)attrbufptr)++ = hp->h_meta->h_pflags;
-               if (a & ATTR_CMN_USERACCESS) {
-                       if (VTOVFS(vp)->mnt_flag & MNT_UNKNOWNPERMISSIONS) {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((VTOHFS(vp)->hfs_uid == UNKNOWNUID) ? console_user : VTOHFS(vp)->hfs_uid,
-                                                                                       VTOHFS(vp)->hfs_gid,
-                                                                                       hp->h_meta->h_mode,
-                                                                                       VTOVFS(vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       } else {
-                               *((u_long *)attrbufptr)++ =
-                                       DerivePermissionSummary((hp->h_meta->h_uid == UNKNOWNUID) ? console_user : hp->h_meta->h_uid,
-                                                                                       hp->h_meta->h_gid,
-                                                                                       hp->h_meta->h_mode,
-                                                                                       VTOVFS(vp),
-                                                                                       current_proc()->p_ucred,
-                                                                                       current_proc());
-                       };
-               };
-       };
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
+       TAILQ_INSERT_HEAD(&dcp->c_hintlist, hint, dh_link);
+       ++dcp->c_dirhintcnt;
 }
 
-
-//     Pack the directory attributes given hfsCatalogInfo
-void PackCatalogInfoDirAttributeBlock( struct attrlist *alist, struct vnode *vp, 
-       struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr )
+/*
+ * Perform a case-insensitive compare of two UTF-8 filenames.
+ *
+ * Returns 0 if the strings match.
+ */
+__private_extern__
+int
+hfs_namecmp(const u_int8_t *str1, size_t len1, const u_int8_t *str2, size_t len2)
 {
-       void            *attrbufptr;
-       attrgroup_t     a;
-       u_long valence;
-       
-       attrbufptr      = *attrbufptrptr;
-       a                       = alist->dirattr;
-       
-       if ( (catInfo->nodeData.cnd_type == kCatalogFolderNode) && (a != 0) ) {
-               valence = catInfo->nodeData.cnd_valence;
-               if ((catInfo->nodeData.cnm_parID == kRootParID) &&
-                   (VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
-                       --valence;      /* hide private dir */
-               }
-               /* The 'link count' is faked */
-               if (a & ATTR_DIR_LINKCOUNT)
-                       *((u_long *)attrbufptr)++ = 2 + valence;
-               if (a & ATTR_DIR_ENTRYCOUNT)
-                       *((u_long *)attrbufptr)++ = valence;
-               if (a & ATTR_DIR_MOUNTSTATUS)
-                       *((u_long *)attrbufptr)++ = 0;
-       };
-       
-       *attrbufptrptr = attrbufptr;
-}
-
-
-void PackDirAttributeBlock(struct attrlist *alist,
-                                                  struct vnode *vp,
-                                                  struct hfsCatalogInfo *catInfo,
-                                                  void **attrbufptrptr,
-                                                  void **varbufptrptr) {
-       void *attrbufptr;
-       attrgroup_t a;
-       u_long valence;
-       
-       attrbufptr = *attrbufptrptr;
-       
-       a = alist->dirattr;
-       if ((vp->v_type == VDIR) && (a != 0)) {
-               valence = catInfo->nodeData.cnd_valence;
-               if ((catInfo->nodeData.cnm_parID == kRootParID) &&
-                   (VTOHFS(vp)->hfs_private_metadata_dir != 0)) {
-                               --valence;      /* hide private dir */
-               }
-
-               /* The 'link count' is faked */
-               if (a & ATTR_DIR_LINKCOUNT)
-                       *((u_long *)attrbufptr)++ = 2 + valence;
-               if (a & ATTR_DIR_ENTRYCOUNT) 
-                       *((u_long *)attrbufptr)++ = valence;
-               if (a & ATTR_DIR_MOUNTSTATUS) {
-                       if (vp->v_mountedhere) {
-                               *((u_long *)attrbufptr)++ = DIR_MNTSTATUS_MNTPOINT;
-                       } else {
-                               *((u_long *)attrbufptr)++ = 0;
-                       };
-               };
-       };
-       
-       *attrbufptrptr = attrbufptr;
+       u_int16_t *ustr1, *ustr2;
+       size_t ulen1, ulen2;
+       size_t maxbytes;
+       int cmp = -1;
+
+       if (len1 != len2)
+               return (cmp);
+
+       maxbytes = kHFSPlusMaxFileNameChars << 1;
+       MALLOC(ustr1, u_int16_t *, maxbytes << 1, M_TEMP, M_WAITOK);
+       ustr2 = ustr1 + (maxbytes >> 1);
+
+       if (utf8_decodestr(str1, len1, ustr1, &ulen1, maxbytes, ':', 0) != 0)
+               goto out;
+       if (utf8_decodestr(str2, len2, ustr2, &ulen2, maxbytes, ':', 0) != 0)
+               goto out;
+       
+       cmp = FastUnicodeCompare(ustr1, ulen1>>1, ustr2, ulen2>>1);
+out:
+       FREE(ustr1, M_TEMP);
+       return (cmp);
 }
 
 
+typedef struct jopen_cb_info {
+       off_t   jsize;
+       char   *desired_uuid;
+        struct  vnode *jvp;
+       size_t  blksize;
+       int     need_clean;
+       int     need_init;
+} jopen_cb_info;
 
-//     Pack the file attributes from the hfsCatalogInfo for the file.
-void PackCatalogInfoFileAttributeBlock( struct attrlist *alist, struct vnode *root_vp, struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr )
+static int
+journal_open_cb(const char *bsd_dev_name, const char *uuid_str, void *arg)
 {
-       void                    *attrbufptr;
-       void                    *varbufptr;
-       attrgroup_t             a;
-       u_long                  attrlength;
-       ExtendedVCB             *vcb                    = VTOVCB(root_vp);
-       
-       attrbufptr      = *attrbufptrptr;
-       varbufptr       = *varbufptrptr;
+       struct nameidata nd;
+       jopen_cb_info *ji = (jopen_cb_info *)arg;
+       char bsd_name[256];
+       int error;
        
-       a = alist->fileattr;
-       if ( (catInfo->nodeData.cnd_type == kCatalogFileNode) && (a != 0) )
-       {
-#if HFS_HARDLINKS
-               if (a & ATTR_FILE_LINKCOUNT) {
-                       u_long linkcnt = catInfo->nodeData.cnd_linkCount;
+       strlcpy(&bsd_name[0], "/dev/", sizeof(bsd_name));
+       strlcpy(&bsd_name[5], bsd_dev_name, sizeof(bsd_name)-5);
 
-                       if (linkcnt < 1)
-                               linkcnt = 1;
-                       *((u_long *)attrbufptr)++ = linkcnt;
-               }
-#else
-               if (a & ATTR_FILE_LINKCOUNT) *((u_long *)attrbufptr)++ = 1;
-#endif
-               if (a & ATTR_FILE_TOTALSIZE) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)catInfo->nodeData.cnd_datafork.logicalSize +
-                           (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
-               }
-               if (a & ATTR_FILE_ALLOCSIZE) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize) +
-                           (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-               }
-               if (a & ATTR_FILE_IOBLOCKSIZE) {
-                       *((u_long *)attrbufptr)++ = (u_long)(VTOHFS(root_vp)->hfs_logBlockSize);
-               }
-               if (a & ATTR_FILE_CLUMPSIZE) {
-                       *((u_long *)attrbufptr)++ = vcb->vcbClpSiz;
-               }
-               if (a & ATTR_FILE_DEVTYPE) {
-                       u_long rawdev;
-                       u_short filetype;
+       if (ji->desired_uuid && ji->desired_uuid[0] && strcmp(uuid_str, ji->desired_uuid) != 0) {
+               return 1;   // keep iterating
+       }
 
-                       filetype = (catInfo->nodeData.cnd_mode & IFMT);
-                       if (filetype == IFCHR || filetype == IFBLK)
-                               rawdev = (u_long)catInfo->nodeData.cnd_rawDevice;
-                       else
-                               rawdev = 0;
-                       
-                       *((u_long *)attrbufptr)++ = rawdev;
-               }
-               if (a & ATTR_FILE_FILETYPE) {
-                       *((u_long *)attrbufptr)++ = 0;  /* XXX PPD */
-               }
-               if (a & ATTR_FILE_FORKCOUNT) {
-                       *((u_long *)attrbufptr)++ = 2;  /* XXX PPD */
-               }
-               if (a & ATTR_FILE_FORKLIST) {
-                       attrlength = 0;
-                       ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
-                       ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-                       
-                       /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-                       (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-                       ++((struct attrreference *)attrbufptr);
-               };
-               if (a & ATTR_FILE_DATALENGTH) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)catInfo->nodeData.cnd_datafork.logicalSize;
-               }
-               if (a & ATTR_FILE_DATAALLOCSIZE) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
-               }
-               if (a & ATTR_FILE_DATAEXTENTS) {
-                       bcopy(&catInfo->nodeData.cnd_datafork.extents, attrbufptr, sizeof(extentrecord));
-                       (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-               };
-               if (a & ATTR_FILE_RSRCLENGTH) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
-               }
-               if (a & ATTR_FILE_RSRCALLOCSIZE) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-               }
-               if (a & ATTR_FILE_RSRCEXTENTS) {
-                       bcopy(&catInfo->nodeData.cnd_rsrcfork.extents, attrbufptr, sizeof(extentrecord));
-                       (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-               };
-       };
+       // if we're here, either the desired uuid matched or there was no
+       // desired uuid so let's try to open the device for writing and
+       // see if it works.  if it does, we'll use it.
        
-       *attrbufptrptr  = attrbufptr;
-       *varbufptrptr   = varbufptr;
-}
+       NDINIT(&nd, LOOKUP, LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(bsd_name), vfs_context_kernel());
+       if ((error = namei(&nd))) {
+               printf("hfs: journal open cb: error %d looking up device %s (dev uuid %s)\n", error, bsd_name, uuid_str);
+               return 1;   // keep iterating
+       }
 
+       ji->jvp = nd.ni_vp;
+       nameidone(&nd);
 
-void PackFileAttributeBlock(struct attrlist *alist,
-                                                       struct vnode *vp,
-                                                       struct hfsCatalogInfo *catInfo,
-                                                       void **attrbufptrptr,
-                                                       void **varbufptrptr) {
-    struct hfsnode *hp = VTOH(vp);
-    FCB *fcb = HTOFCB(hp);
-       ExtendedVCB *vcb = HTOVCB(hp);
-    Boolean isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
-    void *attrbufptr = *attrbufptrptr;
-    void *varbufptr = *varbufptrptr;
-    attrgroup_t a = alist->fileattr;
-       u_long attrlength;
-       
-       if (a != 0) {
-#if HFS_HARDLINKS
-               if (a & ATTR_FILE_LINKCOUNT) {
-                       u_long linkcnt = catInfo->nodeData.cnd_linkCount;
-                       
-                       if (linkcnt < 1)
-                               linkcnt = 1;
-                       *((u_long *)attrbufptr)++ = linkcnt;
-               }
-#else
-               if (a & ATTR_FILE_LINKCOUNT) *((u_long *)attrbufptr)++ = 1;
-#endif
-               if (a & ATTR_FILE_TOTALSIZE) {
-                       *((off_t *)attrbufptr)++ =
-                           (off_t)catInfo->nodeData.cnd_datafork.logicalSize +
-                           (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
-               }
-               if (a & ATTR_FILE_ALLOCSIZE) {
-                       switch (H_FORKTYPE(hp)) {
-                       case kDataFork:
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)fcb->fcbPLen +
-                                   (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-                               break;
-                       case kRsrcFork:
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)fcb->fcbPLen +
-                                   (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
-                               break;
-                       default:
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize) +
-                                   (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-                 };
-               }; 
-               if (a & ATTR_FILE_IOBLOCKSIZE) *((u_long *)attrbufptr)++ = GetLogicalBlockSize(vp);
-               if (a & ATTR_FILE_CLUMPSIZE) *((u_long *)attrbufptr)++ = fcb->fcbClmpSize;
-               if (a & ATTR_FILE_DEVTYPE) {
-                       u_long rawdev;
-                       
-                       if ((vp->v_type == VBLK) || (vp->v_type == VCHR))
-                               rawdev = (u_long)catInfo->nodeData.cnd_rawDevice;
-                       else
-                               rawdev = 0;
-                       *((u_long *)attrbufptr)++ = rawdev;
-               }
-               if (a & ATTR_FILE_FILETYPE) *((u_long *)attrbufptr)++ = 0;                      /* XXX PPD */
-               if (a & ATTR_FILE_FORKCOUNT) *((u_long *)attrbufptr)++ = 2;                     /* XXX PPD */
-               if (a & ATTR_FILE_FORKLIST) {
-                       attrlength = 0;
-            ((struct attrreference *)attrbufptr)->attr_dataoffset = 0;
-            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
-                       
-                       /* Advance beyond the space just allocated and round up to the next 4-byte boundary: */
-            (char *)varbufptr += attrlength + ((4 - (attrlength & 3)) & 3);
-            ++((struct attrreference *)attrbufptr);
-               };
-               if (H_FORKTYPE(hp) == kDataFork) {
-                       if (a & ATTR_FILE_DATALENGTH)
-                          *((off_t *)attrbufptr)++ = fcb->fcbEOF;
-                       if (a & ATTR_FILE_DATAALLOCSIZE) *((off_t *)attrbufptr)++ = fcb->fcbPLen;
-                       if (a & ATTR_FILE_DATAEXTENTS) {
-                           bcopy ( fcb->fcbExtents, attrbufptr, sizeof(extentrecord));
-                               (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-                       };
-               } else {
-                       if (a & ATTR_FILE_DATALENGTH) {
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)catInfo->nodeData.cnd_datafork.logicalSize;
+       if (ji->jvp == NULL) {
+               printf("hfs: journal open cb: did not find %s (error %d)\n", bsd_name, error);
+       } else {
+               error = VNOP_OPEN(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
+               if (error == 0) {
+                       // if the journal is dirty and we didn't specify a desired
+                       // journal device uuid, then do not use the journal.  but
+                       // if the journal is just invalid (e.g. it hasn't been
+                       // initialized) then just set the need_init flag.
+                       if (ji->need_clean && ji->desired_uuid && ji->desired_uuid[0] == '\0') {
+                               error = journal_is_clean(ji->jvp, 0, ji->jsize, (void *)1, ji->blksize);
+                               if (error == EBUSY) {
+                                       VNOP_CLOSE(ji->jvp, FREAD|FWRITE, vfs_context_kernel());
+                                       vnode_put(ji->jvp);
+                                       ji->jvp = NULL;
+                                       return 1;    // keep iterating
+                               } else if (error == EINVAL) {
+                                       ji->need_init = 1;
+                               }
                        }
-                       if (a & ATTR_FILE_DATAALLOCSIZE) {
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)((off_t)catInfo->nodeData.cnd_datafork.totalBlocks * (off_t)vcb->blockSize);
+
+                       if (ji->desired_uuid && ji->desired_uuid[0] == '\0') {
+                               strlcpy(ji->desired_uuid, uuid_str, 128);
                        }
-                       if (a & ATTR_FILE_DATAEXTENTS) {
-                               bcopy(&catInfo->nodeData.cnd_datafork.extents, attrbufptr, sizeof(extentrecord));
-                               (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-                       };
-               };
-               if (H_FORKTYPE(hp) == kRsrcFork) {
-                       if (a & ATTR_FILE_RSRCLENGTH) 
-                          *((off_t *)attrbufptr)++ = fcb->fcbEOF;
-                       if (a & ATTR_FILE_RSRCALLOCSIZE) *((off_t *)attrbufptr)++ = fcb->fcbPLen;
-                       if (a & ATTR_FILE_RSRCEXTENTS) {
-                           bcopy ( fcb->fcbExtents, attrbufptr, sizeof(extentrecord));
-                               (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-                       };
+                       vnode_setmountedon(ji->jvp);
+                       // printf("hfs: journal open cb: got device %s (%s)\n", bsd_name, uuid_str);
+                       return 0;   // stop iterating
                } else {
-                       if (a & ATTR_FILE_RSRCLENGTH) {
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)catInfo->nodeData.cnd_rsrcfork.logicalSize;
-                       }
-                       if (a & ATTR_FILE_RSRCALLOCSIZE) {
-                               *((off_t *)attrbufptr)++ =
-                                   (off_t)((off_t)catInfo->nodeData.cnd_rsrcfork.totalBlocks * (off_t)vcb->blockSize);
-                       }
-                       if (a & ATTR_FILE_RSRCEXTENTS) {
-                               bcopy(&catInfo->nodeData.cnd_rsrcfork.extents, attrbufptr, sizeof(extentrecord));
-                               (char *)attrbufptr += sizeof(extentrecord) + ((4 - (sizeof(extentrecord) & 3)) & 3);
-                       };
-               };
-       };
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
-}
-
-#if 0
-void PackForkAttributeBlock(struct attrlist *alist,
-                                                       struct vnode *vp,
-                                                       struct hfsCatalogInfo *catInfo,
-                                                       void **attrbufptrptr,
-                                                       void **varbufptrptr) {
-       /* XXX PPD TBC */
-}
-#endif
-
-
-//     This routine takes catInfo, and alist, as inputs and packs it into an attribute block.
-void PackCatalogInfoAttributeBlock ( struct attrlist *alist, struct vnode *root_vp, struct hfsCatalogInfo *catInfo, void **attrbufptrptr, void **varbufptrptr)
-{
-       //XXX   Preflight that alist only contains bits with fields in catInfo
-
-       PackCommonCatalogInfoAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
-       
-       switch ( catInfo->nodeData.cnd_type )
-       {
-               case kCatalogFolderNode:
-            PackCatalogInfoDirAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
-                       break;
-               
-               case kCatalogFileNode:
-            PackCatalogInfoFileAttributeBlock( alist, root_vp, catInfo, attrbufptrptr, varbufptrptr );
-                       break;
-         
-               default:        /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR not being handled... */
-                       /* XXX PPD - Panic? */
-                       break;
+                       vnode_put(ji->jvp);
+                       ji->jvp = NULL;
+               }
        }
-}
-
 
-
-void PackAttributeBlock(struct attrlist *alist,
-                                               struct vnode *vp,
-                                               struct hfsCatalogInfo *catInfo,
-                                               void **attrbufptrptr,
-                                               void **varbufptrptr)
-{
-       if (alist->volattr != 0) {
-               DBG_ASSERT((vp->v_flag & VROOT) != 0);
-               PackVolumeInfo(alist,vp, catInfo, attrbufptrptr, varbufptrptr);
-       } else {
-               PackCommonAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-               
-               switch (vp->v_type) {
-               case VDIR:
-                       PackDirAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-                       break;
-                       
-               case VREG:
-               case VLNK:
-                       PackFileAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-                       break;
-                 
-                 /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR
-                    not being handled...
-                  */
-               default:
-                       /* XXX PPD - Panic? */
-                       break;
-               };
-       };
-};
-
-
-
-void UnpackVolumeAttributeBlock(struct attrlist *alist,
-                                                               struct vnode *root_vp,
-                                                               ExtendedVCB *vcb,
-                                                               void **attrbufptrptr,
-                                                               void **varbufptrptr) {
-       void *attrbufptr = *attrbufptrptr;
-       attrgroup_t a;
-       
-    if ((alist->commonattr == 0) && (alist->volattr == 0)) {
-        return;                /* Get out without dirtying the VCB */
-    };
-
-    VCB_LOCK(vcb);
-
-       a = alist->commonattr;
-       
-       if (a & ATTR_CMN_SCRIPT) {
-               vcb->volumeNameEncodingHint = (u_int32_t)*(((text_encoding_t *)attrbufptr)++);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_SCRIPT;
-#endif
-       };
-       if (a & ATTR_CMN_CRTIME) {
-               vcb->localCreateDate = UTCToLocal(to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec));
-               ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_CRTIME;
-#endif
-       };
-       if (a & ATTR_CMN_MODTIME) {
-               vcb->vcbLsMod = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_MODTIME;
-#endif
-       };
-       if (a & ATTR_CMN_BKUPTIME) {
-               vcb->vcbVolBkUp = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_BKUPTIME;
-#endif
-       };
-       if (a & ATTR_CMN_FNDRINFO) {
-               bcopy (attrbufptr, &vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
-               (char *)attrbufptr += sizeof(vcb->vcbFndrInfo);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_FNDRINFO;
-#endif
-       };
-       
-       DBG_ASSERT(a == 0);                             /* All common attributes for volumes must've been handled by now... */
-
-       a = alist->volattr & ~ATTR_VOL_INFO;
-       if (a & ATTR_VOL_NAME) {
-        copystr(((char *)attrbufptr) + *((u_long *)attrbufptr), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);
-        (char *)attrbufptr += sizeof(struct attrreference);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_VOL_NAME;
-#endif
-       };
-       
-       DBG_ASSERT(a == 0);                             /* All common attributes for volumes must've been handled by now... */
-
-    vcb->vcbFlags |= 0xFF00;           // Mark the VCB dirty
-
-    VCB_UNLOCK(vcb);
+       return 1;   // keep iterating
 }
 
+extern dev_t IOBSDGetMediaWithUUID(const char *uuid_cstring, char *bsd_name, int bsd_name_len, int timeout);
+extern void IOBSDIterateMediaWithContent(const char *uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg);
+extern kern_return_t IOBSDGetPlatformUUID(__darwin_uuid_t uuid, mach_timespec_t timeoutp);
+kern_return_t IOBSDGetPlatformSerialNumber(char *serial_number_str, u_int32_t len);
 
-void UnpackCommonAttributeBlock(struct attrlist *alist,
-                                                               struct vnode *vp,
-                                                               struct hfsCatalogInfo *catInfo,
-                                                               void **attrbufptrptr,
-                                                               void **varbufptrptr) {
-       struct hfsnode *hp = VTOH(vp);
-    void *attrbufptr;
-    attrgroup_t a;
-       
-       attrbufptr = *attrbufptrptr;
-
-    DBG_ASSERT(catInfo != NULL);
-       
-       a = alist->commonattr;
-       if (a & ATTR_CMN_SCRIPT) {
-               catInfo->nodeData.cnd_textEncoding = (u_int32_t)*((text_encoding_t *)attrbufptr)++;
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_SCRIPT;
-#endif
-       };
-       if (a & ATTR_CMN_CRTIME) {
-               catInfo->nodeData.cnd_createDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               VTOH(vp)->h_meta->h_crtime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
-               ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_CRTIME;
-#endif
-       };
-       if (a & ATTR_CMN_MODTIME) {
-               catInfo->nodeData.cnd_contentModDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               VTOH(vp)->h_meta->h_mtime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
-               ++((struct timespec *)attrbufptr);
-               hp->h_nodeflags &= ~IN_UPDATE;
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_MODTIME;
-#endif
-       };
-       if (a & ATTR_CMN_CHGTIME) {
-               catInfo->nodeData.cnd_attributeModDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               VTOH(vp)->h_meta->h_ctime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
-               ++((struct timespec *)attrbufptr);
-               hp->h_nodeflags &= ~IN_CHANGE;
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_CHGTIME;
-#endif
-       };
-       if (a & ATTR_CMN_ACCTIME) {
-               catInfo->nodeData.cnd_accessDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               VTOH(vp)->h_meta->h_atime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
-               ++((struct timespec *)attrbufptr);
-               hp->h_nodeflags &= ~IN_ACCESS;
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_ACCTIME;
-#endif
-       };
-       if (a & ATTR_CMN_BKUPTIME) {
-               catInfo->nodeData.cnd_backupDate = to_hfs_time((UInt32)((struct timespec *)attrbufptr)->tv_sec);
-               VTOH(vp)->h_meta->h_butime = (UInt32)((struct timespec *)attrbufptr)->tv_sec;
-               ++((struct timespec *)attrbufptr);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_BKUPTIME;
-#endif
-       };
-       if (a & ATTR_CMN_FNDRINFO) {
-               bcopy (attrbufptr, &catInfo->nodeData.cnd_finderInfo, sizeof(catInfo->nodeData.cnd_finderInfo));
-               (char *)attrbufptr += sizeof(catInfo->nodeData.cnd_finderInfo);
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_FNDRINFO;
-#endif
-       };
-       if (a & ATTR_CMN_OWNERID) {
-        if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
-                       u_int32_t uid = (u_int32_t)*((uid_t *)attrbufptr)++;
-                       if (uid != (uid_t)VNOVAL)
-                               hp->h_meta->h_uid = uid;        /* catalog will get updated by hfs_chown() */
-        }
-               else {
-            ((uid_t *)attrbufptr)++;
-               }
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_OWNERID;
-#endif
-       };
-       if (a & ATTR_CMN_GRPID) {
-        u_int32_t gid = (u_int32_t)*((gid_t *)attrbufptr)++;
-        if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
-            if (gid != (gid_t)VNOVAL)
-                hp->h_meta->h_gid = gid;                                       /* catalog will get updated by hfs_chown() */
-        };
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_GRPID;
-#endif
-       };
-       if (a & ATTR_CMN_ACCESSMASK) {
-        u_int16_t mode = (u_int16_t)*((u_long *)attrbufptr)++;
-        if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
-            if (mode != (mode_t)VNOVAL) {
-                hp->h_meta->h_mode &= ~ALLPERMS;
-                hp->h_meta->h_mode |= (mode & ALLPERMS);       /* catalog will get updated by hfs_chmod() */
-            }
-        };
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_ACCESSMASK;
-#endif
-       };
-       if (a & ATTR_CMN_FLAGS) {
-               u_long flags = *((u_long *)attrbufptr)++;
-        /* Flags are settable only on HFS+ volumes.  A special exception is made for the IMMUTABLE
-           flags (SF_IMMUTABLE and UF_IMMUTABLE), which can be set on HFS volumes as well: */
-        if ((VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) ||
-            ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && ((flags & ~IMMUTABLE) == 0))) {
-            if (flags != (u_long)VNOVAL) {
-                hp->h_meta->h_pflags = flags;                          /* catalog will get updated by hfs_chflags */
-            };
-        };
-#if HFS_DIAGNOSTIC
-               a &= ~ATTR_CMN_FLAGS;
-#endif
-       };
 
-#if HFS_DIAGNOSTIC
-       if (a != 0) {
-               DEBUG_BREAK_MSG(("UnpackCommonAttributes: unhandled bit: 0x%08X\n", a));
-       };
-#endif
+static vnode_t
+open_journal_dev(const char *vol_device,
+                int need_clean,
+                char *uuid_str,
+                char *machine_serial_num,
+                off_t jsize,
+                size_t blksize,
+                int *need_init)
+{
+    int retry_counter=0;
+    jopen_cb_info ji;
+
+    ji.jsize        = jsize;
+    ji.desired_uuid = uuid_str;
+    ji.jvp          = NULL;
+    ji.blksize      = blksize;
+    ji.need_clean   = need_clean;
+    ji.need_init    = 0;
+
+//    if (uuid_str[0] == '\0') {
+//         printf("hfs: open journal dev: %s: locating any available non-dirty external journal partition\n", vol_device);
+//    } else {
+//         printf("hfs: open journal dev: %s: trying to find the external journal partition w/uuid %s\n", vol_device, uuid_str);
+//    }
+    while (ji.jvp == NULL && retry_counter++ < 4) {
+           if (retry_counter > 1) {
+                   if (uuid_str[0]) {
+                           printf("hfs: open_journal_dev: uuid %s not found.  waiting 10sec.\n", uuid_str);
+                   } else {
+                           printf("hfs: open_journal_dev: no available external journal partition found.  waiting 10sec.\n");
+                   }
+                   delay_for_interval(10* 1000000, NSEC_PER_USEC);    // wait for ten seconds and then try again
+           }
 
-       *attrbufptrptr = attrbufptr;
-//     *varbufptrptr = varbufptr;
-}
+           IOBSDIterateMediaWithContent(EXTJNL_CONTENT_TYPE_UUID, journal_open_cb, &ji);
+    }
 
+    if (ji.jvp == NULL) {
+           printf("hfs: volume: %s: did not find jnl device uuid: %s from machine serial number: %s\n",
+                  vol_device, uuid_str, machine_serial_num);
+    }
 
+    *need_init = ji.need_init;
 
-#if 0
-void UnpackDirAttributeBlock(struct attrlist *alist,
-                                                        struct vnode *vp,
-                                                        struct hfsCatalogInfo *catInfo,
-                                                        void **attrbufptrptr,
-                                                        void **varbufptrptr) {
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-       u_long attrlength;
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-       
-       /* XXX PPD TBC */
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
+    return ji.jvp;
 }
-#endif
 
 
+__private_extern__
+int
+hfs_early_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
+                                          void *_args, off_t embeddedOffset, daddr64_t mdb_offset,
+                                          HFSMasterDirectoryBlock *mdbp, kauth_cred_t cred)
+{
+       JournalInfoBlock *jibp;
+       struct buf       *jinfo_bp, *bp;
+       int               sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+       int               retval, write_jibp = 0;
+       uint32_t                  blksize = hfsmp->hfs_logical_block_size;
+       struct vnode     *devvp;
+       struct hfs_mount_args *args = _args;
+       u_int32_t         jib_flags;
+       u_int64_t         jib_offset;
+       u_int64_t         jib_size;
+       const char *dev_name;
+       
+       devvp = hfsmp->hfs_devvp;
+       dev_name = vnode_name(devvp);
+       if (dev_name == NULL) {
+               dev_name = "unknown-dev";
+       }
 
-#if 0
-void UnpackFileAttributeBlock(struct attrlist *alist,
-                                                         struct vnode *vp,
-                                                         struct hfsCatalogInfo *catInfo,
-                                                         void **attrbufptrptr,
-                                                         void **varbufptrptr) {
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-       u_long attrlength;
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-       
-       /* XXX PPD TBC */
-       
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
-}
-#endif
-
-
+       if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+               arg_flags  = args->journal_flags;
+               arg_tbufsz = args->journal_tbuffer_size;
+       }
 
-#if 0
-void UnpackForkAttributeBlock(struct attrlist *alist,
-                                                       struct vnode *vp,
-                                                       struct hfsCatalogInfo *catInfo,
-                                                       void **attrbufptrptr,
-                                                       void **varbufptrptr) {
-    void *attrbufptr;
-    void *varbufptr;
-    attrgroup_t a;
-       u_long attrlength;
-       
-       attrbufptr = *attrbufptrptr;
-       varbufptr = *varbufptrptr;
-       
-       /* XXX PPD TBC */
+       sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / blksize;
+                               
+       jinfo_bp = NULL;
+       retval = (int)buf_meta_bread(devvp,
+                                               (daddr64_t)((embeddedOffset/blksize) + 
+                                               ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+                                               hfsmp->hfs_physical_block_size, cred, &jinfo_bp);
+       if (retval) {
+               if (jinfo_bp) {
+                       buf_brelse(jinfo_bp);
+               }
+               return retval;
+       }
        
-       *attrbufptrptr = attrbufptr;
-       *varbufptrptr = varbufptr;
-}
-#endif
-
-
+       jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+       jib_flags  = SWAP_BE32(jibp->flags);
+       jib_size   = SWAP_BE64(jibp->size);
 
-void UnpackAttributeBlock(struct attrlist *alist,
-                                                 struct vnode *vp,
-                                                 struct hfsCatalogInfo *catInfo,
-                                                 void **attrbufptrptr,
-                                                 void **varbufptrptr) {
-
-
-       if (alist->volattr != 0) {
-               UnpackVolumeAttributeBlock(alist, vp, VTOVCB(vp), attrbufptrptr, varbufptrptr);
-               return;
-       };
-       
-       /* We're dealing with a vnode object here: */
-       UnpackCommonAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
+       if (jib_flags & kJIJournalInFSMask) {
+               hfsmp->jvp = hfsmp->hfs_devvp;
+               jib_offset = SWAP_BE64(jibp->offset);
+       } else {
+           int need_init=0;
        
-#if 0
-       switch (vp->v_type) {
-         case VDIR:
-               UnpackDirAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-               break;
-
-         case VREG:
-   /* case VCPLX: */                   /* XXX PPD TBC */
-               UnpackFileAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-               break;
-
-         case VFORK:
-               UnpackForkAttributeBlock(alist, vp, catInfo, attrbufptrptr, varbufptrptr);
-               break;
-
-         /* Without this the compiler complains about VNON,VBLK,VCHR,VLNK,VSOCK,VFIFO,VBAD and VSTR
-            not being handled...
-          */
-         default:
-               /* XXX PPD - Panic? */
-               break;
-       };
-#endif
-
-};
-
-
-unsigned long BestBlockSizeFit(unsigned long allocationBlockSize,
-                               unsigned long blockSizeLimit,
-                               unsigned long baseMultiple) {
-    /*
-       Compute the optimal (largest) block size (no larger than allocationBlockSize) that is less than the
-       specified limit but still an even multiple of the baseMultiple.
-     */
-    int baseBlockCount, blockCount;
-    unsigned long trialBlockSize;
-
-    if (allocationBlockSize % baseMultiple != 0) {
-        /*
-           Whoops: the allocation blocks aren't even multiples of the specified base:
-           no amount of dividing them into even parts will be a multiple, either then!
-        */
-        return 512;            /* Hope for the best */
-    };
-
-    /* Try the obvious winner first, to prevent 12K allocation blocks, for instance,
-       from being handled as two 6K logical blocks instead of 3 4K logical blocks.
-       Even though the former (the result of the loop below) is the larger allocation
-       block size, the latter is more efficient: */
-    if (allocationBlockSize % PAGE_SIZE == 0) return PAGE_SIZE;
-
-    /* No clear winner exists: pick the largest even fraction <= MAXBSIZE: */
-    baseBlockCount = allocationBlockSize / baseMultiple;                               /* Now guaranteed to be an even multiple */
-
-    for (blockCount = baseBlockCount; blockCount > 0; --blockCount) {
-        trialBlockSize = blockCount * baseMultiple;
-        if (allocationBlockSize % trialBlockSize == 0) {                               /* An even multiple? */
-            if ((trialBlockSize <= blockSizeLimit) &&
-                (trialBlockSize % baseMultiple == 0)) {
-                return trialBlockSize;
-            };
-        };
-    };
+           // if the volume was unmounted cleanly then we'll pick any
+           // available external journal partition
+           //
+           if (SWAP_BE32(vhp->attributes) & kHFSVolumeUnmountedMask) {
+                   *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+           }
 
-    /* Note: we should never get here, since blockCount = 1 should always work,
-       but this is nice and safe and makes the compiler happy, too ... */
-    return 512;
-}
+           hfsmp->jvp = open_journal_dev(dev_name,
+                                         !(jib_flags & kJIJournalNeedInitMask),
+                                         (char *)&jibp->ext_jnl_uuid[0],
+                                         (char *)&jibp->machine_serial_num[0],
+                                         jib_size,
+                                         hfsmp->hfs_logical_block_size,
+                                         &need_init);
+           if (hfsmp->jvp == NULL) {
+               buf_brelse(jinfo_bp);
+               return EROFS;
+           } else {
+                   if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+                           strlcpy(&jibp->machine_serial_num[0], "unknown-machine-uuid", sizeof(jibp->machine_serial_num));
+                   }
+           }
 
+           jib_offset = 0;
+           write_jibp = 1;
+           if (need_init) {
+                   jib_flags |= kJIJournalNeedInitMask;
+           }
+       }
 
-/*
- * To make the HFS Plus filesystem follow UFS unlink semantics, a remove
- * of an active vnode is translated to a move/rename so the file appears
- * deleted. The destination folder for these move/renames is setup here
- * and a reference to it is place in hfsmp->hfs_private_metadata_dir.
- */
-u_long
-FindMetaDataDirectory(ExtendedVCB *vcb)
-{
-       char namep[32];
-       hfsCatalogInfo catInfo;
-       HFSCatalogNodeID dirID;
-       u_int32_t metadata_createdate;
-       int retval;
-       
-       if (vcb->vcbSigWord != kHFSPlusSigWord)
-               return (0);
+       // save this off for the hack-y check in hfs_remove()
+       hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+       hfsmp->jnl_size  = jib_size;
+
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+           // if the file system is read-only, check if the journal is empty.
+           // if it is, then we can allow the mount.  otherwise we have to
+           // return failure.
+           retval = journal_is_clean(hfsmp->jvp,
+                                     jib_offset + embeddedOffset,
+                                     jib_size,
+                                     devvp,
+                                     hfsmp->hfs_logical_block_size);
+
+           hfsmp->jnl = NULL;
+
+           buf_brelse(jinfo_bp);
+
+           if (retval) {
+               const char *name = vnode_getname(devvp);
+             printf("hfs: early journal init: volume on %s is read-only and journal is dirty.  Can not mount volume.\n",
+                     name ? name : "");
+               if (name)
+                       vnode_putname(name);
+           }
 
-       dirID = 0;
-       metadata_createdate = 0;
-       strncpy(namep, HFSPLUSMETADATAFOLDER, sizeof(namep));
-       INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
-       catInfo.hint = kNoHint;
-
-       /* lock catalog b-tree */
-       retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());      
-       if (retval)  goto Err_Exit;
-
-       if (hfs_getcatalog(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
-               dirID = catInfo.nodeData.cnd_nodeID;
-               metadata_createdate = catInfo.nodeData.cnd_createDate;
-       } else if (VCBTOHFS(vcb)->hfs_fs_ronly == 0) {
-               if (CreateCatalogNode(vcb, kRootDirID, namep, kCatalogFolderNode, &dirID, &catInfo.hint) == 0) {
-                       catInfo.hint = kNoHint;
-                       if (hfs_getcatalog(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
-
-                               /* create date is later used for validation */
-                               catInfo.nodeData.cnd_createDate = vcb->vcbCrDate;
-                               metadata_createdate = catInfo.nodeData.cnd_createDate;
-
-                               /* directory with no permissions owned by root */
-                               catInfo.nodeData.cnd_mode = IFDIR;
-                               catInfo.nodeData.cnd_adminFlags = (SF_IMMUTABLE >> 16);
-
-                               /* hidden and off the desktop view */
-                               ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.v = SWAP_BE16 (22460);
-                               ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.h = SWAP_BE16 (22460);
-                               ((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frFlags |= SWAP_BE16 (kIsInvisible + kNameLocked);                
-       
-                               (void) UpdateCatalogNode(vcb, kRootDirID, namep, catInfo.hint, &catInfo.nodeData);
+           return retval;
+       }
+
+       if (jib_flags & kJIJournalNeedInitMask) {
+               printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+                          jib_offset + embeddedOffset, jib_size);
+               hfsmp->jnl = journal_create(hfsmp->jvp,
+                                                                       jib_offset + embeddedOffset,
+                                                                       jib_size,
+                                                                       devvp,
+                                                                       blksize,
+                                                                       arg_flags,
+                                                                       arg_tbufsz,
+                                                                       hfs_sync_metadata, hfsmp->hfs_mp);
+
+               // no need to start a transaction here... if this were to fail
+               // we'd just re-init it on the next mount.
+               jib_flags &= ~kJIJournalNeedInitMask;
+               jibp->flags  = SWAP_BE32(jib_flags);
+               buf_bwrite(jinfo_bp);
+               jinfo_bp = NULL;
+               jibp     = NULL;
+       } else { 
+               //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+               //         jib_offset + embeddedOffset,
+               //         jib_size, SWAP_BE32(vhp->blockSize));
+                               
+               hfsmp->jnl = journal_open(hfsmp->jvp,
+                                                                 jib_offset + embeddedOffset,
+                                                                 jib_size,
+                                                                 devvp,
+                                                                 blksize,
+                                                                 arg_flags,
+                                                                 arg_tbufsz,
+                                                                 hfs_sync_metadata, hfsmp->hfs_mp);
+
+               if (write_jibp) {
+                       buf_bwrite(jinfo_bp);
+               } else {
+                       buf_brelse(jinfo_bp);
+               }
+               jinfo_bp = NULL;
+               jibp     = NULL;
+
+               if (hfsmp->jnl && mdbp) {
+                       // reload the mdb because it could have changed
+                       // if the journal had to be replayed.
+                       if (mdb_offset == 0) {
+                               mdb_offset = (daddr64_t)((embeddedOffset / blksize) + HFS_PRI_SECTOR(blksize));
                        }
+                       bp = NULL;
+                       retval = (int)buf_meta_bread(devvp, 
+                                       HFS_PHYSBLK_ROUNDDOWN(mdb_offset, hfsmp->hfs_log_per_phys),
+                                       hfsmp->hfs_physical_block_size, cred, &bp);
+                       if (retval) {
+                               if (bp) {
+                                       buf_brelse(bp);
+                               }
+                               printf("hfs: failed to reload the mdb after opening the journal (retval %d)!\n",
+                                          retval);
+                               return retval;
+                       }
+                       bcopy((char *)buf_dataptr(bp) + HFS_PRI_OFFSET(hfsmp->hfs_physical_block_size), mdbp, 512);
+                       buf_brelse(bp);
+                       bp = NULL;
                }
        }
 
-       /* unlock catalog b-tree */
-       (void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
+
+       //printf("journal @ 0x%x\n", hfsmp->jnl);
        
-       VCBTOHFS(vcb)->hfs_metadata_createdate = metadata_createdate;
-Err_Exit:      
-       CLEAN_CATALOGDATA(&catInfo.nodeData);
+       // if we expected the journal to be there and we couldn't
+       // create it or open it then we have to bail out.
+       if (hfsmp->jnl == NULL) {
+               printf("hfs: early jnl init: failed to open/create the journal (retval %d).\n", retval);
+               return EINVAL;
+       }
 
-       return dirID;
+       return 0;
 }
 
 
-static void
-RemovedMetaDataDirectory(ExtendedVCB *vcb)
+//
+// This function will go and re-locate the .journal_info_block and
+// the .journal files in case they moved (which can happen if you
+// run Norton SpeedDisk).  If we fail to find either file we just
+// disable journaling for this volume and return.  We turn off the
+// journaling bit in the vcb and assume it will get written to disk
+// later (if it doesn't on the next mount we'd do the same thing
+// again which is harmless).  If we disable journaling we don't
+// return an error so that the volume is still mountable.
+//
+// If the info we find for the .journal_info_block and .journal files
+// isn't what we had stored, we re-set our cached info and proceed
+// with opening the journal normally.
+//
+static int
+hfs_late_journal_init(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp, void *_args)
 {
-       char name[32];
-       hfsCatalogInfo catInfo;
-       int retval;
+       JournalInfoBlock *jibp;
+       struct buf       *jinfo_bp;
+       int               sectors_per_fsblock, arg_flags=0, arg_tbufsz=0;
+       int               retval, write_jibp = 0, recreate_journal = 0;
+       struct vnode     *devvp;
+       struct cat_attr   jib_attr, jattr;
+       struct cat_fork   jib_fork, jfork;
+       ExtendedVCB      *vcb;
+       u_int32_t            fid;
+       struct hfs_mount_args *args = _args;
+       u_int32_t         jib_flags;
+       u_int64_t         jib_offset;
+       u_int64_t         jib_size;
+       
+       devvp = hfsmp->hfs_devvp;
+       vcb = HFSTOVCB(hfsmp);
        
-       strncpy(name, HFSPLUSMETADATAFOLDER, sizeof(name));
-       INIT_CATALOGDATA(&catInfo.nodeData, kCatNameNoCopyName);
+       if (args != NULL && (args->flags & HFSFSMNT_EXTENDED_ARGS)) {
+               if (args->journal_disable) {
+                       return 0;
+               }
 
-       /* lock catalog b-tree */
-       retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());      
-       if (retval)  goto Err_Exit;
+               arg_flags  = args->journal_flags;
+               arg_tbufsz = args->journal_tbuffer_size;
+       }
 
-       /* If the HFSPLUSMETADATAFOLDER exists then delete it. */
-       retval = GetCatalogNode(vcb, kRootDirID, name, strlen(name), kNoHint,
-                                                       &catInfo.nodeData, &catInfo.hint);
-       if (retval == 0 && (catInfo.nodeData.cnd_type == kCatalogFolderNode)) {
-               (void) DeleteCatalogNode(vcb, kRootDirID, name, catInfo.hint);
-               printf("hfs_mount: removed \"%s\" from hfs volume \"%s\"\n", name, vcb->vcbVN);
+       fid = GetFileInfo(vcb, kRootDirID, ".journal_info_block", &jib_attr, &jib_fork);
+       if (fid == 0 || jib_fork.cf_extents[0].startBlock == 0 || jib_fork.cf_size == 0) {
+               printf("hfs: can't find the .journal_info_block! disabling journaling (start: %d).\n",
+                          jib_fork.cf_extents[0].startBlock);
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
        }
+       hfsmp->hfs_jnlinfoblkid = fid;
 
-       /* unlock catalog b-tree */
-       (void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
+       // make sure the journal_info_block begins where we think it should.
+       if (SWAP_BE32(vhp->journalInfoBlock) != jib_fork.cf_extents[0].startBlock) {
+               printf("hfs: The journal_info_block moved (was: %d; is: %d).  Fixing up\n",
+                          SWAP_BE32(vhp->journalInfoBlock), jib_fork.cf_extents[0].startBlock);
 
-Err_Exit:      
-       CLEAN_CATALOGDATA(&catInfo.nodeData);
-}
+               vcb->vcbJinfoBlock    = jib_fork.cf_extents[0].startBlock;
+               vhp->journalInfoBlock = SWAP_BE32(jib_fork.cf_extents[0].startBlock);
+               recreate_journal = 1;
+       }
 
-/*
- * This will return the correct logical block size for a given vnode.
- * For most files, it is the allocation block size, for meta data like
- * BTrees, this is kept as part of the BTree private nodeSize
- */
-u_int32_t
-GetLogicalBlockSize(struct vnode *vp)
-{
-u_int32_t logBlockSize;
+
+       sectors_per_fsblock = SWAP_BE32(vhp->blockSize) / hfsmp->hfs_logical_block_size;
+       jinfo_bp = NULL;
+       retval = (int)buf_meta_bread(devvp,
+                                               (vcb->hfsPlusIOPosOffset / hfsmp->hfs_logical_block_size + 
+                                               ((u_int64_t)SWAP_BE32(vhp->journalInfoBlock)*sectors_per_fsblock)),
+                                               hfsmp->hfs_physical_block_size, NOCRED, &jinfo_bp);
+       if (retval) {
+               if (jinfo_bp) {
+                       buf_brelse(jinfo_bp);
+               }
+               printf("hfs: can't read journal info block. disabling journaling.\n");
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
+       }
+
+       jibp = (JournalInfoBlock *)buf_dataptr(jinfo_bp);
+       jib_flags  = SWAP_BE32(jibp->flags);
+       jib_offset = SWAP_BE64(jibp->offset);
+       jib_size   = SWAP_BE64(jibp->size);
+
+       fid = GetFileInfo(vcb, kRootDirID, ".journal", &jattr, &jfork);
+       if (fid == 0 || jfork.cf_extents[0].startBlock == 0 || jfork.cf_size == 0) {
+               printf("hfs: can't find the journal file! disabling journaling (start: %d)\n",
+                          jfork.cf_extents[0].startBlock);
+               buf_brelse(jinfo_bp);
+               vcb->vcbAtrb &= ~kHFSVolumeJournaledMask;
+               return 0;
+       }
+       hfsmp->hfs_jnlfileid = fid;
+
+       // make sure the journal file begins where we think it should.
+       if ((jib_flags & kJIJournalInFSMask) && (jib_offset / (u_int64_t)vcb->blockSize) != jfork.cf_extents[0].startBlock) {
+               printf("hfs: The journal file moved (was: %lld; is: %d).  Fixing up\n",
+                          (jib_offset / (u_int64_t)vcb->blockSize), jfork.cf_extents[0].startBlock);
+
+               jib_offset = (u_int64_t)jfork.cf_extents[0].startBlock * (u_int64_t)vcb->blockSize;
+               write_jibp   = 1;
+               recreate_journal = 1;
+       }
+
+       // check the size of the journal file.
+       if (jib_size != (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize) {
+               printf("hfs: The journal file changed size! (was %lld; is %lld).  Fixing up.\n",
+                          jib_size, (u_int64_t)jfork.cf_extents[0].blockCount*vcb->blockSize);
+               
+               jib_size = (u_int64_t)jfork.cf_extents[0].blockCount * vcb->blockSize;
+               write_jibp = 1;
+               recreate_journal = 1;
+       }
        
-       DBG_ASSERT(vp != NULL);
+       if (jib_flags & kJIJournalInFSMask) {
+               hfsmp->jvp = hfsmp->hfs_devvp;
+               jib_offset += (off_t)vcb->hfsPlusIOPosOffset;
+       } else {
+           const char *dev_name;
+           int need_init = 0;
 
-       if ((vp->v_flag & VSYSTEM) && (VTOH(vp)->fcbBTCBPtr!=NULL)) {
-               BTreeInfoRec                    bTreeInfo;
-               int retval;
+           dev_name = vnode_name(devvp);
+           if (dev_name == NULL) {
+                   dev_name = "unknown-dev";
+           }
 
-               /*
-                * We do not lock the BTrees, because if we are getting block..then the tree
-                * should be locked in the first place.
-                * We just want the nodeSize wich will NEVER change..so even if the world
-                * is changing..the nodeSize should remain the same. Which argues why lock
-                * it in the first place??
-                */
+            // since the journal is empty, just use any available external journal
+           *((char *)&jibp->ext_jnl_uuid[0]) = '\0';
+
+           // this fills in the uuid of the device we actually get
+           hfsmp->jvp = open_journal_dev(dev_name,
+                                         !(jib_flags & kJIJournalNeedInitMask),
+                                         (char *)&jibp->ext_jnl_uuid[0],
+                                         (char *)&jibp->machine_serial_num[0],
+                                         jib_size,
+                                         hfsmp->hfs_logical_block_size,
+                                         &need_init);
+           if (hfsmp->jvp == NULL) {
+               buf_brelse(jinfo_bp);
+               return EROFS;
+           } else {
+                   if (IOBSDGetPlatformSerialNumber(&jibp->machine_serial_num[0], sizeof(jibp->machine_serial_num)) != KERN_SUCCESS) {
+                           strlcpy(&jibp->machine_serial_num[0], "unknown-machine-serial-num", sizeof(jibp->machine_serial_num));
+                   }
+           } 
+           jib_offset = 0;
+           recreate_journal = 1;
+           write_jibp = 1;
+           if (need_init) {
+                   jib_flags |= kJIJournalNeedInitMask;
+           }
+       }
+
+       // save this off for the hack-y check in hfs_remove()
+       hfsmp->jnl_start = jib_offset / SWAP_BE32(vhp->blockSize);
+       hfsmp->jnl_size  = jib_size;
+
+       if ((hfsmp->hfs_flags & HFS_READ_ONLY) && (vfs_flags(hfsmp->hfs_mp) & MNT_ROOTFS) == 0) {
+           // if the file system is read-only, check if the journal is empty.
+           // if it is, then we can allow the mount.  otherwise we have to
+           // return failure.
+           retval = journal_is_clean(hfsmp->jvp,
+                                     jib_offset,
+                                     jib_size,
+                                     devvp,
+                                     hfsmp->hfs_logical_block_size);
+
+           hfsmp->jnl = NULL;
+
+           buf_brelse(jinfo_bp);
+
+           if (retval) {
+               const char *name = vnode_getname(devvp);
+             printf("hfs: late journal init: volume on %s is read-only and journal is dirty.  Can not mount volume.\n",
+                    name ? name : "");
+               if (name)
+                       vnode_putname(name);
+           }
+
+           return retval;
+       }
+
+       if ((jib_flags & kJIJournalNeedInitMask) || recreate_journal) {
+               printf("hfs: Initializing the journal (joffset 0x%llx sz 0x%llx)...\n",
+                          jib_offset, jib_size);
+               hfsmp->jnl = journal_create(hfsmp->jvp,
+                                                                       jib_offset,
+                                                                       jib_size,
+                                                                       devvp,
+                                                                       hfsmp->hfs_logical_block_size,
+                                                                       arg_flags,
+                                                                       arg_tbufsz,
+                                                                       hfs_sync_metadata, hfsmp->hfs_mp);
+
+               // no need to start a transaction here... if this were to fail
+               // we'd just re-init it on the next mount.
+               jib_flags &= ~kJIJournalNeedInitMask;
+               write_jibp   = 1;
+
+       } else { 
+               //
+               // if we weren't the last person to mount this volume
+               // then we need to throw away the journal because it
+               // is likely that someone else mucked with the disk.
+               // if the journal is empty this is no big deal.  if the
+               // disk is dirty this prevents us from replaying the
+               // journal over top of changes that someone else made.
+               //
+               arg_flags |= JOURNAL_RESET;
                
-               (void) BTGetInformation (VTOFCB(vp), kBTreeInfoVersion, &bTreeInfo);
+               //printf("hfs: Opening the journal (joffset 0x%llx sz 0x%llx vhp_blksize %d)...\n",
+               //         jib_offset,
+               //         jib_size, SWAP_BE32(vhp->blockSize));
                                
-               logBlockSize = bTreeInfo.nodeSize;
-               }
-       else
-               logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
-               
-               
-       DBG_ASSERT(logBlockSize > 0);
+               hfsmp->jnl = journal_open(hfsmp->jvp,
+                                                                 jib_offset,
+                                                                 jib_size,
+                                                                 devvp,
+                                                                 hfsmp->hfs_logical_block_size,
+                                                                 arg_flags,
+                                                                 arg_tbufsz,
+                                                                 hfs_sync_metadata, hfsmp->hfs_mp);
+       }
+                       
+
+       if (write_jibp) {
+               jibp->flags  = SWAP_BE32(jib_flags);
+               jibp->offset = SWAP_BE64(jib_offset);
+               jibp->size   = SWAP_BE64(jib_size);
+
+               buf_bwrite(jinfo_bp);
+       } else {
+               buf_brelse(jinfo_bp);
+       } 
+       jinfo_bp = NULL;
+       jibp     = NULL;
+
+       //printf("hfs: journal @ 0x%x\n", hfsmp->jnl);
        
-       return logBlockSize;    
+       // if we expected the journal to be there and we couldn't
+       // create it or open it then we have to bail out.
+       if (hfsmp->jnl == NULL) {
+               printf("hfs: late jnl init: failed to open/create the journal (retval %d).\n", retval);
+               return EINVAL;
+       }
+
+       return 0;
 }
 
 /*
- * Map HFS Common errors (negative) to BSD error codes (positive).
- * Positive errors (ie BSD errors) are passed through unchanged.
+ * Calculate the allocation zone for metadata.
+ *
+ * This zone includes the following:
+ *     Allocation Bitmap file
+ *     Overflow Extents file
+ *     Journal file
+ *     Quota files
+ *     Clustered Hot files
+ *     Catalog file
+ *
+ *                          METADATA ALLOCATION ZONE
+ * ____________________________________________________________________________
+ * |    |    |     |               |                              |           |
+ * | BM | JF | OEF |    CATALOG    |--->                          | HOT FILES |
+ * |____|____|_____|_______________|______________________________|___________|
+ *
+ * <------------------------------- N * 128 MB ------------------------------->
+ *
  */
-short MacToVFSError(OSErr err)
+#define GIGABYTE  (u_int64_t)(1024*1024*1024)
+
+#define OVERFLOW_DEFAULT_SIZE (4*1024*1024)
+#define OVERFLOW_MAXIMUM_SIZE (128*1024*1024)
+#define JOURNAL_DEFAULT_SIZE  (8*1024*1024)
+#define JOURNAL_MAXIMUM_SIZE  (512*1024*1024)
+#define HOTBAND_MINIMUM_SIZE  (10*1024*1024)
+#define HOTBAND_MAXIMUM_SIZE  (512*1024*1024)
+
+void
+hfs_metadatazone_init(struct hfsmount *hfsmp)
 {
-    if (err >= 0) {
-        if (err > 0) {
-            DBG_ERR(("MacToVFSError: passing error #%d unchanged...\n", err));
-        };
-        return err;
-    };
+       ExtendedVCB  *vcb;
+       u_int64_t  fs_size;
+       u_int64_t  zonesize;
+       u_int64_t  temp;
+       u_int64_t  filesize;
+       u_int32_t  blk;
+       int  items, really_do_it=1;
 
-    if (err != 0) {
-        DBG_ERR(("MacToVFSError: mapping error code %d...\n", err));
-    };
-    
-       switch (err) {
-         case dirFulErr:                                                       /*    -33 */
-         case dskFulErr:                                                       /*    -34 */
-         case btNoSpaceAvail:                                          /* -32733 */
-         case fxOvFlErr:                                                       /* -32750 */
-               return ENOSPC;                                                  /*    +28 */
-
-         case btBadNode:                                                       /* -32731 */
-         case ioErr:                                                           /*   -36 */
-               return EIO;                                                             /*    +5 */
-
-         case mFulErr:                                                         /*   -41 */
-         case memFullErr:                                                      /*  -108 */
-               return ENOMEM;                                                  /*   +12 */
-
-         case tmfoErr:                                                         /*   -42 */
-               /* Consider EMFILE (Too many open files, 24)? */        
-               return ENFILE;                                                  /*   +23 */
-
-         case nsvErr:                                                          /*   -35 */
-         case fnfErr:                                                          /*   -43 */
-         case dirNFErr:                                                        /*  -120 */
-         case fidNotFound:                                                     /* -1300 */
-               return ENOENT;                                                  /*    +2 */
-
-         case wPrErr:                                                          /*   -44 */
-         case vLckdErr:                                                        /*   -46 */
-         case fsDSIntErr:                                                      /*  -127 */
-               return EROFS;                                                   /*   +30 */
-
-         case opWrErr:                                                         /*   -49 */
-         case fLckdErr:                                                        /*   -45 */
-               return EACCES;                                                  /*   +13 */
-
-         case permErr:                                                         /*   -54 */
-         case wrPermErr:                                                       /*   -61 */
-               return EPERM;                                                   /*    +1 */
-
-         case fBsyErr:                                                         /*   -47 */
-               return EBUSY;                                                   /*   +16 */
-
-         case dupFNErr:                                                        /*    -48 */
-         case fidExists:                                                       /*  -1301 */
-         case cmExists:                                                        /* -32718 */
-         case btExists:                                                        /* -32734 */
-               return EEXIST;                                                  /*    +17 */
-
-         case rfNumErr:                                                        /*   -51 */
-               return EBADF;                                                   /*    +9 */
-
-         case notAFileErr:                                                     /* -1302 */
-               return EISDIR;                                                  /*   +21 */
-
-         case cmNotFound:                                                      /* -32719 */
-         case btNotFound:                                                      /* -32735 */    
-               return ENOENT;                                                  /*     28 */
-
-         case cmNotEmpty:                                                      /* -32717 */
-               return ENOTEMPTY;                                               /*     66 */
-
-         case cmFThdDirErr:                                            /* -32714 */
-               return EISDIR;                                                  /*     21 */
-
-         case fxRangeErr:                                                      /* -32751 */
-               return EIO;                                                             /*      5 */
-
-         case bdNamErr:                                                        /*   -37 */
-               return ENAMETOOLONG;                                    /*    63 */
-
-         case fnOpnErr:                                                        /*   -38 */
-         case eofErr:                                                          /*   -39 */
-         case posErr:                                                          /*   -40 */
-         case paramErr:                                                        /*   -50 */
-         case badMDBErr:                                                       /*   -60 */
-         case badMovErr:                                                       /*  -122 */
-         case sameFileErr:                                                     /* -1306 */
-         case badFidErr:                                                       /* -1307 */
-         case fileBoundsErr:                                           /* -1309 */
-               return EINVAL;                                                  /*   +22 */
-
-         default:
-               DBG_UTILS(("Unmapped MacOS error: %d\n", err));
-               return EIO;                                                             /*   +5 */
+       vcb = HFSTOVCB(hfsmp);
+       fs_size = (u_int64_t)vcb->blockSize * (u_int64_t)vcb->allocLimit;
+
+       /*
+        * For volumes less than 10 GB, don't bother.
+        */
+       if (fs_size < ((u_int64_t)10 * GIGABYTE)) {
+               really_do_it = 0;
        }
-}
+       
+       /*
+        * Skip non-journaled volumes as well.
+        */
+       if (hfsmp->jnl == NULL) {
+               really_do_it = 0;
+       }
+
+       /*
+        * Start with space for the boot blocks and Volume Header.
+        * 1536 = byte offset from start of volume to end of volume header:
+        * 1024 bytes is the offset from the start of the volume to the
+        * start of the volume header (defined by the volume format)
+        * + 512 bytes (the size of the volume header).
+        */
+       zonesize = roundup(1536, hfsmp->blockSize);
+       
+       /*
+        * Add the on-disk size of allocation bitmap.
+        */
+       zonesize += hfsmp->hfs_allocation_cp->c_datafork->ff_blocks * hfsmp->blockSize;
+       
+       /* 
+        * Add space for the Journal Info Block and Journal (if they're in
+        * this file system).
+        */
+       if (hfsmp->jnl && hfsmp->jvp == hfsmp->hfs_devvp) {
+               zonesize += hfsmp->blockSize + hfsmp->jnl_size;
+       }
+       
+       /*
+        * Add the existing size of the Extents Overflow B-tree.
+        * (It rarely grows, so don't bother reserving additional room for it.)
+        */
+       zonesize += hfsmp->hfs_extents_cp->c_datafork->ff_blocks * hfsmp->blockSize;
+       
+       /*
+        * If there is an Attributes B-tree, leave room for 11 clumps worth.
+        * newfs_hfs allocates one clump, and leaves a gap of 10 clumps.
+        * When installing a full OS install onto a 20GB volume, we use
+        * 7 to 8 clumps worth of space (depending on packages), so that leaves
+        * us with another 3 or 4 clumps worth before we need another extent.
+        */
+       if (hfsmp->hfs_attribute_cp) {
+               zonesize += 11 * hfsmp->hfs_attribute_cp->c_datafork->ff_clumpsize;
+       }
+       
+       /*
+        * Leave room for 11 clumps of the Catalog B-tree.
+        * Again, newfs_hfs allocates one clump plus a gap of 10 clumps.
+        * When installing a full OS install onto a 20GB volume, we use
+        * 7 to 8 clumps worth of space (depending on packages), so that leaves
+        * us with another 3 or 4 clumps worth before we need another extent.
+        */
+       zonesize += 11 * hfsmp->hfs_catalog_cp->c_datafork->ff_clumpsize;
+       
+       /*
+        * Add space for hot file region.
+        *
+        * ...for now, use 5 MB per 1 GB (0.5 %)
+        */
+       filesize = (fs_size / 1024) * 5;
+       if (filesize > HOTBAND_MAXIMUM_SIZE)
+               filesize = HOTBAND_MAXIMUM_SIZE;
+       else if (filesize < HOTBAND_MINIMUM_SIZE)
+               filesize = HOTBAND_MINIMUM_SIZE;
+       /*
+        * Calculate user quota file requirements.
+        */
+       if (hfsmp->hfs_flags & HFS_QUOTAS) {
+               items = QF_USERS_PER_GB * (fs_size / GIGABYTE);
+               if (items < QF_MIN_USERS)
+                       items = QF_MIN_USERS;
+               else if (items > QF_MAX_USERS)
+                       items = QF_MAX_USERS;
+               if (!powerof2(items)) {
+                       int x = items;
+                       items = 4;
+                       while (x>>1 != 1) {
+                               x = x >> 1;
+                               items = items << 1;
+                       }
+               }
+               filesize += (items + 1) * sizeof(struct dqblk);
+               /*
+                * Calculate group quota file requirements.
+                *
+                */
+               items = QF_GROUPS_PER_GB * (fs_size / GIGABYTE);
+               if (items < QF_MIN_GROUPS)
+                       items = QF_MIN_GROUPS;
+               else if (items > QF_MAX_GROUPS)
+                       items = QF_MAX_GROUPS;
+               if (!powerof2(items)) {
+                       int x = items;
+                       items = 4;
+                       while (x>>1 != 1) {
+                               x = x >> 1;
+                               items = items << 1;
+                       }
+               }
+               filesize += (items + 1) * sizeof(struct dqblk);
+       }
+       zonesize += filesize;
 
+       /*
+        * Round up entire zone to a bitmap block's worth.
+        * The extra space goes to the catalog file and hot file area.
+        */
+       temp = zonesize;
+       zonesize = roundup(zonesize, (u_int64_t)vcb->vcbVBMIOSize * 8 * vcb->blockSize);
+       hfsmp->hfs_min_alloc_start = zonesize / vcb->blockSize;
+       /*
+        * If doing the round up for hfs_min_alloc_start would push us past
+        * allocLimit, then just reset it back to 0.  Though using a value 
+        * bigger than allocLimit would not cause damage in the block allocator
+        * code, this value could get stored in the volume header and make it out 
+        * to disk, making the volume header technically corrupt.
+        */
+       if (hfsmp->hfs_min_alloc_start >= hfsmp->allocLimit) {
+               hfsmp->hfs_min_alloc_start = 0;
+       }
 
-/*
- * All of our debugging functions
- */
+       if (really_do_it == 0) {
+               /* If metadata zone needs to be disabled because the 
+                * volume was truncated, clear the bit and zero out 
+                * the values that are no longer needed.
+                */
+               if (hfsmp->hfs_flags & HFS_METADATA_ZONE) {
+                       /* Disable metadata zone */
+                       hfsmp->hfs_flags &= ~HFS_METADATA_ZONE;
+                       
+                       /* Zero out mount point values that are not required */
+                       hfsmp->hfs_catalog_maxblks = 0;
+                       hfsmp->hfs_hotfile_maxblks = 0;
+                       hfsmp->hfs_hotfile_start = 0;
+                       hfsmp->hfs_hotfile_end = 0;
+                       hfsmp->hfs_hotfile_freeblks = 0;
+                       hfsmp->hfs_metazone_start = 0;
+                       hfsmp->hfs_metazone_end = 0;
+               }
+               
+               return;
+       }
+       
+       temp = zonesize - temp;  /* temp has extra space */
+       filesize += temp / 3;
+       hfsmp->hfs_catalog_maxblks += (temp - (temp / 3)) / vcb->blockSize;
 
-#if HFS_DIAGNOSTIC
+       hfsmp->hfs_hotfile_maxblks = filesize / vcb->blockSize;
 
-void debug_vn_status (char* introStr, struct vnode *vn)
-{
-    DBG_VOP(("%s:\t",introStr));
-    if (vn != NULL)
-      {
-        if (vn->v_tag != VT_HFS)
-          {
-            DBG_VOP(("NON-HFS VNODE Ox%08lX\n", (unsigned long)vn));
-          }
-        else if(vn->v_tag==VT_HFS && (vn->v_data==NULL || VTOH((vn))->h_valid != HFS_VNODE_MAGIC))
-          {
-            DBG_VOP(("BAD VNODE PRIVATE DATA!!!!\n"));
-          }
-        else
-          {
-            DBG_VOP(("r: %d & ", vn->v_usecount));
-            if (lockstatus(&VTOH(vn)->h_lock))
-              {
-                DBG_VOP_CONT(("is L\n"));
-              }
-            else
-              {
-                DBG_VOP_CONT(("is U\n"));
-              }
-          }
-      }
-    else
-      {
-        DBG_VOP(("vnode is NULL\n"));
-      };
-}
+       /* Convert to allocation blocks. */
+       blk = zonesize / vcb->blockSize;
 
-void debug_vn_print (char* introStr, struct vnode *vn)
-{
-//  DBG_FUNC_NAME("DBG_VN_PRINT");
-    DBG_ASSERT (vn != NULL);
-    DBG_VFS(("%s: ",introStr));
-    DBG_VFS_CONT(("vnode: 0x%x is a ", (uint)vn));
-    switch (vn->v_tag)
-      {
-        case VT_UFS:
-            DBG_VFS_CONT(("%s","UFS"));
-            break;
-        case VT_HFS:
-            DBG_VFS_CONT(("%s","HFS"));
-            break;
-        default:
-            DBG_VFS_CONT(("%s","UNKNOWN"));
-            break;
-      }
+       /* The default metadata zone location is at the start of volume. */
+       hfsmp->hfs_metazone_start = 1;
+       hfsmp->hfs_metazone_end = blk - 1;
+       
+       /* The default hotfile area is at the end of the zone. */
+       hfsmp->hfs_hotfile_start = blk - (filesize / vcb->blockSize);
+       hfsmp->hfs_hotfile_end = hfsmp->hfs_metazone_end;
+       hfsmp->hfs_hotfile_freeblks = hfs_hotfile_freeblocks(hfsmp);
+#if 0
+       printf("hfs: metadata zone is %d to %d\n", hfsmp->hfs_metazone_start, hfsmp->hfs_metazone_end);
+       printf("hfs: hot file band is %d to %d\n", hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end);
+       printf("hfs: hot file band free blocks = %d\n", hfsmp->hfs_hotfile_freeblks);
+#endif
+       hfsmp->hfs_flags |= HFS_METADATA_ZONE;
+}
 
-    DBG_VFS_CONT((" vnode\n"));
-    if (vn->v_tag==VT_HFS)
-      {
-        if (vn->v_data==NULL)
-          {
-            DBG_VFS(("BAD VNODE PRIVATE DATA!!!!\n"));
-          }
-        else
-          {
-            DBG_VFS(("     Name: %s Id: %ld ",H_NAME(VTOH(vn)), H_FILEID(VTOH(vn))));
-          }
-      }
-    else
-        DBG_VFS(("     "));
 
-    DBG_VFS_CONT(("Refcount: %d\n", vn->v_usecount));
-    if (VOP_ISLOCKED(vn))
-      {
-        DBG_VFS(("     The vnode is locked\n"));
-      }
-    else
-      {
-        DBG_VFS(("     The vnode is not locked\n"));
-      }
+static u_int32_t
+hfs_hotfile_freeblocks(struct hfsmount *hfsmp)
+{
+       ExtendedVCB  *vcb = HFSTOVCB(hfsmp);
+       int  lockflags;
+       int  freeblocks;
+
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_BITMAP, HFS_EXCLUSIVE_LOCK);
+       freeblocks = MetaZoneFreeBlocks(vcb);
+       hfs_systemfile_unlock(hfsmp, lockflags);
+
+       /* Minus Extents overflow file reserve. */
+       freeblocks -=
+               hfsmp->hfs_overflow_maxblks - VTOF(hfsmp->hfs_extents_vp)->ff_blocks;
+       /* Minus catalog file reserve. */
+       freeblocks -=
+               hfsmp->hfs_catalog_maxblks - VTOF(hfsmp->hfs_catalog_vp)->ff_blocks;
+       if (freeblocks < 0)
+               freeblocks = 0;
+
+       return MIN(freeblocks, hfsmp->hfs_hotfile_maxblks);
 }
 
-void debug_rename_test_locks (char*                    introStr,
-                            struct vnode       *fvp,
-                            struct vnode       *fdvp,
-                            struct vnode       *tvp,
-                            struct vnode       *tdvp,
-                            int                                fstatus,
-                            int                                fdstatus,
-                            int                                tstatus,
-                            int                                tdstatus
-)
+/*
+ * Determine if a file is a "virtual" metadata file.
+ * This includes journal and quota files.
+ */
+__private_extern__
+int
+hfs_virtualmetafile(struct cnode *cp)
 {
-    DBG_VOP(("\t%s: ", introStr));
-    if (fvp) {if(lockstatus(&VTOH(fvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
-    if (fdvp) {if(lockstatus(&VTOH(fdvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
-    if (tvp) {if(lockstatus(&VTOH(tvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
-    if (tdvp) {if(lockstatus(&VTOH(tdvp)->h_lock)){DBG_VFS_CONT(("L"));} else {DBG_VFS_CONT(("U"));}} else { DBG_VFS_CONT(("X"));};
-    DBG_VFS_CONT(("\n"));
-
-    if (fvp) {
-        if (lockstatus(&VTOH(fvp)->h_lock)) {
-            if (fstatus==VOPDBG_UNLOCKED) {
-                DBG_VOP(("\tfvp should be NOT LOCKED and it is\n"));
-            }
-        } else if (fstatus == VOPDBG_LOCKED) {
-            DBG_VOP(("\tfvp should be LOCKED and it isnt\n"));
-        }
-    }
+       const char * filename;
 
-    if (fdvp) {
-        if (lockstatus(&VTOH(fdvp)->h_lock)) {
-            if (fdstatus==VOPDBG_UNLOCKED) {
-                DBG_VOP(("\tfdvp should be NOT LOCKED and it is\n"));
-            }
-        } else if (fdstatus == VOPDBG_LOCKED) {
-            DBG_VOP(("\tfdvp should be LOCKED and it isnt\n"));
-        }
-    }
 
-    if (tvp) {
-        if (lockstatus(&VTOH(tvp)->h_lock)) {
-            if (tstatus==VOPDBG_UNLOCKED) {
-                DBG_VOP(("\ttvp should be NOT LOCKED and it is\n"));
-            }
-        } else if (tstatus == VOPDBG_LOCKED) {
-            DBG_VOP(("\ttvp should be LOCKED and it isnt\n"));
-        }
-    }
+       if (cp->c_parentcnid != kHFSRootFolderID)
+               return (0);
 
-    if (tdvp) {
-        if (lockstatus(&VTOH(tdvp)->h_lock)) {
-            if (tdstatus==VOPDBG_UNLOCKED) {
-                DBG_VOP(("\ttdvp should be NOT LOCKED and it is\n"));
-            }
-        } else if (tdstatus == VOPDBG_LOCKED) {
-            DBG_VOP(("\ttdvp should be LOCKED and it isnt\n"));
+       filename = (const char *)cp->c_desc.cd_nameptr;
+       if (filename == NULL)
+               return (0);
 
-        }
-    }
+       if ((strncmp(filename, ".journal", sizeof(".journal")) == 0) ||
+           (strncmp(filename, ".journal_info_block", sizeof(".journal_info_block")) == 0) ||
+           (strncmp(filename, ".quota.user", sizeof(".quota.user")) == 0) ||
+           (strncmp(filename, ".quota.group", sizeof(".quota.group")) == 0) ||
+           (strncmp(filename, ".hotfiles.btree", sizeof(".hotfiles.btree")) == 0))
+               return (1);
 
+       return (0);
 }
-#endif /* HFS_DIAGNOSTIC */
-
 
-#if HFS_DIAGNOSTIC
-void debug_check_buffersizes(struct vnode *vp, struct hfsnode *hp, struct buf *bp) {
-    DBG_ASSERT(bp->b_validoff == 0);
-    DBG_ASSERT(bp->b_dirtyoff == 0);
-    DBG_ASSERT((bp->b_bcount == HTOHFS(hp)->hfs_logBlockSize) ||
-                   ((bp->b_bcount % 512 == 0) &&
-                    (bp->b_validend > 0) &&
-                    (bp->b_dirtyend > 0) &&
-                    (bp->b_bcount < HTOHFS(hp)->hfs_logBlockSize)));
-
-    if (bp->b_validend == 0) {
-        DBG_ASSERT(bp->b_dirtyend == 0);
-    } else {
-        DBG_ASSERT(bp->b_validend == bp->b_bcount);
-        DBG_ASSERT(bp->b_dirtyend <= bp->b_bcount);
-    };
-}
 
+//
+// Fire off a timed callback to sync the disk if the
+// volume is on ejectable media.
+//
+ __private_extern__
+void
+hfs_sync_ejectable(struct hfsmount *hfsmp)
+{
+       if (hfsmp->hfs_syncer)  {
+               clock_sec_t secs;
+               clock_usec_t usecs;
+               uint64_t now;
+
+               clock_get_calendar_microtime(&secs, &usecs);
+               now = ((uint64_t)secs * 1000000ULL) + (uint64_t)usecs;
+
+               if (hfsmp->hfs_sync_incomplete && hfsmp->hfs_mp->mnt_pending_write_size >= hfsmp->hfs_max_pending_io) {
+                       // if we have a sync scheduled but i/o is starting to pile up,
+                       // don't call thread_call_enter_delayed() again because that
+                       // will defer the sync.
+                       return;
+               }
 
-void debug_check_blocksizes(struct vnode *vp) {
-    struct hfsnode *hp = VTOH(vp);
-    struct buf *bp;
+               if (hfsmp->hfs_sync_scheduled == 0) {
+                       uint64_t deadline;
 
-    if (vp->v_flag & VSYSTEM) return;
+                       hfsmp->hfs_last_sync_request_time = now;
 
-    for (bp = vp->v_cleanblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
-        debug_check_buffersizes(vp, hp, bp);
-    };
+                       clock_interval_to_deadline(HFS_META_DELAY, HFS_MILLISEC_SCALE, &deadline);
 
-    for (bp = vp->v_dirtyblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
-        debug_check_buffersizes(vp, hp, bp);
-    };
+                       /*
+                        * Increment hfs_sync_scheduled on the assumption that we're the
+                        * first thread to schedule the timer.  If some other thread beat
+                        * us, then we'll decrement it.  If we *were* the first to
+                        * schedule the timer, then we need to keep track that the
+                        * callback is waiting to complete.
+                        */
+                       OSIncrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_scheduled);
+                       if (thread_call_enter_delayed(hfsmp->hfs_syncer, deadline))
+                               OSDecrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_scheduled);
+                       else
+                               OSIncrementAtomic((volatile SInt32 *)&hfsmp->hfs_sync_incomplete);
+               }               
+       }
 }
 
-void debug_check_catalogdata(struct CatalogNodeData *cat) {
 
-       if (cat->cnm_nameptr == NULL) {
-        DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
+__private_extern__
+int
+hfs_start_transaction(struct hfsmount *hfsmp)
+{
+       int ret, unlock_on_err=0;
+       void * thread = current_thread();
+
+#ifdef HFS_CHECK_LOCK_ORDER
+       /*
+        * You cannot start a transaction while holding a system
+        * file lock. (unless the transaction is nested.)
+        */
+       if (hfsmp->jnl && journal_owner(hfsmp->jnl) != thread) {
+               if (hfsmp->hfs_catalog_cp && hfsmp->hfs_catalog_cp->c_lockowner == thread) {
+                       panic("hfs_start_transaction: bad lock order (cat before jnl)\n");
+               }
+               if (hfsmp->hfs_attribute_cp && hfsmp->hfs_attribute_cp->c_lockowner == thread) {
+                       panic("hfs_start_transaction: bad lock order (attr before jnl)\n");
+               }
+               if (hfsmp->hfs_extents_cp && hfsmp->hfs_extents_cp->c_lockowner == thread) {
+                       panic("hfs_start_transaction: bad lock order (ext before jnl)\n");
+               }
        }
-       else if (cat->cnm_nameptr == cat->cnm_namespace) {
-        DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
-    }
-    else {
-        DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == kCatNameIsAllocated);
+#endif /* HFS_CHECK_LOCK_ORDER */
+
+    if (hfsmp->jnl == NULL || journal_owner(hfsmp->jnl) != thread) {
+       lck_rw_lock_shared(&hfsmp->hfs_global_lock);
+       OSAddAtomic(1, (SInt32 *)&hfsmp->hfs_active_threads);
+       unlock_on_err = 1;
     }
 
-       if (cat->cnm_nameptr) {
-               DBG_ASSERT(strlen(cat->cnm_nameptr) == cat->cnm_length);
+       /* If a downgrade to read-only mount is in progress, no other
+        * process than the downgrade process is allowed to modify 
+        * the file system.
+        */
+       if ((hfsmp->hfs_flags & HFS_RDONLY_DOWNGRADE) && 
+                       (hfsmp->hfs_downgrading_proc != thread)) {
+               ret = EROFS;
+               goto out;
        }
-               
-       if (cat->cnm_flags & kCatNameIsConsumed) {
-        DBG_ASSERT((cat->cnm_flags & kCatNameIsAllocated) == 0);
+
+    if (hfsmp->jnl) {
+       ret = journal_start_transaction(hfsmp->jnl);
+       if (ret == 0) {
+           OSAddAtomic(1, &hfsmp->hfs_global_lock_nesting);
+       }
+    } else {
+       ret = 0;
     }
 
-       if (cat->cnm_flags & kCatNameNoCopyName) {
-        DBG_ASSERT((cat->cnm_flags & (kCatNameIsAllocated|kCatNameIsConsumed|kCatNameIsMangled)) == 0);
-        DBG_ASSERT(cat->cnm_length == 0);
-        DBG_ASSERT(cat->cnm_nameptr == 0);
-        DBG_ASSERT(strlen(cat->cnm_namespace) == 0);
-        }
+out:
+    if (ret != 0 && unlock_on_err) {
+       lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
+       OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
+    }
 
+    return ret;
 }
 
-extern void hfs_vhash_dbg(struct hfsnode *hp);
+__private_extern__
+int
+hfs_end_transaction(struct hfsmount *hfsmp)
+{
+    int need_unlock=0, ret;
 
-/* Checks the valicity of a hfs vnode */
-void debug_check_vnode(struct vnode *vp, int stage) {
-    struct hfsnode *hp;
-    u_long size;
-       int i;
+    if (    hfsmp->jnl == NULL
+       || (   journal_owner(hfsmp->jnl) == current_thread()
+           && (OSAddAtomic(-1, &hfsmp->hfs_global_lock_nesting) == 1)) ) {
 
-       /* vcb stuff */
-       if (VTOHFS(vp)->hfs_mount_flags & kHFSBootVolumeInconsistentMask)
-        DEBUG_BREAK_MSG(("Volume is damaged!"));
-       
-    /* vnode stuff */
-    if (vp==NULL)
-        DEBUG_BREAK_MSG(("Null vnode"));
-    if (vp->v_tag != VT_HFS)
-        DEBUG_BREAK_MSG(("Not a HFS vnode, it is a %d", vp->v_tag));
-    if (vp->v_data==NULL)
-        DEBUG_BREAK_MSG(("v_data is NULL"));
-
-    /* hfsnode stuff */
-    hp = VTOH(vp);
-    if (hp->h_valid != HFS_VNODE_MAGIC)
-        DEBUG_BREAK_MSG(("Bad Formed HFS node"));
-    if (hp->h_vp==NULL || hp->h_vp!=vp)
-        DEBUG_BREAK_MSG(("Bad hfsnode vnode pte"));
-    if (hp->h_meta == NULL)
-        DEBUG_BREAK_MSG(("Bad hfsnode meta ptr"));
-    switch (H_FORKTYPE(hp)) {
-        case kDataFork:
-        case kRsrcFork:
-            if ((hp->h_meta->h_siblinghead.cqh_first == NULL) || (hp->h_meta->h_siblinghead.cqh_last == NULL))
-                DEBUG_BREAK_MSG(("Null sibling header"));
-            if ((hp->h_sibling.cqe_next==NULL) || (hp->h_sibling.cqe_prev==NULL))
-                DEBUG_BREAK_MSG(("Null sibling list"));
-                if (hp->h_meta->h_usecount<1 || hp->h_meta->h_usecount>2)
-                    DEBUG_BREAK_MSG(("Bad sibling usecount"));
-                    break;
-        case kDirectory:
-        case kSysFile:
-            if ((hp->h_meta->h_siblinghead.cqh_first != NULL) || (hp->h_meta->h_siblinghead.cqh_last != NULL))
-                DEBUG_BREAK_MSG(("Non Null sibling header"));
-            if ((hp->h_sibling.cqe_next!=NULL) || (hp->h_sibling.cqe_prev!=NULL))
-                DEBUG_BREAK_MSG(("Null sibling list"));
-                if (hp->h_meta->h_usecount!=1)
-                    DEBUG_BREAK_MSG(("Bad usecount"));
-
-                    break;
-        default:
-            DEBUG_BREAK_MSG(("Bad hfsnode fork type"));
-            }
-
-    /* hfsmeta stuff */
-    if (hp->h_meta->h_devvp == NULL)
-        DEBUG_BREAK_MSG(("Bad hfsnode dev vnode"));
-    if (H_DEV(hp) == 0)
-        DEBUG_BREAK_MSG(("Bad dev id"));
-    if (H_FILEID(hp) == 0)
-        DEBUG_BREAK_MSG(("Bad file id"));
-    
-    if (((hp->h_meta->h_metaflags & IN_DATANODE)==0) && (H_DIRID(hp) == 0) && (H_FILEID(hp) != 1))
-        DEBUG_BREAK_MSG(("Bad dir id"));
-    
-    if (hp->h_meta->h_namePtr == NULL && hp->h_meta->h_namelen!=0)
-        DEBUG_BREAK_MSG(("hfs meta h_namelen is not 0"));
-    if (hp->h_meta->h_namePtr != NULL && strlen(hp->h_meta->h_namePtr) != hp->h_meta->h_namelen)
-        DEBUG_BREAK_MSG(("Bad hfs meta h_namelen"));
-
-   /* Check the hash */
-       hfs_vhash_dbg(hp);
-
-       /* Check to see if we want to compare with the disk */
-       if (stage > 200) {
-               int retval;
-               hfsCatalogInfo catInfo;
-
-               INIT_CATALOGDATA(&catInfo.nodeData, 0);
-               catInfo.hint = 0;
-
-               if (hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, current_proc()))
-                       return;
+           need_unlock = 1;
+    } 
 
-               if (hfs_getcatalog(VTOVCB(vp), H_DIRID(hp), hp->h_meta->h_namePtr, hp->h_meta->h_namelen, &catInfo))
-                       DEBUG_BREAK_MSG(("Could not find hfsnode Catalog record"));
+    if (hfsmp->jnl) {
+       ret = journal_end_transaction(hfsmp->jnl);
+    } else {
+       ret = 0;
+    }
 
-               (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, current_proc());
+    if (need_unlock) {
+       OSAddAtomic(-1, (SInt32 *)&hfsmp->hfs_active_threads);
+       lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
+       hfs_sync_ejectable(hfsmp);
+    }
 
-               if (H_FILEID(hp) != catInfo.nodeData.cnd_nodeID)
-                       DEBUG_BREAK_MSG(("hfsnode catalog node id mismatch"));
-               if (H_DIRID(hp) != catInfo.nodeData.cnm_parID)
-                       DEBUG_BREAK_MSG(("hfsnode catalog dir id mismatch"));
-               if (strcmp(hp->h_meta->h_namePtr, catInfo.nodeData.cnm_nameptr) != 0)
-                       DEBUG_BREAK_MSG(("hfsnode catalog name mismatch"));
-               /* Check dates too??? */
+    return ret;
+}
 
-               CLEAN_CATALOGDATA(&catInfo.nodeData);
 
-               }
+__private_extern__
+int
+hfs_journal_flush(struct hfsmount *hfsmp)
+{
+       int ret;
+       
+       /* Only peek at hfsmp->jnl while holding the global lock */
+       lck_rw_lock_shared(&hfsmp->hfs_global_lock);
+       if (hfsmp->jnl) {
+               ret = journal_flush(hfsmp->jnl);
+       } else {
+               ret = 0;
+       }
+       lck_rw_unlock_shared(&hfsmp->hfs_global_lock);
+       
+       return ret;
+}
 
 
-       /* Check Extents */
+/*
+ * hfs_erase_unused_nodes
+ *
+ * Check wheter a volume may suffer from unused Catalog B-tree nodes that
+ * are not zeroed (due to <rdar://problem/6947811>).  If so, just write
+ * zeroes to the unused nodes.
+ *
+ * How do we detect when a volume needs this repair?  We can't always be
+ * certain.  If a volume was created after a certain date, then it may have
+ * been created with the faulty newfs_hfs.  Since newfs_hfs only created one
+ * clump, we can assume that if a Catalog B-tree is larger than its clump size,
+ * that means that the entire first clump must have been written to, which means
+ * there shouldn't be unused and unwritten nodes in that first clump, and this
+ * repair is not needed.
+ *
+ * We have defined a bit in the Volume Header's attributes to indicate when the
+ * unused nodes have been repaired.  A newer newfs_hfs will set this bit.
+ * As will fsck_hfs when it repairs the unused nodes.
+ */
+__private_extern__
+int hfs_erase_unused_nodes(struct hfsmount *hfsmp)
+{
+       int result; 
+       struct filefork *catalog;
+       int lockflags;
+       
+       if (hfsmp->vcbAtrb & kHFSUnusedNodeFixMask)
        {
-    for(i = 0, size = 0; i < kHFSPlusExtentDensity; i++)
-      {        
-        size += hp->fcbExtents[i].blockCount;  
-      }
-
-    if (hp->fcbEOF > hp->fcbPLen)
-        DEBUG_BREAK_MSG(("fcbPLen is smaller than fcbEOF"));
-
-    if (hp->fcbExtents[kHFSPlusExtentDensity-1].blockCount == 0) {
-        if ((off_t)size * (off_t)VTOVCB(vp)->blockSize != hp->fcbPLen)
-            DEBUG_BREAK_MSG(("fcbPLen does not match extents"));
-       } else {
-        if ( hp->fcbPLen < (off_t)size * (off_t)VTOVCB(vp)->blockSize)
-            DEBUG_BREAK_MSG(("fcbPLen is smaller than extents"));
-       }
-    for(i = 0; i < kHFSPlusExtentDensity; i++)
-      {        
-        if (hp->fcbExtents[i].blockCount == 0 || hp->fcbExtents[i].startBlock == 0)
-            break;     
-      }
-    if ((VTOVCB(vp)->vcbSigWord == kHFSSigWord) && i > kHFSExtentDensity)
-        DEBUG_BREAK_MSG(("Illegal value in extents for ordinary HFS"));
-    if (i > kHFSPlusExtentDensity) {
-        for(; i < kHFSPlusExtentDensity; i++)
-          {
-            if (hp->fcbExtents[i].blockCount != 0 || hp->fcbExtents[i].startBlock != 0)
-                DEBUG_BREAK_MSG(("Illegal value in extents"));
-          }
-    }
+               /* This volume has already been checked and repaired. */
+               return 0;
        }
 
-    
-    /* BTree stuff */
-    if (0 && vp->v_flag & VSYSTEM) {
-       BTreeInfoRec                    info;
-       
-       BTGetInformation(hp, 0, &info);
-       if (hp->fcbBTCBPtr == NULL)
-               DEBUG_BREAK_MSG(("Null fcbBTCBPtr"));
-       if (H_HINT(hp) == 0)
-               DEBUG_BREAK_MSG(("hint is 0"));
-       if (H_HINT(hp) > info.numNodes)
-               DEBUG_BREAK_MSG(("hint > numNodes"));
-    }
+       if ((hfsmp->localCreateDate < kHFSUnusedNodesFixDate))
+       {
+               /* This volume is too old to have had the problem. */
+               hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+               return 0;
+       }
 
+       catalog = hfsmp->hfs_catalog_cp->c_datafork;
+       if (catalog->ff_size > catalog->ff_clumpsize)
+       {
+               /* The entire first clump must have been in use at some point. */
+               hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+               return 0;
+       }
+       
+       /*
+        * If we get here, we need to zero out those unused nodes.
+        *
+        * We start a transaction and lock the catalog since we're going to be
+        * making on-disk changes.  But note that BTZeroUnusedNodes doens't actually
+        * do its writing via the journal, because that would be too much I/O
+        * to fit in a transaction, and it's a pain to break it up into multiple
+        * transactions.  (It behaves more like growing a B-tree would.)
+        */
+       printf("hfs_erase_unused_nodes: updating volume %s.\n", hfsmp->vcbVN);
+       result = hfs_start_transaction(hfsmp);
+       if (result)
+               goto done;
+       lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
+       result = BTZeroUnusedNodes(catalog);
+       vnode_waitforwrites(hfsmp->hfs_catalog_vp, 0, 0, 0, "hfs_erase_unused_nodes");
+       hfs_systemfile_unlock(hfsmp, lockflags);
+       hfs_end_transaction(hfsmp);
+       if (result == 0)
+               hfsmp->vcbAtrb |= kHFSUnusedNodeFixMask;
+       printf("hfs_erase_unused_nodes: done updating volume %s.\n", hfsmp->vcbVN);
+
+done:
+       return result;
 }
-
-#endif /* HFS_DIAGNOSTIC */