Source to bsd/hfs/hfs_vnodeops.c


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (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.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1982, 1986, 1989, 1993, 1995
 *	The Regents of the University of California.  All rights reserved.
 * (c) UNIX System Laboratories, Inc.
 * All or some portions of this file are derived from material licensed
 * to the University of California by American Telephone and Telegraph
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
 * the permission of UNIX System Laboratories, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)hfs_vnodeops.c	3.0
 *	derived from @(#)ufs_vnops.c	8.27 (Berkeley) 5/27/95
 *
 *	(c) 1997-1999	Apple Computer, Inc.  All Rights Reserved
 *	(c) 1990, 1992 NeXT Computer, Inc.  All Rights Reserved
 *	
 *
 *	hfs_vnodeops.c -- vnode layer for loadable Macintosh file system
 *
 *	MODIFICATION HISTORY:
 *       1-Mar-1999	Scott Roberts	h_meta is now released when the complex vnode is relesed
 *      26-Feb-1999	Pat Dirks (copied by Chw) Fixed hfs_lookup to check for
 *                                error return on vget.
 *      25-Feb-1999     Pat Dirks       Fixed hfs_remove to use a local copy of the h_sibling pointer around vnode_uncache.
 *	 3-Feb-1999	Pat Dirks		Changed to stop updating wrapper volume name in MDB since wrapper volume's
 *								catalog isn't updated and this inconsistency trips Disk First Aid's checks.
 *	22-Jan-1999	Pat Dirks		Changed hfs_rename, hfs_remove, and hfs_rmdir to call cache_purge.
 *	22-Jan-1999	Don Brady		After calling hfsMoveRename call hfsLookup to get new name.
 *	12-Jan-1999	Don Brady		Fixed the size of ATTR_CMN_NAME buffer to NAME_MAX + 1.
 *	 8-Jan-1999	Pat Dirks		Added hfs_writepermission and change hfs_setattrlist to use it instead of
 *								including an incorrect derivative of hfs_access in-line.
 *	15-Dec-1998 Pat Dirks		Changed setattrlist to do permission checking as appropriate (Radar #2290212).
 *	17-Nov-1998 Scott Roberts	Added support for long volume names in SetAttrList().
 *	6-Nov-1998 Don Brady		Add support for UTF-8 names.
 *	 3-Nov-1998	Umesh Vaishampayan	Changes to deal with "struct timespec"
 *						change in the kernel.	
 *  21-Oct-1998 Scott Roberts	Added support for advisory locking (Radar #2237914).
 *  25-Sep-1998 Don Brady		Changed hfs_exchange to call hfs_chid after updating catalog (radar #2276605).
 *	23-Sep-1998 Don Brady		hfs_setattrlist now calls hfs_chown and hfs_chmod to change values.
 *	15-Sep-1998 Pat Dirks		Cleaned up vnode unlocking on various error exit paths and changed
 *								to use new error stub routines in place of hfs_mknod and hfs_link.
 *  16-Sep-1998	Don Brady		When renaming a volume in hfs_setattrlist, also update hfs+ wrapper name (radar #2272925).
 *   1-Sep-1998	Don Brady		Fix uninitiazed time variable in hfs_makenode (radar #2270372).
 *  31-Aug-1998	Don Brady		Adjust change time for DST in hfs_update (radar #2265075).
 *  12-Aug-1998	Don Brady		Update complex node name in hfs_rename (radar #2262111).
 *   5-Aug-1998	Don Brady		In hfs_setattrlist call MacToVFSError after calling UpdateCatalogNode (radar #2261247).
 *  21-Jul-1998	Don Brady		Fixed broken preflight in hfs_getattrlist.
 *      17-Jul-1998	Clark Warner		Fixed the one left out case of freeing M_NAMEI in hfs_abort
 *	13-Jul-1998	Don Brady		Add uio_resid preflight check to hfs_search (radar #2251855).
 *	30-Jun-1998	Scott Roberts	        Changed hfs_makenode and its callers to free M_NAMEI.
 *	29-Jun-1998	Don Brady		Fix unpacking order in UnpackSearchAttributeBlock (radar #2249248).
 *	13-Jun-1998	Scott Roberts		Integrated changes to hfs_lock (radar #2237243).
 *	 4-Jun-1998	Pat Dirks		Split off hfs_lookup.c and hfs_readwrite.c
 *	 3-Jun-1998	Don Brady		Fix hfs_rename bugs (radar #2229259, #2239823, 2231108 and #2237380).
 *								Removed extra vputs in hfs_rmdir (radar #2240309).
 *	28-May-1998	Don Brady		Fix hfs_truncate to correctly extend files (radar #2237242).
 *	20-May-1998	Don Brady		In hfs_close shrink the peof to the smallest size neccessary (radar #2230094).
 *	 5-May-1998	Don Brady		Fixed typo in hfs_rename (apply H_FILEID macro to VTOH result).
 *	29-Apr-1998	Joe Sokol		Don't do cluster I/O when logical block size is not 4K multiple.
 *	28-Apr-1998	Pat Dirks		Cleaned up unused variable physBlockNo in hfs_write.
 *	28-Apr-1998	Joe Sokol		Touched up support for cluster_read/cluster_write and enabled it.
 *	27-Apr-1998	Don Brady		Remove some DEBUG_BREAK calls in DbgVopTest.
 *	24-Apr-1998	Pat Dirks		Fixed read logic to read-ahead only ONE block, and of only logBlockSize instead of 64K...
 *								Added calls to brelse() on errors from bread[n]().
 *								Changed logic to add overall length field to AttrBlockSize only on attribute return operations.
 *	23-Apr-1998	Don Brady		The hfs_symlink call is only supported on HFS Plus disks.
 *	23-Apr-1998	Deric Horn		Fixed hfs_search bug where matches were skipped when buffer was full.
 *	22-Apr-1998	Scott Roberts		Return on error if catalog mgr returns an error in truncate.
 *	21-Apr-1998	Don Brady		Fix up time/date conversions.
 *	20-Apr-1998	Don Brady		Remove course-grained hfs metadata locking.
 *	17-Apr-1998	Pat Dirks		Officially enabled searchfs in vops table.
 *	17-Apr-1998	Deric Horn		Bug fixes to hfs_search, reenabled searchfs trap for upcoming kernel build.
 *	15-Apr-1998	Don Brady		Add locking for HFS B-trees. Don't lock file meta lock for VSYSTEM files.
 *								Don't call VOP_UPDATE for system files. Roll set_time into hfs_update.
 *	14-Apr-1998	Pat Dirks		Cleaned up fsync to skip complex nodes and not hit sibling nodes.
 *	14-Apr-1998	Deric Horn		Added hfs_search() and related routines for searchfs() support.
 *	14-Apr-1998	Scott Roberts		Fixed paramaters to ExchangeFileIDs()
 *	13-Apr-1998	Pat Dirks		Changed to update H_HINT whenever hfsLookup was called.
 *	 8-Apr-1998	Pat Dirks		Added page-in and page-out passthrough routines to keep MapFS happy.
 *	 6-Apr-1998	Pat Dirks		Changed hfs_write to clean up code and fix bug that caused
 *								zeroes to be interspersed in data.  Added debug printf to hfs_read.
 *	 6-Apr-1998	Scott Roberts		Added complex file support.
 *	02-apr-1998	Don Brady		UpdateCatalogNode now takes parID and name as input.
 *	31-mar-1998	Don Brady		Sync up with final HFSVolumes.h header file.
 *	27-mar-1998	Don Brady		Check result from UFSToHFSStr to make sure hfs/hfs+ names are not greater than 31 characters.
 *	27-mar-1998	chw			minor link fixes.
 *	19-Mar-1998	ser			Added hfs_readdirattr.
 *	17-Mar-1998	ser			Removed CheckUserAccess. Added code to implement ExchangeFileIDs
 *	16-Mar-1998	Pat Dirks		Fixed logic in hfs_read to properly account for space
 *								remaining past selected offset and avoid premature panic.
 *	16-jun-1997	Scott Roberts
 *	   Dec-1991	Kevin Wells at NeXT:
 *			Significantly modified for Macintosh file system.
 *			Added support for NFS exportability.
 *	25-Jun-1990	Doug Mitchell at NeXT:
 *			Created (for DOS file system).
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/mount.h>
#include <mach/machine/vm_types.h>
#include <sys/vnode.h>
#include <sys/malloc.h>
#include <sys/namei.h>
#include <sys/signalvar.h>
#include <sys/attr.h>
#include <miscfs/specfs/specdev.h>
#include <vfs/vfs_support.h>

#include <libkern/libkern.h>
#include <mach/machine/simple_lock.h>
#include <machine/spl.h>
#include <mach/features.h>
#include <kern/mapfs.h>

#include	"hfs.h"
#include	"hfs_lockf.h"
#include	"hfs_dbg.h"
#include	"hfscommon/headers/FileMgrInternal.h"
#include	"hfscommon/headers/CatalogPrivate.h"
#include 	"hfscommon/headers/system/HFSUnicodeWrappers.h"

//	Private description used in hfs_search
struct SearchState {
	long				searchBits;
	BTreeIterator		btreeIterator;
	short				vRefNum;		//	Volume reference of volume being searched
	char				isHFSPlus;		//	True if volume is HFS
	char				pad1[3];		//	long align the structure
};
typedef struct SearchState SearchState;


enum {
	MAXHFSFILESIZE = 0x7FFFFFFF		/* this needs to go in the mount structure */
};

#define OWNERSHIP_ONLY_ATTRS (ATTR_CMN_OWNERID | ATTR_CMN_GRPID | ATTR_CMN_ACCESSMASK | ATTR_CMN_FLAGS)

/* Global vfs data structures for hfs */
int (**hfs_vnodeop_p)();

/* external routines defined in vfs_cache.c */

void hfs_vhashrem(struct hfsnode *hp);

extern void cache_purge (struct vnode *vp);
extern int cache_lookup (struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);
extern void cache_enter (struct vnode *dvp, struct vnode *vpp, struct componentname *cnp);

extern void vnode_pager_setsize( struct vnode *vp, u_long nsize);
extern int vnode_uncache( struct vnode	*vp);

extern groupmember(gid_t gid, struct ucred *cred);

static int hfs_makenode( int mode, struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);

static void hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name);

static int UnpackSearchAttributeBlock(struct vnode *vp, struct attrlist	*alist, searchinfospec_t *searchInfo, void *attributeBuffer);

Boolean CheckCriteria( ExtendedVCB *vcb, const SearchState *searchState, u_long searchBits, struct attrlist *attrList, CatalogRecord	*catalogRecord, CatalogKey *key, searchinfospec_t *searchInfo1, searchinfospec_t *searchInfo2 );

static int InsertMatch( struct vnode *vp, struct uio *a_uio, CatalogRecord *catalogRecord, CatalogKey *key, struct attrlist *returnAttrList, void *attributesBuffer, void *variableBuffer, u_long bufferSize, u_long * nummatches );

extern	Boolean ComparePartialName(ConstStr31Param thisName, ConstStr31Param substring);
extern	Boolean CompareFullName(ConstStr31Param thisName, ConstStr31Param testName);
extern	Boolean CompareMasked(const UInt32 *thisValue, const UInt32 *compareData,  const UInt32 *compareMask, UInt32 count);

static Boolean CompareRange(u_long val, u_long low, u_long high);

static Boolean CompareRange( u_long val, u_long low, u_long high )
{
	return( (val >= low) && (val <= high) );
}
//#define CompareRange(val, low, high)	((val >= low) && (val <= high))


static int hfs_chown( struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred, struct proc *p);
static int hfs_chmod( struct vnode *vp, int mode, struct ucred *cred, struct proc *p);
static int hfs_chflags( struct vnode *vp, u_long flags, struct ucred *cred, struct proc *p);

/*
 * Enabling cluster read/write operations.
 */
extern int doclusterread;
extern int doclusterwrite;

int hfs_lookup();		/* in hfs_lookup.c */
int hfs_read();			/* in hfs_readwrite.c */
int hfs_write();		/* in hfs_readwrite.c */
int hfs_ioctl();		/* in hfs_readwrite.c */
int hfs_select();		/* in hfs_readwrite.c */
int hfs_mmap();			/* in hfs_readwrite.c */
int hfs_seek();			/* in hfs_readwrite.c */
int hfs_bmap();			/* in hfs_readwrite.c */
int hfs_strategy();		/* in hfs_readwrite.c */
int hfs_reallocblks();	/* in hfs_readwrite.c */
int hfs_truncate();		/* in hfs_readwrite.c */
int hfs_allocate();		/* in hfs_readwrite.c */
int hfs_pagein();		/* in hfs_readwrite.c */
int hfs_pageout();		/* in hfs_readwrite.c */

/*****************************************************************************
*
*	Operations on vnodes
*
*****************************************************************************/

/*
 * Create a regular file
#% create	dvp	L U U
#% create	vpp	- L -
#
 vop_create {
     IN WILLRELE struct vnode *dvp;
     OUT struct vnode **vpp;
     IN struct componentname *cnp;
     IN struct vattr *vap;
	
     We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is
	a previous error.

*/

static int
hfs_create(ap)
struct vop_create_args /* {
    struct vnode *a_dvp;
    struct vnode **a_vpp;
    struct componentname *a_cnp;
    struct vattr *a_vap;
} */ *ap;
{
	struct proc		*p = CURRENT_PROC;
    int				retval;
    int				mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
    DBG_FUNC_NAME("create");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);
    DBG_VOP_CONT(("\tva_type %d va_mode 0x%x\n",
             ap->a_vap->va_type, ap->a_vap->va_mode));

#if DIAGNOSTIC
    DBG_HFS_NODE_CHECK(ap->a_dvp);
    DBG_ASSERT(ap->a_dvp->v_type == VDIR);
    if(ap->a_vap == NULL) {
        panic("NULL attr on create");
    }

    switch(ap->a_vap->va_type) {
        case VDIR:
    		VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
            VPUT(ap->a_dvp);
            DBG_VOP_LOCKS_TEST(EISDIR);
            return (EISDIR);	/* use hfs_mkdir instead */
        case VREG:
        case VLNK:
            break;
        default:
            DBG_ERR(("%s: INVALID va_type: %d, %s, %s\n", funcname, ap->a_vap->va_type, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
    		VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
            VPUT(ap->a_dvp);
            DBG_VOP_LOCKS_TEST(EINVAL);
            return (EINVAL);
            }
//    if(ap->a_vap->va_mode & (VSUID | VSGID | VSVTX)) {
//        DBG_ERR(("%s: INVALID va_mode (%o): %s, %s\n", funcname, ap->a_vap->va_mode, H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
//        DBG_VOP_LOCKS_TEST(EINVAL);
//		  VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
//        VPUT(ap->a_dvp);
//        return (EINVAL);		/* Can't do these */
//    };
#endif

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
	if (retval != E_NONE) {
    	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
		VPUT(ap->a_dvp);
        DBG_VOP_LOCKS_TEST( retval);
        return (retval);
	}

	/* Create the vnode */
    retval = hfs_makenode(mode, ap->a_dvp, ap->a_vpp, ap->a_cnp);
    DBG_VOP_UPDATE_VP(1, *ap->a_vpp);

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_RELEASE, p);

    if (retval != E_NONE) {
        DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
	}
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}



#if 0	/* Now stubbed out in the vnode ops table with err_mknod */
/*
 * Mknod vnode call

#% mknod	dvp	L U U
#% mknod	vpp	- X -
#
 vop_mknod {
     IN WILLRELE struct vnode *dvp;
     OUT WILLRELE struct vnode **vpp;
     IN struct componentname *cnp;
     IN struct vattr *vap;
     */
/* ARGSUSED */

static int
hfs_mknod(ap)
struct vop_mknod_args /* {
    struct vnode *a_dvp;
    struct vnode **a_vpp;
    struct componentname *a_cnp;
    struct vattr *a_vap;
} */ *ap;
{
    DBG_FUNC_NAME("mknod");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKNOTNIL, VOPDBG_IGNORE, VOPDBG_POS);

    DBG_VOP(("%s: parent 0x%x name %s\n", funcname, (u_int)ap->a_dvp, ap->a_cnp->cn_nameptr));

    VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
	VPUT(ap->a_dvp);
	
    DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
    return (EOPNOTSUPP);
}
#endif


/*
 * mkcomplex vnode call
 *

#% mkcomplex	dvp	L U U
#% mkcomplex	vpp	- L -
#
vop_mkcomplex {
	IN WILLRELE struct vnode *dvp;
	OUT struct vnode **vpp;
	IN struct componentname *cnp;
	IN struct vattr *vap;
	IN u_long type;
}

 */
 
static int
hfs_mkcomplex(ap)
struct vop_mkcomplex_args /* {
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
	struct vattr *a_vap;
	u_long a_type;
} */ *ap;
{
    int		retval = E_NONE;
    DBG_FUNC_NAME("make_complex");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);

    retval = VOP_CREATE(ap->a_dvp, ap->a_vpp, ap->a_cnp, ap->a_vap);

    DBG_VOP_LOCKS_TEST(retval);
    return retval;
}


/*
 * Open called.
#% open		vp	L L L
#
 vop_open {
     IN struct vnode *vp;
     IN int mode;
     IN struct ucred *cred;
     IN struct proc *p;
     */


static int
hfs_open(ap)
struct vop_open_args /* {
    struct vnode *a_vp;
    int  a_mode;
    struct ucred *a_cred;
    struct proc *a_p;
} */ *ap;
{
    struct hfsnode	*hp = VTOH(ap->a_vp);
    int				retval = E_NONE;
    DBG_FUNC_NAME("open");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_CONT(("\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);

    if (ap->a_vp->v_type == VREG)	 /* Only files */
      {	
        /*
         * Files marked append-only must be opened for appending.
         */
        if ((hp->h_meta->h_pflags & APPEND) &&
            (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
            retval = EPERM;
        }


    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}

/*
 * Close called.
 *
 * Update the times on the hfsnode.
#% close	vp	U U U
#
 vop_close {
     IN struct vnode *vp;
     IN int fflag;
     IN struct ucred *cred;
     IN struct proc *p;
     */


static int
hfs_close(ap)
struct vop_close_args /* {
    struct vnode *a_vp;
    int  a_fflag;
    struct ucred *a_cred;
    struct proc *a_p;
} */ *ap;
{
    register struct vnode	*vp = ap->a_vp;
    struct hfsnode 			*hp = VTOH(vp);
    struct proc				*p = ap->a_p;
	FCB						*fcb;
    struct timeval 			tv;
	off_t					leof;
	u_long					blks, blocksize;
    int 					retval = E_NONE;

    DBG_FUNC_NAME("close");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_CONT(("\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);

    simple_lock(&vp->v_interlock);
    if (vp->v_usecount > 1) {
		tv = time;
        HFSTIMES(hp, &tv, &tv);
	}
    simple_unlock(&vp->v_interlock);

	/*
	 * VOP_CLOSE can be called with vp locked (from vclean).
	 * We check for this case using VOP_ISLOCKED and bail.
	 *
	 * also, ignore complex nodes; there's no data associated with them.
	 */
    if (H_FORKTYPE(hp) == kDirCmplx || VOP_ISLOCKED(vp)) {
        DBG_VOP_LOCKS_TEST(E_NONE);
        return E_NONE;
    };

	fcb = HTOFCB(hp);
	leof = fcb->fcbEOF;
	
	if (leof != 0) {
		blocksize = HTOVCB(hp)->blockSize;
		blks = leof / blocksize;
		if ((blks * blocksize) != leof)
			blks++;
	
		/*
		 * Shrink the peof to the smallest size neccessary to contain the leof.
		 */
		if ((blks * blocksize) < fcb->fcbPLen) {
			vn_lock(vp, LK_EXCLUSIVE | LK_CANRECURSE, p);
	 		retval = VOP_TRUNCATE(vp, leof, 0, ap->a_cred, p);
			VOP_UNLOCK(vp, 0, p);
		}
	}

    /* File is already flushed, so just reset values */
    H_HINT(hp) = kNoHint;		/* reset catalog hint */

    if (doclusterwrite) {
            long devBlockSize = 0;

	    vn_lock(vp, LK_EXCLUSIVE | LK_CANRECURSE, p);

            VOP_DEVBLOCKSIZE(hp->h_devvp, &devBlockSize);
            cluster_close(vp, PAGE_SIZE, devBlockSize);

	    VOP_UNLOCK(vp, 0, p);
    }

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}

/*
#% access	vp	L L L
#
 vop_access {
     IN struct vnode *vp;
     IN int mode;
     IN struct ucred *cred;
     IN struct proc *p;

     */

static int
hfs_access(ap)
struct vop_access_args /* {
    struct vnode *a_vp;
    int  a_mode;
    struct ucred *a_cred;
    struct proc *a_p;
} */ *ap;
{
    struct vnode *vp 			= ap->a_vp;
    struct ucred *cred 			= ap->a_cred;
    struct hfsnode *hp 			= VTOH(vp);
    ExtendedVCB	*vcb			= HTOVCB(hp);
    register gid_t *gp;
    mode_t mask, mode;
    Boolean isHFSPlus;
    int retval 					= E_NONE;
    int i;
    DBG_FUNC_NAME("access");
    DBG_VOP_LOCKS_DECL(1);
//    DBG_VOP_PRINT_FUNCNAME();
//    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
   
	 mode 		= ap->a_mode;
     isHFSPlus	= (vcb->vcbSigWord == kHFSPlusSigWord );

     /*
      * Disallow write attempts on read-only file systems;
      * unless the file is a socket, fifo, or a block or
      * character device resident on the file system.
      */
     if (mode & VWRITE) {
         switch (vp->v_type) {
         case VDIR:
         case VLNK:
         case VREG:
             if (VTOVFS(vp)->mnt_flag & MNT_RDONLY)
                 return (EROFS);
             break;
		default:
			break;
         }
     }

     /* If immutable bit set, nobody gets to write it. */
     if ((mode & VWRITE) && (hp->h_meta->h_pflags & IMMUTABLE))
         return (EPERM);

     /* Otherwise, user id 0 always gets access. */
     if (ap->a_cred->cr_uid == 0) {
         retval = 0;
         goto Exit;
     };

     mask = 0;

    /* Otherwise, check the owner. */
    if (cred->cr_uid == hp->h_meta->h_uid) {
        if (mode & VEXEC)
            mask |= S_IXUSR;
        if (mode & VREAD)
            mask |= S_IRUSR;
        if (mode & VWRITE)
            mask |= S_IWUSR;
        retval =  ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
        goto Exit;
    }

    /* Otherwise, check the groups. */
    for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
        if (hp->h_meta->h_gid == *gp) {
            if (mode & VEXEC)
                mask |= S_IXGRP;
            if (mode & VREAD)
                mask |= S_IRGRP;
            if (mode & VWRITE)
                mask |= S_IWGRP;
            retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);
			goto Exit;
        }

    /* Otherwise, check everyone else. */
    if (mode & VEXEC)
        mask |= S_IXOTH;
    if (mode & VREAD)
        mask |= S_IROTH;
    if (mode & VWRITE)
        mask |= S_IWOTH;
    retval = ((hp->h_meta->h_mode & mask) == mask ? 0 : EACCES);

Exit:
	DBG_VOP_LOCKS_TEST(retval);
	return (retval);    
}



/*
#% getattr	vp	= = =
#
 vop_getattr {
     IN struct vnode *vp;
     IN struct vattr *vap;
     IN struct ucred *cred;
     IN struct proc *p;

     */


/* ARGSUSED */
static int
hfs_getattr(ap)
struct vop_getattr_args /* {
    struct vnode *a_vp;
    struct vattr *a_vap;
    struct ucred *a_cred;
    struct proc *a_p;
} */ *ap;
{
    register struct vnode 	*vp = ap->a_vp;
    register struct hfsnode *hp = VTOH(vp);
    register struct vattr	*vap = ap->a_vap;
    struct timeval 			tv;
    DBG_FUNC_NAME("getattr");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);

    DBG_HFS_NODE_CHECK(ap->a_vp);

    tv = time;
    HFSTIMES(hp, &tv, &tv);

    vap->va_fsid = hp->h_dev;
    vap->va_fileid = H_FILEID(hp);
    vap->va_mode = hp->h_meta->h_mode;
    vap->va_nlink = 1;
    vap->va_uid = hp->h_meta->h_uid;
    vap->va_gid = hp->h_meta->h_gid;
    vap->va_rdev = hp->h_meta->h_rdev;
#if	MACH_NBC
    if ((vp->v_type == VREG) && vp->v_vm_info && vp->v_vm_info->mapped &&
        (!vp->v_vm_info->filesize)) {
        vap->va_size = vp->v_vm_info->vnode_size;
    	} 
	else 
#endif /* MACH_NBC */
    if (vp->v_type == VDIR) {
        vap->va_size = hp->h_size;
        vap->va_bytes = 0;
    }
    else {
        vap->va_size = hp->h_xfcb->fcb_fcb.fcbEOF;
        vap->va_bytes = hp->h_size;
    }
    vap->va_atime.tv_nsec = 0;
    vap->va_atime.tv_sec = hp->h_meta->h_atime;
    vap->va_mtime.tv_nsec = 0;
    vap->va_mtime.tv_sec = hp->h_meta->h_mtime;
    vap->va_ctime.tv_nsec = 0;
    vap->va_ctime.tv_sec = hp->h_meta->h_ctime;
    vap->va_flags = hp->h_meta->h_pflags;
    vap->va_gen = 0;
    /* this doesn't belong here */
    if (vp->v_type == VBLK)
        vap->va_blocksize = BLKDEV_IOSIZE;
    else if (vp->v_type == VCHR)
        vap->va_blocksize = MAXPHYSIO;
    else
        vap->va_blocksize = VTOVFS(vp)->mnt_stat.f_iosize;
	vap->va_type = vp->v_type;
    vap->va_filerev = 0;

    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}

/*
 * Set attribute vnode op. called from several syscalls
#% setattr	vp	L L L
#
 vop_setattr {
     IN struct vnode *vp;
     IN struct vattr *vap;
     IN struct ucred *cred;
     IN struct proc *p;

     */

static int
hfs_setattr(ap)
struct vop_setattr_args /* {
struct vnode *a_vp;
struct vattr *a_vap;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
    struct vnode 	*vp = ap->a_vp;
    struct hfsnode 	*hp = VTOH(vp);
    struct vattr 	*vap = ap->a_vap;
    struct ucred 	*cred = ap->a_cred;
    struct proc 	*p = ap->a_p;
    struct timeval 	atimeval, mtimeval;
    int				retval;
    DBG_FUNC_NAME("setattr");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
    WRITE_CK(vp, funcname);
    DBG_HFS_NODE_CHECK(ap->a_vp);

    /*
     * Check for unsettable attributes.
     */
    if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
        (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
        (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
        ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
        retval = EINVAL;
        goto ErrorExit;
    }

    if (vap->va_flags != VNOVAL) {
        if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
            retval = EROFS;
            goto ErrorExit;
        };
        if ((retval = hfs_chflags(vp, vap->va_flags, cred, p))) {
            goto ErrorExit;
        };
        if (vap->va_flags & (IMMUTABLE | APPEND)) {
            retval = 0;
            goto ErrorExit;
        };
    }

    if (hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) {
        retval = EPERM;
        goto ErrorExit;
    };
    /*
     * Go through the fields and update iff not VNOVAL.
     */
    if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
        if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
            retval = EROFS;
            goto ErrorExit;
        };
        if ((retval = hfs_chown(vp, vap->va_uid, vap->va_gid, cred, p))) {
            goto ErrorExit;
        };
    }
    if (vap->va_size != VNOVAL) {
        /*
         * Disallow write attempts on read-only file systems;
         * unless the file is a socket, fifo, or a block or
         * character device resident on the file system.
         */
        switch (vp->v_type) {
            case VDIR:
                retval = EISDIR;
                goto ErrorExit;
            case VLNK:
            case VREG:
                if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
                    retval = EROFS;
                    goto ErrorExit;
                };
                break;
            default:
                break;
        }
        if ((retval = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p))) {
            goto ErrorExit;
        };
    }
    hp = VTOH(vp);
    if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
        if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
            retval = EROFS;
            goto ErrorExit;
        };
        if (cred->cr_uid != hp->h_meta->h_uid &&
            (retval = suser(cred, &p->p_acflag)) &&
            ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
             (retval = VOP_ACCESS(vp, VWRITE, cred, p)))) {
            goto ErrorExit;
        };
        if (vap->va_atime.tv_sec != VNOVAL)
            hp->h_meta->h_nodeflags |= IN_ACCESS;
        if (vap->va_mtime.tv_sec != VNOVAL)
            hp->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;
        atimeval.tv_sec = vap->va_atime.tv_sec;
        atimeval.tv_usec = 0;
        mtimeval.tv_sec = vap->va_mtime.tv_sec;
        mtimeval.tv_usec = 0;
        if ((retval = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))) {
            goto ErrorExit;
        };
    }
    retval = 0;
    if (vap->va_mode != (mode_t)VNOVAL) {
        if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
            retval = EROFS;
            goto ErrorExit;
        };
        retval = hfs_chmod(vp, (int)vap->va_mode, cred, p);
    };

ErrorExit: ;

    DBG_VOP(("hfs_setattr: returning %d...\n", retval));
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


/*

#
#% getattrlist	vp	= = =
#
 vop_getattrlist {
     IN struct vnode *vp;
     IN struct attrlist *alist;
     INOUT struct uio *uio;
     IN struct ucred *cred;
     IN struct proc *p;
 };

 */

static int
hfs_getattrlist(ap)
struct vop_getattrlist_args /* {
struct vnode *a_vp;
struct attrlist *a_alist
struct uio *a_uio;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct hfsnode *hp = VTOH(vp);
    struct attrlist *alist = ap->a_alist;
    int error = 0;
    struct hfsCatalogInfo catalogInfo;
    struct hfsCatalogInfo *catInfoPtr = NULL;
    int fixedblocksize;
    int attrblocksize;
    int attrbufsize;
    void *attrbufptr;
    void *attrptr;
    void *varptr;
    u_long fileID;
    DBG_FUNC_NAME("getattrlist");
    DBG_VOP_LOCKS_DECL(1);

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
    DBG_HFS_NODE_CHECK(ap->a_vp);
    DBG_VOP(("%s: Common attr:0x%lx, buff size Ox%lX,\n",funcname, (u_long)alist->commonattr,(u_long)ap->a_uio->uio_resid));

    DBG_ASSERT(ap->a_uio->uio_rw == UIO_READ);

    if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
        ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
        ((alist->volattr & ~ATTR_VOL_VALIDMASK) != 0) ||
        ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
        ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
        ((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0)) {
        DBG_ERR(("%s: bad attrlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
        };

    /* Requesting volume information requires setting the ATTR_VOL_INFO bit and
        volume info requests are mutually exclusive with all other info requests: */
   if ((alist->volattr != 0) && (((alist->volattr & ATTR_VOL_INFO) == 0) ||
        (alist->dirattr != 0) || (alist->fileattr != 0) || (alist->forkattr != 0)
		)) {
        DBG_ERR(("%s: conflicting information requested\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
        };

    /* Reject requests for unsupported options for now: */
    if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
        (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))) {
        DBG_ERR(("%s: illegal bits in attlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
        };

	/* Requesting volume information requires root vnode */ 
    if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
        DBG_ERR(("%s: not root vnode\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    	};

    /* If a FileID (ATTR_CMN_OBJPERMANENTID) is requested on an HFS volume we must be sure
        to create the thread record before returning it:
        */
    if (((vp->v_type == VREG) || (vp->v_type == VCPLX)) &&
        (alist->commonattr & ATTR_CMN_OBJPERMANENTID)) {
        /* Only HFS-Plus volumes are guaranteed to have a thread record in place already: */
        if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord) {
            /* Create a thread record and return the FileID [which is the file's fileNumber] */
            /* lock catalog b-tree */
            error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
            error = hfsCreateFileID(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &fileID);
            (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
            if (error) {
                DBG_VOP_LOCKS_TEST(error);
                DBG_ERR(("hfs_getattrlist: error %d on CreateFileIDRef.\n", error));
                return error;
            };
            ASSERT(fileID == H_FILEID(hp));
        };
    };

    /*
	 * Avoid unnecessary catalog lookups for volume info which is available directly
	 * in the VCB and root vnode, or can be synthesized.
	 */
    if (((alist->volattr == 0) && ((alist->commonattr & HFS_ATTR_CMN_LOOKUPMASK) != 0)) ||
        ((alist->dirattr & HFS_ATTR_DIR_LOOKUPMASK) != 0) ||
        ((alist->fileattr & HFS_ATTR_FILE_LOOKUPMASK) != 0)) {

        /* lock catalog b-tree */
        error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_SHARED, ap->a_p);
        if (error) {
            DBG_VOP_LOCKS_TEST(error);
            return (error);
            }

        if (alist->volattr != 0) {
            /* Look up the root info, regardless of the vnode provided */
            error = hfsLookup(VTOVCB(vp), 2, NULL,  -1, 0, &catalogInfo);
            } else {
                error = hfsLookup(VTOVCB(vp), H_DIRID(hp), H_NAME(hp),  -1, 0, &catalogInfo);
                if (error == 0) H_HINT(hp) = catalogInfo.hint;						/* Remember the last valid hint */
                };
        catInfoPtr = &catalogInfo;

        /* unlock catalog b-tree */
        (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, ap->a_p);
    };

    fixedblocksize = AttributeBlockSize(alist);
    attrblocksize = fixedblocksize + (sizeof(u_long));							/* u_long for length longword */
    if (alist->commonattr & ATTR_CMN_NAME) attrblocksize += NAME_MAX + 1;
    if (alist->commonattr & ATTR_CMN_NAMEDATTRLIST) attrblocksize += 0;			/* XXX PPD */
    if (alist->volattr & ATTR_VOL_MOUNTPOINT) attrblocksize += PATH_MAX;
    if (alist->volattr & ATTR_VOL_NAME) attrblocksize += NAME_MAX + 1;
    if (alist->fileattr & ATTR_FILE_FORKLIST) attrblocksize += 0;				/* XXX PPD */

    attrbufsize = MIN(ap->a_uio->uio_resid, attrblocksize);
    DBG_VOP(("hfs_getattrlist: allocating Ox%X byte buffer (Ox%X + Ox%X) for attributes...\n",
             attrblocksize,
             fixedblocksize,
             attrblocksize - fixedblocksize));
    MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
    attrptr = attrbufptr;
    *((u_long *)attrptr) = 0;									/* Set buffer length in case of errors */
    ++((u_long *)attrptr);										/* Reserve space for length field */
    varptr = ((char *)attrptr) + fixedblocksize;				/* Point to variable-length storage */
    DBG_VOP(("hfs_getattrlist: attrptr = 0x%08X, varptr = 0x%08X...\n", (u_int)attrptr, (u_int)varptr));

    PackAttributeBlock(alist, vp, catInfoPtr, &attrptr, &varptr);
    *((u_long *)attrbufptr) = (varptr - attrbufptr);		/* Store length of fixed + var block */
    attrbufsize = MIN(attrbufsize, varptr - attrbufptr);	/* Don't copy out more data than was generated */
    DBG_VOP(("hfs_getattrlist: copying Ox%X bytes to user address 0x%08X.\n", attrbufsize, (u_int)ap->a_uio->uio_iov->iov_base));
    error = uiomove((caddr_t)attrbufptr, attrbufsize, ap->a_uio);
    if (error != E_NONE) {
        DBG_ERR(("hfs_getattrlist: error %d on uiomove.\n", error));
        };

    FREE(attrbufptr, M_TEMP);

    DBG_VOP_LOCKS_TEST(error);
    return error;
}



/* This is a special permission-checking routine that tests for "write" access without
   regard for the UF_IMMUTABLE or SF_IMMUTABLE bits in the flags.  hfs_setattrlist cannot
   use VOP_ACCESS for this check so this is a derivative of that code that does this check.
 */
static int hfs_writepermission(struct vnode *vp, struct ucred *cred, struct proc *p) {
    struct hfsnode *hp = VTOH(vp);
    int i;
    register gid_t *gp;

	/* Start by checking for root */
	if (suser(cred, &p->p_acflag) == 0) return 0;
	
	/* Check the owner: */
	if (cred->cr_uid == hp->h_meta->h_uid) {
		return (hp->h_meta->h_mode & S_IWUSR) ? 0 : EACCES;
	};

	/* Otherwise, check the groups: */
    for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++) {
       	if (hp->h_meta->h_gid == *gp) {
			return (hp->h_meta->h_mode & S_IWGRP) ? 0 : EACCES;
        };
   };

   /* Otherwise, check everyone else. */
   return (hp->h_meta->h_mode & S_IWOTH) ? 0 : EACCES;
}



/*

#
#% setattrlist	vp	L L L
#
 vop_setattrlist {
     IN struct vnode *vp;
     IN struct attrlist *alist;
     INOUT struct uio *uio;
     IN struct ucred *cred;
     IN struct proc *p;
 };

 */

static int
hfs_setattrlist(ap)
struct vop_setattrlist_args /* {
struct vnode *a_vp;
struct attrlist *a_alist
struct uio *a_uio;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct hfsnode *hp = VTOH(vp);
    struct attrlist *alist = ap->a_alist;
    struct ucred *cred = ap->a_cred;
    struct proc *p = ap->a_p;
    int error;
    struct hfsCatalogInfo catalogInfo;
    int attrblocksize;
    void *attrbufptr = NULL;
    void *attrptr;
    void *varptr = NULL;
	uid_t saved_uid;
	gid_t saved_gid;
	mode_t saved_mode;
    u_long saved_flags;
    int retval = 0;

    DBG_FUNC_NAME("setattrlist");
    DBG_VOP_LOCKS_DECL(1);

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_SAME, VOPDBG_POS);
    DBG_HFS_NODE_CHECK(ap->a_vp);
    DBG_VOP(("%s: Common attr:0x%x, buff size Ox%X,\n",funcname, (u_int)alist->commonattr,(u_int)ap->a_uio->uio_resid));

    DBG_ASSERT(ap->a_uio->uio_rw == UIO_WRITE);

    if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
        ((alist->commonattr & ~ATTR_CMN_SETMASK) != 0) ||
        ((alist->volattr & ~ATTR_VOL_SETMASK) != 0) ||
        ((alist->dirattr & ~ATTR_DIR_SETMASK) != 0) ||
        ((alist->fileattr & ~ATTR_FILE_SETMASK) != 0) ||
        ((alist->forkattr & ~ATTR_FORK_SETMASK) != 0)) {
        DBG_ERR(("%s: Bad attrlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
        };

    if ((alist->volattr != 0) && 							/* Setting volume info */
		(((alist->volattr & ATTR_VOL_INFO) == 0) ||			/* Not explicitly indicating this or ... */
		 (alist->commonattr & ~ATTR_CMN_VOLSETMASK)))		/* ... setting invalid attributes for volume */
      {
        DBG_ERR(("%s: Bad attrlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
      };

    if (VTOVFS(vp)->mnt_flag & MNT_RDONLY) {
        DBG_VOP_LOCKS_TEST(EROFS);
        return EROFS;
    };

	/* NOTE: The following isn't ENTIRELY complete: even if you're the superuser
			 you cannot change the flags as long as SF_IMMUTABLE or SF_APPEND is
			 set and securelevel > 0.  This is verified in hfs_chflags which gets
			 invoked to do the actual flags field change so this check is sufficient
			 for now.
	 */
	if (alist->commonattr & (OWNERSHIP_ONLY_ATTRS)) {
		/* Check to see if the user owns the object [or is superuser]: */
    	if (cred->cr_uid != hp->h_meta->h_uid &&
        	(retval = suser(cred, &p->p_acflag))) {
        	DBG_VOP_LOCKS_TEST(retval);
        	return retval;
        };
	};
	
	/* For any other attributes, check to see if the user has write access to
	    the object in question [unlike VOP_ACCESS, ignore IMMUTABLE here]: */
	    
	if ((((alist->commonattr & ~(OWNERSHIP_ONLY_ATTRS)) != 0) ||
		 (alist->volattr != 0) ||
		 (alist->dirattr != 0) ||
		 (alist->fileattr != 0) ||
		 (alist->forkattr != 0)) &&
		((retval = hfs_writepermission(vp, cred, p)) != 0)) {
        DBG_VOP_LOCKS_TEST(retval);
        return retval;
	}; /* end of if ownership attr */
	
    /* Allocate the buffer now to minimize the time we might be blocked holding the catalog lock */
    attrblocksize = ap->a_uio->uio_resid;
    if (attrblocksize < AttributeBlockSize(alist)) {
        DBG_ERR(("%s: bad attrblocksize\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    };

   MALLOC(attrbufptr, void *, attrblocksize, M_TEMP, M_WAITOK);
    
    /* lock catalog b-tree */
    error = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
    if (error != E_NONE) {
        goto FreeBuffer;
    };

	error = hfsLookup(VTOVCB(vp), H_DIRID(hp), H_NAME(hp), -1, 0,  &catalogInfo);
    if (error != E_NONE) {
        DBG_ERR(("%s: Lookup failed on file '%s'\n", funcname,  H_NAME(hp)));
        goto ErrorExit;
    };
    H_HINT(hp) = catalogInfo.hint;						/* Remember the last valid hint */

    error = uiomove((caddr_t)attrbufptr, attrblocksize, ap->a_uio);
    if (error) goto ErrorExit;

    if ((alist->volattr) && (H_FILEID(hp) != kRootDirID)) {
        error = EINVAL;
        goto ErrorExit;
    };

	/* do we have permission to change the dates? */
//  if (alist->commonattr & (ATTR_CMN_CRTIME | ATTR_CMN_MODTIME | ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME | ATTR_CMN_BKUPTIME)) {
    if (alist->commonattr & (ATTR_CMN_CHGTIME | ATTR_CMN_ACCTIME)) {
        if (cred->cr_uid != hp->h_meta->h_uid && (error = suser(cred, &p->p_acflag))) {
            goto ErrorExit;
        };
    };

    /* save these in case hfs_chown() or hfs_chmod() fail */
	saved_uid = hp->h_meta->h_uid;
	saved_gid = hp->h_meta->h_gid;
    saved_mode = hp->h_meta->h_mode;
    saved_flags = hp->h_meta->h_pflags;

    attrptr = attrbufptr;
    UnpackAttributeBlock(alist, vp, &catalogInfo, &attrptr, &varptr);

	/* if unpacking changed the owner or group then call hfs_chown() */
    if (saved_uid != hp->h_meta->h_uid || saved_gid != hp->h_meta->h_gid) {
		uid_t uid;
		gid_t gid;
		
		uid = hp->h_meta->h_uid;
 		hp->h_meta->h_uid = saved_uid;
		gid = hp->h_meta->h_gid;
		hp->h_meta->h_gid = saved_gid;
        if ((error = hfs_chown(vp, uid, gid, cred, p)))
			goto ErrorExit;
    }

	/* if unpacking changed the mode then call hfs_chmod() */
	if (saved_mode != hp->h_meta->h_mode) {
		mode_t mode;

		mode = hp->h_meta->h_mode;
		hp->h_meta->h_mode = saved_mode;
		if ((error = hfs_chmod(vp, mode, cred, p)))
			goto ErrorExit;
	};

    /* if unpacking changed the flags then call hfs_chflags */
    if (saved_flags != hp->h_meta->h_pflags) {
        u_long flags;

        flags = hp->h_meta->h_pflags;
        hp->h_meta->h_pflags = saved_flags;
        if ((error = hfs_chflags(vp, flags, cred, p)))
            goto ErrorExit;
    };

	if (alist->volattr == 0) {
    	error = MacToVFSError( UpdateCatalogNode(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &catalogInfo.nodeData));
	}

    if (alist->volattr & ATTR_VOL_NAME) {
        ExtendedVCB *vcb 	= VTOVCB(vp);
        int			namelen = strlen(vcb->vcbVN);
    	
        error = MoveRenameCatalogNode(vcb, kRootParID, H_NAME(hp), H_HINT(hp), kRootParID, vcb->vcbVN, &H_HINT(hp));
        if (error) {
            VCB_LOCK(vcb);
            copystr(H_NAME(hp), vcb->vcbVN, sizeof(vcb->vcbVN), NULL);	/* Restore the old name in the VCB */
            vcb->vcbFlags |= 0xFF00;		// Mark the VCB dirty
            VCB_UNLOCK(vcb);
            goto ErrorExit;
        };

        if (namelen > hp->h_meta->h_namelen) {
            if (hp->h_meta->h_nodeflags & IN_LONGNAME)
              {
                FREE(H_NAME(hp), M_HFSNODE);
                MALLOC(H_NAME(hp), char *, namelen+1, M_HFSNODE, M_WAITOK);
              }
            else if (namelen > MAXHFSVNODELEN) {
                MALLOC(H_NAME(hp), char *, namelen+1, M_HFSNODE, M_WAITOK);
                hp->h_meta->h_nodeflags |= IN_LONGNAME;
            }
        };
        hp->h_meta->h_namelen = namelen;
        copystr(vcb->vcbVN, H_NAME(hp), hp->h_meta->h_namelen+1, NULL);
        hp->h_meta->h_nodeflags |= IN_CHANGE;
        
#if 0
        /* if hfs wrapper exists, update its name too */
        if (vcb->vcbSigWord == kHFSPlusSigWord && vcb->vcbAlBlSt != 0) {
			HFSMasterDirectoryBlock *mdb;
			struct buf *bp = NULL;
			int size = kMDBSize;	/* 512 */
            int volnamelen = MIN(sizeof(Str27), namelen);

			if ( bread(VTOHFS(vp)->hfs_devvp, IOBLKNOFORBLK(kMasterDirectoryBlock, size),
				 IOBYTECCNTFORBLK(kMasterDirectoryBlock, kMDBSize, size), NOCRED, &bp) == 0) {

				mdb = (HFSMasterDirectoryBlock *)((char *)bp->b_data + IOBYTEOFFSETFORBLK(kMasterDirectoryBlock, size));
                if (mdb->drSigWord == kHFSSigWord) {
                    /* Convert the string to MacRoman, ignoring any errors, */
                    (void) ConvertUTF8ToMacRoman(volnamelen, vcb->vcbVN, mdb->drVN);
                    bawrite(bp);
                    bp = NULL;
                }
			}

			if (bp) brelse(bp);
        }
#endif
    };

ErrorExit:
    /* unlock catalog b-tree */
    (void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);

FreeBuffer:
    if (attrbufptr) FREE(attrbufptr, M_TEMP);

    DBG_VOP_LOCKS_TEST(error);
    return error;
}

/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
static int
hfs_chmod(vp, mode, cred, p)
register struct vnode *vp;
register int mode;
register struct ucred *cred;
struct proc *p;
{
    register struct hfsnode *hp = VTOH(vp);
    int retval;

    if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
        return E_NONE;

    if (cred->cr_uid != hp->h_meta->h_uid &&
        (retval = suser(cred, &p->p_acflag)))
        return (retval);
    if (cred->cr_uid) {
        if (vp->v_type != VDIR && (mode & S_ISTXT))
            return (EFTYPE);
        if (!groupmember(hp->h_meta->h_gid, cred) && (mode & ISGID))
            return (EPERM);
    }
    hp->h_meta->h_mode &= ~ALLPERMS;
    hp->h_meta->h_mode |= (mode & ALLPERMS);
    hp->h_meta->h_nodeflags &= ~IN_UNSETACCESS;
    hp->h_meta->h_nodeflags |= IN_CHANGE;
    if ((vp->v_flag & VTEXT) && (hp->h_meta->h_mode & S_ISTXT) == 0)
        (void) vnode_uncache(vp);
    return (0);
}


/*
 * Change the flags on a file or directory.
 * Inode must be locked before calling.
 */
static int
hfs_chflags(vp, flags, cred, p)
register struct vnode *vp;
register u_long flags;
register struct ucred *cred;
struct proc *p;
{
    register struct hfsnode *hp = VTOH(vp);
    int retval;

    if (cred->cr_uid != hp->h_meta->h_uid &&
        (retval = suser(cred, &p->p_acflag)))
        return retval;

    if (cred->cr_uid == 0) {
        if ((hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND)) &&
            securelevel > 0) {
            return EPERM;
        };
        hp->h_meta->h_pflags = flags;
    } else {
        if (hp->h_meta->h_pflags & (SF_IMMUTABLE | SF_APPEND) ||
            (flags & UF_SETTABLE) != flags) {
            return EPERM;
        };
        hp->h_meta->h_pflags &= SF_SETTABLE;
        hp->h_meta->h_pflags |= (flags & UF_SETTABLE);
    }
    hp->h_meta->h_nodeflags &= ~IN_UNSETACCESS;
    hp->h_meta->h_nodeflags |= IN_CHANGE;

    return 0;
}


/*
 * Perform chown operation on hfsnode hp;
 * hfsnode must be locked prior to call.
 */
static int
hfs_chown(vp, uid, gid, cred, p)
register struct vnode *vp;
uid_t uid;
gid_t gid;
struct ucred *cred;
struct proc *p;
{
    register struct hfsnode *hp = VTOH(vp);
    uid_t ouid;
    gid_t ogid;
    int retval = 0;

    if (VTOVCB(vp)->vcbSigWord != kHFSPlusSigWord)
        return EOPNOTSUPP;

    if (uid == (uid_t)VNOVAL)
        uid = hp->h_meta->h_uid;
    if (gid == (gid_t)VNOVAL)
        gid = hp->h_meta->h_gid;
    /*
     * If we don't own the file, are trying to change the owner
     * of the file, or are not a member of the target group,
     * the caller must be superuser or the call fails.
     */
    if ((cred->cr_uid != hp->h_meta->h_uid || uid != hp->h_meta->h_uid ||
         (gid != hp->h_meta->h_gid && !groupmember((gid_t)gid, cred))) &&
        (retval = suser(cred, &p->p_acflag)))
        return (retval);
    ogid = hp->h_meta->h_gid;
    ouid = hp->h_meta->h_uid;

    hp->h_meta->h_gid = gid;
    hp->h_meta->h_uid = uid;

    hp->h_meta->h_nodeflags &= ~IN_UNSETACCESS;
    if (ouid != uid || ogid != gid)
        hp->h_meta->h_nodeflags |= IN_CHANGE;
    if (ouid != uid && cred->cr_uid != 0)
        hp->h_meta->h_mode &= ~ISUID;
    if (ogid != gid && cred->cr_uid != 0)
        hp->h_meta->h_mode &= ~ISGID;
    return (0);
}



/*
#
#% exchange fvp		L L L
#% exchange tvp		L L L
#
 vop_exchange {
     IN struct vnode *fvp;
     IN struct vnode *tvp;
     IN struct ucred *cred;
     IN struct proc *p;
 };

 */
static int
hfs_exchange(ap)
struct vop_exchange_args /* {
struct vnode *a_fvp;
struct vnode *a_tvp;
struct ucred *a_cred;
struct proc *a_p;
} */ *ap;
{
    struct hfsnode *fromhp, *tohp;
    struct hfsmount *hfsmp;
//	struct hfsCatalogInfo catalogInfo;
	u_char tmp_name[NAME_MAX+1];		/* 256 bytes! */
    ExtendedVCB *vcb;
	u_int32_t fromFileID;
	u_int32_t fromParID;
	u_int32_t tmpLong;
    Boolean isHFSPlus;
    int retval = E_NONE;
    DBG_FUNC_NAME("exchange");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_fvp);DBG_VOP_CONT(("\n"));
    DBG_VOP_PRINT_VNODE_INFO(ap->a_tvp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_fvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,ap->a_tvp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);

    fromhp 	= VTOH(ap->a_fvp);
    tohp 	= VTOH(ap->a_tvp);
    hfsmp	= VTOHFS(ap->a_fvp);
    vcb 	= HTOVCB(fromhp);
    isHFSPlus = (vcb->vcbSigWord == kHFSPlusSigWord);

    if (ap->a_fvp->v_mount != ap->a_tvp->v_mount) {
        DBG_VOP_LOCKS_TEST(EXDEV);
        return EXDEV;
    }

    /* Can only exchange file objects */
    if (ap->a_fvp->v_type != VREG || ap->a_tvp->v_type != VREG) {
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    }


    /* Make sure buffers are written out and trashed */
    if ((retval = vinvalbuf(ap->a_fvp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)))
        goto Err_Exit;

    if ((retval = vinvalbuf(ap->a_tvp, V_SAVE, ap->a_cred, ap->a_p, 0, 0)))
        goto Err_Exit;

    if (VTOH(ap->a_fvp)->h_sibling) {
        if ((retval = vinvalbuf(VTOH(ap->a_fvp)->h_sibling, V_SAVE, ap->a_cred, ap->a_p, 0, 0)))
            goto Err_Exit;
        }

    if (VTOH(ap->a_tvp)->h_sibling) {
        if ((retval = vinvalbuf(VTOH(ap->a_tvp)->h_sibling, V_SAVE, ap->a_cred, ap->a_p, 0, 0)))
            goto Err_Exit;
        }

    /* lock catalog b-tree */
    retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, ap->a_p);
    if (retval) goto Err_Exit;

    /* lock extents b-tree iff there are overflow extents */
    /* XXX SER ExchangeFileIDs() always tries to delete the virtual extent id for exchanging files
        so we neeed the tree to be always locked.
    */
    retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, ap->a_p);
    if (retval) goto Err_Exit_Relse;

    /* Do the exchange */
    retval = MacToVFSError( ExchangeFileIDs(vcb, H_NAME(fromhp), H_NAME(tohp), H_DIRID(fromhp), H_DIRID(tohp), H_HINT(fromhp), H_HINT(tohp) ));

    (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, ap->a_p);

    if (retval != E_NONE) {
        DBG_ERR(("/tError trying to exchange: %d\n", retval));
        goto Err_Exit_Relse;
    }

	
    /* Now exchange fileID, parID, name for the vnode itself */
    fromFileID = H_FILEID(fromhp);
    fromParID = H_DIRID(fromhp);
    copystr(H_NAME(fromhp), (char*) tmp_name, strlen(H_NAME(fromhp))+1, NULL);
    hfs_chid(fromhp, H_FILEID(tohp), H_DIRID(tohp), H_NAME(tohp));
    hfs_chid(tohp, fromFileID, fromParID, (char*) tmp_name);
	
	/* copy rest */
    tmpLong = HTOFCB(fromhp)->fcbFlags;
    HTOFCB(fromhp)->fcbFlags = HTOFCB(tohp)->fcbFlags;
    HTOFCB(tohp)->fcbFlags = tmpLong;

     tmpLong = fromhp->h_meta->h_crtime;
    fromhp->h_meta->h_crtime = tohp->h_meta->h_crtime;
    tohp->h_meta->h_crtime = tmpLong;

    tmpLong = fromhp->h_meta->h_butime;
    fromhp->h_meta->h_butime = tohp->h_meta->h_butime;
    tohp->h_meta->h_butime = tmpLong;

    tmpLong = fromhp->h_meta->h_atime;
    fromhp->h_meta->h_atime = tohp->h_meta->h_atime;
    tohp->h_meta->h_atime = tmpLong;

    tmpLong = fromhp->h_meta->h_ctime;
    fromhp->h_meta->h_ctime = tohp->h_meta->h_ctime;
    tohp->h_meta->h_ctime = tmpLong;


    tmpLong = fromhp->h_meta->h_gid;
    fromhp->h_meta->h_gid = tohp->h_meta->h_gid;
    tohp->h_meta->h_gid = tmpLong;

    tmpLong = fromhp->h_meta->h_uid;
    fromhp->h_meta->h_uid = tohp->h_meta->h_uid;
    tohp->h_meta->h_uid = tmpLong;

    tmpLong = fromhp->h_meta->h_pflags;	
    fromhp->h_meta->h_pflags = tohp->h_meta->h_pflags;
    tohp->h_meta->h_pflags = tmpLong;

    tmpLong = fromhp->h_meta->h_mode;	
    fromhp->h_meta->h_mode = tohp->h_meta->h_mode;
    tohp->h_meta->h_mode = tmpLong;

    tmpLong = fromhp->h_meta->h_rdev;	
    fromhp->h_meta->h_rdev = tohp->h_meta->h_rdev;
    tohp->h_meta->h_rdev = tmpLong;

    tmpLong = fromhp->h_size;	
    fromhp->h_size = tohp->h_size;
    tohp->h_size = tmpLong;

	

Err_Exit_Relse:;
    /* unlock catalog b-tree */
    (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, ap->a_p);

Err_Exit:;

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


/*
 * Change a vnode's file id, parent id and name
 * 
 * Assumes the vnode is locked and is of type VREG
 */
static void
hfs_chid(struct hfsnode *hp, u_int32_t fid, u_int32_t pid, char* name)
{
	struct hfsnode *tp;
	int		namelen = strlen(name);

	DBG_ASSERT(HTOV(hp)->v_type == VREG);

	hfs_vhashrem(hp);
	H_HINT(hp) = 0;
	H_FILEID(hp) = fid;					/* change h_nodeID */
	hp->h_xfcb->fcb_fcb.fcbFlNm = fid;	/* and this one too */
	H_DIRID(hp) = pid;
    if (namelen > hp->h_meta->h_namelen) {
        if (hp->h_meta->h_nodeflags & IN_LONGNAME)
          {
            FREE(H_NAME(hp), M_HFSNODE);
            MALLOC(H_NAME(hp), char *, namelen+1, M_HFSNODE, M_WAITOK);
          }
        else if (namelen > MAXHFSVNODELEN) {
            MALLOC(H_NAME(hp), char *, namelen+1, M_HFSNODE, M_WAITOK);
            hp->h_meta->h_nodeflags |= IN_LONGNAME;
        }
    };
    hp->h_meta->h_namelen = namelen;
    copystr(name, H_NAME(hp), hp->h_meta->h_namelen+1, NULL);
	hfs_vhashinslocked(hp);

	/* Change complex node also */
	tp = VTOH(hp->h_relative);
	if (tp) {
		cache_purge(HTOV(tp));

		hfs_vhashrem(tp);
		H_HINT(tp) = 0;
 		H_FILEID(tp) = fid;
		tp->h_xfcb->fcb_fcb.fcbFlNm = fid;
		hfs_vhashinslocked(tp);
	}

	/* Now change sibling */
	tp = VTOH(hp->h_sibling);
	if (tp) {
		hfs_vhashrem(tp);
		H_HINT(tp) = 0;
		H_FILEID(tp) = fid;
		tp->h_xfcb->fcb_fcb.fcbFlNm = fid;
		hfs_vhashinslocked(tp);
	}
}


/*

#% fsync	vp	L L L
#
 vop_fsync {
     IN struct vnode *vp;
     IN struct ucred *cred;
     IN int waitfor;
     IN struct proc *p;

     */


static int
hfs_fsync(ap)
struct vop_fsync_args /* {
    struct vnode *a_vp;
    struct ucred *a_cred;
    int a_waitfor;
    struct proc *a_p;
} */ *ap;
{
    struct vnode 		*vp = ap->a_vp ;
    struct hfsnode 		*hp;
    int					retval = 0;
    register struct buf *bp;
    struct timeval 		tv;
    struct buf 			*nbp;
    int 				s;

    DBG_FUNC_NAME("fsync");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("  "));
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);
    DBG_HFS_NODE_CHECK(ap->a_vp);
	
#if DIAGNOSTIC
    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
#endif

    if (vp->v_type == VCPLX) {
		/* Ignore complex nodes; there's no data associated with them. */
		DBG_ASSERT(vp->v_dirtyblkhd.lh_first == NULL);
		DBG_VOP_LOCKS_TEST(E_NONE);
    	return E_NONE;
    };
	

    /*
     * Flush all dirty buffers associated with a vnode.
     */
loop:
	hp = VTOH(vp);
    s = splbio();
    for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
        nbp = bp->b_vnbufs.le_next;
        if ((bp->b_flags & B_BUSY))
            continue;
        if ((bp->b_flags & B_DELWRI) == 0)
            panic("hfs_fsync: not dirty");
        bremfree(bp);
        bp->b_flags |= B_BUSY;
        bp->b_flags &= ~B_LOCKED;	/* Clear flag, should only be set on meta files */
        splx(s);
        /*
         * Wait for I/O associated with indirect blocks to complete,
         * since there is no way to quickly wait for them below.
         */
        DBG_VOP(("\t\t\tFlushing out phys block %d == log block %d\n", bp->b_blkno, bp->b_lblkno));
        if (bp->b_vp == vp || ap->a_waitfor == MNT_NOWAIT)
            (void) bawrite(bp);
        else
            (void) bwrite(bp);
        goto loop;
    }

    if (ap->a_waitfor == MNT_WAIT) {
        while (vp->v_numoutput) {
            vp->v_flag |= VBWAIT;
            tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "hfs_fsync", 0);
        }

        /* I have seen this happen for swapfile. So it is safer to
         * check for dirty buffers again.  --Umesh
         */
        if (vp->v_dirtyblkhd.lh_first) {
            vprint("hfs_fsync: dirty", vp);
            splx(s);
            goto loop;
        }
    }
    splx(s);

#if DIAGNOSTIC
    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
#endif

    tv = time;
    hp->h_lastfsync = tv.tv_sec;
	if (H_FORKTYPE(hp) != kSysFile) {
    	retval = VOP_UPDATE(ap->a_vp, &tv, &tv, ap->a_waitfor == MNT_WAIT);

    	if (retval != E_NONE) {
        	DBG_ERR(("%s: FLUSH FAILED: %s\n", funcname, H_NAME(hp)));
    	}
    }
	else
        hp->h_meta->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);

    if (ap->a_waitfor == MNT_WAIT) {
      DBG_ASSERT(vp->v_dirtyblkhd.lh_first == NULL);
    };
    DBG_VOP_LOCKS_TEST(retval);
    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
    return (retval);
}



/*

#% remove	dvp	L U U
#% remove	vp	L U U
#
 vop_remove {
     IN WILLRELE struct vnode *dvp;
     IN WILLRELE struct vnode *vp;
     IN struct componentname *cnp;

     */

int
hfs_remove(ap)
struct vop_remove_args /* {
    struct vnode *a_dvp;
    struct vnode *a_vp;
    struct componentname *a_cnp;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct vnode *dvp = ap->a_dvp;
    struct vnode *ttp,*tp = NULL;
    struct hfsnode *hp = VTOH(ap->a_vp);
    struct hfsmount *hfsmp = HTOHFS(hp);
    struct proc *p = CURRENT_PROC;
    struct timeval tv;
    int retval;
    struct vnode *sibling_vp;
    DBG_FUNC_NAME("remove");

    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    /* see if they really meant to call VOP_RMDIR */
    if (vp->v_type == VDIR)
        return VOP_RMDIR(dvp, vp, ap->a_cnp);

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);

    if ((hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
        (VTOH(dvp)->h_meta->h_pflags & APPEND)) {
        retval = EPERM;
        goto out;
    }

    /*  HFS does not have unlinking, do we cannot delete a file that is inuse */
    DBG_VOP(("\tusecount = %d", vp->v_usecount));
    if (VTOH(vp)->h_sibling) {
        DBG_VOP_CONT((", sibling usecount = %d\n", VTOH(vp)->h_sibling->v_usecount));
    }
    else {
        DBG_VOP_CONT(("\n"));
    }
    if (vp->v_usecount > 1) {
        retval = EBUSY;
        goto out;
    }
    /* Check other siblings for in use also */
    tp = vp;
	/* XXX SER Process all siblings here */
	/* XXX SER MUST BE CHANGED BY MACOS X */
    /* This unlocks the vnode!!!! Because vnode_uncache does the same
	 * so it depends on no context switches.
     * See the comment in vnode_uncache()
	 */
	/* Uncache everything */
 sibling_loop:
    sibling_vp = VTOH(tp)->h_sibling;	/* Note - this is a local copy for good reason! */
    if (sibling_vp) {
    	VOP_UNLOCK(vp, 0, p);
        if (vget(sibling_vp, LK_EXCLUSIVE | LK_RETRY, p)) {
	  /* Couldn't get the sibling - it might have gone away.
	     Clean up to try again from scratch */
	  vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
	  goto sibling_loop;
	};
        (void) vnode_uncache(sibling_vp);
        vput(sibling_vp);
	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
    };

    while (VTOH(tp)->h_sibling && (VTOH(tp)->h_sibling != vp)) {
        simple_lock(&tp->v_interlock);
        if (VTOH(tp)->h_sibling->v_usecount > 0) {
            retval = EBUSY;
            simple_unlock(&tp->v_interlock);
            goto out;
        }
		ttp = tp;
        tp = VTOH(tp)->h_sibling;
        simple_unlock(&ttp->v_interlock);
    }	

	/* Flush out any catalog changes */
	/* XXX SER: This is a hack, becasue hfsDelete reads the data from the disk
	 * and not from memory which is more correct
	 */
    if ((hp->h_meta->h_nodeflags & IN_MODIFIED) || (HTOFCB(hp)->fcbFlags & fcbModifiedMask))
        {
        DBG_ASSERT((hp->h_meta->h_nodeflags & IN_MODIFIED) != 0);
        tv = time;
        VOP_UPDATE(vp, &tv, &tv, 0);
        }

    /* lock catalog b-tree */
    retval = hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_EXCLUSIVE, p);
    if (retval != E_NONE) {
        retval = EBUSY;
        goto out;
        }

    /* lock extents b-tree (also protects volume bitmap) */
    retval = hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_EXCLUSIVE, p);
    if (retval != E_NONE) {
        retval = EBUSY;
        goto out2;	/* unlock catalog b-tree on the way out */
        }

    /* remove the entry from the namei cache: */
    cache_purge(VTOH(vp)->h_relative);

    /* remove entry from catalog and free any blocks used */
    retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), TRUE, H_HINT(hp));

    hp->h_meta->h_mode = 0;				/* Makes the node go away...see inactive */
    tp = VTOH(vp)->h_sibling;			/* Remember any siblings, to inactivate later */

    (void) hfs_metafilelocking(hfsmp, kHFSExtentsFileID, LK_RELEASE, p);
out2:
    (void) hfs_metafilelocking(hfsmp, kHFSCatalogFileID, LK_RELEASE, p);

out:;

    if (! retval)
   		VTOH(dvp)->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;

    if (dvp == vp) {
        VRELE(vp);
    } else {
        VPUT(vp);
    };

	/* Before leaving, remove any siblings also */
	/* Note: vp and all its values are gone, so do not use */
    if ((! retval) && (tp != NULL)) {
        if (! vget (tp, LK_EXCLUSIVE, p)) {
            VTOH(tp)->h_meta->h_mode = 0;				/* Makes the node go away...see inactive */
            VPUT(tp);
		}
    }

    VPUT(dvp);
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


#if 0	/* Now stubbed out in the vnode ops table with err_mknod */
/*
 * link vnode call
 */
/*
 * HFS filesystems don't know what links are. But since we already called
 * lookup() with create and lockparent, the parent is locked so we
 * have to free it before we return the retval.
#% link		vp	U U U
#% link		targetPar_vp	L U U
#
 vop_link {
     IN WILLRELE struct vnode *vp;
     IN struct vnode *targetPar_vp;
     IN struct componentname *cnp;

     */
static int
hfs_link(ap)
struct vop_link_args /* {
    struct vnode *a_vp;
    struct vnode *a_tdvp;
    struct componentname *a_cnp;
} */ *ap;
{
    DBG_FUNC_NAME("link");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,ap->a_tdvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);

    VOP_UNLOCK(ap->a_vp, 0, ap->a_cnp->cn_proc);

    VOP_ABORTOP(ap->a_tdvp, ap->a_cnp);
    VPUT(ap->a_tdvp);
    
    DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
    return (EOPNOTSUPP);
}
#endif

/*

#% rename	sourcePar_vp	U U U
#% rename	source_vp		U U U
#% rename	targetPar_vp	L U U
#% rename	target_vp		X U U
#
 vop_rename {
     IN WILLRELE struct vnode *sourcePar_vp;
     IN WILLRELE struct vnode *source_vp;
     IN struct componentname *source_cnp;
     IN WILLRELE struct vnode *targetPar_vp;
     IN WILLRELE struct vnode *target_vp;
     IN struct componentname *target_cnp;


     */
/*
* On entry:
*	source's parent directory is unlocked
*	source file or directory is unlocked
*	destination's parent directory is locked
*	destination file or directory is locked if it exists
*
* On exit:
*	all denodes should be released
*
*/

static int
hfs_rename(ap)
struct vop_rename_args  /* {
    struct vnode *a_fdvp;
    struct vnode *a_fvp;
    struct componentname *a_fcnp;
    struct vnode *a_tdvp;
    struct vnode *a_tvp;
    struct componentname *a_tcnp;
} */ *ap;
{
    struct vnode 			*target_vp = ap->a_tvp;
    struct vnode 			*targetPar_vp = ap->a_tdvp;
    struct vnode 			*source_vp = ap->a_fvp;
    struct vnode 			*sourcePar_vp = ap->a_fdvp;
    struct vnode 			*tp;
    struct componentname 	*target_cnp = ap->a_tcnp;
    struct componentname 	*source_cnp = ap->a_fcnp;
    struct proc 			*p = source_cnp->cn_proc;
    struct hfsnode 			*target_hp, *targetPar_hp, *source_hp, *sourcePar_hp;
    u_short					doingdirectory = 0, oldparent = 0, newparent = 0;
    int 					retval = 0;
    struct timeval 			tv;
	struct hfsCatalogInfo catInfo;
    int targetnamelen;
    DBG_VOP_LOCKS_DECL(4);

    DBG_FUNC_NAME("rename");DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Source:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_fvp);DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourcePar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_fdvp);DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("Target:\t"));DBG_VOP_PRINT_VNODE_INFO(ap->a_tvp);DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetPar: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_tdvp);DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("SourceName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_fcnp);DBG_VOP_CONT(("\n"));
    DBG_VOP_CONT(("\t"));DBG_VOP_CONT(("TargetName:\t"));DBG_VOP_PRINT_CPN_INFO(ap->a_tcnp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_fdvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,ap->a_fvp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(2,ap->a_tdvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(3,ap->a_tvp, VOPDBG_LOCKNOTNIL, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    WRITE_CK(ap->a_fdvp, funcname);
    DBG_HFS_NODE_CHECK(ap->a_fdvp);
    DBG_HFS_NODE_CHECK(ap->a_tdvp);

#if DIAGNOSTIC
    if ((target_cnp->cn_flags & HASBUF) == 0 ||
        (source_cnp->cn_flags & HASBUF) == 0)
        panic("hfs_rename: no name");
#endif

    ASSERT((ap->a_fdvp->v_type == VDIR) && (ap->a_tdvp->v_type == VDIR));
    target_hp = targetPar_hp = source_hp = sourcePar_hp = 0;
	targetnamelen = target_cnp->cn_namelen;

    /*
     * Check for cross-device rename.
     */
    if ((source_vp->v_mount != targetPar_vp->v_mount) ||
        (target_vp && (source_vp->v_mount != target_vp->v_mount))) {
        retval = EXDEV;
        goto abortit;
    }

    /*
     * Check for access permissions
     */
    if (target_vp && ((VTOH(target_vp)->h_meta->h_pflags & (IMMUTABLE | APPEND)) ||
                      (VTOH(targetPar_vp)->h_meta->h_pflags & APPEND))) {
        retval = EPERM;
        goto abortit;
    }

    if ((retval = vn_lock(source_vp, LK_EXCLUSIVE, p)))
        goto abortit;

    sourcePar_hp = VTOH(sourcePar_vp);
    source_hp = VTOH(source_vp);
    oldparent = H_FILEID(sourcePar_hp);
    if ((source_hp->h_meta->h_pflags & (IMMUTABLE | APPEND)) || (sourcePar_hp->h_meta->h_pflags & APPEND)) {
        VOP_UNLOCK(source_vp, 0, p);
        retval = EPERM;
        goto abortit;
    }

    /*
     * Be sure we are not renaming ".", "..", or an alias of ".". This
     * leads to a crippled directory tree.  It's pretty tough to do a
     * "ls" or "pwd" with the "." directory entry missing, and "cd .."
     * doesn't work if the ".." entry is missing.
     */
    if ((source_hp->h_meta->h_mode & IFMT) == IFDIR) {
        if ((source_cnp->cn_namelen == 1 && source_cnp->cn_nameptr[0] == '.')
            || sourcePar_hp == source_hp
            || (source_cnp->cn_flags&ISDOTDOT)
            || (source_hp->h_meta->h_nodeflags & IN_RENAME)) {
            VOP_UNLOCK(source_vp, 0, p);
            retval = EINVAL;
            goto abortit;
        }
        source_hp->h_meta->h_nodeflags |= IN_RENAME;
        doingdirectory = TRUE;
    }

    // Transit between abort and bad

    targetPar_hp = VTOH(targetPar_vp);
    target_hp = target_vp ? VTOH(target_vp) : NULL;
    newparent = H_FILEID(targetPar_hp);

    retval = VOP_ACCESS(source_vp, VWRITE, target_cnp->cn_cred, target_cnp->cn_proc);
    if (doingdirectory && (newparent != oldparent)) {
        if (retval)		/* write access check above */
            goto bad;
    }

    /*
     * If the destination exists, then be sure its type (file or dir)
     * matches that of the source.  And, if it is a directory make sure
     * it is empty.  Then delete the destination.
     */
    if (target_vp) {
        if (target_hp->h_dev != targetPar_hp->h_dev || target_hp->h_dev != source_hp->h_dev)
            panic("rename: EXDEV");

        /*
         * If the parent directory is "sticky", then the user must
         * own the parent directory, or the destination of the rename,
         * otherwise the destination may not be changed (except by
                                                         * root). This implements append-only directories.
         */
        if ((targetPar_hp->h_meta->h_mode & S_ISTXT) && target_cnp->cn_cred->cr_uid != 0 &&
            target_cnp->cn_cred->cr_uid != targetPar_hp->h_meta->h_uid &&
            target_hp->h_meta->h_uid != target_cnp->cn_cred->cr_uid) {
            retval = EPERM;
            goto bad;
        }

		/*
		 * VOP_REMOVE will vput targetPar_vp so we better bump 
		 * its ref count and relockit, always set target_vp to
		 * NULL afterwards to indicate that were done with it.
		 */
		VREF(targetPar_vp);
		if (target_vp->v_type == VREG) cache_purge(VTOH(target_vp)->h_relative);
		retval = VOP_REMOVE(targetPar_vp, target_vp, target_cnp);
		(void) vn_lock(targetPar_vp, LK_EXCLUSIVE, p);

		target_vp = NULL;
        target_hp = NULL;		
		
        if (retval) goto bad;

    } else {
        if (targetPar_hp->h_dev != source_hp->h_dev)
            panic("rename: EXDEV");
    };


	if (newparent != oldparent)
		vn_lock(sourcePar_vp, LK_EXCLUSIVE | LK_RETRY, p);

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
	if (retval) goto badcataloglock;
	
	/* remove the existing entry from the namei cache: */
	if (source_vp->v_type == VREG) cache_purge(VTOH(source_vp)->h_relative);

	retval = hfsMoveRename(	HTOVCB(source_hp), H_DIRID(source_hp), H_NAME(source_hp),
							H_FILEID(VTOH(targetPar_vp)), target_cnp->cn_nameptr, &H_HINT(source_hp));

	if (retval == 0) {	
	    /* Look up the catalog entry just renamed since it might have been auto-decomposed */
	    catInfo.hint = H_HINT(source_hp);
	    retval = hfsLookup(HTOVCB(source_hp), H_FILEID(VTOH(targetPar_vp)), target_cnp->cn_nameptr, -1, 0, &catInfo);
		targetnamelen = strlen(catInfo.spec.name);
    }

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(source_vp), kHFSCatalogFileID, LK_RELEASE, p);

	if (newparent != oldparent)
		VOP_UNLOCK(sourcePar_vp, 0, p);

	if (retval)  goto bad;

	H_DIRID(source_hp) = H_FILEID(VTOH(targetPar_vp));

    if (targetnamelen > source_hp->h_meta->h_namelen) {
        if (source_hp->h_meta->h_nodeflags & IN_LONGNAME)
          {
            FREE(H_NAME(source_hp), M_HFSNODE);
            MALLOC(H_NAME(source_hp), char *, targetnamelen+1, M_HFSNODE, M_WAITOK);
          }
        else if (targetnamelen > MAXHFSVNODELEN) {
            MALLOC(H_NAME(source_hp), char *, targetnamelen+1, M_HFSNODE, M_WAITOK);
            source_hp->h_meta->h_nodeflags |= IN_LONGNAME;
        }
    };

    source_hp->h_meta->h_namelen = targetnamelen;
    copystr(catInfo.spec.name, H_NAME(source_hp), targetnamelen+1, NULL);
    source_hp->h_meta->h_nodeflags &= ~IN_RENAME;

#if 0
    source_hp->h_meta->h_nodeflags |= IN_CHANGE;
    tv = time;
    if ((retval = VOP_UPDATE(source_vp, &tv, &tv, 1))) {
        VOP_UNLOCK(source_vp, 0, p);
        goto bad;
    }
#endif

	/* Copy common fcb info to complex node */
	tp = source_hp->h_relative;
    if (tp) {
		H_FILEID(VTOH(tp)) = H_FILEID(source_hp);
	}

	/* Now copy common fcb info to siblings */
	tp = source_hp->h_sibling;
    while (tp) {
		H_FILEID(VTOH(tp)) = H_FILEID(source_hp);
        tp = VTOH(tp)->h_sibling;
        if (tp == source_vp)
			break;
	}

	/* Timestamp the parents, if this is a move */
    if (newparent != oldparent) {
        targetPar_hp->h_meta->h_nodeflags |= IN_UPDATE;
        sourcePar_hp->h_meta->h_nodeflags |= IN_UPDATE;
        tv = time;
        HFSTIMES(targetPar_hp, &tv, &tv);
        HFSTIMES(sourcePar_hp, &tv, &tv);
        };

	VPUT(targetPar_vp);
	VRELE(sourcePar_vp);
    VPUT(source_vp);

    DBG_VOP_LOCKS_TEST(retval);
    if (retval != E_NONE) {
        DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
    }
    return (retval);

badcataloglock:
	if (newparent != oldparent)
		VOP_UNLOCK(sourcePar_vp, 0, p);
	
bad:;
    if (retval && doingdirectory)
    	source_hp->h_meta->h_nodeflags &= ~IN_RENAME;

    if (targetPar_vp == target_vp)
	    VRELE(targetPar_vp);
    else
	    VPUT(targetPar_vp);

    if (target_vp)
	    VPUT(target_vp);

	VRELE(sourcePar_vp);

    if (VOP_ISLOCKED(source_vp))
        VPUT(source_vp);
	else
    	VRELE(source_vp);

    DBG_VOP_LOCKS_TEST(retval);
    if (retval != E_NONE) {
        DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
    }
    return (retval);

abortit:;

    VOP_ABORTOP(targetPar_vp, target_cnp); /* XXX, why not in NFS? */

    if (targetPar_vp == target_vp)
	    VRELE(targetPar_vp);
    else
	    VPUT(targetPar_vp);

    if (target_vp)
	    VPUT(target_vp);

    VOP_ABORTOP(sourcePar_vp, source_cnp); /* XXX, why not in NFS? */

	VRELE(sourcePar_vp);
    VRELE(source_vp);

    DBG_VOP_LOCKS_TEST(retval);
    if (retval != E_NONE) {
        DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT(("\tReturning with error %d\n",retval));
    }
    return (retval);
}



/*
 * Mkdir system call
#% mkdir	dvp	L U U
#% mkdir	vpp	- L -
#
 vop_mkdir {
     IN WILLRELE struct vnode *dvp;
     OUT struct vnode **vpp;
     IN struct componentname *cnp;
     IN struct vattr *vap;

     We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is
    a previous error.

*/

int
hfs_mkdir(ap)
struct vop_mkdir_args /* {
    struct vnode *a_dvp;
    struct vnode **a_vpp;
    struct componentname *a_cnp;
    struct vattr *a_vap;
} */ *ap;
{
	struct proc		*p = CURRENT_PROC;
    int				retval;
    int				mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);

    DBG_FUNC_NAME("mkdir");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_LOCKED, VOPDBG_IGNORE, VOPDBG_POS);

    DBG_VOP(("%s: parent 0x%x (%s)  ap->a_cnp->cn_nameptr %s\n", funcname, (u_int)VTOH(ap->a_dvp), H_NAME(VTOH(ap->a_dvp)), ap->a_cnp->cn_nameptr));
    WRITE_CK( ap->a_dvp, funcname);
    DBG_HFS_NODE_CHECK(ap->a_dvp);
    ASSERT(ap->a_dvp->v_type == VDIR);

 	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
    if (retval != E_NONE) {
    	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
		VPUT(ap->a_dvp);
        DBG_VOP_LOCKS_TEST( retval);
        return (retval);
    }

	/* Create the vnode */
    DBG_ASSERT((ap->a_cnp->cn_flags & SAVESTART) == 0);
	retval = hfs_makenode(mode, ap->a_dvp, ap->a_vpp, ap->a_cnp);
    DBG_VOP_UPDATE_VP(1, *ap->a_vpp);

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_RELEASE, p);

    if (retval != E_NONE) {
        DBG_ERR(("%s: hfs_makenode FAILED: %s, %s\n", funcname, ap->a_cnp->cn_nameptr, H_NAME(VTOH(ap->a_dvp))));
        DBG_VOP_LOCKS_TEST(retval);
        return (retval);		
    }

    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}

/*
 * Rmdir system call.
#% rmdir	dvp	L U U
#% rmdir	vp	L U U
#
 vop_rmdir {
     IN WILLRELE struct vnode *dvp;
     IN WILLRELE struct vnode *vp;
     IN struct componentname *cnp;

     */

int
hfs_rmdir(ap)
struct vop_rmdir_args /* {
    struct vnode *a_dvp;
    struct vnode *a_vp;
    struct componentname *a_cnp;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct vnode *dvp = ap->a_dvp;
    struct hfsnode *hp = VTOH(vp);
	struct proc *p = CURRENT_PROC;
    int retval;
    DBG_FUNC_NAME("rmdir");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP(("\tParent: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);DBG_VOP_CONT(("\n"));
    DBG_VOP(("\tTarget: "));DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP(("\tTarget Name: "));DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);

    if (dvp == vp) {
        VRELE(vp);
        VPUT(vp);
        DBG_VOP_LOCKS_TEST(EINVAL);
        return (EINVAL);
    }

    if (vp->v_usecount > 2) {
        DBG_ERR(("%s: dir is busy, usecount is %d\n", funcname, vp->v_usecount ));
		retval = EBUSY;
		goto Err_Exit;
    }

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
	if (retval != E_NONE) {
		goto Err_Exit;
	}

	/* remove the entry from the namei cache: */
	cache_purge(vp);

	/* remove entry from catalog */
    retval = hfsDelete (HTOVCB(hp), H_DIRID(hp), H_NAME(hp), FALSE, H_HINT(hp));
    hp->h_meta->h_mode = 0;				/* Makes the vnode go away...see inactive */

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(vp), kHFSCatalogFileID, LK_RELEASE, p);

	/* Set the parent to be updated */
    if (! retval)
    	VTOH(dvp)->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;

Err_Exit:;
    if (dvp != 0) 
		VPUT(dvp);
    VPUT(vp);

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}

/*
 * symlink -- make a symbolic link
#% symlink	dvp	L U U
#% symlink	vpp	- U -
#
# XXX - note that the return vnode has already been VRELE'ed
#	by the filesystem layer.  To use it you must use vget,
#	possibly with a further namei.
#
 vop_symlink {
     IN WILLRELE struct vnode *dvp;
     OUT WILLRELE struct vnode **vpp;
     IN struct componentname *cnp;
     IN struct vattr *vap;
     IN char *target;

     We are responsible for freeing the namei buffer, it is done in hfs_makenode(), unless there is
    a previous error.


*/

int
hfs_symlink(ap)
    struct vop_symlink_args /* {
        struct vnode *a_dvp;
        struct vnode **a_vpp;
        struct componentname *a_cnp;
        struct vattr *a_vap;
        char *a_target;
    } */ *ap;
{
    register struct vnode *vp, **vpp = ap->a_vpp;
	struct proc *p = CURRENT_PROC;
    int len, retval;
    DBG_FUNC_NAME("symlink");
    DBG_VOP_LOCKS_DECL(2);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    DBG_VOP_LOCKS_INIT(1,*ap->a_vpp, VOPDBG_IGNORE, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_POS);

    if (VTOVCB(ap->a_dvp)->vcbSigWord != kHFSPlusSigWord) {
    	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
        VPUT(ap->a_dvp);
        DBG_VOP((" ...sorry HFS disks don't support symbolic links.\n"));
        DBG_VOP_LOCKS_TEST(EOPNOTSUPP);
        return (EOPNOTSUPP);
    }

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_EXCLUSIVE, p);
	if (retval != E_NONE) {
    	VOP_ABORTOP(ap->a_dvp, ap->a_cnp);
    	VPUT(ap->a_dvp);
        DBG_VOP_LOCKS_TEST( retval);
        return (retval);
	}

	/* Create the vnode */
	retval = hfs_makenode(IFLNK | ap->a_vap->va_mode, ap->a_dvp, vpp, ap->a_cnp);
    DBG_VOP_UPDATE_VP(1, *ap->a_vpp);

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(ap->a_dvp), kHFSCatalogFileID, LK_RELEASE, p);

    if (retval != E_NONE) {
        DBG_VOP_LOCKS_TEST(retval);
        return (retval);
	}


    vp = *vpp;
    len = strlen(ap->a_target);
    retval = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
                     UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int *)0,
                     (struct proc *)0);


    VPUT(vp);
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}

/*
 * Dummy dirents to simulate the "." and ".." entries of the directory
 * in a hfs filesystem.  HFS doesn't provide these. Note that each entry
 * must be the same size as a hfs directory entry (44 bytes).
 */
static hfsdirentry  rootdots[2] = {
    {
        1,								/* d_fileno			 */
        sizeof(struct hfsdirentry),		/* d_reclen			 */
        DT_DIR,							/* d_type			 */
        1,								/* d_namlen			 */
        "."								/* d_name			 */
    },
    {
        1,								/* d_fileno			 */
        sizeof(struct hfsdirentry),		/* d_reclen			 */
        DT_DIR,							/* d_type			 */
        2,								/* d_namlen			 */
        ".."							/* d_name			 */
    }
};


/*	4.3 Note:
*	There is some confusion as to what the semantics of uio_offset are.
*	In ufs, it represents the actual byte offset within the directory
*	"file."  HFS, however, just uses it as an entry counter - essentially
*	assuming that it has no meaning except to the hfs_readdir function.
*	This approach would be more efficient here, but some callers may
*	assume the uio_offset acts like a byte offset.  NFS in fact
*	monkeys around with the offset field a lot between readdir calls.
*
*	We could also speed things up by remembering the last offset position,
*	but its not clear that that would buy us much (do readdirs need to
                                                 *	be fast?) ??
*
*	The use of the resid uiop->uio_resid and uiop->uio_iov->iov_len
*	fields is a mess as well.  The libc function readdir() returns
*	NULL (indicating the end of a directory) when either
*	the getdirentries() syscall (which calls this and returns
                               *	the size of the buffer passed in less the value of uiop->uio_resid)
*	returns 0, or a direct record with a d_reclen of zero.
*	nfs_server.c:rfs_readdir(), on the other hand, checks for the end
*	of the directory by testing uiop->uio_resid == 0.  The solution
*	is to pad the size of the last struct direct in a given
*	block to fill the block if we are not at the end of the directory.
*/

/*
 * NOTE: We require a minimal buffer size of DIRBLKSIZ for two reasons. One, it is the same value
 * returned be stat() call as the block size. This is mentioned in the man page for getdirentries():
 * "Nbytes must be greater than or equal to the block size associated with the file,
 * see stat(2)". Might as well settle on the same size of ufs. Second, this makes sure there is enough
 * room for the . and .. entries that have to added manually.
 */

/* 			
#% readdir	vp	L L L
#
vop_readdir {
    IN struct vnode *vp;
    INOUT struct uio *uio;
    IN struct ucred *cred;
    INOUT int *eofflag;
    OUT int *ncookies;
    INOUT u_long **cookies;
    */


static int
hfs_readdir(ap)
struct vop_readdir_args /* {
    struct vnode *vp;
    struct uio *uio;
    struct ucred *cred;
    int *eofflag;
    int *ncookies;
    u_long **cookies;
} */ *ap;
{
    register struct uio *uio = ap->a_uio;
    struct hfsnode 		*hp = VTOH(ap->a_vp);
	struct proc			*p = CURRENT_PROC;
    ExtendedVCB 		*vcb = HTOVCB(hp);
    off_t 				off = uio->uio_offset;
    size_t 				count;
    size_t				lost = 0;
    CatalogNodeData 	nodeData;
    struct hfsdirentry	catalogEntry;
    FSSpec 				fileSpec;	/* 264 bytes */
    UInt32 				dirID = H_FILEID(hp);
    UInt32				index = 0;
    UInt32				origOffset;
    UInt32				hint;
    Boolean				eofReached = FALSE;
    int					retval = 0;
    OSErr				result = noErr;

    DBG_FUNC_NAME("readdir");
    DBG_VOP_LOCKS_DECL(1);

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_HFS_NODE_CHECK(ap->a_vp);

    /* We assume it's all one big buffer... */
    if (uio->uio_iovcnt > 1) DEBUG_BREAK_MSG(("hfs_readdir: uio->uio_iovcnt = %d?\n", uio->uio_iovcnt));

    origOffset = uio->uio_offset;
    count = uio->uio_resid;
    /* Make sure we don't return partial entries.  */
    count -= ((uio->uio_offset + count) % sizeof(hfsdirentry));
    if (count <= 0) {
        DBG_ERR(("%s: Not enough buffer to read in entries\n",funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return (EINVAL);
    }

    /* Adjust uio to be on correct boundaries */
    lost = uio->uio_resid - count;
    uio->uio_resid = count;
    uio->uio_iov->iov_len = count;

    DBG_VOP(("%s: offset Ox%lX, bytes Ox%lX\n",funcname,
             (u_long)uio->uio_offset, uio->uio_iov->iov_len));

    /* Create the entries for . and ..
        * We do it here since we are assuming that the directory is guarenteed to exist
        */
    index = 0;
    if (uio->uio_offset < (2 * sizeof(struct hfsdirentry))) {
        if ((uio->uio_offset > 0) && (uio->uio_offset != sizeof(struct hfsdirentry))) {
            retval = EINVAL;
            goto Exit;
        }
        index = 2;
        if (uio->uio_offset == sizeof(struct hfsdirentry)) {
            index = 1;
        }
        retval = uiomove((caddr_t) (rootdots + uio->uio_offset), index * sizeof(struct hfsdirentry), uio);
    };

    /* Compute the starting index in the directory */
    index = (uio->uio_offset - sizeof(struct hfsdirentry)) / sizeof(struct hfsdirentry);

	/* lock catalog b-tree */
	retval = hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p);
    if (retval != E_NONE) {
		goto Exit;
    };

    hint = kNoHint;
    while (uio->uio_resid > sizeof(struct hfsdirentry))
      {
        result = GetCatalogOffspring(vcb, dirID, index, &fileSpec, &nodeData, &hint);
        if (result != noErr) {
            if (result == cmNotFound) {
                eofReached = TRUE;
                if (origOffset == uio->uio_offset) {		/* we were already past eof */
                    uio->uio_offset = 0;
                    retval = E_NONE;
					/* unlock catalog b-tree */
 					(void) hfs_metafilelocking(VTOHFS(ap->a_vp),
 												kHFSCatalogFileID, LK_RELEASE, p);
					goto Exit;
                }
                result = noErr;
            }
            retval = MacToVFSError(result);
            break;
        }

        /* Copy entry into the buffer */
        catalogEntry.fileno = nodeData.nodeID;
        catalogEntry.reclen = sizeof(struct hfsdirentry);
        catalogEntry.type = (nodeData.nodeType == kCatalogFolderNode) ? DT_DIR : DT_REG;
        catalogEntry.namelen = strlen(fileSpec.name);
		(void) strncpy(catalogEntry.name, fileSpec.name, sizeof(catalogEntry.name));

        /* copy this entry into the user's buffer: */
        retval = uiomove((caddr_t) &catalogEntry, sizeof(struct hfsdirentry), uio);
        if (retval != E_NONE) {
            DBG_ERR(("%s: uiomove returned %d.\n", funcname, retval));
            break;
        };
        ++index;
      };

	/* unlock catalog b-tree */
	(void) hfs_metafilelocking(VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p);

    if (retval != E_NONE) {
        DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname, retval,
                H_FILEID(hp), H_NAME(hp)));
		goto Exit;
    }
    else if (vcb->vcbSigWord == kHFSPlusSigWord)
    	hp->h_meta->h_nodeflags |= IN_ACCESS;

    /* Bake any cookies */
    if (!retval && ap->a_ncookies != NULL) {
        struct dirent* dpStart;
        struct dirent* dpEnd;
        struct dirent* dp;
        int ncookies;
        u_long *cookies;
        u_long *cookiep;

        /*
        * Only the NFS server uses cookies, and it loads the
        * directory block into system space, so we can just look at
        * it directly.
        */
        if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
            panic("hfs_readdir: unexpected uio from NFS server");
        dpStart = (struct dirent *)
            (uio->uio_iov->iov_base - (uio->uio_offset - off));
        dpEnd = (struct dirent *) uio->uio_iov->iov_base;
        for (dp = dpStart, ncookies = 0;
            dp < dpEnd && dp->d_reclen != 0;
            dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
            ncookies++;
        MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
            M_WAITOK);
        for (dp = dpStart, cookiep = cookies;
            dp < dpEnd;
            dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
            off += dp->d_reclen;
            *cookiep++ = (u_long) off;
        }
        *ap->a_ncookies = ncookies;
        *ap->a_cookies = cookies;
    }

Exit:;
	uio->uio_resid += lost;

    if (ap->a_eofflag)
        *ap->a_eofflag = eofReached;

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


/*
 * readdirattr operation
 */

/* 			

#
#% readdirattr	vp	L L L
#
vop_readdirattr {
	IN struct vnode *vp;
	IN struct attrlist *alist;
	INOUT struct uio *uio;
	INOUT int index;
	INOUT int *eofflag;
	OUT u_long *ncookies;
	INOUT u_long **cookies;
	IN struct ucred *cred;
};

*/


static int
hfs_readdirattr(ap)
struct vop_readdirattr_args /* {
    struct vnode *vp;
    struct attrlist *alist;
    struct uio *uio;
    int index;
    int *eofflag;
    u_long *ncookies;
    u_long **cookies;
    struct ucred *cred;
} */ *ap;
{
    struct vnode 		*vp = ap->a_vp;
    struct attrlist 	*alist = ap->a_alist;
    register struct uio *uio = ap->a_uio;
    struct hfsnode 		*hp = VTOH(ap->a_vp);
    ExtendedVCB 		*vcb = HTOVCB(hp);
    off_t 				off = uio->uio_offset;
    size_t 				count;
    struct hfsCatalogInfo catalogInfo;
    struct hfsCatalogInfo *catInfoPtr = NULL;
    UInt32 				dirID = H_FILEID(hp);
    UInt32				index = 0;
    OSErr				result = noErr;
    UInt32				origOffset;
    Boolean				eofReached = FALSE;
    int					retval = 0;
    u_long 				fixedblocksize;
    u_long 				maxattrblocksize;
	u_long				currattrbufsize;
    void 				*attrbufptr = NULL;
    void 				*attrptr;
    void 				*varptr;
    DBG_FUNC_NAME("readdirattr");
    DBG_VOP_LOCKS_DECL(1);
    
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_HFS_NODE_CHECK(ap->a_vp);

    if ((alist->bitmapcount != ATTR_BIT_MAP_COUNT) ||
        ((alist->commonattr & ~ATTR_CMN_VALIDMASK) != 0) ||
        ((alist->dirattr & ~ATTR_DIR_VALIDMASK) != 0) ||
        ((alist->fileattr & ~ATTR_FILE_VALIDMASK) != 0) ||
        ((alist->forkattr & ~ATTR_FORK_VALIDMASK) != 0)) {
        DBG_ERR(("%s: bad attrlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    };

    /* Requesting volume information is illegal : */
    if (alist->volattr != 0) {
        DBG_ERR(("%s: requesting volume attribute is illegal\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    };

    /* Reject requests for unsupported options for now: */
    if ((alist->commonattr & (ATTR_CMN_NAMEDATTRCOUNT | ATTR_CMN_NAMEDATTRLIST)) ||
        (alist->fileattr & (ATTR_FILE_FILETYPE | ATTR_FILE_FORKCOUNT | ATTR_FILE_FORKLIST))) {
        DBG_ERR(("%s: illegal bits in attlist\n", funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return EINVAL;
    };

    origOffset 	= uio->uio_offset;
    count 		= uio->uio_resid;

  /* Make sure we don't return partial entries.  */
    count -= ((uio->uio_offset + count) % sizeof(hfsdirentry));
    if (count <= 0) {
        DBG_ERR(("%s: Not enough buffer to read in entries\n",funcname));
        DBG_VOP_LOCKS_TEST(EINVAL);
        return (EINVAL);
    }

    DBG_VOP(("%s: offset Ox%lX, bytes Ox%lX\n",funcname,
             (u_long)uio->uio_offset, (u_long)uio->uio_iov->iov_len));

	/* Preflight and alloc buffer to do packings */
    maxattrblocksize = fixedblocksize = (sizeof(u_long) + AttributeBlockSize(alist));	/* u_long for length longword */
    if (alist->commonattr & ATTR_CMN_NAME) maxattrblocksize += NAME_MAX + 1;
    if (alist->commonattr & ATTR_CMN_NAMEDATTRLIST) maxattrblocksize += 0;			/* XXX PPD */
    if (alist->fileattr & ATTR_FILE_FORKLIST) maxattrblocksize += 0;				/* XXX PPD */

    DBG_VOP(("%s: allocating Ox%lX byte buffer (Ox%lX + Ox%lX) for attributes...\n",
                funcname,
             	maxattrblocksize,
                fixedblocksize,
             	maxattrblocksize - fixedblocksize));
    MALLOC(attrbufptr, void *, maxattrblocksize, M_TEMP, M_WAITOK);
    attrptr = attrbufptr;
    varptr = attrbufptr + fixedblocksize;					/* Point to variable-length storage */
    DBG_VOP(("%s: attrptr = 0x%08X, varptr = 0x%08X...\n", funcname, (u_int)attrptr, (u_int)varptr));

#if 0
    /* Create the entries for . and ..
     * We do it here since we are assuming that the directory is guarenteed to exist
     */
    index = 0;
    if (uio->uio_offset < (2 * sizeof(struct hfsdirentry))) {
        if ((uio->uio_offset > 0) && (uio->uio_offset != sizeof(struct hfsdirentry))) {
            retval = EINVAL;
            goto Exit;
        }
        index = 2;
        if (uio->uio_offset == sizeof(struct hfsdirentry)) {
            index = 1;
        }
        retval = uiomove((caddr_t) (rootdots + uio->uio_offset), index * sizeof(struct hfsdirentry), uio);
    };
#endif

   /* Compute the starting index in the directory */
    index = (uio->uio_offset - sizeof(struct hfsdirentry)) / sizeof(struct hfsdirentry);

    catalogInfo.hint = kNoHint;
    while (uio->uio_resid > sizeof(struct hfsdirentry))
      {

        result = GetCatalogOffspring(vcb, dirID, index, &catalogInfo.spec, &catalogInfo.nodeData, &catalogInfo.hint);
        if (result != noErr) {
            if (result == cmNotFound) {
                eofReached = TRUE;
                if (origOffset == uio->uio_offset) {		/* we were already past eof */
                    uio->uio_offset = 0;
                    retval = E_NONE;
                    goto Err_Exit;
                }
                result = noErr;
            }
            retval = MacToVFSError(result);
            break;
        }
        catInfoPtr = &catalogInfo;

        *((u_long *)attrptr)++ = 0;			/* Reserve space for length field */
        PackAttributeBlock(alist, vp, catInfoPtr, &attrptr, &varptr);
        currattrbufsize = *((u_long *)attrbufptr) = (varptr - attrbufptr);		/* Store length of fixed + var block */

        /* Make sure that there is enough room to copy to */
        if (currattrbufsize > uio->uio_resid)
          {
            if (uio->uio_offset == origOffset)
              {
                DBG_ERR(("%s: Not enough buffer to read in entries\n",funcname));
                retval = EINVAL;
                goto Err_Exit;
              }
            break;
          }

        DBG_VOP(("%s: copying Ox%lX bytes to user address 0x%08X.\n",funcname, currattrbufsize, (u_int)ap->a_uio->uio_iov->iov_base));
        retval = uiomove((caddr_t)attrbufptr, currattrbufsize, ap->a_uio);
        if (retval != E_NONE) {
            DBG_ERR(("%s: error %d on uiomove.\n",funcname, retval));
            break;
        };
        attrptr = (void *)((u_long)attrptr + currattrbufsize);
        ++index;
      };

   if (retval != E_NONE) {
        DBG_ERR(("%s: retval %d when trying to read directory %ld: %s\n",funcname, retval,
                H_FILEID(hp), H_NAME(hp)));
       retval = EINVAL;
    };

    /* Bake any cookies */
    if (!retval && ap->a_ncookies != NULL) {
        struct dirent* dpStart;
        struct dirent* dpEnd;
        struct dirent* dp;
        int ncookies;
        u_long *cookies;
        u_long *cookiep;

        /*
        * Only the NFS server uses cookies, and it loads the
        * directory block into system space, so we can just look at
        * it directly.
        */
        if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
            panic("hfs_readdir: unexpected uio from NFS server");
        dpStart = (struct dirent *)
            (uio->uio_iov->iov_base - (uio->uio_offset - off));
        dpEnd = (struct dirent *) uio->uio_iov->iov_base;
        for (dp = dpStart, ncookies = 0;
            dp < dpEnd && dp->d_reclen != 0;
            dp = (struct dirent *)((caddr_t)dp + dp->d_reclen))
            ncookies++;
        MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
            M_WAITOK);
        for (dp = dpStart, cookiep = cookies;
            dp < dpEnd;
            dp = (struct dirent *)((caddr_t) dp + dp->d_reclen)) {
            off += dp->d_reclen;
            *cookiep++ = (u_long) off;
        }
        *ap->a_ncookies = ncookies;
        *ap->a_cookies = cookies;
    }

Err_Exit:;

    if (attrbufptr != NULL)
		FREE(attrbufptr, M_TEMP);

    if (ap->a_eofflag)
    	*ap->a_eofflag = eofReached;

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


/*
 * Return target name of a symbolic link
#% readlink	vp	L L L
#
 vop_readlink {
     IN struct vnode *vp;
     INOUT struct uio *uio;
     IN struct ucred *cred;
     */

int
hfs_readlink(ap)
struct vop_readlink_args /* {
struct vnode *a_vp;
struct uio *a_uio;
struct ucred *a_cred;
} */ *ap;
{
    int retval;
    DBG_FUNC_NAME("readlink");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);
    retval = VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred);
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);

}


/*
 * hfs abort op, called after namei() when a CREATE/DELETE isn't actually
 * done. If a buffer has been saved in anticipation of a CREATE, delete it.
#% abortop	dvp	= = =
#
 vop_abortop {
     IN struct vnode *dvp;
     IN struct componentname *cnp;

     */

/* ARGSUSED */

static int
hfs_abortop(ap)
struct vop_abortop_args /* {
    struct vnode *a_dvp;
    struct componentname *a_cnp;
} */ *ap;
{
    DBG_FUNC_NAME("abortop");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_dvp);
    DBG_VOP_PRINT_CPN_INFO(ap->a_cnp);DBG_VOP_CONT(("\n"));


    DBG_VOP_LOCKS_INIT(0,ap->a_dvp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);

    if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
        FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
    }
    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}

// int	prthfsactive = 0;		/* 1 => print out reclaim of active vnodes */

/*
#% inactive	vp	L U U
#
 vop_inactive {
     IN struct vnode *vp;
     IN struct proc *p;

     */

static int
hfs_inactive(ap)
struct vop_inactive_args /* {
    struct vnode *a_vp;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct hfsnode *hp = VTOH(vp);
    struct proc *p = ap->a_p;
    struct timeval tv;
    extern int prtactive;

    DBG_FUNC_NAME("inactive");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);

    /*
        NOTE: vnodes need careful handling because fork vnodes that failed to be created
              in their entirity could be getting cleaned up here, in which case h_meta == NULL...
     */
    if (prtactive && vp->v_usecount <= 0)
        vprint("hfs_inactive: pushing active", vp);

    if (vp->v_usecount != 0)
        DBG_VOP(("%s: bad usecount = %d\n",funcname,vp->v_usecount ));

    /* 
	 * Skip all complex nodes 
	 * At this point, there should be no fork nodes existing for its complex 
	 */
    if (vp->v_type == VCPLX) {
#if DIAGNOSTIC
        if (vp->v_usecount == 0) {
            struct vnode *tvp;
            tvp  = hfs_vhashget(hp->h_dev, H_FILEID(hp), kDataFork);
            DBG_ASSERT(tvp == NULL);
            if (tvp)
                vput (tvp);
            tvp  = hfs_vhashget(hp->h_dev, H_FILEID(hp), kRsrcFork);
            DBG_ASSERT(tvp == NULL);
            if (tvp)
                vput (tvp);
        }
#endif	/* DIAGNOSTIC */
        hp->h_meta->h_mode = 0;				/* Makes the node go away */
        goto out;
    }

	/*
     * Ignore inodes related to stale file handles.
     */
    if ((vp->v_type == VNON) || (hp->h_meta->h_mode == 0))
        goto out;
	
    if (hp->h_meta->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
        tv = time;
        VOP_UPDATE(vp, &tv, &tv, 0);
    }

out:
    VOP_UNLOCK(vp, 0, p);
    /*
     * If we are done with the inode, reclaim it
     * so that it can be reused immediately.
     */
    if ((vp->v_type == VNON) || (hp->h_meta->h_mode == 0))
        vrecycle(vp, (struct slock *)0, p);

    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}

/*
 Ignored since the locks are gone......
#% reclaim	vp	U I I
#
 vop_reclaim {
     IN struct vnode *vp;
     IN struct proc *p;

     */

static int
hfs_reclaim(ap)
struct vop_reclaim_args /* {
    struct vnode *a_vp;
} */ *ap;
{
    struct vnode *vp = ap->a_vp;
    struct vnode *tp = NULL;
    struct hfsnode *hp = VTOH(vp);
    extern int prtactive;
    DBG_FUNC_NAME("reclaim");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));

    DBG_VOP_LOCKS_INIT(0, ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);

    /*
        NOTE: vnodes need careful handling because fork vnodes that failed to be
              created in their entirity could be getting cleaned up here, in which
              case v_type == VNON and h_meta == NULL...
     */

    if (prtactive && vp->v_usecount != 0)
        vprint("hfs_reclaim(): pushing active", vp);

    hfs_vhashrem(hp);	

    /* release the file meta */
    if ((vp->v_type == VREG) || (vp->v_type == VLNK)) {
        DBG_ASSERT(hp->h_meta != NULL);

        if (H_FORKTYPE(hp) == kSysFile) {
            /* XXX SER Here we release meta for kSysFile, which do not need it!!! */
            FREE(hp->h_meta, M_HFSNODE);
            hp->h_meta = NULL;
        }
        else {
            HFSFILEMETA_LOCK_EXCLUSIVE(hp, CURRENT_PROC);
            hp->h_meta->h_usecount--;							/* XXX SER eventually remove h_usecount */

			/*
			 * if there are still more forks, make the fork ptr point to another
			 */
            if ((hp->h_meta->h_usecount > 0) && (hp->h_meta->h_fork == vp)) {
                hp->h_meta->h_fork = hp->h_sibling;
			}
				

            if ((H_FORKTYPE(hp) == kDataFork) && hp->h_relative) {
                DBG_ASSERT(VTOH(hp->h_relative)->h_relative != NULL);
                VTOH(hp->h_relative)->h_relative = NULL;		/* Mark the default node as NULL from a complex view */
            };

            if (hp->h_sibling) {
                tp = vp;
                do {
                    tp = VTOH(tp)->h_sibling;
                } while (VTOH(tp)->h_sibling != vp);
                VTOH(tp)->h_sibling = hp->h_sibling;
                if (VTOH(tp)->h_sibling == tp)					/* pointing to ourselves */
                    VTOH(tp)->h_sibling = NULL;
                hp->h_sibling = NULL;
            };

            HFSFILEMETA_UNLOCK(hp, CURRENT_PROC);
            hp->h_meta = NULL;

            /* h_relative could be NULL, if a force unmount was done, and the complex node is already gone */
            if (hp->h_relative) {
                DBG_ASSERT(H_FORKTYPE(VTOH(hp->h_relative)) == kDirCmplx);
                DBG_ASSERT((hp->h_relative)->v_type == VCPLX);
                VRELE (hp->h_relative);								/* release the complex node */
                hp->h_relative = NULL;
			}
        }
    }
    else if (vp->v_type == VCPLX) {
#if DIAGNOSTIC
		/* Unless a force unmount, no other forks should exist */
        if (vp->v_usecount == 0) {
            struct vnode *tvp;
            tvp  = hfs_vhashget(hp->h_dev, H_FILEID(hp), kDataFork);
            DBG_ASSERT(tvp == NULL);
            if (tvp)
                vput (tvp);
            tvp  = hfs_vhashget(hp->h_dev, H_FILEID(hp), kRsrcFork);
            DBG_ASSERT(tvp == NULL);
            if (tvp)
                vput (tvp);
        }
#endif	/* DIAGNOSTIC */

        DBG_ASSERT(hp->h_meta != NULL);
		tp = hp->h_meta->h_fork;
		if (tp == NULL) {
				/* No more forks, so do nothing here */
		     }
		 else {
		 	/* This should only occur when a force unmount occurs */
		 	/* so, usually, no other forks should exist */
		 	VTOH(tp)->h_relative = NULL;
		 	if (VTOH(tp)->h_sibling)
		  		VTOH(VTOH(tp)->h_sibling)->h_relative = NULL;
		  	}
		
		  if (hp->h_meta->h_nodeflags & IN_LONGNAME) {
        	DBG_ASSERT(H_NAME(hp) != NULL);
		  	FREE(H_NAME(hp), M_HFSNODE);
		  	}
		
		  FREE(hp->h_meta, M_HFSNODE);
		  hp->h_meta = NULL;
		  hp->h_relative = NULL;

    	}
    else if (vp->v_type == VDIR) {
        if (hp->h_meta->h_nodeflags & IN_LONGNAME)
            FREE(H_NAME(hp), M_HFSNODE);

        FREE(hp->h_meta, M_HFSNODE);
        hp->h_meta = NULL;
        }
	else
	    DBG_ASSERT(0);	/* Unexpected v_type !!!! XXX SER Change to panic */

    /*
     * Purge old data structures associated with the inode.
     */
    cache_purge(vp);
    if ((vp->v_type != VNON) && hp->h_devvp) {
        VRELE(hp->h_devvp);
        hp->h_devvp = 0;
    }

    /* Free our data structs */
    if (vp->v_type != VNON) {
        DBG_ASSERT(hp->h_sibling == NULL);
        DBG_ASSERT(hp->h_relative == NULL);
        DBG_ASSERT(hp->h_meta == NULL);
        FREE(hp->h_xfcb, M_HFSNODE);
        FREE(vp->v_data, M_HFSNODE);
    };
    vp->v_data = NULL;

    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}


/*
 * Lock an hfsnode. If its already locked, set the WANT bit and sleep.
#% lock		vp	U L U
#
 vop_lock {
     IN struct vnode *vp;
     IN int flags;
     IN struct proc *p;
     */

static int
hfs_lock(ap)
struct vop_lock_args /* {
    struct vnode *a_vp;
    int a_flags;
    struct proc *a_p;
} */ *ap;
{
    struct vnode * vp = ap->a_vp;
    struct hfsnode *hp = VTOH(ap->a_vp);
    int			retval;

    DBG_FUNC_NAME("lock");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();DBG_VOP_CONT((" "));
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_ZERO);

#if DIAGNOSTIC
    DBG_ASSERT(hp != (struct hfsnode *)NULL);
    if (ap->a_flags & LK_INTERLOCK) {
        DBG_ASSERT(*((int*)&vp->v_interlock) != 0);
    } else {
        DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
    };
    if (H_FORKTYPE(hp) != kDirCmplx && (vp->v_flag & VSYSTEM) == 0) {
        DBG_ASSERT(hp->h_meta != NULL);
        if ((lockstatus(&hp->h_meta->h_fmetalock)) != (lockstatus(&hp->h_lock)))
            DBG_VOP(("hfs_lock: Warning, mismatched locks. h_lock = %d.meta = %d\n", lockstatus(&hp->h_lock), 									lockstatus(&hp->h_meta->h_fmetalock)));
    }

    /* Attempting to lock the vnode */
    if (lockstatus(&hp->h_lock) && ((ap->a_flags & LK_NOWAIT) == 0)) {
        DBG_VOP(("hfs_lock: waiting for vnode lock (forkype = %d)...\n", H_FORKTYPE(hp)));
    };
#endif	/* DIAGNOSTIC */

    retval = lockmgr(&hp->h_lock, ap->a_flags, &vp->v_interlock, ap->a_p);
    if (retval != E_NONE) {
        if ((ap->a_flags & LK_NOWAIT) == 0)
            DBG_ERR(("hfs_lock: error %d trying to lock vnode (flags = 0x%08X).\n", retval, ap->a_flags));
        goto Err_Exit;
    };

    /* Now get a lock on the file meta lock if necessary */
    if (H_FORKTYPE(hp) != kDirCmplx && (vp->v_flag & VSYSTEM) == 0) {
        if (lockstatus(&hp->h_meta->h_fmetalock) && ((ap->a_flags & LK_NOWAIT) == 0))
            DBG_VOP(("hfs_lock: waiting for meta-data lock...\n"));

        retval = lockmgr(&hp->h_meta->h_fmetalock, ap->a_flags & ~LK_INTERLOCK, (simple_lock_t) 0, ap->a_p);
        if (retval != E_NONE) {
            if ((ap->a_flags & LK_NOWAIT) == 0) {
                DBG_ERR(("hfs_lock: error %d trying to lock meta-data (flags = 0x%08X).\n", retval, ap->a_flags));
            };
            (void)lockmgr(&hp->h_lock, LK_RELEASE, (simple_lock_t) 0, ap->a_p);
        };
    };

Err_Exit:;
    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}

/*
 * Unlock an hfsnode.
#% unlock	vp	L U L
#
 vop_unlock {
     IN struct vnode *vp;
     IN int flags;
     IN struct proc *p;

     */
int
hfs_unlock(ap)
struct vop_unlock_args /* {
    struct vnode *a_vp;
    int a_flags;
    struct proc *a_p;
} */ *ap;
{
    struct hfsnode *hp = VTOH(ap->a_vp);
    struct vnode *vp = ap->a_vp;
    int		retval = E_NONE;

    DBG_FUNC_NAME("unlock");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(vp);DBG_VOP_CONT((" flags = 0x%08X.\n", ap->a_flags));
    DBG_VOP_LOCKS_INIT(0,vp, VOPDBG_LOCKED, VOPDBG_UNLOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);

#if DIAGNOSTIC
    DBG_ASSERT(hp != (struct hfsnode *)NULL);
    if (ap->a_flags & LK_INTERLOCK) {
        DBG_ASSERT(*((int*)&vp->v_interlock) != 0);
    } else {
        DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
    };
    if (H_FORKTYPE(hp) != kDirCmplx && (vp->v_flag & VSYSTEM) == 0) {
        DBG_ASSERT(hp->h_meta != NULL);
    };
#endif	/* DIAGNOSTIC */

    DBG_ASSERT((ap->a_flags & (LK_EXCLUSIVE|LK_SHARED)) == 0);
    retval = lockmgr(&hp->h_lock, ap->a_flags | LK_RELEASE, &vp->v_interlock, ap->a_p);
    if (retval != E_NONE) {
        DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock vnode (forktype = %d).\n", retval, H_FORKTYPE(hp)));
    };

    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);

    /* Release lock on the file meta lock */
    if ((retval == E_NONE) && (H_FORKTYPE(hp) != kDirCmplx) && ((vp->v_flag & VSYSTEM) == 0)) {
        retval = lockmgr(&hp->h_meta->h_fmetalock, (ap->a_flags & ~LK_INTERLOCK) | LK_RELEASE, (simple_lock_t) 0, ap->a_p);
        if (retval != E_NONE) {
            /* XXX PPD Wouldn't this be the perfect time to panic?  We're now half-unlocked! */
            DEBUG_BREAK_MSG(("hfs_unlock: error %d trying to unlock meta-data (forktype = %d).\n", retval, H_FORKTYPE(hp)));
        };
    };

    DBG_ASSERT(*((int*)&vp->v_interlock) == 0);
    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}


/*
 * Print out the contents of an hfsnode.
#% print	vp	= = =
#
 vop_print {
     IN struct vnode *vp;
     */
int
hfs_print(ap)
struct vop_print_args /* {
    struct vnode *a_vp;
} */ *ap;
{
    register struct vnode * vp = ap->a_vp;
    register struct hfsnode *hp = VTOH( vp);
    DBG_FUNC_NAME("print");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_POS);

    printf("tag VT_HFS, dirID %ld, on dev %d, %d", H_DIRID(hp),
           major(hp->h_dev), minor(hp->h_dev));
    /* lockmgr_printinfo(&hp->h_lock); */
    printf("\n");
    DBG_VOP_LOCKS_TEST(E_NONE);
    return (E_NONE);
}


/*
 * Check for a locked hfsnode.
#% islocked	vp	= = =
#
 vop_islocked {
     IN struct vnode *vp;

     */
int
hfs_islocked(ap)
struct vop_islocked_args /* {
    struct vnode *a_vp;
} */ *ap;
{
    int		lockStatus;
    //DBG_FUNC_NAME("islocked");
    //DBG_VOP_LOCKS_DECL(1);
    //DBG_VOP_PRINT_FUNCNAME();
    //DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);

    //DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_IGNORE, VOPDBG_ZERO);

    lockStatus = lockstatus(&VTOH( ap->a_vp)->h_lock);
    //DBG_VOP_LOCKS_TEST(E_NONE);
    return (lockStatus);
}

/*

#% pathconf	vp	L L L
#
 vop_pathconf {
     IN struct vnode *vp;
     IN int name;
     OUT register_t *retval;

     */
static int
hfs_pathconf(ap)
struct vop_pathconf_args /* {
    struct vnode *a_vp;
    int a_name;
    int *a_retval;
} */ *ap;
{
    int retval = E_NONE;
    DBG_FUNC_NAME("pathconf");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);

    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_POS);

    DBG_HFS_NODE_CHECK (ap->a_vp);

    switch (ap->a_name) {
        case _PC_LINK_MAX:
            *ap->a_retval = 1;
            break;
        case _PC_NAME_MAX:
            *ap->a_retval = NAME_MAX;
            break;
        case _PC_PATH_MAX:
            *ap->a_retval = PATH_MAX; /* 1024 */
            break;
        case _PC_CHOWN_RESTRICTED:
            *ap->a_retval = 1;
            break;
        case _PC_NO_TRUNC:
            *ap->a_retval = 0;
            break;
        default:
            retval = EINVAL;
    }

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}





/*
 * Advisory record locking support
#% advlock	vp	U U U
#
 vop_advlock {
     IN struct vnode *vp;
     IN caddr_t id;
     IN int op;
     IN struct flock *fl;
     IN int flags;

     */
int
hfs_advlock(ap)
struct vop_advlock_args /* {
    struct vnode *a_vp;
    caddr_t  a_id;
    int  a_op;
    struct flock *a_fl;
    int  a_flags;
} */ *ap;
{
    register struct hfsnode *hp = VTOH(ap->a_vp);
    register struct flock *fl = ap->a_fl;
    register struct hfslockf *lock;
    off_t start, end;
    int retval;
    DBG_FUNC_NAME("advlock");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_UNLOCKED, VOPDBG_POS);
    /*
     * Avoid the common case of unlocking when inode has no locks.
     */
    if (hp->h_lockf == (struct hfslockf *)0) {
        if (ap->a_op != F_SETLK) {
            fl->l_type = F_UNLCK;
            return (0);
        }
    }
    /*
     * Convert the flock structure into a start and end.
     */
    start = 0;
    switch (fl->l_whence) {
        case SEEK_SET:
        case SEEK_CUR:
            /*
             * Caller is responsible for adding any necessary offset
             * when SEEK_CUR is used.
             */
            start = fl->l_start;
            break;

        case SEEK_END:
            start = HTOFCB(hp)->fcbEOF + fl->l_start;
            break;

        default:
            return (EINVAL);
    }

    if (start < 0)
        return (EINVAL);
    if (fl->l_len == 0)
        end = -1;
    else
        end = start + fl->l_len - 1;

    /*
     * Create the hfslockf structure
     */
    MALLOC(lock, struct hfslockf *, sizeof *lock, M_LOCKF, M_WAITOK);
    lock->lf_start = start;
    lock->lf_end = end;
    lock->lf_id = ap->a_id;
    lock->lf_hfsnode = hp;
    lock->lf_type = fl->l_type;
    lock->lf_next = (struct hfslockf *)0;
    TAILQ_INIT(&lock->lf_blkhd);
    lock->lf_flags = ap->a_flags;
    /*
     * Do the requested operation.
     */
    switch(ap->a_op) {
        case F_SETLK:
            retval = hfs_setlock(lock);
            break;

        case F_UNLCK:
            retval = hfs_clearlock(lock);
            FREE(lock, M_LOCKF);
            break;

        case F_GETLK:
            retval = hfs_getlock(lock, fl);
            FREE(lock, M_LOCKF);
            break;

        default:
            retval = EINVAL;
            _FREE(lock, M_LOCKF);
            break;
    }

    DBG_VOP_LOCKS_TEST(retval);
    return (retval);
}



/*
 * Update the access, modified, and node change times as specified by the
 * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
 * used to specify that the node needs to be updated but that the times have
 * already been set. The access and modified times are taken from the second
 * and third parameters; the node change time is always taken from the current
 * time. If waitfor is set, then wait for the disk write of the node to
 * complete.
 */
/*
#% update	vp	L L L
	IN struct vnode *vp;
	IN struct timeval *access;
	IN struct timeval *modify;
	IN int waitfor;
*/

int
hfs_update(ap)
    struct vop_update_args /* {
        struct vnode *a_vp;
        struct timeval *a_access;
        struct timeval *a_modify;
        int a_waitfor;
    } */ *ap;
{
    struct hfsnode 	*hp;
    struct proc 	*p;
    CatalogNodeData nodeData;
    FSSpec 			nodeSpec;	/* 264 bytes */
    int 			retval;
    DBG_FUNC_NAME("update");
    DBG_VOP_LOCKS_DECL(1);
    DBG_VOP_PRINT_FUNCNAME();
    DBG_VOP_PRINT_VNODE_INFO(ap->a_vp);DBG_VOP_CONT(("\n"));
    DBG_VOP_LOCKS_INIT(0,ap->a_vp, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_LOCKED, VOPDBG_ZERO);

    hp = VTOH(ap->a_vp);

    DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
    DBG_ASSERT(ap->a_vp->v_type != VCPLX);
    if (ap->a_vp->v_type == VCPLX) {	//XXX SER Shoulf never be true, change to a panic before ship
        DBG_VOP_LOCKS_TEST(0);
		return (0);
    };

    if (H_FORKTYPE(hp) == kSysFile) {
        hp->h_meta->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
        DBG_VOP_LOCKS_TEST(0);
        return (0);
    }

    if (VTOVFS(ap->a_vp)->mnt_flag & MNT_RDONLY) {
        hp->h_meta->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
        DBG_VOP_LOCKS_TEST(0);
        DBG_VOP(("hfs_update: returning 0 (all flags were cleared because the volume is read-only.\n"));
        return (0);
    }
	
    /* Check to see if MacOS set the fcb to be dirty, if so, translate it to IN_MODIFIED */
    if (HTOFCB(hp)->fcbFlags &fcbModifiedMask)
        hp->h_meta->h_nodeflags |= IN_MODIFIED;

    if ((hp->h_meta->h_nodeflags & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0) {
        DBG_VOP_LOCKS_TEST(0);
        DBG_VOP(("hfs_update: returning 0 because the metadata is unchanged.\n"));
        return (0);
    };

    if (hp->h_meta->h_nodeflags & IN_ACCESS)
        hp->h_meta->h_atime = ap->a_access->tv_sec;
    if (hp->h_meta->h_nodeflags & IN_UPDATE)
        hp->h_meta->h_mtime = ap->a_modify->tv_sec;
    if (hp->h_meta->h_nodeflags & IN_CHANGE) {
        hp->h_meta->h_ctime = time.tv_sec;
		/*
		 * HFS dates that WE set must be adjusted for DST
		 */
		if ((HTOVCB(hp)->vcbSigWord == kHFSSigWord) && gTimeZone.tz_dsttime) {
			hp->h_meta->h_ctime += 3600;
			hp->h_meta->h_mtime = hp->h_meta->h_ctime;
		}
	}

	p = CURRENT_PROC;
	/*
	 * Since VOP_UPDATE can be called from withing another VOP (eg VOP_RENAME),
	 * the Catalog b-tree may aready be locked by the current thread. So we
	 * allow recursive locking of the Catalog from within VOP_UPDATE.
	 */

	/* Lock the Catalog b-tree file */
	retval = hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_EXCLUSIVE | LK_CANRECURSE, p);
	if (retval) {
        DBG_VOP_LOCKS_TEST(retval);
        DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
        return (retval);
    };
    DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);

    retval = GetCatalogNode(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &nodeSpec, &nodeData, &(H_HINT(hp)));
    if (retval != noErr) {
        DBG_ERR(("hfs_update: error %d on GetCatalogNode...\n", retval));
        retval = MacToVFSError(retval);
        goto exit_relse;
    };

	/* If there is a sibling (resource fork), copy its contents first */
	/* This makes sure all changes in the FCB are reflected. i.e. LEOF, etc */
	if (hp->h_sibling)
    	CopyVNodeToCatalogNode (hp->h_sibling, &nodeData);

    CopyVNodeToCatalogNode (HTOV(hp), &nodeData);

    retval = UpdateCatalogNode(HTOVCB(hp), H_DIRID(hp), H_NAME(hp), H_HINT(hp), &nodeData);
    if (retval != noErr) {
 		DBG_ERR(("hfs_update: error %d on UpdateCatalogNode...\n", retval));
        retval = MacToVFSError(retval);
        goto exit_relse;
    };

	/* After the updates are finished, clear the flags */
    hp->h_meta->h_nodeflags &= ~(IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE);
	HTOFCB(hp)->fcbFlags &= ~fcbModifiedMask;

	/* Update general data */
    if (ap->a_vp->v_type == VDIR) {
        hp->h_size = sizeof (hfsdirentry) * (nodeData.valence + 2);
	}
	else {
        hp->h_size = nodeData.rsrcPhysicalSize + nodeData.dataPhysicalSize;
	}


exit_relse:    
	 /* unlock the Catalog b-tree file */
	(void) hfs_metafilelocking(HTOHFS(hp), kHFSCatalogFileID, LK_RELEASE, p);

    DBG_VOP(("hfs_update: returning %d.\n", retval));
    DBG_VOP_LOCKS_TEST(retval);
    DBG_ASSERT(*((int*)&ap->a_vp->v_interlock) == 0);
	return (retval);
}




/*
 * Allocate a new node
 *
 * Assumes that the catalog b-tree is locked
 *
 * Upon leaving, namei buffer must be freed.
 *
 */
static int
hfs_makenode(mode, dvp, vpp, cnp)
    int mode;
    struct vnode *dvp;
    struct vnode **vpp;
    struct componentname *cnp;
{
    register struct hfsnode *hp, *parhp;
    struct timeval 			tv;
    struct vnode 			*tvp;
    struct hfsCatalogInfo 	catInfo;
    ExtendedVCB				*vcb;
    UInt8					forkType;
    int 					retval;
    DBG_FUNC_NAME("makenode");

    parhp	= VTOH(dvp);
    vcb		= HTOVCB(parhp);
    *vpp	= NULL;
	tvp 	= NULL;
    if ((mode & IFMT) == 0)
        mode |= IFREG;

#if DIAGNOSTIC
    if ((cnp->cn_flags & HASBUF) == 0)
        panic("hfs_makenode: no name");
#endif

    /* Create the Catalog B*-Tree entry */
    retval = hfsCreate(vcb, H_FILEID(parhp), cnp->cn_nameptr, mode);
    if (retval != E_NONE) {
        DBG_ERR(("%s: hfsCreate FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
        goto bad1;
    }

    /* Look up the catalog entry just created: */
    retval = hfsLookup(vcb, H_FILEID(parhp), cnp->cn_nameptr, cnp->cn_namelen, 0, &catInfo);
    if (retval != E_NONE) {
        DBG_ERR(("%s: hfsLookup FAILED: %s, %s\n", funcname, cnp->cn_nameptr, H_NAME(parhp)));
        goto bad1;
    }

    /* Create a vnode for the object just created: */
    forkType = (catInfo.nodeData.nodeType == kCatalogFolderNode) ? kDirCmplx : kDataFork;
    if ((retval = hfsGet(vcb, &catInfo, forkType, dvp, &tvp))) {
        goto bad1;
    }

    /* Set other vnode values not init'd in getnewvnode(). */
    tvp->v_type = IFTOVT(mode);

	/* reinit hp from the passed in mode, only if hfsplus */
    if (vcb->vcbSigWord == kHFSPlusSigWord) {
        hp = VTOH(tvp);
        hp->h_meta->h_gid = parhp->h_meta->h_gid;
        if ((mode & IFMT) == IFLNK)
            hp->h_meta->h_uid = parhp->h_meta->h_uid;
        else
            hp->h_meta->h_uid = cnp->cn_cred->cr_uid;

        hp->h_meta->h_nodeflags &= ~IN_UNSETACCESS;
        hp->h_meta->h_nodeflags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;

        hp->h_meta->h_mode = mode;
        if ((hp->h_meta->h_mode & ISGID) && !groupmember(hp->h_meta->h_gid, cnp->cn_cred) &&
            suser(cnp->cn_cred, NULL))
            hp->h_meta->h_mode &= ~ISGID;

        if (cnp->cn_flags & ISWHITEOUT)
            hp->h_meta->h_pflags |= UF_OPAQUE;

        /* Write out the new values */
        tv = time;
        if ((retval = VOP_UPDATE(tvp, &tv, &tv, 1)))
            goto bad2;
    }

	VTOH(dvp)->h_meta->h_nodeflags |= IN_CHANGE | IN_UPDATE;
    tv = time;
    if ((retval = VOP_UPDATE(dvp, &tv, &tv, 1)))
        goto bad2;

    if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
        FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
    };
    VPUT(dvp);
#if MACH_NBC
    if ((tvp->v_type == VREG)  && !(tvp->v_vm_info)){
        vm_info_init(tvp);
    }
#endif /* MACH_NBC */
    *vpp = tvp;
    return (0);

bad2:
    /*
     * Write retval occurred trying to update the node
     * or the directory so must deallocate the node.
    */
    /* XXX SER In the future maybe set *vpp to 0xdeadbeef for testing */
    VPUT(tvp);

bad1:
    if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
        FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
    };
    VPUT(dvp);

    return (retval);

}


/************************************************************************/
/*	Entry for searchfs() 		  										*/
/************************************************************************/

#define	errSearchBufferFull	101		//	Internal search errors
/*
#
#% searchfs	vp	L L L
#
vop_searchfs {
    IN struct vnode *vp;
    IN off_t length;
    IN int flags;
    IN struct ucred *cred;
    IN struct proc *p;
};
*/

//XXX	Optimize this call for calls being made from VDI
int	hfs_search( ap )
struct vop_searchfs_args *ap; /*
 struct vnodeop_desc *a_desc;
 struct vnode *a_vp;
 void *a_searchparams1;
 void *a_searchparams2;
 struct attrlist *a_searchattrs;
 u_long a_maxmatches;
 struct timeval *a_timelimit;
 struct attrlist *a_returnattrs;
 u_long *a_nummatches;
 u_long a_scriptcode;
 u_long a_options;
 struct uio *a_uio;
 struct searchstate *a_searchstate;
*/
{
	CatalogRecord		catalogRecord;
	BTreeKey			*key;
	FSBufferDescriptor	btRecord;
	FCB*				catalogFCB;
	SearchState			*searchState;
	searchinfospec_t	searchInfo1;
	searchinfospec_t	searchInfo2;
	void				*attributesBuffer;
	void				*variableBuffer;
	short				recordSize;
	short				operation;
	u_long				fixedBlockSize;
	u_long				eachReturnBufferSize;
	struct proc			*p						= CURRENT_PROC;
	u_long				nodesToCheck			= 30;				//	After we search 30 nodes we must give up time
	u_long				lastNodeNum				= 0XFFFFFFFF;
	ExtendedVCB			*vcb					= VTOVCB(ap->a_vp);
	int					err						= E_NONE;

    DBG_FUNC_NAME("hfs_search");
	DBG_VOP_LOCKS_DECL(1);

    //XXX Parameter check a_searchattrs

	*(ap->a_nummatches)	= 0;

	if ( ap->a_options & ~SRCHFS_VALIDOPTIONSMASK )
		return( EINVAL );

	if (ap->a_uio->uio_resid <= 0)
		return (EINVAL);

	searchState	= (SearchState *)ap->a_searchstate;

	//	Check if this is the first time we are being called.
	//	If it is, allocate SearchState and we'll move it to the users space on exit
	if ( ap->a_options & SRCHFS_START )
	{
		bzero( (caddr_t)searchState, sizeof(SearchState) );
		searchState->isHFSPlus	= ( vcb->vcbSigWord == kHFSPlusSigWord );
		operation	= kBTreeFirstRecord;
		ap->a_options &= ~SRCHFS_START;		//	clear the bit
	}
	else
	{
		operation	= kBTreeCurrentRecord;
	}

	//	UnPack the search boundries, searchInfo1, searchInfo2
	err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo1, ap->a_searchparams1 );
	if (err) return err;
	err = UnpackSearchAttributeBlock( ap->a_vp, ap->a_searchattrs, &searchInfo2, ap->a_searchparams2 );
	if (err) return err;

	btRecord.itemCount			= 1;
	btRecord.itemSize			= sizeof( catalogRecord );
	btRecord.bufferAddress		= &catalogRecord;
	catalogFCB					= VTOFCB( vcb->catalogRefNum );
	key							= (BTreeKey*) &(searchState->btreeIterator.key);
	fixedBlockSize 				= sizeof(u_long) + AttributeBlockSize( ap->a_returnattrs );	/* u_long for length longword */
	eachReturnBufferSize		= fixedBlockSize;
	
	if ( ap->a_returnattrs->commonattr & ATTR_CMN_NAME )				//XXX should be more robust
		eachReturnBufferSize += NAME_MAX + 1;

    MALLOC( attributesBuffer, void *, eachReturnBufferSize, M_TEMP, M_WAITOK );
	variableBuffer				= attributesBuffer + fixedBlockSize;

	//	Lock catalog b-tree
	err = hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_SHARED, p );
	if ( err != E_NONE )
	{
        DBG_VOP_LOCKS_TEST( err );
        goto ExitThisRoutine;
    };

    //
	//	Iterate over all the catalog btree records
	//
	
	err = BTIterateRecord( catalogFCB, operation, &(searchState->btreeIterator), &btRecord, &recordSize );

	while( err == E_NONE )
	{
		if ( CheckCriteria( vcb, searchState, ap->a_options, ap->a_searchattrs, &catalogRecord, (CatalogKey *)key, &searchInfo1, &searchInfo2 ) == true )
		{
			err = InsertMatch( ap->a_vp, ap->a_uio, &catalogRecord, (CatalogKey *)key, ap->a_returnattrs, attributesBuffer, variableBuffer, eachReturnBufferSize, ap->a_nummatches );
			if ( err != E_NONE )
				break;
		}
		
		err = BTIterateRecord( catalogFCB, kBTreeNextRecord, &(searchState->btreeIterator), &btRecord, &recordSize );
		
			if  ( *(ap->a_nummatches) >= ap->a_maxmatches )
				break;

  		if ( searchState->btreeIterator.hint.nodeNum != lastNodeNum )
		{
			lastNodeNum	= searchState->btreeIterator.hint.nodeNum;
			if ( --nodesToCheck == 0 )
				break;								//	We must leave the kernel to give up time
		}
	}

	//	Unlock catalog b-tree
	(void) hfs_metafilelocking( VTOHFS(ap->a_vp), kHFSCatalogFileID, LK_RELEASE, p );


    if ( err == E_NONE )
	{
		err = EAGAIN;								//	signal to the user to call searchfs again
	}
    else if ( err == errSearchBufferFull )
	{
		if ( *(ap->a_nummatches) > 0 )
			err = EAGAIN;
 		else
			err = ENOBUFS;
	}
	else if ( err == btNotFound )
	{
		err = E_NONE;								//	the entire disk has been searched
	}

ExitThisRoutine:
        FREE( attributesBuffer, M_TEMP );

	return( err );
}


static Boolean
ComparePartialUnicodeName ( register ConstUniCharArrayPtr str, register ItemCount s_len,
							register ConstUniCharArrayPtr find, register ItemCount f_len )
{
	if (f_len == 0 || s_len == 0)
		return FALSE;

	do {
		if (s_len-- < f_len)
			return FALSE;
	} while (FastUnicodeCompare(str++, f_len, find, f_len) != 0);

	return TRUE;
}


static Boolean
ComparePartialPascalName ( register ConstStr31Param str, register ConstStr31Param find )
{
	register u_char s_len = str[0];
	register u_char f_len = find[0];
	register u_char *tsp;
	Str31 tmpstr;

	if (f_len == 0 || s_len == 0)
		return FALSE;

	bcopy(str, tmpstr, s_len + 1);
	tsp = &tmpstr[0];

	while (s_len-- >= f_len) {
		*tsp = f_len;

		if (FastRelString(tsp++, find) == 0)
			return TRUE;
	}

	return FALSE;
}

//
//	CheckCriteria()
//
Boolean CheckCriteria( ExtendedVCB *vcb, const SearchState *searchState, u_long searchBits,
						struct attrlist *attrList, CatalogRecord *catalogRecord, CatalogKey *key,
						searchinfospec_t  *searchInfo1, searchinfospec_t *searchInfo2 )
{
	Boolean			matched;
	CatalogNodeData	catData;
	attrgroup_t		searchAttributes;
	
	switch ( catalogRecord->recordType )
	{
		case kHFSFolderRecord:
		case kHFSPlusFolderRecord:
			if ( (searchBits & SRCHFS_MATCHDIRS) == 0 )		//	If we are NOT searching folders
			{
				matched	= false;
				goto TestDone;
			}
			break;
			
		case kHFSFileRecord:
		case kHFSPlusFileRecord:
				if ( (searchBits & SRCHFS_MATCHFILES) == 0 )		//	If we are NOT searching files
			{
				matched	= false;
				goto TestDone;
			}
			break;

		default:						// Never match a thread record or any other type.
			return( false );				// Not a file or folder record, so can't search it
	}
	
	//	Change the catalog record data into a single common form
	matched				= true;							//	Assume we got a match
	catData.nodeType	= 0;							//	mark this record as not in use
	CopyCatalogNodeData( vcb, catalogRecord, &catData );

	/* First, attempt to match the name -- either partial or complete */
	if ( attrList->commonattr & ATTR_CMN_NAME ) {
		if ( searchState->isHFSPlus ) {
			/* Check for partial/full HFS Plus name match */

			if ( searchBits & SRCHFS_MATCHPARTIALNAMES ) {
				matched = ComparePartialUnicodeName( key->hfsPlus.nodeName.unicode,
													 key->hfsPlus.nodeName.length,
													 (UniChar*)searchInfo1->name,
													 searchInfo1->nameLength );
			} else /* full HFS Plus name match */ { 
				matched = (FastUnicodeCompare(	key->hfsPlus.nodeName.unicode,
												key->hfsPlus.nodeName.length,
												(UniChar*)searchInfo1->name,
												searchInfo1->nameLength ) == 0);
			}
		} else {
			/* Check for partial/full HFS name match */

			if ( searchBits & SRCHFS_MATCHPARTIALNAMES )
				matched = ComparePartialPascalName(key->hfs.nodeName, (u_char*)searchInfo1->name);
			else /* full HFS name match */
				matched = (FastRelString(key->hfs.nodeName, (u_char*)searchInfo1->name) == 0);
		}
		
		if ( matched == false || (searchBits & ~SRCHFS_MATCHPARTIALNAMES) == 0 )
			goto TestDone;	/* no match, or nothing more to compare */
	}
	
	
	//	Now that we have a record worth searching, see if it matches the search attributes
	if ( (catData.nodeType == kCatalogFileNode) && ((attrList->fileattr & ATTR_FILE_VALIDMASK) != 0) )
	{
		searchAttributes	= attrList->fileattr;
		//
		//	File logical length (data fork)
		//
		if ( searchAttributes & ATTR_FILE_DATALENGTH )
		{
			matched = CompareRange( catData.dataLogicalSize, searchInfo1->f.dataLogicalLength, searchInfo2->f.dataLogicalLength );
			if (matched == false) goto TestDone;
		}

		//
		//	File physical length (data fork)
		//
		if ( searchAttributes & ATTR_FILE_DATAALLOCSIZE )
		{
			matched = CompareRange( catData.dataPhysicalSize, searchInfo1->f.dataPhysicalLength, searchInfo2->f.dataPhysicalLength );
			if (matched == false) goto TestDone;
		}

		//
		//	File logical length (resource fork)
		//
		if ( searchAttributes & ATTR_FILE_RSRCLENGTH )
		{
			matched = CompareRange( catData.rsrcLogicalSize, searchInfo1->f.resourceLogicalLength, searchInfo2->f.resourceLogicalLength );
			if (matched == false) goto TestDone;
		}
		
		//
		//	File physical length (resource fork)
		//
		if ( searchAttributes & ATTR_FILE_RSRCALLOCSIZE )
		{
			matched = CompareRange( catData.rsrcPhysicalSize, searchInfo1->f.resourcePhysicalLength, searchInfo2->f.resourcePhysicalLength );
			if (matched == false) goto TestDone;
		}
		
		//
		//	File logical length (resource + data fork)
		//
		if ( searchAttributes & ATTR_FILE_TOTALSIZE )
		{
			matched = CompareRange( catData.rsrcLogicalSize + catData.dataLogicalSize, 
									searchInfo1->f.resourceLogicalLength + searchInfo1->f.dataLogicalLength, 
									searchInfo2->f.resourceLogicalLength + searchInfo2->f.dataLogicalLength );
			if (matched == false) goto TestDone;
		}
		
		//
		//	File physical length (resource + data fork)
		//
		if ( searchAttributes & ATTR_FILE_TOTALSIZE )
		{
			matched = CompareRange( catData.rsrcPhysicalSize + catData.dataPhysicalSize, 
									searchInfo1->f.resourcePhysicalLength + searchInfo1->f.dataPhysicalLength, 
									searchInfo2->f.resourcePhysicalLength + searchInfo2->f.dataPhysicalLength );
			if (matched == false) goto TestDone;
		}
	}
	//
	//	Check the directory attributes
	//
	else if ( (catData.nodeType == kCatalogFolderNode) && ((attrList->dirattr & ATTR_DIR_VALIDMASK) != 0) )
	{
		searchAttributes	= attrList->dirattr;
		
		//
		//	Directory valence
		//
		if ( searchAttributes & ATTR_DIR_ENTRYCOUNT )
		{
			matched = CompareRange( catData.valence, searchInfo1->d.numFiles, searchInfo2->d.numFiles );
			if (matched == false) goto TestDone;
		}
	}
	
	//
	//	Check the common attributes
	//
	searchAttributes	= attrList->commonattr;
	if ( (searchAttributes & ATTR_CMN_VALIDMASK) != 0 )
	{
		//
		//	Parent ID
		//
		if ( searchAttributes & ATTR_CMN_PAROBJID )
		{
			HFSCatalogNodeID	parentID;
			
			if (searchState->isHFSPlus)
				parentID = key->hfsPlus.parentID;
			else
				parentID = key->hfs.parentID;
				
			matched = CompareRange( parentID, searchInfo1->parentDirID, searchInfo2->parentDirID );
			if (matched == false) goto TestDone;
		}

		//
		//	Finder Info & Extended Finder Info where extFinderInfo is last 32 bytes
		//
		if ( searchAttributes & ATTR_CMN_FNDRINFO )
		{
			UInt32 *thisValue;
			thisValue = (UInt32 *) &catData.finderInfo[0];

			//	Note: ioFlFndrInfo and ioDrUsrWds have the same offset in search info, so
			//	no need to test the object type here.
			matched = CompareMasked( thisValue, (UInt32 *) &searchInfo1->finderInfo, (UInt32 *) &searchInfo2->finderInfo, 8 );	//	8 * UInt32
			if (matched == false) goto TestDone;
		}

		//
		//	Create date
		//
		if ( searchAttributes & ATTR_CMN_CRTIME )
		{
			matched = CompareRange( to_bsd_time(catData.createDate), searchInfo1->creationDate.tv_sec,  searchInfo2->creationDate.tv_sec );
			if (matched == false) goto TestDone;
		}
	
		//
		//	Mod date
		//
		if ( searchAttributes & ATTR_CMN_MODTIME )
		{
			matched = CompareRange( to_bsd_time(catData.contentModDate), searchInfo1->modificationDate.tv_sec, searchInfo2->modificationDate.tv_sec );
			if (matched == false) goto TestDone;
		}
	
		//
		//	Backup date
		//
		if ( searchAttributes & ATTR_CMN_BKUPTIME )
		{
			matched = CompareRange( to_bsd_time(catData.backupDate), searchInfo1->lastBackupDate.tv_sec, searchInfo2->lastBackupDate.tv_sec );
			if (matched == false) goto TestDone;
		}
	
	}

	
TestDone:
	//
	//	Finally, determine whether we need to negate the sense of the match (i.e. find
	//	all objects that DON'T match).
	//
	if ( searchBits & SRCHFS_NEGATEPARAMS )
		matched = !matched;
	
	return( matched );
}


//
//	Adds another record to the packed array for output
//
static int InsertMatch( struct vnode *root_vp, struct uio *a_uio, CatalogRecord *catalogRecord,
						CatalogKey *key, struct attrlist *returnAttrList, void *attributesBuffer,
						void *variableBuffer, u_long bufferSize, u_long * nummatches )
{
	int						err;
	void					*rovingAttributesBuffer;
	void					*rovingVariableBuffer;
	struct hfsCatalogInfo	catalogInfo;
	u_long					packedBufferSize;
	ExtendedVCB				*vcb			= VTOVCB(root_vp);
	Boolean					isHFSPlus		= vcb->vcbSigWord == kHFSPlusSigWord;

	rovingAttributesBuffer	= attributesBuffer + sizeof(u_long);		//	Reserve space for length field
	rovingVariableBuffer	= variableBuffer;

	CopyCatalogNodeData( vcb, catalogRecord, &catalogInfo.nodeData );

	catalogInfo.spec.parID = isHFSPlus ? key->hfsPlus.parentID : key->hfs.parentID;

	if ( returnAttrList->commonattr & ATTR_CMN_NAME )
	{
		u_long actualDstLen;

		/*	Return result in UTF-8 */
		if ( isHFSPlus ) {
			err = ConvertUnicodeToUTF8(	key->hfsPlus.nodeName.length * sizeof(UniChar),
										key->hfsPlus.nodeName.unicode,
										sizeof(catalogInfo.spec.name),
										&actualDstLen,
										catalogInfo.spec.name);
		 } else {
			err = ConvertMacRomanToUTF8( key->hfs.nodeName,
										 sizeof(catalogInfo.spec.name),
										 &actualDstLen,
										 catalogInfo.spec.name);
		}
	}

	PackCatalogInfoAttributeBlock( returnAttrList,root_vp,  &catalogInfo, &rovingAttributesBuffer, &rovingVariableBuffer );

	packedBufferSize = rovingVariableBuffer - attributesBuffer;

	if ( packedBufferSize > a_uio->uio_resid )
		return( errSearchBufferFull );

   	(* nummatches)++;
	
	*((u_long *)attributesBuffer) = packedBufferSize;	//	Store length of fixed + var block
	
	err = uiomove( (caddr_t)attributesBuffer, packedBufferSize, a_uio );		//	XXX should be packedBufferSize
	
	if (err != E_NONE)
		DBG_ERR(("InsertMatch: error %d on uiomove.\n", err));
	
	return( err );
}


static int
UnpackSearchAttributeBlock( struct vnode *vp, struct attrlist	*alist, searchinfospec_t *searchInfo, void *attributeBuffer )
{
	attrgroup_t		a;
	u_long			bufferSize;

	ASSERT(searchInfo != NULL);

	bufferSize =  *((u_long *)attributeBuffer);
	if (bufferSize == 0)
		return (EINVAL);	/* XXX -DJB is a buffer size of zero ever valid for searchfs? */

	++((u_long *)attributeBuffer);	//	advance past the size
	
	//
	//	UnPack common attributes
	//
	a	= alist->commonattr;
	if ( a != 0 )
	{
		if ( a & ATTR_CMN_NAME )
		{
			char *s	= attributeBuffer + ((attrreference_t *) attributeBuffer)->attr_dataoffset;
			size_t len = ((attrreference_t *) attributeBuffer)->attr_length;

			if (len > sizeof(searchInfo->name))
				return (EINVAL);

			if (VTOVCB(vp)->vcbSigWord == kHFSPlusSigWord) {
				ByteCount actualDstLen;
				/* Convert name to Unicode to match HFS Plus B-Tree names */

				if (len > 0) {
					if (ConvertUTF8ToUnicode(len-1, s, sizeof(searchInfo->name),
							&actualDstLen, (UniChar*)searchInfo->name) != 0)
						return (EINVAL);


					searchInfo->nameLength = actualDstLen / sizeof(UniChar);
				} else {
					searchInfo->nameLength = 0;
				}
				++((attrreference_t *)attributeBuffer);

			} else {
				/* Convert name to pascal string to match HFS B-Tree names */

				if (len > 0) {
					if (ConvertUTF8ToMacRoman(len-1, s, (u_char*)searchInfo->name) != 0)
						return (EINVAL);

					searchInfo->nameLength = searchInfo->name[0];
				} else {
					searchInfo->name[0] = searchInfo->nameLength = 0;
				}
				++((attrreference_t *)attributeBuffer);
			}
		}
		if ( a & ATTR_CMN_PAROBJID )
		{
			searchInfo->parentDirID	= ((fsobj_id_t *) attributeBuffer)->fid_objno;	/* ignore fid_generation */
			++((fsobj_id_t *)attributeBuffer);
		}
		if ( a & ATTR_CMN_CRTIME )
		{
			searchInfo->creationDate = *((struct timespec *)attributeBuffer);
			++((struct timespec *)attributeBuffer);
		}
		if ( a & ATTR_CMN_MODTIME )
		{
			searchInfo->modificationDate = *((struct timespec *)attributeBuffer);
			++((struct timespec *)attributeBuffer);
		}
		if ( a & ATTR_CMN_BKUPTIME )
		{
			searchInfo->lastBackupDate = *((struct timespec *)attributeBuffer);
			++((struct timespec *)attributeBuffer);
		}
		if ( a & ATTR_CMN_FNDRINFO )
		{
		   bcopy( attributeBuffer, searchInfo->finderInfo, sizeof(u_long) * 8 );
		   attributeBuffer += (sizeof(u_long) * 8 );
		}
	}

	a	= alist->dirattr;
	if ( a != 0 )
	{
		if ( a & ATTR_DIR_ENTRYCOUNT )
		{
			searchInfo->d.numFiles = *((u_long *)attributeBuffer);
			++((u_long *)attributeBuffer);
		}
	}

	a	= alist->fileattr;
	if ( a != 0 )
	{
		if ( a & ATTR_FILE_DATALENGTH )
		{
			searchInfo->f.dataLogicalLength	= *((off_t *)attributeBuffer);
			++((off_t *)attributeBuffer);
		}
		if ( a & ATTR_FILE_DATAALLOCSIZE )
		{
			searchInfo->f.dataPhysicalLength = *((off_t *)attributeBuffer);
			++((off_t *)attributeBuffer);
		}
		if ( a & ATTR_FILE_RSRCLENGTH )
		{
			searchInfo->f.resourceLogicalLength	= *((off_t *)attributeBuffer);
			++((off_t *)attributeBuffer);
		}
		if ( a & ATTR_FILE_RSRCALLOCSIZE )
		{
			searchInfo->f.resourcePhysicalLength = *((off_t *)attributeBuffer);
			++((off_t *)attributeBuffer);
		}
	}

	return (0);
}




#if DBG_VOP_TEST_LOCKS


void DbgVopTest( int maxSlots,
                 int retval,
                 VopDbgStoreRec *VopDbgStore,
                 char *funcname)
{
    int index;

    for (index = 0; index < maxSlots; index++)
      {
        if (VopDbgStore[index].id != index) {
            DEBUG_BREAK_MSG(("%s: DBG_VOP_LOCK: invalid id field (%d) in target entry (#%d).\n", funcname, VopDbgStore[index].id, index));
        };

        if ((VopDbgStore[index].vp != NULL) &&
            ((VopDbgStore[index].vp->v_data==NULL) || (VTOH(VopDbgStore[index].vp)->h_valid != HFS_VNODE_MAGIC)))
            continue;

        switch (VopDbgStore[index].inState)
          {
            case VOPDBG_IGNORE:
            case VOPDBG_SAME:
                /* Do Nothing !!! */
                break;
            case VOPDBG_LOCKED:
            case VOPDBG_UNLOCKED:
            case VOPDBG_LOCKNOTNIL:
              {
                  if (VopDbgStore[index].vp == NULL && (VopDbgStore[index].inState != VOPDBG_LOCKNOTNIL)) {
                      DBG_ERR (("%s: InState check: Null vnode ptr in entry #%d\n", funcname, index));
                  } else if (VopDbgStore[index].vp != NULL) {
                      switch (VopDbgStore[index].inState)
                        {
                          case VOPDBG_LOCKED:
                          case VOPDBG_LOCKNOTNIL:
                              if (VopDbgStore[index].inValue == 0)
                                {
                                  kprintf ("%s: Entry: not LOCKED:", funcname);
                                  DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                }
                              break;
                          case VOPDBG_UNLOCKED:
                              if (VopDbgStore[index].inValue != 0)
                                {
                                  kprintf ("%s: Entry: not UNLOCKED:", funcname);
                                  DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                }
                              break;
                        }
                  }
                  break;
              }
            default:
                DBG_ERR (("%s: DBG_VOP_LOCK on entry: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
          }


        if (retval != 0)
          {
            switch (VopDbgStore[index].errState)
              {
                case VOPDBG_IGNORE:
                    /* Do Nothing !!! */
                    break;
                case VOPDBG_LOCKED:
                case VOPDBG_UNLOCKED:
                case VOPDBG_SAME:
                  {
                      if (VopDbgStore[index].vp == NULL) {
                          DBG_ERR (("%s: ErrState check: Null vnode ptr in entry #%d\n", funcname, index));
                      } else {
                          VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
                          switch (VopDbgStore[index].errState)
                            {
                              case VOPDBG_LOCKED:
                                  if (VopDbgStore[index].outValue == 0)
                                    {
                                      kprintf ("%s: Error: not LOCKED:", funcname);
                                      DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                   }
                                  break;
                              case VOPDBG_UNLOCKED:
                                  if (VopDbgStore[index].outValue != 0)
                                    {
                                      kprintf ("%s: Error: not UNLOCKED:", funcname);
                                      DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                    }
                                  break;
                              case VOPDBG_SAME:
                                  if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
                                      DBG_ERR (("%s: Error: In/Out locks are DIFFERENT: 0x%x, inis %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
                                  break;
                            }
                      }
                      break;
                  }
                case VOPDBG_LOCKNOTNIL:
                    if (VopDbgStore[index].vp != NULL) {
                        VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
                        if (VopDbgStore[index].outValue == 0)
                            DBG_ERR (("%s: Error: Not LOCKED: 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
                    }
                    break;
                default:
                    DBG_ERR (("%s: Error: bad lock test value: %d\n", funcname, VopDbgStore[index].errState));
              }
          }
        else
          {
            switch (VopDbgStore[index].outState)
              {
                case VOPDBG_IGNORE:
                    /* Do Nothing !!! */
                    break;
                case VOPDBG_LOCKED:
                case VOPDBG_UNLOCKED:
                case VOPDBG_SAME:
                    if (VopDbgStore[index].vp == NULL) {
                        DBG_ERR (("%s: OutState: Null vnode ptr in entry #%d\n", funcname, index));
                    };
                    if (VopDbgStore[index].vp != NULL)
                      {
                        VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
                        switch (VopDbgStore[index].outState)
                          {
                            case VOPDBG_LOCKED:
                                if (VopDbgStore[index].outValue == 0)
                                  {
                                    kprintf ("%s: Out: not LOCKED:", funcname);
                                    DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                 }
                                break;
                            case VOPDBG_UNLOCKED:
                                if (VopDbgStore[index].outValue != 0)
                                  {
                                    kprintf ("%s: Out: not UNLOCKED:", funcname);
                                    DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                                  }
                                break;
                            case VOPDBG_SAME:
                                if (VopDbgStore[index].outValue != VopDbgStore[index].inValue)
                                    DBG_ERR (("%s: Out: In/Out locks are DIFFERENT: 0x%x, in is %d and out is %d\n", funcname, (u_int)VopDbgStore[index].vp, VopDbgStore[index].inValue, VopDbgStore[index].outValue));
                                break;
                          }
                      }
                    break;
                case VOPDBG_LOCKNOTNIL:
                    if (VopDbgStore[index].vp != NULL) {
                        if (&VTOH(VopDbgStore[index].vp)->h_lock == NULL) {
                            DBG_ERR (("%s: DBG_VOP_LOCK on out: Null lock on vnode 0x%x\n", funcname, (u_int)VopDbgStore[index].vp));
                        }
                        else {
                            VopDbgStore[index].outValue = lockstatus(&VTOH(VopDbgStore[index].vp)->h_lock);
                            if (VopDbgStore[index].outValue == 0)
                              {
                                kprintf ("%s: DBG_VOP_LOCK on out: Should be LOCKED:", funcname);
                                DBG_VOP_PRINT_VNODE_INFO(VopDbgStore[index].vp); kprintf ("\n");
                              }
                        }
                    }
                    break;
                default:
                    DBG_ERR (("%s: DBG_VOP_LOCK on out: bad lock test value: %d\n", funcname, VopDbgStore[index].outState));
              }
          }

        VopDbgStore[index].id = -1;		/* Invalidate the entry to allow panic-free re-use */
      }	
}

#endif /* DBG_VOP_TEST_LOCKS */

/*****************************************************************************
*
*	VOP Table
*
*****************************************************************************/


struct vnodeopv_entry_desc hfs_vnodeop_entries[] = {
    { &vop_default_desc, vn_default_error },
    { &vop_lookup_desc, hfs_lookup },			/* lookup */
    { &vop_create_desc, hfs_create },			/* create */
    { &vop_mknod_desc, err_mknod },				/* mknod (NOT SUPPORTED) */
    { &vop_open_desc, hfs_open },				/* open */
    { &vop_close_desc, hfs_close },				/* close */
    { &vop_access_desc, hfs_access },			/* access */
    { &vop_getattr_desc, hfs_getattr },			/* getattr */
    { &vop_setattr_desc, hfs_setattr },			/* setattr */
    { &vop_read_desc, hfs_read },				/* read */
    { &vop_write_desc, hfs_write },				/* write */
    { &vop_ioctl_desc, hfs_ioctl },				/* ioctl */
    { &vop_select_desc, hfs_select },			/* select */
    { &vop_exchange_desc, hfs_exchange },		/* exchange */
    { &vop_mmap_desc, hfs_mmap },				/* mmap */
    { &vop_fsync_desc, hfs_fsync },				/* fsync */
    { &vop_seek_desc, hfs_seek },				/* seek */
    { &vop_remove_desc, hfs_remove },			/* remove */
    { &vop_link_desc, err_link },				/* link (NOT SUPPORTED) */
    { &vop_rename_desc, hfs_rename },			/* rename */
    { &vop_mkdir_desc, hfs_mkdir },				/* mkdir */
    { &vop_rmdir_desc, hfs_rmdir },				/* rmdir */
    { &vop_mkcomplex_desc,hfs_mkcomplex },		/* mkcomplex */
    { &vop_getattrlist_desc,hfs_getattrlist },  /* getattrlist */
    { &vop_setattrlist_desc,hfs_setattrlist },  /* setattrlist */
    { &vop_symlink_desc, hfs_symlink },			/* symlink */
    { &vop_readdir_desc, hfs_readdir },			/* readdir */
    { &vop_readdirattr_desc, hfs_readdirattr },	/* readdirattr */
    { &vop_readlink_desc, hfs_readlink },		/* readlink */
    { &vop_abortop_desc, hfs_abortop },			/* abortop */
    { &vop_inactive_desc, hfs_inactive },		/* inactive */
    { &vop_reclaim_desc, hfs_reclaim },			/* reclaim */
    { &vop_lock_desc, hfs_lock },				/* lock */
    { &vop_unlock_desc, hfs_unlock },			/* unlock */
    { &vop_bmap_desc, hfs_bmap },				/* bmap */
    { &vop_strategy_desc, hfs_strategy },		/* strategy */
    { &vop_print_desc, hfs_print },				/* print */
    { &vop_islocked_desc, hfs_islocked },		/* islocked */
    { &vop_pathconf_desc, hfs_pathconf },		/* pathconf */
    { &vop_advlock_desc, hfs_advlock },			/* advlock */
    { &vop_reallocblks_desc, hfs_reallocblks },	/* reallocblks */
    { &vop_truncate_desc, hfs_truncate },		/* truncate */
    { &vop_allocate_desc, hfs_allocate },		/* allocate */
    { &vop_update_desc, hfs_update },			/* update */
	{ &vop_searchfs_desc,hfs_search },			/* search fs */
    { &vop_bwrite_desc, vn_bwrite },			/* bwrite */
    { &vop_pagein_desc, hfs_pagein },           /* pagein */
    { &vop_pageout_desc, hfs_pageout },         /* pageout */
    { NULL, NULL }
};

struct vnodeopv_desc hfs_vnodeop_opv_desc =
{ &hfs_vnodeop_p, hfs_vnodeop_entries };