/*

Copyright (c) 1992  Microsoft Corporation

Module Name:

	idindex.h

Abstract:

	This module contains the file and directory id structures.

Author:

	Jameel Hyder (microsoft!jameelh)


Revision History:
	25 Apr 1992		Initial Version

Notes:	Tab stop: 4
--*/

#ifndef _IDINDEX_
#define _IDINDEX_

// AFP_IDDBHDR_VERSION2 was an experimental version.
#define AFP_IDDBHDR_VERSION1	0x00010000
#define AFP_IDDBHDR_VERSION2	0x00020000
#define AFP_IDDBHDR_VERSION3	0x00030000
#define AFP_IDDBHDR_VERSION		AFP_IDDBHDR_VERSION3

typedef struct _IdDbHeader				// Database header
{
	DWORD		idh_Signature;			// Signature
	DWORD		idh_Version;			// Version number
	DWORD		idh_LastId;				// Highest id that is assigned
	AFPTIME		idh_CreateTime;			// Creation time for this volume
	AFPTIME		idh_ModifiedTime;		// Modified time for this volume
	AFPTIME		idh_BackupTime;			// Backup time for this volume
} IDDBHDR, *PIDDBHDR;
// IdDb header is followed by a ULONG count of entries, then the DISKENTRies

#define VALID_DFE(pDfe)		((pDfe) != NULL)

struct _DfeBlock;
struct _DirFileEntry;

#define	MAX_CHILD_HASH_BUCKETS	6

typedef	struct _DirEntry
{
	// NOTE: Keep the ChildDir and ChildFile entries together and in this order
	//		 The code in AfpAddDfEntry depends on this to efficiently zero it out
	//		 Also de_ChildDir is accessed as de_ChildFile[-1] !!!
	struct _DirFileEntry *	de_ChildDir;		// First child in the dir list
	struct _DirFileEntry *	de_ChildFile[MAX_CHILD_HASH_BUCKETS];
												// File 'children' are hashed for faster lookup
	DWORD					de_Access;			// Combined access bits
#ifdef	AGE_DFES
	AFPTIME					de_LastAccessTime;	// Time when this DFE was last accessed
												// Valid for directories only
	LONG					de_ChildForkOpenCount;// Count of open forks within this directory
#endif
} DIRENTRY, *PDIRENTRY;

// Owner access mask (SFI vs. SFO)
#define	DE_OWNER_ACCESS(_pDE)	*((PBYTE)(&(_pDE)->de_Access) + 0)
// Group access mask (SFI vs. SFO)
#define	DE_GROUP_ACCESS(_pDE)	*((PBYTE)(&(_pDE)->de_Access) + 1)
// World access mask (SFI vs. SFO)
#define	DE_WORLD_ACCESS(_pDE)	*((PBYTE)(&(_pDE)->de_Access) + 2)

typedef struct _DirFileEntry
{
	// The dfe_Overflow is overloaded with dfe_NextFree for use by the block
	// allocation package for the DFEs.
#define	dfe_NextFree		dfe_NextOverflow
	struct _DirFileEntry *	dfe_NextOverflow;	// Overflow links
	struct _DirFileEntry **	dfe_PrevOverflow;	// Overflow links
	struct _DirFileEntry *	dfe_NextSibling;	// Next sibling.
	struct _DirFileEntry **	dfe_PrevSibling;	// Previous sibling.
	struct _DirFileEntry *	dfe_Parent;			// Parent entry

	DWORD					dfe_AfpId;			// Afp FileId or DirId (from AfpInfo)
	AFPTIME					dfe_BackupTime;		// Backup time for the file/dir (from AfpInfo)
												// (Volume backup time is stored
												// in the AFP_IdIndex stream)
	AFPTIME					dfe_CreateTime;		// Creation time

	TIME					dfe_LastModTime;	// Last modify time (as a LARGE_INTEGER)

	SHORT					dfe_DirDepth;		// Parent of root at -1, root at 0
	USHORT					dfe_Flags;			// file, dir or file with id

	USHORT					dfe_NtAttr;			// NT Attributes (FILE_ATTRIBUTE_VALID_FLAGS)
	USHORT					dfe_AfpAttr;		// Attributes mask (From AfpInfo)

	union
	{
		// File specific information
		struct									// For Files Only
		{
			DWORD			dfe_DataLen;		// Data fork length
			DWORD			dfe_RescLen;		// Resource fork length
		};
		// Directory specific information
		struct									// For Directories Only
		{
			DWORD			dfe_DirOffspring;	// count of dir offspring
			DWORD			dfe_FileOffspring;	// count of file offspring
		};
	};
	FINDERINFO				dfe_FinderInfo;		// Finder Info (32 bytes) (from AfpInfo)

	// NOTE: When Dfes are copied as a structure, the fields below are NOT TO BE COPIED.
	//		 The fields above should be.
#define	dfe_CopyUpto		dfe_UnicodeName

	UNICODE_STRING			dfe_UnicodeName;	// 'Munged' Unicode Name of the entity

	DWORD					dfe_NameHash;		// Hash value for the upcased munged Unicode name

	// For directories, the DirEntry structure follows this structure. The space for
	// this is allocated immediately after the DFENTRY structure and before the space
	// for the name strings. For files it is NULL. This pointer should not be copied
	// as well !!!
	PDIRENTRY				dfe_pDirEntry;		// Directory related fields
												// NULL for files
} DFENTRY, *PDFENTRY;

// Owner access mask (SFI vs. SFO)
#define	DFE_OWNER_ACCESS(_pDFE)	*((PBYTE)(&(_pDFE)->dfe_pDirEntry->de_Access) + 0)
// Group access mask (SFI vs. SFO)
#define	DFE_GROUP_ACCESS(_pDFE)	*((PBYTE)(&(_pDFE)->dfe_pDirEntry->de_Access) + 1)
// World access mask (SFI vs. SFO)
#define	DFE_WORLD_ACCESS(_pDFE)	*((PBYTE)(&(_pDFE)->dfe_pDirEntry->de_Access) + 2)

typedef	struct _EnumIdAndType
{
	DWORD			eit_Id;
	DWORD			eit_Flags;
} EIT, *PEIT;

// There is the result of enumeration of a directory for this session and is stored
// within the connection descriptor. This is purely for performance reasons. This is
// deleted whenever an api other than AfpEnumerate is called and a result is around.
typedef	struct _EnumDir
{
	DWORD			ed_ParentDirId;		// Anchor point
	DWORD			ed_Bitmap;			// Combination of file & dir bitmaps
	LONG			ed_ChildCount;		// Count of children of the dir being enumerated
	AFPTIME			ed_TimeStamp;		// Time at which created

	PEIT			ed_pEit;			// list of actual entries
	ANSI_STRING		ed_PathName;		// This is the name as passed by the client
										// and is not normalised.
	USHORT			ed_BadCount;		// Count of failed entities
	BYTE			ed_PathType;		// Long name or short name
} ENUMDIR, *PENUMDIR;

typedef	struct _CatSearchSpec
{
	BYTE			__StructLength;
	BYTE			__FillerOrFileDir;
	// The rest of the parameters follow
} CATSEARCHSPEC, *PCATSEARCHSPEC;

// Must be 16 bytes as per AfpCatSearch API
typedef struct _CatalogPosition
{
	USHORT			cp_Flags;			// if zero, then start search from beginning
	USHORT			cp_usPad1;
	DWORD			cp_CurParentId;
	DWORD			cp_NextFileId;
	AFPTIME			cp_TimeStamp;
} CATALOGPOSITION, *PCATALOGPOSITION;

#define CATFLAGS_SEARCHING_FILES		0x0001
#define CATFLAGS_SEARCHING_DIRCHILD		0x0002
#define CATFLAGS_SEARCHING_SIBLING		0x0004
#define	CATFLAGS_WRITELOCK_REQUIRED 	0x0008
#define	CATFLAGS_VALID					(CATFLAGS_SEARCHING_FILES		|	\
										 CATFLAGS_SEARCHING_DIRCHILD	|	\
										 CATFLAGS_SEARCHING_SIBLING		|	\
										 CATFLAGS_WRITELOCK_REQUIRED)

// Maximum time that a mac can hold onto a catsearch position and still
// have the search pickup from there instead of the beginning of the catalog
#define MAX_CATSEARCH_TIME				3600	// In seconds


// DFE_FLAGS_xxxx values for dfe_Flags field of DFENTRY structure
#define DFE_FLAGS_FILE_WITH_ID			0x0100
#define DFE_FLAGS_FILE_NO_ID			0x0200
#define DFE_FLAGS_DIR					0x0400
#define DFE_FLAGS_DFBITS				(DFE_FLAGS_FILE_WITH_ID | \
										 DFE_FLAGS_FILE_NO_ID	| \
										 DFE_FLAGS_DIR)
#define DFE_FLAGS_HAS_COMMENT			0x0800
#define DFE_FLAGS_ENUMERATED			0x8000

// Encode the child and sibling pointers
#define DFE_FLAGS_HAS_CHILD				0x1000	// Used for reading in IdDb from disk
#define DFE_FLAGS_HAS_SIBLING			0x2000	// Used for reading in IdDb from disk
#define DFE_FLAGS_CSENCODEDBITS			(DFE_FLAGS_HAS_CHILD | DFE_FLAGS_HAS_SIBLING)
#define DFE_FLAGS_NAMELENBITS			0x001F	// Encodes the length of the longname
												// which is 31 *characters* max

#define DFE_FLAGS_VALID_DSKBITS			(DFE_FLAGS_CSENCODEDBITS	| \
										 DFE_FLAGS_NAMELENBITS		| \
										 DFE_FLAGS_HAS_COMMENT)

// Valid only for directories when their files have been enumerated from disk
// and now all have cached DFEs in the IDDB tree structure
#define DFE_FLAGS_FILES_CACHED 			0x4000

// DAlreadyOpen and RAlreadyOpen flags for a File
#define DFE_FLAGS_R_ALREADYOPEN			0x0040
#define	DFE_FLAGS_D_ALREADYOPEN			0x0080
#define DFE_FLAGS_OPEN_BITS				(DFE_FLAGS_D_ALREADYOPEN | \
										 DFE_FLAGS_R_ALREADYOPEN)

	
typedef struct _DiskEntry
{
	DWORD		dsk_AfpId;
	AFPTIME		dsk_CreateTime;		// File Creation time
	TIME		dsk_LastModTime;	// Last modify time
	FINDERINFO	dsk_FinderInfo;		// Finder Info (32 bytes) (from AfpInfo)
	AFPTIME		dsk_BackupTime;		// Backup time for the file/dir (from AfpInfo)
									// (Volume backup time is stored
									// in the AFP_IdIndex stream)

	union
	{
		DWORD	dsk_DataLen;		// Data fork length
		DWORD	dsk_Access;			// Combined access rights
	};
	DWORD		dsk_RescLen;		// Resource fork length
	USHORT		dsk_Flags;			// DFE_FLAGS_XXXX
	USHORT		dsk_AfpAttr;		// Attributes mask (From AfpInfo)
	USHORT		dsk_NtAttr;			// From FileAttributes

	USHORT		dsk_Signature;		// AFP_DISKENTRY_SIGNATURE
	WCHAR		dsk_Name[2];		// Longname in 'munged' Unicode will follow and be padded
									// out, if necessary, to DWORD boundry (max 64 bytes)
} DISKENTRY, *PDISKENTRY;

#define AFP_DISKENTRY_SIGNATURE		*(PUSHORT)"::" // illegal name character

// size of buffer used to read-in/write-out the IdDb entries from/to disk
#define	IDDB_UPDATE_BUFLEN				(16*1024)

// Round the length to 4*N
#define DWLEN(_b)	(((_b) + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1))

// #define	DFE_IS_DIRECTORY(_pDFE)		(((_pDFE)->dfe_Flags & DFE_FLAGS_DIR) ? True : False)
#define	DFE_IS_DIRECTORY(_pDFE)			((_pDFE)->dfe_pDirEntry != NULL)

// #define	DFE_IS_FILE(_pDFE)			(((_pDFE)->dfe_Flags & (DFE_FLAGS_FILE_NO_ID | DFE_FLAGS_FILE_WITH_ID)) ? True : False)
#define	DFE_IS_FILE(_pDFE)				((_pDFE)->dfe_pDirEntry == NULL)

#define	DFE_IS_FILE_WITH_ID(_pDFE)		(((_pDFE)->dfe_Flags & DFE_FLAGS_FILE_WITH_ID) ? True : False)

#define	DFE_IS_ROOT(_pDFE)				((_pDFE)->dfe_AfpId == AFP_ID_ROOT)

#define	DFE_IS_PARENT_OF_ROOT(_pDFE)	((_pDFE)->dfe_AfpId == AFP_ID_PARENT_OF_ROOT)

#define DFE_IS_NWTRASH(_pDFE)			((_pDFE)->dfe_AfpId == AFP_ID_NETWORK_TRASH)

#define	DFE_SET_DIRECTORY(_pDFE, _ParentDepth)				\
	{														\
		((_pDFE)->dfe_DirDepth = _ParentDepth + 1);			\
		((_pDFE)->dfe_Flags |= DFE_FLAGS_DIR);				\
	}

#define	DFE_SET_FILE(_pDFE)				((_pDFE)->dfe_Flags |= DFE_FLAGS_FILE_NO_ID)

#define	DFE_SET_FILE_ID(_pDFE)			((_pDFE)->dfe_Flags |= DFE_FLAGS_FILE_WITH_ID)

#define	DFE_CLR_FILE_ID(_pDFE)			((_pDFE)->dfe_Flags &= ~DFE_FLAGS_FILE_WITH_ID)

// update just the AFPinfo in the dfentry
#define DFE_UPDATE_CACHED_AFPINFO(_pDFE, pAfpInfo)				\
	{															\
		(_pDFE)->dfe_BackupTime = (pAfpInfo)->afpi_BackupTime;	\
		(_pDFE)->dfe_FinderInfo = (pAfpInfo)->afpi_FinderInfo;	\
		(_pDFE)->dfe_AfpAttr = (pAfpInfo)->afpi_Attributes;		\
		if ((_pDFE)->dfe_Flags & DFE_FLAGS_DIR)					\
		{														\
			DFE_OWNER_ACCESS(_pDFE) = (pAfpInfo)->afpi_AccessOwner;	\
			DFE_GROUP_ACCESS(_pDFE) = (pAfpInfo)->afpi_AccessGroup;	\
			DFE_WORLD_ACCESS(_pDFE) = (pAfpInfo)->afpi_AccessWorld;	\
		}														\
	}


#define DFE_SET_COMMENT(_pDFE)			((_pDFE)->dfe_Flags |= DFE_FLAGS_HAS_COMMENT)

#define DFE_CLR_COMMENT(_pDFE)			((_pDFE)->dfe_Flags &= ~DFE_FLAGS_HAS_COMMENT)

// Check to see if this entry was enumerated on an NTFS directory
#define DFE_HAS_BEEN_SEEN(_pDFE)		((_pDFE)->dfe_Flags & DFE_FLAGS_ENUMERATED)

#define DFE_MARK_UNSEEN(_pDFE)			((_pDFE)->dfe_Flags &= ~DFE_FLAGS_ENUMERATED)

#define DFE_MARK_AS_SEEN(_pDFE)			((_pDFE)->dfe_Flags |= DFE_FLAGS_ENUMERATED)

// Directories only
#define DFE_CHILDREN_ARE_PRESENT(_pDFE) ((_pDFE)->dfe_Flags & DFE_FLAGS_FILES_CACHED)

// Directories only
#define DFE_MARK_CHILDREN_PRESENT(_pDFE) ((_pDFE)->dfe_Flags |= DFE_FLAGS_FILES_CACHED)

#define	DFE_FILE_HAS_SIBLING(_pDFE, _fbi, _pfHasSibling)			\
	{																\
		DWORD		_i;												\
		PDIRENTRY	_pDirEntry;										\
																	\
		*(_pfHasSibling) = False;									\
		if (((_pDFE)->dfe_NextSibling != NULL)	||					\
			((_pDFE)->dfe_Parent->dfe_pDirEntry->de_ChildDir != NULL)) \
		{															\
			*(_pfHasSibling) = True;								\
		}															\
		else														\
		{															\
			_pDirEntry = (_pDFE)->dfe_Parent->dfe_pDirEntry;	 	\
			ASSERT(_pDirEntry != NULL);								\
			for (_i = (_fbi) + 1;									\
				 _i < MAX_CHILD_HASH_BUCKETS;						\
				 _i++)												\
			{														\
				if (_pDirEntry->de_ChildFile[_i] != NULL)			\
				{													\
					*(_pfHasSibling) = True;						\
					break;											\
				}													\
			}														\
		}															\
	}

#define	HASH_DIR_ID(Id)					((Id) & (IDINDEX_DIR_BUCKETS-1))
#define	HASH_FILE_ID(Id)				((Id) % (IDINDEX_FILE_BUCKETS-1))
#define	HASH_CACHE_ID(Id)				((Id) % (IDINDEX_CACHE_ENTRIES-1))

#define	QUAD_SIZED(_X_)			(((_X_) % 8) == 0)

// Values for access checking
#define	ACCESS_READ						1
#define	ACCESS_WRITE					2

extern
NTSTATUS
AfpDfeInit(
	VOID
);

extern
VOID
AfpDfeDeInit(
	VOID
);

extern
PDFENTRY
AfpFindDfEntryById(
	IN	struct _VolDesc *			pVolDesc,
	IN	DWORD						Id,
	IN	DWORD						EntityMask
);

extern
PDFENTRY
AfpFindEntryByUnicodeName(
	IN	struct _VolDesc *			pVolDesc,
	IN	PUNICODE_STRING				pName,
	IN	DWORD						PathType,
	IN	PDFENTRY					pDfeParent,
	IN	DWORD						EntityMask
);

extern
PDFENTRY
AfpAddDfEntry(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfeParent,
	IN	PUNICODE_STRING				pUName,
	IN	BOOLEAN						Directory,
	IN	DWORD						AfpId			OPTIONAL
);

extern
PDFENTRY
AfpRenameDfEntry(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfEntry,
	IN	PUNICODE_STRING				pNewName
);

extern
PDFENTRY
AfpMoveDfEntry(				
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfEntry,
	IN	PDFENTRY					pNewParentDfE,
	IN	PUNICODE_STRING				pNewName		OPTIONAL
);

extern
VOID FASTCALL
AfpDeleteDfEntry(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfE
);

extern
VOID
AfpExchangeIdEntries(
	IN	struct _VolDesc *			pVolDesc,
	IN	DWORD						AfpId1,
	IN	DWORD						AfpId2
);

extern
VOID FASTCALL
AfpPruneIdDb(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfeTarget
);

extern
NTSTATUS FASTCALL
AfpInitIdDb(
	IN	struct _VolDesc *			pVolDesc
);

extern
VOID FASTCALL
AfpFreeIdIndexTables(
	IN	struct _VolDesc *			pVolDesc
);

extern
AFPSTATUS
AfpEnumerate(
	IN	struct _ConnDesc *			pConnDesc,
	IN	DWORD						ParentDirId,
	IN	PANSI_STRING				pPath,
	IN	DWORD						BitmapF,
	IN	DWORD						BitmapD,
	IN	BYTE						PathType,
	IN	DWORD						DFFlags,
	OUT PENUMDIR *					ppEnumDir
);

extern
AFPSTATUS
AfpSetDFFileFlags(
	IN	struct _VolDesc *			pVolDesc,
	IN	DWORD						AfpId,
	IN	DWORD						FlagSet		OPTIONAL,
	IN	BOOLEAN						SetFileId,
	IN	BOOLEAN						ClrFileId
);

extern
VOID
AfpChangeNotifyThread(
	IN	PVOID						pContext
);

extern
VOID FASTCALL
AfpProcessChangeNotify(		
	IN	struct _VolumeNotify *		pVolNotify
);

extern
VOID
AfpQueuePrivateChangeNotify(
	IN	struct _VolDesc *			pVolDesc,
	IN	PUNICODE_STRING				pName,
	IN	PUNICODE_STRING				pPath,
	IN	DWORD						ParentId
);

extern
BOOLEAN FASTCALL
AfpShouldWeIgnoreThisNotification(
	IN	struct _VolumeNotify *		pVolNotify
);

extern
VOID
AfpQueueOurChange(
	IN	struct _VolDesc *			pVolDesc,
	IN DWORD						Action,
	IN PUNICODE_STRING				pPath,
	IN PUNICODE_STRING				pParentPath	OPTIONAL
);

extern
VOID
AfpDequeueOurChange(
	IN	struct _VolDesc *			pVolDesc,
	IN DWORD						Action,
	IN PUNICODE_STRING				pPath,
	IN PUNICODE_STRING				pParentPath	OPTIONAL
);

extern
VOID
AfpCacheParentModTime(
	IN	struct _VolDesc *			pVolDesc,
	IN	PFILESYSHANDLE				pHandle		OPTIONAL,	// if pPath not supplied
	IN	PUNICODE_STRING				pPath		OPTIONAL,	// if pHandle not supplied
	IN	PDFENTRY					pDfeParent	OPTIONAL,	// if ParentId not supplied
	IN	DWORD						ParentId	OPTIONAL	// if pDfeParent not supplied
);

extern
AFPSTATUS
AfpCatSearch(
	IN	struct _ConnDesc *			pConnDesc,
	IN	PCATALOGPOSITION			pCatPosition,
	IN	DWORD						Bitmap,
	IN	DWORD						FileBitmap,
	IN	DWORD						DirBitmap,
	IN	struct _FileDirParms *		pFDParm1,
	IN	struct _FileDirParms *		pFDParm2,
	IN	PUNICODE_STRING				pMatchString	OPTIONAL,
	IN OUT	PDWORD					pCount,
	IN	SHORT						Buflen,
	OUT	PSHORT						pSizeLeft,
	OUT	PBYTE						pResults,
	OUT	PCATALOGPOSITION			pNewCatPosition
);

#ifdef	AGE_DFES

extern
VOID FASTCALL
AfpAgeDfEntries(
	IN	struct _VolDesc *			pVolDesc
);

#endif

#define	REENUMERATE		   			0x0001
#define	GETDIRSKELETON				0x0002
#define	GETFILES	    			0x0004
#define	GETENTIRETREE   			0x0008

extern
NTSTATUS
AfpCacheDirectoryTree(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDFETreeRoot,
	IN	DWORD						Method,
	IN	PFILESYSHANDLE				phRootDir		OPTIONAL,
	IN	PUNICODE_STRING				pDirPath		OPTIONAL
);

extern
AFPSTATUS FASTCALL
AfpOurChangeScavenger(
	IN	struct _VolDesc *			pVolDesc
);

extern
VOID FASTCALL
AfpFlushIdDb(
	IN	struct _VolDesc *			pVolDesc,
	IN	PFILESYSHANDLE				phIdDb
);

#ifdef IDINDEX_LOCALS

#define	afpConvertBasicToBothDirInfo(_pFBasInfo, _pFBDInfo)			\
{																	\
	(_pFBDInfo)->CreationTime = (_pFBasInfo)->CreationTime;			\
	(_pFBDInfo)->LastWriteTime = (_pFBasInfo)->LastWriteTime;		\
	(_pFBDInfo)->ChangeTime = (_pFBasInfo)->ChangeTime;				\
	(_pFBDInfo)->FileAttributes = (_pFBasInfo)->FileAttributes;		\
	(_pFBDInfo)->EndOfFile.QuadPart = 0;																\
}

#undef	EQU
#ifdef	_IDDB_GLOBALS_
#define	IDDBGLOBAL
#define	EQU				=
#else
#define	IDDBGLOBAL		extern
#define	EQU				; / ## /
#endif

// This bit on a Notify action indicates it is a simulated notify. Volume
// modified time is not updated when such a notify comes in
#define	AFP_ACTION_PRIVATE		0x80000000

// DFEs come in four sizes. This helps in efficiently managing them in a block
// package (see later). THESE SIZES NEED TO BE 4*N, else we run into alignment
// faults on architectures that require it.
#define	DFE_INDEX_TINY			0
#define	DFE_INDEX_SMALL			1
#define	DFE_INDEX_MEDIUM		2
#define	DFE_INDEX_LARGE			3

//
// Make sure each of the sizes below (XXX_U) are multiple of 8
//
#define	DFE_SIZE_TINY			8		// These are lengths for ANSI names
#define	DFE_SIZE_SMALL			12		//		- ditto -
#define	DFE_SIZE_MEDIUM			20		//		- ditto -
#define	DFE_SIZE_LARGE			32		//		- ditto -	corres. to AFP_FILENAME_LEN

#define	DFE_SIZE_TINY_U			DFE_SIZE_TINY*sizeof(WCHAR)		// These are lengths for UNICODE names
#define	DFE_SIZE_SMALL_U		DFE_SIZE_SMALL*sizeof(WCHAR)	//		- ditto -
#define	DFE_SIZE_MEDIUM_U		DFE_SIZE_MEDIUM*sizeof(WCHAR)	//		- ditto -
#define	DFE_SIZE_LARGE_U		DFE_SIZE_LARGE*sizeof(WCHAR)	//		- ditto -	corres. to AFP_FILENAME_LEN

#define	ASIZE_TO_INDEX(_Size)												\
		(((_Size) <= DFE_SIZE_TINY) ? DFE_INDEX_TINY :						\
						(((_Size) <= DFE_SIZE_SMALL) ? DFE_INDEX_SMALL :	\
						 (((_Size) <= DFE_SIZE_MEDIUM) ? DFE_INDEX_MEDIUM : DFE_INDEX_LARGE)))

#define	USIZE_TO_INDEX(_Size)												\
		(((_Size) <= DFE_SIZE_TINY_U) ? DFE_INDEX_TINY :					\
						(((_Size) <= DFE_SIZE_SMALL_U) ? DFE_INDEX_SMALL :	\
						 (((_Size) <= DFE_SIZE_MEDIUM_U) ? DFE_INDEX_MEDIUM : DFE_INDEX_LARGE)))

#define	ALLOC_DFE(Index, fDir)	afpAllocDfe(Index, fDir)
#define FREE_DFE(pDfEntry)		afpFreeDfe(pDfEntry)


LOCAL DWORD FASTCALL
afpGetNextId(
	IN	struct _VolDesc *			pVolDesc
);

LOCAL
NTSTATUS FASTCALL
afpSeedIdDb(
	IN	struct _VolDesc *			pVolDesc
);

LOCAL
VOID
afpPackSearchParms(
	IN	PDFENTRY					pDfe,
	IN	DWORD						Bitmap,
	IN	PBYTE						pBuf
);

LOCAL
NTSTATUS FASTCALL
afpReadIdDb(
	IN	struct _VolDesc *			pVolDesc,
	IN	PFILESYSHANDLE				pfshIdDb
);

VOID
afpAddDfEntryAndCacheInfo(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfeParent,
	IN	PUNICODE_STRING				pUName,			// munged unicode name
	IN	PFILESYSHANDLE				pfshParentDir,	// open handle to parent directory
	IN	PFILE_BOTH_DIR_INFORMATION	pFBDInfo,		// from enumerate
	IN	PUNICODE_STRING				pNotifyPath,	// to filter out our own AFP_AfpInfo change notifies
	IN	PDFENTRY	*				ppDfEntry,
	IN	BOOLEAN						CheckDuplicate
);

VOID
afpVerifyDFE(
	IN	struct _VolDesc *			pVolDesc,
	IN	PDFENTRY					pDfeParent,
	IN	PUNICODE_STRING				pUName,			// munged unicode name
	IN	PFILESYSHANDLE				pfshParentDir,	// open handle to parent directory
	IN	PFILE_BOTH_DIR_INFORMATION	pFBDInfo,		// from enumerate
	IN	PUNICODE_STRING				pNotifyPath,	// to filter out our own AFP_AfpInfo change notifies
	IN	PDFENTRY	*				ppDfEntry
);

PDFENTRY
afpFindEntryByNtPath(
	IN	struct _VolDesc *			pVolDesc,
	IN	DWORD						ChangeAction,	// if ADDED then lookup parent DFE
	IN	PUNICODE_STRING				pPath,
	OUT PUNICODE_STRING				pParent,
	OUT PUNICODE_STRING				pTail
);

PDFENTRY
afpFindEntryByNtName(
	IN	struct _VolDesc *			pVolDesc,
	IN	PUNICODE_STRING				pName,
	IN	PDFENTRY					pDfeParent		// pointer to parent DFENTRY
);

VOID FASTCALL
afpProcessPrivateNotify(
	IN	struct _VolumeNotify *		pVolNotify
);

VOID FASTCALL
afpActivateVolume(
	IN	struct _VolDesc *			pVolDesc
);

VOID
afpRenameInvalidWin32Name(
	IN	PFILESYSHANDLE				phRootDir,
	IN	BOOLEAN						IsDir,
	IN PUNICODE_STRING				pName
);

#define	afpInitializeIdDb(_pVolDesc)									\
	{																	\
		AFPTIME		CurrentTime;										\
		PDFENTRY	pDfEntry;											\
																		\
		/* RO volumes don't use the network trash folder at all */		\
		(_pVolDesc)->vds_LastId	= AFP_ID_NETWORK_TRASH;					\
																		\
		AfpGetCurrentTimeInMacFormat(&CurrentTime);						\
		(_pVolDesc)->vds_CreateTime = CurrentTime;						\
		(_pVolDesc)->vds_ModifiedTime = CurrentTime;					\
		(_pVolDesc)->vds_BackupTime = BEGINNING_OF_TIME;				\
																		\
		/* Create a DFE for the root directory */						\
		afpCreateParentOfRoot(_pVolDesc, &pDfEntry);					\
	}


#ifdef AGE_DFES

#define	afpCreateParentOfRoot(_pVolDesc, _ppDfEntry)					\
	{																	\
		PDFENTRY	pDFE;												\
																		\
		/*																\
		 * add the parent of root to the id index.						\
		 * This has to be done here										\
		 * (i.e. cannot call AfpAddDfEntry for parentofroot).			\
		 */																\
																		\
		if ((*(_ppDfEntry) = ALLOC_DFE(0, True)) != NULL)				\
		{																\
			pDFE = *(_ppDfEntry);										\
																		\
			(_pVolDesc)->vds_NumFileDfEntries = 0;						\
			(_pVolDesc)->vds_NumDirDfEntries = 0;						\
			pDFE->dfe_Flags = DFE_FLAGS_DIR | DFE_FLAGS_FILES_CACHED;	\
			pDFE->dfe_DirDepth = -1;									\
			pDFE->dfe_Parent = NULL;									\
			pDFE->dfe_NextOverflow = NULL;								\
																		\
			/* Initialize the DirEntry for ParentOfRoot */				\
			ASSERT((FIELD_OFFSET(DIRENTRY, de_ChildFile) -				\
					FIELD_OFFSET(DIRENTRY, de_ChildDir)) == sizeof(PVOID));\
																		\
			/* These fields are relevant to directories only */			\
			pDFE->dfe_pDirEntry->de_LastAccessTime = BEGINNING_OF_TIME;	\
			pDFE->dfe_pDirEntry->de_ChildForkOpenCount = 0;				\
																		\
			/*															\
			 * The parent of root has no siblings and this should		\
			 * never be referenced										\
			 */															\
			pDFE->dfe_NameHash = 0;										\
			pDFE->dfe_NextSibling = NULL;								\
			pDFE->dfe_PrevSibling = NULL;								\
			pDFE->dfe_AfpId = AFP_ID_PARENT_OF_ROOT;					\
			pDFE->dfe_DirOffspring = pDFE->dfe_FileOffspring = 0;		\
																		\
			/* link it into the hash buckets */							\
			AfpLinkDoubleAtHead((_pVolDesc)->vds_pDfeDirBuckets[HASH_DIR_ID(AFP_ID_PARENT_OF_ROOT)],\
								pDFE,									\
								dfe_NextOverflow,						\
								dfe_PrevOverflow);						\
		}																\
	}

#else

#define	afpCreateParentOfRoot(_pVolDesc, _ppDfEntry)					\
	{																	\
		PDFENTRY	pDfEntry;											\
																		\
		/*																\
		 * add the parent of root to the id index.						\
		 * This has to be done here										\
		 *	(i.e. cannot call AfpAddDfEntry for parentofroot).			\
		 */																\
																		\
		if ((*(_ppDfEntry) = ALLOC_DFE(0, True)) != NULL)				\
		{																\
			pDfEntry = *(_ppDfEntry);									\
																		\
			(_pVolDesc)->vds_NumFileDfEntries = 0;						\
			(_pVolDesc)->vds_NumDirDfEntries = 0;						\
			pDfEntry->dfe_Flags = DFE_FLAGS_DIR | DFE_FLAGS_FILES_CACHED;\
			pDfEntry->dfe_DirDepth = -1;								\
			pDfEntry->dfe_Parent = NULL;								\
			pDfEntry->dfe_NextOverflow = NULL;							\
																		\
			/* Initialize the DirEntry for ParentOfRoot */				\
			ASSERT((FIELD_OFFSET(DIRENTRY, de_ChildFile) -				\
					FIELD_OFFSET(DIRENTRY, de_ChildDir)) == sizeof(PVOID));\
																		\
			/* The parent of root has no siblings and this should never be referenced */ \
			pDfEntry->dfe_NameHash = 0;									\
			pDfEntry->dfe_NextSibling = NULL;							\
			pDfEntry->dfe_PrevSibling = NULL;							\
			pDfEntry->dfe_AfpId = AFP_ID_PARENT_OF_ROOT;				\
			pDfEntry->dfe_DirOffspring = pDfEntry->dfe_FileOffspring = 0;\
																		\
			/* link it into the hash buckets */							\
			AfpLinkDoubleAtHead((_pVolDesc)->vds_pDfeDirBuckets[HASH_DIR_ID(AFP_ID_PARENT_OF_ROOT)],\
								pDfEntry,								\
								dfe_NextOverflow,						\
								dfe_PrevOverflow);						\
		}																\
	}

#endif

#define	afpHashUnicodeName(_pUnicodeName, _pHashValue)					\
	{																	\
		DWORD				j;											\
		UNICODE_STRING		upcaseName;									\
		WCHAR				buffer[AFP_LONGNAME_LEN+1];					\
		PDWORD				pbuf;										\
																		\
		AfpSetEmptyUnicodeString(&upcaseName, sizeof(buffer), buffer);	\
		RtlUpcaseUnicodeString(&upcaseName, _pUnicodeName, False);		\
		j = upcaseName.Length/sizeof(WCHAR);							\
		buffer[j] = UNICODE_NULL;										\
		pbuf = (PDWORD)buffer;											\
		j /= (sizeof(DWORD)/sizeof(WCHAR));								\
																		\
		for (*(_pHashValue) = 0; j > 0; j--, pbuf++)					\
		{																\
			*(_pHashValue) = (*(_pHashValue) << 3) + *pbuf;				\
		}																\
	}

#ifdef	SORT_DFE_BY_HASH
#define	afpFindDFEByUnicodeNameInSiblingList(_pVolDesc, _pDfeParent, _pName, _ppDfEntry, _EntityMask) \
	{																	\
		DWORD		NameHash; 											\
		PDFENTRY	pD, pF;												\
		BOOLEAN		Found, fFiles;										\
																		\
		afpHashUnicodeName(_pName, &NameHash);							\
																		\
		pD = (_pDfeParent)->dfe_pDirEntry->de_ChildDir;					\
		if (((_EntityMask) & (DFE_ANY | DFE_DIR)) == 0)					\
			pD = NULL;													\
																		\
		pF = NULL;														\
		if ((_EntityMask) & (DFE_ANY | DFE_FILE))						\
			pF = (_pDfeParent)->dfe_pDirEntry->de_ChildFile[NameHash % MAX_CHILD_HASH_BUCKETS];\
																		\
		*(_ppDfEntry) = pD;												\
		Found = fFiles = False;											\
		do																\
		{																\
			for (NOTHING;												\
				 *(_ppDfEntry) != NULL;									\
				 *(_ppDfEntry) = (*(_ppDfEntry))->dfe_NextSibling)		\
			{															\
				if ((*(_ppDfEntry))->dfe_NameHash < NameHash)			\
				{														\
					*(_ppDfEntry) = NULL;								\
					break;												\
				}														\
																		\
				if (((*(_ppDfEntry))->dfe_NameHash == NameHash)	&&		\
					EQUAL_UNICODE_STRING(&((*(_ppDfEntry))->dfe_UnicodeName), \
										 _pName,						\
										 True))							\
				{														\
					afpUpdateDfeAccessTime(_pVolDesc, *(_ppDfEntry));	\
					Found = True;										\
					break;												\
				}														\
			}															\
			if (Found)													\
				break;													\
																		\
			fFiles ^= True;												\
			if (fFiles)													\
			{															\
				*(_ppDfEntry) = pF;										\
			}															\
		} while (fFiles);												\
	}

#define	afpFindDFEByUnicodeNameInSiblingList_CS(_pVolDesc, _pDfeParent, _pName, _ppDfEntry, _EntityMask) \
	{																	\
		DWORD		NameHash; 											\
		PDFENTRY	pD, pF;												\
		BOOLEAN		Found, fFiles;										\
																		\
		afpHashUnicodeName(_pName, &NameHash);							\
																		\
		pD = (_pDfeParent)->dfe_pDirEntry->de_ChildDir;				 	\
		if (((_EntityMask) & (DFE_ANY | DFE_DIR)) == 0)					\
			pD = NULL;													\
																		\
		pF = NULL;														\
		if ((_EntityMask) & (DFE_ANY | DFE_FILE))						\
			pF = (_pDfeParent)->dfe_pDirEntry->de_ChildFile[NameHash % MAX_CHILD_HASH_BUCKETS];\
																		\
		*(_ppDfEntry) = pD;												\
		Found = fFiles = False;											\
		do																\
		{																\
			for (NOTHING;												\
				 *(_ppDfEntry) != NULL;									\
				 *(_ppDfEntry) = (*(_ppDfEntry))->dfe_NextSibling)		\
			{															\
				if ((*(_ppDfEntry))->dfe_NameHash < NameHash)			\
				{														\
					*(_ppDfEntry) = NULL;								\
					break;												\
				}														\
																		\
				if (((*(_ppDfEntry))->dfe_NameHash == NameHash)	&&		\
					EQUAL_UNICODE_STRING_CS(&((*(_ppDfEntry))->dfe_UnicodeName), _pName)) \
				{														\
					afpUpdateDfeAccessTime(_pVolDesc, *(_ppDfEntry));	\
					Found = True;										\
					break;												\
				}														\
			}															\
			if (Found)													\
				break;													\
																		\
			fFiles ^= True;												\
			if (fFiles)													\
			{															\
				*(_ppDfEntry) = pF;										\
			}															\
		} while (fFiles);												\
	}
#else
#define	afpFindDFEByUnicodeNameInSiblingList(_pVolDesc, _pDfeParent, _pName, _ppDfEntry, _EntityMask) \
	{																	\
		DWORD		NameHash; 											\
		PDFENTRY	pD, pF;												\
		BOOLEAN		Found, fFiles;										\
																		\
		afpHashUnicodeName(_pName, &NameHash);							\
																		\
		pD = (_pDfeParent)->dfe_pDirEntry->de_ChildDir;					\
		if (((_EntityMask) & (DFE_ANY | DFE_DIR)) == 0)					\
			pD = NULL;													\
																		\
		pF = NULL;														\
		if ((_EntityMask) & (DFE_ANY | DFE_FILE))						\
			pF = (_pDfeParent)->dfe_pDirEntry->de_ChildFile[NameHash % MAX_CHILD_HASH_BUCKETS];\
																		\
		*(_ppDfEntry) = pD;												\
		Found = fFiles = False;											\
		do																\
		{																\
			for (NOTHING;												\
				 *(_ppDfEntry) != NULL;									\
				 *(_ppDfEntry) = (*(_ppDfEntry))->dfe_NextSibling)		\
			{															\
				if (((*(_ppDfEntry))->dfe_NameHash == NameHash)	&&		\
					EQUAL_UNICODE_STRING(&((*(_ppDfEntry))->dfe_UnicodeName), \
										 _pName,						\
										 True))							\
				{														\
					afpUpdateDfeAccessTime(_pVolDesc, *(_ppDfEntry));	\
					Found = True;										\
					break;												\
				}														\
			}															\
			if (Found)													\
				break;													\
																		\
			fFiles ^= True;												\
			if (fFiles)													\
			{															\
				*(_ppDfEntry) = pF;										\
			}															\
		} while (fFiles);												\
	}

#define	afpFindDFEByUnicodeNameInSiblingList_CS(_pVolDesc, _pDfeParent, _pName, _ppDfEntry, _EntityMask) \
	{																	\
		DWORD		NameHash; 											\
		PDFENTRY	pD, pF;												\
		BOOLEAN		Found, fFiles;										\
																		\
		afpHashUnicodeName(_pName, &NameHash);							\
																		\
		pD = (_pDfeParent)->dfe_pDirEntry->de_ChildDir;				 	\
		if (((_EntityMask) & (DFE_ANY | DFE_DIR)) == 0)					\
			pD = NULL;													\
																		\
		pF = NULL;														\
		if ((_EntityMask) & (DFE_ANY | DFE_FILE))						\
			pF = (_pDfeParent)->dfe_pDirEntry->de_ChildFile[NameHash % MAX_CHILD_HASH_BUCKETS];\
																		\
		*(_ppDfEntry) = pD;												\
		Found = fFiles = False;											\
		do																\
		{																\
			for (NOTHING;												\
				 *(_ppDfEntry) != NULL;									\
				 *(_ppDfEntry) = (*(_ppDfEntry))->dfe_NextSibling)		\
			{															\
				if (((*(_ppDfEntry))->dfe_NameHash == NameHash)	&&		\
					EQUAL_UNICODE_STRING_CS(&((*(_ppDfEntry))->dfe_UnicodeName), _pName)) \
				{														\
					afpUpdateDfeAccessTime(_pVolDesc, *(_ppDfEntry));	\
					Found = True;										\
					break;												\
				}														\
			}															\
			if (Found)													\
				break;													\
																		\
			fFiles ^= True;												\
			if (fFiles)													\
			{															\
				*(_ppDfEntry) = pF;										\
			}															\
		} while (fFiles);												\
	}
#endif

#define	afpInsertDFEInSiblingList(_pDfeParent, _pDfEntry, _fDirectory)	\
	{																	\
		if (fDirectory)													\
		{																\
	        afpInsertDirDFEInSiblingList(_pDfeParent, _pDfEntry);		\
		}																\
		else															\
		{																\
	        afpInsertFileDFEInSiblingList(_pDfeParent, _pDfEntry);		\
		}																\
	}


#define	afpInsertFileDFEInSiblingList(_pDfeParent, _pDfEntry)			\
	{																	\
		DWORD		Index;												\
		PDFENTRY *	ppDfEntry;											\
																		\
		Index = (_pDfEntry)->dfe_NameHash % MAX_CHILD_HASH_BUCKETS;		\
		ppDfEntry = &(_pDfeParent)->dfe_pDirEntry->de_ChildFile[Index];	\
	    afpInsertInSiblingList(ppDfEntry,								\
							   (_pDfEntry));							\
	}


#define	afpInsertDirDFEInSiblingList(_pDfeParent, _pDfEntry)			\
	{																	\
		PDFENTRY *	ppDfEntry;											\
																		\
		ppDfEntry = &(_pDfeParent)->dfe_pDirEntry->de_ChildDir;			\
	    afpInsertInSiblingList(ppDfEntry,								\
							   (_pDfEntry));							\
	}

#ifdef	SORT_DFE_BY_HASH
#define	afpInsertInSiblingList(_ppHead, _pDfEntry)						\
	{																	\
		for (NOTHING;													\
			 *(_ppHead) != NULL;										\
			 (_ppHead) = &(*(_ppHead))->dfe_NextSibling)				\
		{																\
			if ((_pDfEntry)->dfe_NameHash > (*(_ppHead))->dfe_NameHash)	\
			{															\
				break;													\
			}															\
		}																\
		if (*(_ppHead) != NULL)											\
		{																\
			AfpInsertDoubleBefore(_pDfEntry,							\
								  *(_ppHead),							\
								  dfe_NextSibling,						\
								  dfe_PrevSibling);						\
		}																\
		else															\
		{																\
			*(_ppHead) = (_pDfEntry);									\
			(_pDfEntry)->dfe_NextSibling = NULL;						\
			(_pDfEntry)->dfe_PrevSibling = (_ppHead);					\
		}																\
	}
#else
#define	afpInsertInSiblingList(_ppHead, _pDfEntry)						\
	{																	\
		AfpLinkDoubleAtHead(*(_ppHead),									\
							(_pDfEntry),								\
							dfe_NextSibling,							\
							dfe_PrevSibling);							\
	}
#endif

#define	afpInsertDFEInHashBucket(_pVolDesc, _pDfEntry, _fDirectory, _pfS)\
	{																	\
		PDFENTRY	*ppTmp;												\
																		\
		afpUpdateDfeAccessTime(_pVolDesc, _pDfEntry);					\
		*(_pfS) = True;	/* Assume success */							\
	  retry:															\
		ppTmp = (_fDirectory) ?											\
				&(_pVolDesc)->vds_pDfeDirBuckets[HASH_DIR_ID((_pDfEntry)->dfe_AfpId)] : \
				&(_pVolDesc)->vds_pDfeFileBuckets[HASH_FILE_ID((_pDfEntry)->dfe_AfpId)];\
		for (NOTHING;													\
			 *ppTmp != NULL;											\
			 ppTmp = &(*ppTmp)->dfe_NextOverflow)						\
		{																\
			ASSERT(VALID_DFE(*ppTmp));									\
			if ((_pDfEntry)->dfe_AfpId > (*ppTmp)->dfe_AfpId)			\
			{															\
				/* Found our slot */									\
				break;													\
			}															\
			if ((_pDfEntry)->dfe_AfpId == (*ppTmp)->dfe_AfpId)			\
			{															\
				/* Found a collision. Assign a new id and proceed */	\
				(_pDfEntry)->dfe_AfpId = afpGetNextId(_pVolDesc);		\
				if ((_pDfEntry)->dfe_AfpId == 0)						\
				{														\
					/* Uh-oh */											\
					*(_pfS) = False;									\
					DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,			\
							("afpInsertDFEInHashBucket: Collision & Id Overflow\n"));\
					break;												\
				}														\
				/* Write back the afpinfo stream with the new id */		\
				AfpUpdateIdInAfpInfo(_pVolDesc, _pDfEntry);				\
				goto retry;												\
			}															\
		}																\
		if (*(_pfS))													\
		{																\
			if (*ppTmp != NULL)											\
			{															\
				AfpInsertDoubleBefore((_pDfEntry),						\
									  *ppTmp,							\
									  dfe_NextOverflow,					\
									  dfe_PrevOverflow);				\
			}															\
			else														\
			{															\
				*ppTmp = _pDfEntry;										\
				(_pDfEntry)->dfe_PrevOverflow = ppTmp;					\
			}															\
			(_pVolDesc)->vds_pDfeCache[HASH_CACHE_ID((_pDfEntry)->dfe_AfpId)] = (_pDfEntry); \
		}																\
	}

#define	afpValidateDFEType(_pDfEntry, _EntityMask)						\
	{																	\
		if (((_EntityMask) & DFE_ANY) ||								\
			(((_EntityMask) & DFE_DIR) && DFE_IS_DIRECTORY(_pDfEntry)) || \
			(((_EntityMask) & DFE_FILE) && DFE_IS_FILE(_pDfEntry)))		\
			NOTHING;													\
		else															\
		{																\
			_pDfEntry = NULL;											\
		}																\
	}

/***	afpCheckDfEntry
 *
 *	When enumerating the disk during volume add, if a file/directory
 *	has an AfpId associated with it, then it is validated to see if it is
 *	within range as well as unique.  If there is a collision between AFPIds,
 *	a PC user must have copied (or restored) something from
 *	this volume, or a different volume (or server) that had the same AFPId,
 *	in which case we will give the new entity a different AFP Id;
 *	If there is not a collision between AFPIds, and the Id is larger than the
 *  last Id we know we assigned, then the new entity gets added with a new
 *  AFPId; else if the Id is within the range, we will just use its existing
 *  Id.
 *
 *	Discovered AFPId is:				Action for discovered entity in IdDb is:
 *	--------------------				----------------------------------------
 *	1) > last Id						Add a new entry, assign a new AFPId
 *
 *	2) Collides with existing Id:
 *		* Host copy occurred			Add a new entry, assign a new AFPId
 *
 *	3) < last Id						Insert this entity using same AFPId
 *
 *
 *	LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Exclusive)
 * VOID
 * afpCheckDfEntry(
 * 	IN	PVOLDESC		pVolDesc,
 * 	IN	PAFPINFO		pAfpInfo,	// AFP Info of the discovered entity
 * 	IN	PUNICODE_STRING pUName,		// Munged unicode name
 * 	IN	BOOLEAN			IsDir,		// is this thing a file or a directory?
 * 	IN	PDFENTRY		pParent,	// parent DFE of the discovered thing
 * 	OUT	PDFENTRY	*	ppDfEntry
 * );
 */
#define	afpCheckDfEntry(_pVolDesc, _AfpId, _pUName, _IsDir, _pParent, _ppDfEntry)	\
	{																	\
		PDFENTRY	pDfeNew;											\
																		\
		if (((_AfpId) > (_pVolDesc)->vds_LastId)	||					\
			((_AfpId) <= AFP_ID_NETWORK_TRASH)		||					\
			(AfpFindDfEntryById(_pVolDesc, _AfpId, DFE_ANY) != NULL))	\
		{																\
			/* add the item to the DB but assign it a new AFP Id */     \
			_AfpId = 0;												 	\
		}																\
																		\
		pDfeNew = AfpAddDfEntry(_pVolDesc,								\
								_pParent,								\
								_pUName,								\
								_IsDir,								 	\
								_AfpId);								\
																		\
		*(_ppDfEntry) = pDfeNew;										\
	}

#ifdef	AGE_DFES
#define	afpUpdateDfeAccessTime(_pVolDesc, _pDfEntry)					\
	{																	\
		if (IS_VOLUME_AGING_DFES(_pVolDesc))							\
		{																\
			if (DFE_IS_DIRECTORY(_pDfEntry))							\
				AfpGetCurrentTimeInMacFormat(&(_pDfEntry)->dfe_pDirEntry->de_LastAccessTime);\
			else AfpGetCurrentTimeInMacFormat(&(_pDfEntry)->dfe_Parent->dfe_pDirEntry->de_LastAccessTime);\
		}																\
	}
#else
#define	afpUpdateDfeAccessTime(pVolDesc, pDfEntry)
#endif

#define	afpMarkAllChildrenUnseen(_pDFETree)								\
	{																	\
		LONG		i = -1;												\
		PDFENTRY	pDFE;												\
																		\
		/*																\
		 * Even if this dir has not had its file children cached in		\
		 * yet, we still want to prune out any dead directory children	\
		 */																\
		for (pDFE = (_pDFETree)->dfe_pDirEntry->de_ChildDir;			\
			 True;														\
			 pDFE = (_pDFETree)->dfe_pDirEntry->de_ChildFile[i])		\
		{																\
			for (NOTHING;												\
				 pDFE != NULL;											\
				 pDFE = pDFE->dfe_NextSibling)							\
			{															\
				DFE_MARK_UNSEEN(pDFE);									\
			}															\
			if (++i == MAX_CHILD_HASH_BUCKETS)							\
				break;													\
		}																\
	}

#define	afpPruneUnseenChildren(_pVolDesc, _pDFETree)					\
	{																	\
		PDFENTRY	pDFE, *ppDfEntry;									\
		LONG		i;													\
																		\
		/*																\
		 * Go thru the list of children for this parent, and if there	\
		 * are any left that are not marked as seen, get rid of them.	\
		 */																\
		ppDfEntry = &(_pDFETree)->dfe_pDirEntry->de_ChildDir;			\
		i = -1;															\
		while (True)													\
		{																\
			while ((pDFE = *ppDfEntry) != NULL)							\
			{															\
				if (!DFE_HAS_BEEN_SEEN(pDFE))							\
				{														\
					DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,			\
							("afpPruneUnseenChildren: deleting nonexistant IdDb entry\n")); \
																		\
					AfpDeleteDfEntry(_pVolDesc, pDFE);					\
					continue;	/* make sure we don't skip any */		\
				}														\
				ppDfEntry = &pDFE->dfe_NextSibling;						\
			}															\
			if (++i == MAX_CHILD_HASH_BUCKETS)							\
			{															\
				break;													\
			}															\
			ppDfEntry = &(_pDFETree)->dfe_pDirEntry->de_ChildFile[i];	\
		}																\
	}

#define	afpUpdateDfeWithSavedData(_pDfe, _pDiskEnt)						\
	{																	\
		(_pDfe)->dfe_Flags |= (_pDiskEnt)->dsk_Flags & DFE_FLAGS_CSENCODEDBITS;\
		(_pDfe)->dfe_AfpAttr = (_pDiskEnt)->dsk_AfpAttr;				\
		(_pDfe)->dfe_NtAttr = (_pDiskEnt)->dsk_NtAttr;					\
		(_pDfe)->dfe_CreateTime = (_pDiskEnt)->dsk_CreateTime;			\
		(_pDfe)->dfe_LastModTime = (_pDiskEnt)->dsk_LastModTime;		\
		(_pDfe)->dfe_BackupTime = (_pDiskEnt)->dsk_BackupTime;			\
		(_pDfe)->dfe_FinderInfo = (_pDiskEnt)->dsk_FinderInfo;			\
		if (DFE_IS_DIRECTORY((_pDfe)))									\
		{																\
			(_pDfe)->dfe_pDirEntry->de_Access = (_pDiskEnt)->dsk_Access;\
		}																\
		else															\
		{																\
			(_pDfe)->dfe_DataLen = (_pDiskEnt)->dsk_DataLen;			\
			(_pDfe)->dfe_RescLen = (_pDiskEnt)->dsk_RescLen;			\
		}																\
	}

#define	afpSaveDfeData(_pDfe, _pDiskEnt)								\
	{                                                                   \
		/* Write a signature for sanity checking */                     \
		(_pDiskEnt)->dsk_Signature = AFP_DISKENTRY_SIGNATURE;           \
                                                                        \
		(_pDiskEnt)->dsk_AfpId = (_pDfe)->dfe_AfpId;                    \
		(_pDiskEnt)->dsk_AfpAttr = (_pDfe)->dfe_AfpAttr;                \
		(_pDiskEnt)->dsk_NtAttr = (_pDfe)->dfe_NtAttr;                  \
		(_pDiskEnt)->dsk_BackupTime = (_pDfe)->dfe_BackupTime;          \
		(_pDiskEnt)->dsk_CreateTime = (_pDfe)->dfe_CreateTime;          \
		(_pDiskEnt)->dsk_LastModTime = (_pDfe)->dfe_LastModTime;		\
		(_pDiskEnt)->dsk_FinderInfo = (_pDfe)->dfe_FinderInfo;          \
                                                                        \
		/* Encode the number of characters (not bytes) in the name */   \
		(_pDiskEnt)->dsk_Flags =										\
				((_pDfe)->dfe_Flags & DFE_FLAGS_DFBITS)	|				\
				((_pDfe)->dfe_UnicodeName.Length/sizeof(WCHAR));		\
                                                                        \
		/* Copy the name over */                                        \
		RtlCopyMemory(&(_pDiskEnt)->dsk_Name[0],           				\
					  (_pDfe)->dfe_UnicodeName.Buffer,                  \
					  (_pDfe)->dfe_UnicodeName.Length);                 \
	}

// File DFEs are aged after MAX_BLOCK_AGE*FILE_BLOCK_AGE_TIME seconds (currently 2 mins)
// File DFEs are aged after MAX_BLOCK_AGE*DIR_BLOCK_AGE_TIME seconds  (currently 6 mins)
#define	MAX_BLOCK_AGE			6
#define	FILE_BLOCK_AGE_TIME		600			// # of seconds
#define	DIR_BLOCK_AGE_TIME		3600		// # of seconds
#define	BLOCK_ALLOC_SIZE		PAGE_SIZE
#define	MAX_BLOCK_TYPE			4			// For TINY, SMALL, MEDIUM & LARGE

#define VALID_DFB(pDfeBlock)	((pDfeBlock) != NULL)

typedef	struct _DfeBlock
{
	struct _DfeBlock *	dfb_Next;			// Link to next
	struct _DfeBlock **	dfb_Prev;			// Link to previous
	USHORT				dfb_NumFree;		// # of free DFEs in this block
	BYTE				dfb_Age;			// Age of the Block if all are free
	BOOLEAN				dfb_fDir;			// TRUE if it is a Dir DFB - else a file DFB
	PDFENTRY			dfb_FreeHead;		// Head of the list of free DFEs
} DFEBLOCK, *PDFEBLOCK, **PPDFEBLOCK;

LOCAL PDFENTRY FASTCALL
afpAllocDfe(
	IN	LONG						Index,
	IN	BOOLEAN						fDir
);

LOCAL VOID FASTCALL			
afpFreeDfe(					
	IN	PDFENTRY					pDfEntry
);

LOCAL AFPSTATUS FASTCALL
afpDfeBlockAge(				
	IN	PPDFEBLOCK					pBlockHead
);

#if DBG						

VOID FASTCALL
afpDisplayDfe(
	IN	PDFENTRY					pDfEntry
);

NTSTATUS FASTCALL
afpDumpDfeTree(				
	IN	PVOID						Context
);

#endif

IDDBGLOBAL	PDFEBLOCK	afpDirDfeBlockHead[MAX_BLOCK_TYPE] EQU  { NULL, NULL, NULL };
IDDBGLOBAL	PDFEBLOCK	afpFileDfeBlockHead[MAX_BLOCK_TYPE] EQU { NULL, NULL, NULL };

IDDBGLOBAL	BYTE		afpDfeUnicodeBufSize[MAX_BLOCK_TYPE] EQU	\
	{																\
		DFE_SIZE_TINY_U, DFE_SIZE_SMALL_U,							\
		DFE_SIZE_MEDIUM_U, DFE_SIZE_LARGE_U							\
	};

IDDBGLOBAL	BYTE		afpDfeFileBlockSize[MAX_BLOCK_TYPE] EQU		\
	{																\
		sizeof(DFENTRY) + DFE_SIZE_TINY_U,							\
		sizeof(DFENTRY) + DFE_SIZE_SMALL_U,							\
		sizeof(DFENTRY) + DFE_SIZE_MEDIUM_U,						\
		sizeof(DFENTRY) + DFE_SIZE_LARGE_U							\
	};

IDDBGLOBAL	BYTE		afpDfeDirBlockSize[MAX_BLOCK_TYPE] EQU		\
	{																\
		sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_TINY_U,		\
		sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_SMALL_U,		\
		sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_MEDIUM_U,		\
		sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_LARGE_U		\
	};

IDDBGLOBAL	BYTE		afpDfeNumFileBlocks[MAX_BLOCK_TYPE] EQU		\
	{																\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + DFE_SIZE_TINY_U),					\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + DFE_SIZE_SMALL_U),					\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + DFE_SIZE_MEDIUM_U),					\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + DFE_SIZE_LARGE_U)					\
	};

IDDBGLOBAL	BYTE		afpDfeNumDirBlocks[MAX_BLOCK_TYPE] EQU		\
	{																\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_TINY_U),	\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_SMALL_U),\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_MEDIUM_U),\
		(BLOCK_ALLOC_SIZE - sizeof(DFEBLOCK))/						\
			(sizeof(DFENTRY) + sizeof(DIRENTRY) + DFE_SIZE_LARGE_U)	\
	};

IDDBGLOBAL	SWMR	afpDfeBlockLock EQU { 0 };

#if DBG

IDDBGLOBAL	LONG		afpDfeAllocCount	EQU 0;
IDDBGLOBAL	LONG		afpDfbAllocCount	EQU 0;
IDDBGLOBAL	BOOLEAN		afpDumpDfeTreeFlag	EQU 0;
IDDBGLOBAL	PDFENTRY	afpDfeStack[4096]	EQU { 0 };

#endif

#undef	EQU
#ifdef	_GLOBALS_
#define	EQU				=
#else
#define	EQU				; / ## /
#endif

#endif // IDINDEX_LOCALS

#endif // _IDINDEX_


