Source to bsd/hfs/hfs_vfsutils.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_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 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.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*	@(#)hfs_vfsutils.c	4.0
*
*	(c) 1997-2000 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 <kern/mapfs.h>

#include "hfs.h"
#include "hfs_dbg.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)();
extern int (**hfs_specop_p)();
extern int (**hfs_fifoop_p)();
extern int count_lock_queue __P((void));

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)(), int (**fifoops)(), struct vnode **vpp);
extern int readlinknode(ExtendedVCB *vcb, hfsCatalogInfo *catInfo, UInt32 *nodeID);

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);

void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm);
void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp);
void hfs_set_metaname(char *name, struct hfsfilemeta *fm);
u_int32_t GetLogicalBlockSize(struct vnode *vp);

/* 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);


//*******************************************************************************
//	Routine:	hfs_MountHFSVolume
//
//
//*******************************************************************************

OSErr hfs_MountHFSVolume(struct hfsmount *hfsmp, HFSMasterDirectoryBlock *mdb,
		u_long sectors, struct proc *p)
{
    ExtendedVCB 			*vcb = HFSTOVCB(hfsmp);
    struct vnode 			*tmpvnode;
    OSErr					err;
    HFSPlusExtentRecord		extents;
    DBG_FUNC_NAME("hfs_MountHFSVolume");
    DBG_PRINT_FUNC_NAME();

    if (hfsmp == nil || mdb == nil)				/* exit if bad paramater */
		return (EINVAL);

    err = ValidMasterDirectoryBlock( mdb );		/* make sure this is an HFS disk */
    if (err)
    	return MacToVFSError(err);

	/* don't mount volume if its dirty, it must be cleaned by fsck_hfs */
	if ((mdb->drAtrb & kHFSVolumeUnmountedMask) == 0)
		return (EINVAL);
		
	/*
	 * The MDB seems OK: transfer info from it into VCB
	 * Note - the VCB starts out clear (all zeros)
	 *
	 */

	DBG_ASSERT((hfsmp->hfs_raw_dev & 0xFFFF0000) == 0);
	vcb->vcbVRefNum = MAKE_VREFNUM(hfsmp->hfs_raw_dev);

	vcb->vcbSigWord = mdb->drSigWord;
	vcb->vcbCrDate = LocalToUTC(mdb->drCrDate);
	vcb->vcbLsMod = LocalToUTC(mdb->drLsMod);
	vcb->vcbAtrb = mdb->drAtrb;
	vcb->vcbNmFls = mdb->drNmFls;
	vcb->vcbVBMSt = mdb->drVBMSt;
	vcb->nextAllocation = mdb->drAllocPtr;
	vcb->totalBlocks = mdb->drNmAlBlks;
	vcb->blockSize = mdb->drAlBlkSiz;
	vcb->vcbClpSiz = mdb->drClpSiz;
	vcb->vcbAlBlSt= mdb->drAlBlSt;
	vcb->vcbNxtCNID = mdb->drNxtCNID;
	vcb->freeBlocks = mdb->drFreeBks;
	vcb->vcbVolBkUp = LocalToUTC(mdb->drVolBkUp);
	vcb->vcbVSeqNum = mdb->drVSeqNum;
	vcb->vcbWrCnt = mdb->drWrCnt;
	vcb->vcbNmRtDirs = mdb->drNmRtDirs;
	vcb->vcbFilCnt = mdb->drFilCnt;
	vcb->vcbDirCnt = mdb->drDirCnt;
	bcopy(mdb->drFndrInfo, vcb->vcbFndrInfo, sizeof(vcb->vcbFndrInfo));
	vcb->nextAllocation = mdb->drAllocPtr;
	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
	 */

	/* XXX need to supply real UTF-8 string! */
	bcopy( &mdb->drVN[1], vcb->vcbVN,  mdb->drVN[0]);
	vcb->vcbVN[mdb->drVN[0]] = '\0';

	vcb->altIDSector = sectors - 2;

    //	Initialize our dirID/nodePtr cache associated with this volume.
    err = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
    ReturnIfError( err );

    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);

	/*
	 * Set up Extents B-tree vnode...
	 */ 
	err = GetInitializedVNode(hfsmp, &tmpvnode);
	if (err) goto MtVolErr;
    HFSToHFSPlusExtents(mdb->drXTExtRec, extents);
    err = InitMetaFileVNode(tmpvnode, mdb->drXTFlSize, mdb->drXTClpSiz, extents,
							kHFSExtentsFileID, CompareExtentKeys);
    if (err) goto MtVolErr;

	/*
	 * Set up Catalog B-tree vnode...
	 */ 
	err = GetInitializedVNode(hfsmp, &tmpvnode);
	if (err) goto MtVolErr;
    HFSToHFSPlusExtents(mdb->drCTExtRec, extents);
    err = InitMetaFileVNode(tmpvnode, mdb->drCTFlSize, mdb->drCTClpSiz, extents,
							kHFSCatalogFileID, CompareCatalogKeys);
	if (err) goto MtVolErr;

      	/* mark the volume dirty (clear clean unmount bit) */
	vcb->vcbAtrb &=	~kHFSVolumeUnmountedMask;

	/*
	 * 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 ( err == noErr )
      {
        if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )		//	if the disk is not write protected
          {
            MarkVCBDirty( vcb );								//	mark VCB dirty so it will be written
          }
      }
    goto	CmdDone;

    //--	Release any resources allocated so far before exiting with an error:
MtVolErr:;
	ReleaseMetaFileVNode(vcb->catalogRefNum);
	ReleaseMetaFileVNode(vcb->extentsRefNum);

CmdDone:;
    return( err );

}

//*******************************************************************************
//	Routine:	hfs_MountHFSPlusVolume
//
//
//*******************************************************************************

OSErr hfs_MountHFSPlusVolume(struct hfsmount *hfsmp, HFSPlusVolumeHeader *vhp,
	u_long embBlkOffset, u_long sectors, struct proc *p)
{
    register ExtendedVCB	*vcb;
    HFSPlusForkData			*fdp;
    struct vnode 			*tmpvnode;
    OSErr					retval;

    if (hfsmp == nil || vhp == nil)		/*	exit if bad paramater */
		return (EINVAL);

	DBG_VFS(("hfs_MountHFSPlusVolume: signature=0x%x, version=%d, blockSize=%ld\n", vhp->signature, vhp->version, vhp->blockSize));

    retval = ValidVolumeHeader(vhp);	/*	make sure this is an HFS Plus disk */
    if (retval)
    	return MacToVFSError(retval);
   
	/* don't mount a writable volume if its dirty, it must be cleaned by fsck_hfs */
	if (hfsmp->hfs_fs_ronly == 0 && (vhp->attributes & kHFSVolumeUnmountedMask) == 0)
		return (EINVAL);
	/*
	 * 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		=	vhp->signature;
	vcb->vcbCrDate		=	LocalToUTC(vhp->createDate);				// NOTE: local time, not GMT!
	vcb->vcbLsMod		=	vhp->modifyDate;
	vcb->vcbAtrb		=	(UInt16) vhp->attributes;		// VCB only uses lower 16 bits
	vcb->vcbClpSiz		=	vhp->rsrcClumpSize;
	vcb->vcbNxtCNID		=	vhp->nextCatalogID;
	vcb->vcbVolBkUp		=	vhp->backupDate;
	vcb->vcbWrCnt		=	vhp->writeCount;
	vcb->vcbFilCnt		=	vhp->fileCount;
	vcb->vcbDirCnt		=	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 */

	VCB_LOCK_INIT(vcb);

	/*	Now fill in the Extended VCB info */
	vcb->nextAllocation			=	vhp->nextAllocation;
	vcb->totalBlocks			=	vhp->totalBlocks;
	vcb->freeBlocks				=	vhp->freeBlocks;
	vcb->blockSize				=	vhp->blockSize;
	vcb->checkedDate			=	vhp->checkedDate;
	vcb->encodingsBitmap		=	vhp->encodingsBitmap;
	
	vcb->hfsPlusIOPosOffset		=	embBlkOffset * 512;

	vcb->altIDSector = embBlkOffset + sectors - 2;

    /* 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);

    // 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!!!!

    //	Initialize our dirID/nodePtr cache associated with this volume.
    retval = InitMRUCache( sizeof(UInt32), kDefaultNumMRUCacheBlocks, &(vcb->hintCachePtr) );
    if (retval != noErr) goto ErrorExit;

	/*
	 * Set up Extents B-tree vnode...
	 */ 
	retval = GetInitializedVNode(hfsmp, &tmpvnode);
	if (retval) goto ErrorExit;
	fdp = &vhp->extentsFile;
	retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
								kHFSExtentsFileID, CompareExtentKeysPlus);
    if (retval) goto ErrorExit;

	/*
	 * Set up Catalog B-tree vnode...
	 */ 
	retval = GetInitializedVNode(hfsmp, &tmpvnode);
	if (retval) goto ErrorExit;
	fdp = &vhp->catalogFile;
	retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
								kHFSCatalogFileID, CompareExtendedCatalogKeys);
	if (retval) goto ErrorExit;

	/*
	 * Set up Allocation file vnode...
	 */  
	retval = GetInitializedVNode(hfsmp, &tmpvnode);
	if (retval) goto ErrorExit;
	fdp = &vhp->allocationFile;
	retval = InitMetaFileVNode(tmpvnode, fdp->logicalSize, fdp->clumpSize, fdp->extents,
								kHFSAllocationFileID, NULL);
	if (retval) goto ErrorExit;
 
	/*
	 * Now that Catalog file is open get the volume name from the catalog
	 */
	retval = MacToVFSError( GetVolumeNameFromCatalog(vcb) );	
	if (retval != noErr) goto ErrorExit;

	/* mark the volume dirty (clear clean unmount bit) */
	vcb->vcbAtrb &=	~kHFSVolumeUnmountedMask;

	/* setup private/hidden directory for unlinked files */
	hfsmp->hfs_private_metadata_dir = FindMetaDataDirectory(vcb);

	/*
	 * all done with metadata files so we can unlock now...
	 */
	VOP_UNLOCK(vcb->allocationsRefNum, 0, p);
	VOP_UNLOCK(vcb->catalogRefNum, 0, p);
	VOP_UNLOCK(vcb->extentsRefNum, 0, p);

	if ( !(vcb->vcbAtrb & kHFSVolumeHardwareLockMask) )		//	if the disk is not write protected
	{
		MarkVCBDirty( vcb );								//	mark VCB dirty so it will be written
	}
	
	DBG_VFS(("hfs_MountHFSPlusVolume: returning (%d)\n", retval));

	return (0);


ErrorExit:
	/*
	 * A fatal error occured 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);

	return (retval);
}


/*
 * ReleaseMetaFileVNode
 *
 * vp	L - -
 */
static void ReleaseMetaFileVNode(struct vnode *vp)
{
	if (vp)
	{
		FCB *fcb = VTOFCB(vp);

		if (fcb->fcbBTCBPtr != NULL)
			(void) BTClosePath(fcb);	/* ignore errors since there is only one path open */

		/* 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!");
	}

	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) */
    
    if (keyCompareProc != NULL) {
		result = BTOpenPath(fcb,
							(KeyCompareProcPtr) keyCompareProc,
							GetBTreeBlock,
							ReleaseBTreeBlock,
							ExtendBTreeFile,
							SetBTreeBlockSize);
		result = MacToVFSError(result);
	}

    return (result);
}


/*************************************************************
*
* Unmounts a hfs volume.
*	At this point vflush() has been called (to dump all non-metadata files)
*
*************************************************************/

short hfsUnmount( register struct hfsmount *hfsmp, struct proc *p)
{
    ExtendedVCB	*vcb = HFSTOVCB(hfsmp);
    int			retval = E_NONE;

	(void) DisposeMRUCache(vcb->hintCachePtr);
	InvalidateCatalogCache( vcb );
	// XXX PPD: Should dispose of any allocated volume cache here: call DisposeVolumeCacheBlocks( vcb )?

	(void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
	(void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);

	if (vcb->vcbSigWord == kHFSPlusSigWord)
		ReleaseMetaFileVNode(vcb->allocationsRefNum);

	ReleaseMetaFileVNode(vcb->catalogRefNum);
	ReleaseMetaFileVNode(vcb->extentsRefNum);

	return (retval);
}


/*
 * 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
 */

short hfsLookup (ExtendedVCB *vcb, UInt32 parentDirID, char *name, short len, hfsCatalogInfo *catInfo)
{
	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->spec, &catInfo->nodeData, 	&catInfo->hint);

#if HFS_HARDLINKS
	if (result)
		goto exit;

	fip = (struct FInfo *) &catInfo->nodeData.cnd_finderInfo;

	/*
	 * if we encounter a link node (hardlink) then auto resolve it...
	 */
	if ((catInfo->nodeData.cnd_type == kCatalogFileNode)	&&
	    (fip->fdType == kHardLinkFileType)			&&
	    (fip->fdCreator == kHardLinkCreator)		&&
	    (fip->fdFlags & kIsAlias)
	   ) {
		FSSpec nodeSpec;
		UInt32 dataNodeID;
		
		result = readlinknode(vcb, catInfo, &dataNodeID);
		if (result)	
			goto exit;
		/*
		 * Get nodeData from the data node file. 
		 * Use a local spec so that catInfo->spec is preserved
		 */
		result = GetCatalogNode(vcb, dataNodeID, NULL, 0, 0, &nodeSpec, &catInfo->nodeData, &catInfo->hint);

		/* make sure there's at lease 1 reference */
		if (result == 0) {
			if (catInfo->nodeData.cnd_linkCount == 0)
				catInfo->nodeData.cnd_linkCount = 1;
			
			/* Node should be in private metadata dir */
			DBG_ASSERT(nodeSpec.parID == VCBTOHFS(vcb)->hfs_private_metadata_dir);
		}
	}

exit:
#endif

	if (result)
		DBG_ERR(("on Lookup, GetCatalogNode returned: %d: dirid: %ld name: %s\n", result, parentDirID, name));

	return MacToVFSError(result);
}



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? */

	/*
	 * DeleteFile will delete the catalog node and then
	 * free up any disk space used by the file.
	 */
	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);
}


short hfsMoveRename (ExtendedVCB *vcb, UInt32 oldDirID, char *oldName, UInt32 newDirID, char *newName, UInt32 *hint)
{
    OSErr result = noErr;

    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);
}

/* XXX SER pass back the hint so other people can use it */


short hfsCreate(ExtendedVCB *vcb, UInt32 dirID, char *name, int	mode)
{
    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));
}


/********************************************************************************/
/*																				*/
/*	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)
{
	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);
}



/********************************************************************************/
/*																				*/
/*	hfs_vget_fork - Returns a vnode derived from a sibling						*/
/*		vp is locked															*/
/*																				*/
/********************************************************************************/

int hfs_vget_sibling(struct vnode *vp, u_int16_t forkType, struct vnode **vpp)
{
    struct vnode 	* target_vp = NULL;
    int				retval = E_NONE;


    DBG_ASSERT(vp != NULL);
    DBG_ASSERT(VTOH(vp) != NULL);
    DBG_ASSERT(VTOH(vp)->h_meta != NULL);
    DBG_ASSERT(forkType==kDataFork || forkType==kRsrcFork);

    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;

	    /* lock catalog b-tree */
	    retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, p);
	    if (retval)
	           goto Err_Exit;
		
		catInfo.hint = H_HINT(VTOH(vp));
        retval = hfsLookup (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 Err_Exit;
	
	     retval = hfs_vcreate( VTOVCB(vp), &catInfo, forkType, &target_vp);
    };

Err_Exit:

	if (!retval) {
		DBG_ASSERT(target_vp!=NULL);
	} else {
		DBG_ASSERT(target_vp==NULL);
	}
	
	*vpp = target_vp;
    return (retval);
}


/************************************************************************/
/*	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)
{
	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_flag & MNT_UNMOUNT) {
		return (EPERM);
	}
	DBG_UTILS(("\thfs_vcreate: On '%s' with forktype of %d, nodeType of 0x%08lX\n", catInfo->spec.name, forkType, (unsigned long)catInfo->nodeData.cnd_type));
	
	/* Must malloc() here, since getnewvnode() can sleep */
	MALLOC(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);


	/* 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 (hp, M_HFSNODE);
		*vpp = NULL;
		return (retval);
	};

	/*
	 * 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;
	
	/*
	 * 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);

	/*
	 * If needed allocate and init the object meta data:
	 */
	if (fm == NULL) {
		/* Allocate it....remember we can do a context switch here */
		MALLOC(fm, struct hfsfilemeta *, sizeof(struct hfsfilemeta), M_HFSFMETA, M_WAITOK);
		bzero(fm, sizeof(struct hfsfilemeta));

		/* Fill it in */
		/*
		 * 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
		 */

		/* 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);
		};

		simple_lock_init(&fm->h_metalock);
		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++;


	/*
	 * Init the File Control Block.
	 */
	CopyCatalogToFCB(catInfo, vp);
	if (forkType != kDirectory)
		UpdateBlockMappingTable(hp);

	/*
	 * 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);

    /*
	 * 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);
	}

    /*
     * Finish inode initialization now that aliasing has been resolved.
     */
    hp->h_meta->h_devvp = hfsmp->hfs_devvp;
    VREF(hp->h_meta->h_devvp);
    
    hp->h_valid = HFS_VNODE_MAGIC;
	hp->h_nodeflags &= ~IN_ALLOCATING;				/* vnode is completely initialized */

	/* Wake up anybody waiting for us to finish..see hfs_vhash.c */
	wakeup((caddr_t)hp);

#if HFS_DIAGNOSTIC

	/* 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);
	}
#endif // HFS_DIAGNOSTIC


	*vpp = vp;
	return 0;

}

void CopyCatalogToObjectMeta(struct hfsCatalogInfo *catalogInfo, struct vnode *vp, struct hfsfilemeta *fm)
{
	ExtendedVCB				*vcb = VTOVCB(vp);
	Boolean					isHFSPlus, isDirectory;
	ushort					finderFlags;

	DBG_ASSERT (fm != NULL);
	DBG_UTILS(("\tCopying to file's meta data: name:%s, nodeid:%ld\n", catalogInfo->spec.name, catalogInfo->nodeData.cnd_nodeID));

	isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
	isDirectory = (catalogInfo->nodeData.cnd_type == kCatalogFolderNode);
	finderFlags = ((struct FInfo *)(&catalogInfo->nodeData.cnd_finderInfo))->fdFlags;

	/* Copy over the dirid, and hint */
	fm->h_nodeID = catalogInfo->nodeData.cnd_nodeID;
	fm->h_dirID = catalogInfo->spec.parID;
	fm->h_hint = catalogInfo->hint;

	/* Copy over the name */
	hfs_set_metaname(catalogInfo->spec.name, fm);
	DBG_ASSERT (fm->h_namelen == strlen(catalogInfo->spec.name));
	DBG_ASSERT (strcmp(fm->h_namePtr, catalogInfo->spec.name) == 0);


	/* 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_permissions & IFMT)) {
		fm->h_uid = catalogInfo->nodeData.cnd_ownerID;
		fm->h_gid = catalogInfo->nodeData.cnd_groupID;
		/* The 32-bit permissions field is unpacked to yield the two significant bytes of flags and the
		   mode as follows:
									 +------------------------------------+
						permissions: |	  A	   |	B	|	   mode		  |
									 +------------------------------------+
														|
														V
									 +------------------------------------+
			  fm->h_pflags: |XXXXXXXX|	   A	|XXXXXXXX|	  B	  |
									 +------------------------------------+

		 */
		fm->h_pflags = (((catalogInfo->nodeData.cnd_permissions & 0xFF000000) >> 8) |	/* A */
		((catalogInfo->nodeData.cnd_permissions & 0x00FF0000) >> 16));	/* B */
		fm->h_mode = (mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF);
		fm->h_rdev = catalogInfo->nodeData.cnd_specialDevice;

#if HFS_HARDLINKS
		if (catalogInfo->nodeData.cnd_type == kCatalogFileNode  &&
		    catalogInfo->nodeData.cnd_linkCount > 0) {
			fm->h_nlink = catalogInfo->nodeData.cnd_linkCount;
			fm->h_metaflags |= IN_DATANODE;
		}
#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;
		/* 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;
		}
		
		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_valence = 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 = vcb->blockSize *
			(catalogInfo->nodeData.cnd_rsrcfork.totalBlocks +
			 catalogInfo->nodeData.cnd_datafork.totalBlocks);
	}
}


void CopyCatalogToFCB(struct hfsCatalogInfo *catalogInfo, struct vnode *vp)
{
	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;
		else
			extents = catalogInfo->nodeData.cnd_datafork.extents;

		/* Copy the extents to their correct location: */
		bcopy (extents, fcb->fcbExtents, sizeof(HFSPlusExtentRecord));

		if (isResource) {	
			fcb->fcbEOF = catalogInfo->nodeData.cnd_rsrcfork.logicalSize;
			fcb->fcbPLen = catalogInfo->nodeData.cnd_rsrcfork.totalBlocks * vcb->blockSize;
			fcb->fcbFlags |= fcbResourceMask;
		}  else {
			fcb->fcbEOF = catalogInfo->nodeData.cnd_datafork.logicalSize;
			fcb->fcbPLen = catalogInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize;
		};
	};


}

int hasOverflowExtents(struct hfsnode *hp)
{
	ExtendedVCB		*vcb = HTOVCB(hp);
	FCB				*fcb = HTOFCB(hp);
	u_long			blocks;

	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;	
	  }

	return ((fcb->fcbPLen / vcb->blockSize) > blocks);
}


int hfs_metafilelocking(struct hfsmount *hfsmp, u_long fileID, u_int flags, struct proc *p)
{
	ExtendedVCB		*vcb;
	struct vnode	*vp = NULL;
	int				numOfLockedBuffs;
	int	retval = 0;

	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;

		case kHFSCatalogFileID:
			vp = vcb->catalogRefNum;
			break;

		case kHFSAllocationFileID:
			/* bitmap is covered by Extents B-tree locking */
			/* FALL THROUGH */
		default:
			panic("hfs_lockmetafile: invalid fileID");
	}

	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);
	};

	return retval;
}


void CopyVNodeToCatalogNode (struct vnode *vp, struct CatalogNodeData *nodeData)
{
    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 */
    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) */
		nodeData->cnd_attributeModDate = to_hfs_time(hp->h_meta->h_ctime);
		/* date and time of last access (MacOS X only) */
		nodeData->cnd_accessDate = to_hfs_time(hp->h_meta->h_atime);
		if (! (hp->h_meta->h_metaflags & IN_UNSETACCESS)) {
			/* This is a tricky stuff-job: the pflags longword has two bytes of significance and they're
			   combined with the mode field to yield a 32-bit permissions field as follows:

										 +------------------------------------+
				   hp->h_meta->h_pflags: |XXXXXXXX|	   A	|XXXXXXXX|	  B	  |
										 +------------------------------------+

															|
															V

										 +------------------------------------+
							permissions: |	  A	   |	B	|	   mode		  |
										 +------------------------------------+
			 */
			nodeData->cnd_permissions = (((hp->h_meta->h_pflags << 8) & 0xFF000000) |	/* A */
												 ((hp->h_meta->h_pflags << 16) & 0x00FF0000) |	/* B */
				(hp->h_meta->h_mode & 0x0000FFFF));
			nodeData->cnd_ownerID = hp->h_meta->h_uid;
			nodeData->cnd_groupID = hp->h_meta->h_gid;
			nodeData->cnd_specialDevice = hp->h_meta->h_rdev;
			};
	};

	/* 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 == VLNK) {
	        ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdType = kSymLinkFileType;
	        ((struct FInfo *)(&nodeData->cnd_finderInfo))->fdCreator = kSymLinkCreator;
	
			/* Set this up as an alias */
			#if SUPPORTS_MAC_ALIASES
				((struct FInfo *)(&nodeData->cnd_finderInfo))->fdFlags |= kIsAlias;
			#endif
		}
#if HFS_HARDLINKS
		nodeData->cnd_linkCount = hp->h_meta->h_nlink;
#endif
	}
 }



void MapFileOffset(struct hfsnode *hp,
                   off_t filePosition,
                   daddr_t *logBlockNumber,
                   long *blockSize,
                   long *blockOffset) {
    off_t extentOffset = filePosition;
    daddr_t precedingBlockCount;
    unsigned long logicalBlockSize = 0;
    unsigned long spaceRemaining;

    DBG_IO(("MapFileOffset: hp = 0x%08lX, vp = 0x%08lX ('%s'):\n", (u_long)hp, (u_long)HTOV(hp), H_NAME(hp)));
    // DBG_IO(("\tfilePosition 0x%08lX", (u_long)filePosition));

    if (filePosition < hp->h_optimizedblocksizelimit) {
        int extent;

        precedingBlockCount = 0;
        for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) {
            if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) &&
                (extentOffset < hp->h_logicalblocktable[extent].extentLength)) {
                logicalBlockSize = MAXLOGBLOCKSIZE;
                spaceRemaining = hp->h_logicalblocktable[extent].extentLength - ((extentOffset / logicalBlockSize) * logicalBlockSize);
                *blockSize = MIN(logicalBlockSize, spaceRemaining);
                // DBG_IO(("\tUsing entry #%d: preceding blocks = 0x%X, position = 0x%8lX:\n",
                //         extent, precedingBlockCount, (u_long)extentOffset));
                break;
            };
            precedingBlockCount += hp->h_logicalblocktable[extent].logicalBlockCount;
            extentOffset -= hp->h_logicalblocktable[extent].extentLength;
        };
        DBG_ASSERT(logicalBlockSize > 0);
    } else {
        // DBG_IO(("\tUsing h_uniformblocksizestart = 0x%X and h_optimizedblocksizelimit = 0x%lX:\n",
        //         hp->h_uniformblocksizestart, (u_long)hp->h_optimizedblocksizelimit));
        precedingBlockCount = hp->h_uniformblocksizestart;
        extentOffset -= hp->h_optimizedblocksizelimit;
    	logicalBlockSize = GetLogicalBlockSize(HTOV(hp));
        *blockSize = logicalBlockSize;
    };

    *logBlockNumber =  precedingBlockCount + (extentOffset / logicalBlockSize);
    *blockOffset = extentOffset % logicalBlockSize;
    DBG_IO(("\tfilePosition 0x%08lX -> logBlockNo = 0x%X, blockSize = 0x%lX, blockOffset = 0x%lX.\n",
                 (u_long)filePosition,
                 *logBlockNumber,
                 *blockSize,
                 *blockOffset));
}


long LogicalBlockSize(struct hfsnode *hp, daddr_t logicalBlockNumber) {

    if (logicalBlockNumber < hp->h_uniformblocksizestart) {
        off_t fragmentOffset = 0;
        int extent;

        for (extent = 0; extent < LOGBLOCKMAPENTRIES; ++extent) {
            if ((hp->h_logicalblocktable[extent].logicalBlockCount > 0) &&
                (logicalBlockNumber < hp->h_logicalblocktable[extent].logicalBlockCount)) {
                // *filePosition = fragmentOffset + (logicalBlockNumber * MAXLOGBLOCKSIZE);
                return MIN(MAXLOGBLOCKSIZE, hp->h_logicalblocktable[extent].extentLength - (logicalBlockNumber * MAXLOGBLOCKSIZE));
            };
            logicalBlockNumber -= hp->h_logicalblocktable[extent].logicalBlockCount;
            fragmentOffset += hp->h_logicalblocktable[extent].extentLength;
        };
    } else {
        // *filePosition = hp->h_optimizedblocksizelimit + ((logicalBlockNumber - hp->h_uniformblocksizestart) * HTOHFS(hp)->logBlockSize);
        return (GetLogicalBlockSize(HTOV(hp)));
    };

    DBG_ASSERT(0 /* cannot be reached */);
    return 0;
};


void UpdateBlockMappingTableEntry(struct hfsnode *hp, int index, daddr_t firstFragmentBlockNumber, off_t newFragmentLength) {

    /* Compute the number of logical blocks, rounding up to include any small trailing fragments: */
    hp->h_logicalblocktable[index].logicalBlockCount = (newFragmentLength + MAXLOGBLOCKSIZE - 1) / MAXLOGBLOCKSIZE;

    DBG_IO(("\tblocktable[%d]: length = 0x%lX, # blocks = 0x%lX.\n",
            index,
            (u_long)newFragmentLength,
            (u_long)hp->h_logicalblocktable[index].logicalBlockCount));

#if 0
    currentFragmentLength = hp->h_logicalblocktable[index].extentLength;
    if ((newFragmentLength > currentFragmentLength) && (currentFragmentLength > 0)) {
        daddr_t					currentLastBlockNumber;
        long					currentLastBlockSize;
        long					newLastBlockSize;
        struct buf				*bp;
        int						retval;

        currentLastBlockNumber = firstFragmentBlockNumber + (currentFragmentLength / MAXLOGBLOCKSIZE);
        currentLastBlockSize = currentFragmentLength % MAXLOGBLOCKSIZE;
        if (currentLastBlockSize > 0) {
            newLastBlockSize = MIN(MAXLOGBLOCKSIZE,
                                   newFragmentLength -
                                   ((currentLastBlockNumber - firstFragmentBlockNumber) * MAXLOGBLOCKSIZE));
            if (newLastBlockSize != currentLastBlockSize) {
                DBG_IO(("\t\t(Adjusting block 0x%lX from 0x%lX to 0x%lX...)\n",
                        (unsigned long)currentLastBlockNumber,
                        currentLastBlockSize,
                        newLastBlockSize));
                retval = bread(HTOV(hp), currentLastBlockNumber, currentLastBlockSize, NOCRED, &bp);
                if (retval == 0) {
                    bexpand(bp, newLastBlockSize, NULL, RELEASE_BUFFER);
                } else {
                    DBG_IO(("\t\terror (%d.) acquiring block 0x%lX; bp = 0x%08lX\n",
                            retval,
                            (unsigned long)currentLastBlockNumber,
                            (unsigned long)bp));
                    if (bp) brelse(bp);
                };
            };
        };
    };
#endif
    hp->h_logicalblocktable[index].extentLength = newFragmentLength;
}


void UpdateBlockMappingTable(struct hfsnode *hp)
{
    ExtendedVCB				*vcb = HTOVCB(hp);
    FCB						*fcb = HTOFCB(hp);
    Boolean					isHFSPlus;
    off_t					rangeMappedSoFar;
    u_long					logicalBlocksMappedSoFar;
    int						i;
    off_t					newFragmentLength;

    isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);
    DBG_IO(("UpdateBlockMappingTable: hp = 0x%08lX, vp = 0x%08lX ('%s'):\n", (u_long)hp, (u_long)HTOV(hp), H_NAME(hp)));
    if (H_FORKTYPE(hp) != kDirectory) {
        rangeMappedSoFar = 0;
        logicalBlocksMappedSoFar = 0;

        if ( ! isHFSPlus) {
            DBG_ASSERT(kHFSExtentDensity <= LOGBLOCKMAPENTRIES);
            for (i = 0; i < kHFSExtentDensity; ++i) {
                newFragmentLength = fcb->fcbExtents[i].blockCount * vcb->blockSize;
                UpdateBlockMappingTableEntry(hp, i, logicalBlocksMappedSoFar, newFragmentLength);
                rangeMappedSoFar += newFragmentLength;
                logicalBlocksMappedSoFar += hp->h_logicalblocktable[i].logicalBlockCount;
            }
            /* Zero out any remaining entries: */
            for (i = kHFSExtentDensity; i < LOGBLOCKMAPENTRIES; ++i) {
                hp->h_logicalblocktable[i].extentLength = 0;
            };
#if HFS_DIAGNOSTIC
            if (rangeMappedSoFar < fcb->fcbPLen) DBG_ASSERT(hp->h_logicalblocktable[kHFSExtentDensity-1].extentLength > 0);
#endif
        } else {
            for (i = 0; i < kHFSPlusExtentDensity; ++i) {
                newFragmentLength = fcb->fcbExtents[i].blockCount * vcb->blockSize;
                UpdateBlockMappingTableEntry(hp, i, logicalBlocksMappedSoFar, newFragmentLength);
                rangeMappedSoFar += newFragmentLength;
                logicalBlocksMappedSoFar += hp->h_logicalblocktable[i].logicalBlockCount;
            }
            /* No need to zero out the remaining entries [there are none]: */
            DBG_ASSERT(LOGBLOCKMAPENTRIES <= kHFSPlusExtentDensity);
#if HFS_DIAGNOSTIC
            if (rangeMappedSoFar < fcb->fcbPLen) DBG_ASSERT(hp->h_logicalblocktable[kHFSPlusExtentDensity-1].extentLength > 0);
#endif
        };
        
        hp->h_optimizedblocksizelimit = rangeMappedSoFar;
        hp->h_uniformblocksizestart = logicalBlocksMappedSoFar;
        DBG_IO(("\th_optimizedblocksizelimit = 0x%lX, h_uniformblocksizestart = 0x%lX.\n",
                (u_long)hp->h_optimizedblocksizelimit, (u_long)hp->h_uniformblocksizestart));

#if BYPASSBLOCKINGOPTIMIZATION
        hp->h_optimizedblocksizelimit = 0;
        hp->h_uniformblocksizestart = 0;
#endif
	};
}


/*********************************************************************

	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)
{
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);
	}
	else
		tname = fm->h_fileName;

	simple_lock(&fm->h_metalock);
	
	/* 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(&fm->h_metalock);

	/* Lastly, free the old, if set */
	if (fname != NULL)
		FREE(fname, M_TEMP);

}

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_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_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_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 = 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 = attrlist->dirattr) != 0) {
		if (a & ATTR_DIR_LINKCOUNT) size += sizeof(u_long);
		if (a & ATTR_DIR_ENTRYCOUNT) 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;
}



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) {
            attrlength = root_hp->h_meta->h_namelen+1;
            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
            (void) strncpy((unsigned char *)varbufptr, H_NAME(root_hp), 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)++ = hfsmp->hfs_raw_dev;
		if (a & ATTR_CMN_FSID) {
			*((fsid_t *)attrbufptr) = mp->mnt_stat.f_fsid;
			if (vcb->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
			++((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 local Mac OS time */
		if (a & ATTR_CMN_CRTIME) {
			/*
			 * HFS Plus stores the volume create date in *local*
			 * time in the volume header. So don't use the create
			 * date from the vcb. Use the root's crtime instead.
			 */
			if (vcb->vcbSigWord == kHFSPlusSigWord) {
				((struct timespec *)attrbufptr)->tv_sec = root_hp->h_meta->h_crtime;
			} else {
				((struct timespec *)attrbufptr)->tv_sec = to_bsd_time(vcb->vcbCrDate);
			}
			((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) *((uid_t *)attrbufptr)++ = root_hp->h_meta->h_uid;
		if (a & ATTR_CMN_GRPID) *((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;
	};
	
	*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;
		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 )
{
	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->spec.name) + 1;
            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
            (void) strncpy((unsigned char *)varbufptr, (unsigned char *) &(catalogInfo->spec.name), attrlength);
            //	((char *) varbufptr)[attrlength-1] = 0;									//	Now it's a C string

            /* 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;
			if (VTOVCB(root_vp)->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
			++((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_permissions & IFMT)) {
					*((fsobj_type_t *)attrbufptr)++	=
					    IFTOVT((mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF));
				} 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->spec.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) {
			*((uid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_ownerID;
		}
		if (a & ATTR_CMN_GRPID) {
			*((gid_t *)attrbufptr)++ = catalogInfo->nodeData.cnd_groupID;
		}
		if (a & ATTR_CMN_ACCESSMASK) {
			*((u_long *)attrbufptr)++ =
			    (u_long)(mode_t)(catalogInfo->nodeData.cnd_permissions & 0x0000FFFF);
		}
		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_permissions & 0xFFFF0000) >> 8) & 0x00FF0000) |
			(((catalogInfo->nodeData.cnd_permissions & 0xFFFF0000) >> 24) & 0x000000FF));
	};
	
	*attrbufptrptr	= attrbufptr;
	*varbufptrptr	= varbufptr;
}


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) {
            attrlength = hp->h_meta->h_namelen + 1;
            ((struct attrreference *)attrbufptr)->attr_dataoffset = (char *)varbufptr - (char *)attrbufptr;
            ((struct attrreference *)attrbufptr)->attr_length = attrlength;
            (void) strncpy((unsigned char *)varbufptr, H_NAME(hp), 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(vp)->mnt_stat.f_fsid;
			if (VTOVCB(vp)->vcbSigWord == kHFSSigWord) ((fsid_t *)attrbufptr)->val[0] |= 0x40000000;
			++((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) *((uid_t *)attrbufptr)++ = hp->h_meta->h_uid;
		if (a & ATTR_CMN_GRPID) *((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;
	};
	
	*attrbufptrptr = attrbufptr;
	*varbufptrptr = varbufptr;
}


//	Pack the directory attributes given hfsCatalogInfo
void PackCatalogInfoDirAttributeBlock( 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 ( (catInfo->nodeData.cnd_type == kCatalogFolderNode) && (a != 0) ) {
		valence = catInfo->nodeData.cnd_valence;
		if ((catInfo->spec.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;
	};
	
	*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->spec.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;
	};
	
	*attrbufptrptr = attrbufptr;
}



//	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 )
{
	void			*attrbufptr;
	void			*varbufptr;
	attrgroup_t		a;
	u_long			attrlength;
	ExtendedVCB		*vcb			= VTOVCB(root_vp);
	
	attrbufptr	= *attrbufptrptr;
	varbufptr	= *varbufptrptr;
	
	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;

			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)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize) +
			    (off_t)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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 *)attrbufptr)++ =
			    (u_long)catInfo->nodeData.cnd_specialDevice;
		}
		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)(catInfo->nodeData.cnd_datafork.totalBlocks * 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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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;
}


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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * vcb->blockSize);
				break;
			case kRsrcFork:
				*((off_t *)attrbufptr)++ =
				    (off_t)fcb->fcbPLen +
				    (off_t)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize);
				break;
			default:
				*((off_t *)attrbufptr)++ =
				    (off_t)(catInfo->nodeData.cnd_datafork.totalBlocks * vcb->blockSize) +
				    (off_t)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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 *)attrbufptr)++ = (u_long)catInfo->nodeData.cnd_specialDevice;
		}
		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)
#if	MACH_NBC
                if ((vp->v_type == VREG) && vp->v_vm_info && vp->v_vm_info->mapped &&
                    (!vp->v_vm_info->filesize)) {
                    *((off_t *)attrbufptr)++ = vp->v_vm_info->vnode_size;
                }
                else
#endif /* MACH_NBC */
                   *((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 (a & ATTR_FILE_DATAALLOCSIZE) {
				*((off_t *)attrbufptr)++ =
				    (off_t)(catInfo->nodeData.cnd_datafork.totalBlocks * 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 (H_FORKTYPE(hp) == kRsrcFork) {
			if (a & ATTR_FILE_RSRCLENGTH) 
#if	MACH_NBC
                if ((vp->v_type == VREG) && vp->v_vm_info && vp->v_vm_info->mapped &&
                    (!vp->v_vm_info->filesize)) {
                    *((off_t *)attrbufptr)++ = vp->v_vm_info->vnode_size;
                }
                else
#endif /* MACH_NBC */
                   *((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);
			};
		} 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)(catInfo->nodeData.cnd_rsrcfork.totalBlocks * 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;
	}
}



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;
	text_encoding_t nameEncoding;
	
    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) {
		/* XXX PPD No use for this info right now... */
		nameEncoding = *(((text_encoding_t *)attrbufptr)++);
#if HFS_DIAGNOSTIC
		a &= ~ATTR_CMN_SCRIPT;
#endif
	};
	if (a & ATTR_CMN_CRTIME) {
		/*
		 * HFS Plus stores the volume create date in *local*
		 * time in the volume header. So don't set the create
		 * date in the vcb. Set the root's crtime instead.
		 */
		if (vcb->vcbSigWord == kHFSPlusSigWord) {
			VTOH(root_vp)->h_meta->h_crtime =
					(UInt32)((struct timespec *)attrbufptr)->tv_sec;
		} else {
			vcb->vcbCrDate = 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);
}


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) {
		/* XXX PPD No use for this info right now... */
		++((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);
#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);
#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);
#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

	*attrbufptrptr = attrbufptr;
//	*varbufptrptr = varbufptr;
}



#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;
}
#endif



#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 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 */
	
	*attrbufptrptr = attrbufptr;
	*varbufptrptr = varbufptr;
}
#endif



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 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;
            };
        };
    };

    /* 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;
}


/*
 * To make the HFS 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;
	int retval;

	dirID = 0;
	strncpy(namep, HFSPLUSMETADATAFOLDER, sizeof(namep));

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_SHARED, current_proc());	
	if (retval)
		   goto Err_Exit;

	catInfo.hint = kNoHint;
	if (hfsLookup(vcb, kRootDirID, namep, -1, &catInfo) == 0)
		dirID = catInfo.nodeData.cnd_nodeID;
	else if (VCBTOHFS(vcb)->hfs_fs_ronly == 0) {
		if (CreateCatalogNode(vcb, kRootDirID, namep, kCatalogFolderNode, &dirID, &catInfo.hint) == 0) {
			catInfo.hint = kNoHint;
			if (hfsLookup(vcb, kRootDirID, namep, -1, &catInfo) == 0) {
							
				/* directory with no permissions owned by root */
				catInfo.nodeData.cnd_permissions = IFDIR;

				/* hidden and off the desktop view */
				((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.v = 22460;
				((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frLocation.h = 22460;		
				((struct DInfo *)(&catInfo.nodeData.cnd_finderInfo))->frFlags |= (kIsInvisible + kNameLocked);		
	
				(void) UpdateCatalogNode(vcb, kRootDirID, namep, catInfo.hint, &catInfo.nodeData);
			}
		}
	}

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VCBTOHFS(vcb), kHFSCatalogFileID, LK_RELEASE, current_proc());
	

Err_Exit:	

	return dirID;
}

/*
 * 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;
	
	DBG_ASSERT(vp != NULL);

	if ((vp->v_flag & VSYSTEM) && (VTOH(vp)->fcbBTCBPtr!=NULL)) {
		BTreeInfoRec			bTreeInfo;
		int retval;

		/*
		 * 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??
		 */
		
		(void) BTGetInformation	(VTOFCB(vp), kBTreeInfoVersion, &bTreeInfo);
				
		logBlockSize = bTreeInfo.nodeSize;
		}
	else
		logBlockSize = VTOHFS(vp)->hfs_logBlockSize;
		
		
	DBG_ASSERT(logBlockSize > 0);
	
	return logBlockSize;	
}

/*
 * Map HFS Common errors (negative) to BSD error codes (positive).
 * Positive errors (ie BSD errors) are passed through unchanged.
 */
short MacToVFSError(OSErr err)
{
    if (err >= 0) {
        if (err > 0) {
            DBG_ERR(("MacToVFSError: passing error #%d unchanged...\n", err));
        };
        return err;
    };

    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 */
	}
}


/*
 * All of our debugging functions
 */

#if HFS_DIAGNOSTIC

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"));
      };
}

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;
      }

    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"));
      }
}

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
)
{
    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"));
        }
    }

    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 (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"));

        }
    }

}
#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);
    if (bp->b_lblkno < hp->h_uniformblocksizestart) {
        DBG_ASSERT((bp->b_bcount == MAXLOGBLOCKSIZE) ||
                   (bp->b_bcount == LogicalBlockSize(hp, bp->b_lblkno)) ||
                   ((bp->b_bcount % 512 == 0) &&
                    (bp->b_validend > 0) &&
                    (bp->b_dirtyend >= 0) &&							/* Could be partial block due to file growth */
                    (bp->b_bcount < LogicalBlockSize(hp, bp->b_lblkno))));
    } else {
        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);
    };

    if ((bp->b_lblkno == 0x21) || (bp->b_lblkno == 0x22)) DBG_ASSERT((hp->h_uniformblocksizestart > 0x21) || (bp->b_bcount != MAXLOGBLOCKSIZE));
}


void debug_check_blocksizes(struct vnode *vp) {
    struct hfsnode *hp = VTOH(vp);
    struct buf *bp;

    if (vp->v_flag & VSYSTEM) return;

    for (bp = vp->v_cleanblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
        debug_check_buffersizes(vp, hp, bp);
    };

    for (bp = vp->v_dirtyblkhd.lh_first; bp != NULL; bp = bp->b_vnbufs.le_next) {
        debug_check_buffersizes(vp, hp, bp);
    };
}

extern void hfs_vhash_dbg(struct hfsnode *hp);

/* Checks the valicity of a hfs vnode */
void debug_check_vnode(struct vnode *vp, int stage) {
    struct hfsnode *hp;
    u_long size;
	int i;

	/* 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;

        if (hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, current_proc()))
            return;

        catInfo.hint = 0;
        if (hfsLookup(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"));

        (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, current_proc());

        if (H_FILEID(hp) != catInfo.nodeData.cnd_nodeID)
            DEBUG_BREAK_MSG(("hfsnode catalog node id mismatch"));
        if (H_DIRID(hp) != catInfo.spec.parID)
            DEBUG_BREAK_MSG(("hfsnode catalog dir id mismatch"));
        if (strcmp(hp->h_meta->h_namePtr, catInfo.spec.name) != 0)
            DEBUG_BREAK_MSG(("hfsnode catalog name mismatch"));
        /* Check dates too??? */



        }

    for(i = 0, size = 0; i < kHFSPlusExtentDensity; i++)
      {	
        size += hp->fcbExtents[i].blockCount;	
      }
    if (size*VTOVCB(vp)->blockSize < hp->fcbPLen)
        DEBUG_BREAK_MSG(("fcbPLen too large"));
    if (hp->fcbEOF > hp->fcbPLen)
        DEBUG_BREAK_MSG(("fcbPLen is smaller than fcbEOF"));
    if (hp->fcbExtents[kHFSPlusExtentDensity-1].blockCount == 0) {
        if (size*VTOVCB(vp)->blockSize != hp->fcbPLen)
            DEBUG_BREAK_MSG(("fcbPLen does not match extents"));
	} else {
        if ( hp->fcbPLen < size*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"));
          }
    }
    
    /* BTree stuff */
    if (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"));
    }

}

#endif /* HFS_DIAGNOSTIC */