/*

Copyright (c) 1992  Microsoft Corporation

Module Name:

	pathmap.c

Abstract:

	This module contains the routines that manipulate AFP paths.

Author:

	Sue Adams	(microsoft!suea)


Revision History:
	04 Jun 1992			Initial Version
	05 Oct 1993 JameelH	Performance Changes. Merge cached afpinfo into the
						idindex structure. Make both the ANSI and the UNICODE
						names part of idindex. Added EnumCache for improving
						enumerate perf.

Notes:	Tab stop: 4
--*/

#define	_PATHMAP_LOCALS
#define	FILENUM	FILE_PATHMAP

#include <afp.h>
#include <fdparm.h>
#include <pathmap.h>
#include <afpinfo.h>
#include <client.h>

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpMapAfpPath)
#pragma alloc_text( PAGE, AfpMapAfpPathForLookup)
#pragma alloc_text( PAGE, AfpMapAfpIdForLookup)
#pragma alloc_text( PAGE, afpGetMappedForLookupFDInfo)
#pragma alloc_text( PAGE, afpMapAfpPathToMappedPath)
#pragma alloc_text( PAGE, AfpHostPathFromDFEntry)
#pragma alloc_text( PAGE, AfpCheckParentPermissions)
#pragma alloc_text( PAGE, afpOpenUserHandle)
#endif


/***	AfpMapAfpPath
 *
 *	If mapping is for lookup operation, a FILESYSHANDLE open in the user's
 *	context is returned,  The caller MUST close this handle when done with it.
 *
 *	If pFDParm is non-null, it will be filled in as appropriate according to Bitmap.
 *
 *	If mapping is for create operation, the volume root-relative host pathname
 *	(in unicode) of the item we are about to create is returned. For lookup
 *	operation the paths refer to the item being pathmapped.  This routine
 *	always returns the paths in the PME.  It is the caller's responsibility
 *	to free the Full HostPath Buffer, if it is not supplied already.
 *
 *	The caller MUST have the IdDb locked for Exclusive access.
 *
 *	LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive)
 *
 */
AFPSTATUS
AfpMapAfpPath(
	IN		PCONNDESC		pConnDesc,
	IN		DWORD			DirId,
	IN		PANSI_STRING	pPath,
	IN		BYTE			PathType,			// short names or long names
	IN		PATHMAP_TYPE	MapReason,	 		// for lookup or hard/soft create?
	IN		DWORD			DFFlag,				// map to file? dir? or either?
	IN		DWORD			Bitmap,				// what fields of FDParm to fill in
	OUT		PPATHMAPENTITY	pPME,
	OUT		PFILEDIRPARM	pFDParm OPTIONAL	// for lookups only
)
{
	PVOLDESC		pVolDesc;
	MAPPEDPATH		mappedpath;
	AFPSTATUS		Status;
#ifdef	PROFILING
	TIME			TimeS, TimeE, TimeD;
#endif

	PAGED_CODE( );

	ASSERT((pConnDesc != NULL));

#ifdef	PROFILING
	INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
	AfpGetPerfCounter(&TimeS);
#endif

	pVolDesc = pConnDesc->cds_pVolDesc;
	ASSERT(IS_VOLUME_NTFS(pVolDesc));
	ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock));

	// initialize some fields in the PME
	AfpSetEmptyUnicodeString(&pPME->pme_ParentPath, 0, NULL);

	do
	{
		Status = afpMapAfpPathToMappedPath(pVolDesc,
										   DirId,
										   pPath,
										   PathType,
										   MapReason,
										   DFFlag,
										   True,
										   &mappedpath);
		if ((Status != AFP_ERR_NONE) &&
			!((MapReason == HardCreate) &&
			  (Status == AFP_ERR_OBJECT_EXISTS) &&
			  (DFFlag == DFE_FILE)))
		{
			break;
		}

		ASSERT(pPME != NULL);

		// Get the volume relative path to the parent directory for
		// creates, or to the item for lookups
		if ((Status = AfpHostPathFromDFEntry(mappedpath.mp_pdfe,
											 // since CopyFile and Move have to lookup
											 // the destination parent dir paths, we
											 // need to allocate extra room for them in
											 // the path to append the filename
											 (MapReason == Lookup) ?
												(AFP_LONGNAME_LEN + 1) * sizeof(WCHAR):
												mappedpath.mp_Tail.Length + sizeof(WCHAR),
											 &pPME->pme_FullPath)) != AFP_ERR_NONE)
			break;

		// if Pathmap is for hard (files only) or soft create (file or dir)
		if (MapReason != Lookup)
		{
			ASSERT(pFDParm == NULL);

			// fill in the dfe of parent dir in which create will take place
			pPME->pme_pDfeParent = mappedpath.mp_pdfe;

			// fill in path to parent
			pPME->pme_ParentPath = pPME->pme_FullPath;

			// Add a path separator if we are not at the root
			if (pPME->pme_FullPath.Length > 0)
			{
				pPME->pme_FullPath.Buffer[pPME->pme_FullPath.Length / sizeof(WCHAR)] = L'\\';
				pPME->pme_FullPath.Length += sizeof(WCHAR);
			}

			pPME->pme_UTail.Length = pPME->pme_UTail.MaximumLength = mappedpath.mp_Tail.Length;
			pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
											  pPME->pme_FullPath.Length);

			Status = RtlAppendUnicodeStringToString(&pPME->pme_FullPath,
													&mappedpath.mp_Tail);
			ASSERT(NT_SUCCESS(Status));
		}
		else // lookup operation
		{
			pPME->pme_pDfEntry = mappedpath.mp_pdfe;
			pPME->pme_UTail.Length = mappedpath.mp_pdfe->dfe_UnicodeName.Length;
			pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
											  pPME->pme_FullPath.Length -
											  pPME->pme_UTail.Length);

			pPME->pme_ParentPath.Length =
			pPME->pme_ParentPath.MaximumLength = pPME->pme_FullPath.Length - pPME->pme_UTail.Length;

			if (pPME->pme_FullPath.Length > pPME->pme_UTail.Length)
			{
				// subtract the path separator if not in root dir
				pPME->pme_ParentPath.Length -= sizeof(WCHAR);
				ASSERT(pPME->pme_ParentPath.Length >= 0);
			}
			pPME->pme_ParentPath.Buffer = pPME->pme_FullPath.Buffer;
			pPME->pme_UTail.MaximumLength = pPME->pme_FullPath.MaximumLength - pPME->pme_ParentPath.Length;

			Status = afpGetMappedForLookupFDInfo(pConnDesc,
												 mappedpath.mp_pdfe,
												 Bitmap,
												 pPME,
												 pFDParm);
			// if this fails do not free path buffer and set it back to
			// null.  We don't know that the path buffer isn't on
			// the callers stack. Caller should always clean it up himself.
		}
	} while (False);

#ifdef	PROFILING
	AfpGetPerfCounter(&TimeE);		
	TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
	INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
								 TimeD,
								 &AfpStatisticsLock);
#endif
	return Status;
}

/***	AfpMapAfpPathForLookup
 *
 *	Maps an AFP dirid/pathname pair to an open handle (in the user's context)
 *	to the DATA stream of the file/dir.
 *	The DirID database is locked for read for the duration of this
 *	routine, unless afpMapAfpPathToMappedPath returns
 *  AFP_ERR_WRITE_LOCK_REQUIRED in which case the DirID database will be locked
 *  for write.  This will only happen the first time a mac tries to access
 *  a directory who's files have not yet been cached in.
 *
 *	LOCKS: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
 */
AFPSTATUS
AfpMapAfpPathForLookup(
	IN		PCONNDESC		pConnDesc,
	IN		DWORD			DirId,
	IN		PANSI_STRING	pPath,
	IN		BYTE			PathType,	  // short names or long names
	IN		DWORD			DFFlag,
	IN		DWORD			Bitmap,
	OUT		PPATHMAPENTITY	pPME	OPTIONAL,
	OUT		PFILEDIRPARM	pFDParm OPTIONAL
)
{
	MAPPEDPATH	mappedpath;
	PVOLDESC	pVolDesc;
	PSWMR		pIdDbLock;
	AFPSTATUS	Status;
	BOOLEAN		swmrLockedExclusive = False;
	PATHMAP_TYPE mapReason = Lookup;
#ifdef	PROFILING
	TIME		TimeS, TimeE, TimeD;
#endif

	PAGED_CODE( );

	ASSERT((pConnDesc != NULL));


#ifdef	PROFILING
	INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
	AfpGetPerfCounter(&TimeS);
#endif

#ifndef GET_CORRECT_OFFSPRING_COUNTS
	if (pConnDesc->cds_pSda->sda_AfpFunc == _AFP_ENUMERATE)
	{
		mapReason = LookupForEnumerate;
	}
#endif

	pVolDesc  = pConnDesc->cds_pVolDesc;
	pIdDbLock = &(pVolDesc->vds_IdDbAccessLock);

	AfpSwmrAcquireShared(pIdDbLock);

	do
	{
		do
		{
			Status = afpMapAfpPathToMappedPath(pVolDesc,
											  DirId,
											  pPath,
											  PathType,
											  mapReason,	// lookups only
											  DFFlag,
											  swmrLockedExclusive,
											  &mappedpath);
	
			if (Status == AFP_ERR_WRITE_LOCK_REQUIRED)
			{
				ASSERT (!swmrLockedExclusive);
				// Pathmap needed to cache in the files for the last directory
				// in the path but didn't have the write lock to the ID database
				AfpSwmrRelease(pIdDbLock);
				AfpSwmrAcquireExclusive(pIdDbLock);
				swmrLockedExclusive = True;
				continue;
			}
			break;
		} while (True);

		if (!NT_SUCCESS(Status))
		{
			DBGPRINT (DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
							("AfpMapAfpPathForLookup: afpMapAfpPathToMappedPath failed: Error = %lx\n", Status));
			break;
		}

		if (ARGUMENT_PRESENT(pPME))
		{
			pPME->pme_FullPath.Length = 0;
		}

		if (Bitmap & FD_INTERNAL_BITMAP_RETURN_PMEPATHS)
		{
			ASSERT(ARGUMENT_PRESENT(pPME));
			if ((Status = AfpHostPathFromDFEntry(mappedpath.mp_pdfe,
												 (Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC) ?
														AfpResourceStream.Length : 0,
												 &pPME->pme_FullPath)) != AFP_ERR_NONE)
				break;

			pPME->pme_UTail.Length = mappedpath.mp_pdfe->dfe_UnicodeName.Length;
			pPME->pme_UTail.Buffer = (PWCHAR)((PBYTE)pPME->pme_FullPath.Buffer +
											  pPME->pme_FullPath.Length - pPME->pme_UTail.Length);

			pPME->pme_ParentPath.Length =
			pPME->pme_ParentPath.MaximumLength = pPME->pme_FullPath.Length - pPME->pme_UTail.Length;

			if (pPME->pme_FullPath.Length > pPME->pme_UTail.Length)
			{
				// subtract the path separator if not in root dir
				pPME->pme_ParentPath.Length -= sizeof(WCHAR);
				ASSERT(pPME->pme_ParentPath.Length >= 0);
			}
			pPME->pme_ParentPath.Buffer = pPME->pme_FullPath.Buffer;
			pPME->pme_UTail.MaximumLength = pPME->pme_FullPath.MaximumLength - pPME->pme_ParentPath.Length;
		}

		Status = afpGetMappedForLookupFDInfo(pConnDesc,
											 mappedpath.mp_pdfe,
											 Bitmap,
											 pPME,
											 pFDParm);
	} while (False);

	AfpSwmrRelease(pIdDbLock);

#ifdef	PROFILING
	AfpGetPerfCounter(&TimeE);
	TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
	INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
								 TimeD,
								 &AfpStatisticsLock);
#endif
	return Status;

}

/***	AfpMapAfpIdForLookup
 *
 *	Maps an AFP id to an open FILESYSTEMHANDLE (in the user's context) to
 * 	to the DATA stream of the file/dir.
 *	The DirID database is locked for shared or exclusive access for the duration
 *	of this routine.
 *
 *	LOCKS: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
 */
AFPSTATUS
AfpMapAfpIdForLookup(
	IN		PCONNDESC		pConnDesc,
	IN		DWORD			AfpId,
	IN		DWORD			DFFlag,
	IN		DWORD			Bitmap,
	OUT		PPATHMAPENTITY	pPME	OPTIONAL,
	OUT		PFILEDIRPARM	pFDParm OPTIONAL
)
{
	PVOLDESC	pVolDesc;
	PSWMR		pIdDbLock;
	AFPSTATUS	Status;
	PDFENTRY	pDfEntry;
	BOOLEAN		CleanupLock = False;
#ifdef	PROFILING
	TIME		TimeS, TimeE, TimeD;
#endif

	PAGED_CODE( );

#ifdef	PROFILING
	INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_PathMapCount);
	AfpGetPerfCounter(&TimeS);
#endif

	ASSERT((pConnDesc != NULL));

	do
	{
		if (AfpId == 0)
		{
			Status = AFP_ERR_PARAM;
			break;
		}

		pVolDesc  = pConnDesc->cds_pVolDesc;
		pIdDbLock = &(pVolDesc->vds_IdDbAccessLock);

		AfpSwmrAcquireShared(pIdDbLock);
		CleanupLock = True;

		if ((AfpId == AFP_ID_PARENT_OF_ROOT) ||
			((pDfEntry = AfpFindDfEntryById(pVolDesc, AfpId, DFE_ANY)) == NULL))
		{
			Status = AFP_ERR_OBJECT_NOT_FOUND;
			break;
		}

		if (((DFFlag == DFE_DIR) && DFE_IS_FILE(pDfEntry)) ||
			((DFFlag == DFE_FILE) && DFE_IS_DIRECTORY(pDfEntry)))
		{
			Status = AFP_ERR_OBJECT_TYPE;
			break;
		}

		if (ARGUMENT_PRESENT(pPME))
		{
			pPME->pme_FullPath.Length = 0;
		}

		Status = afpGetMappedForLookupFDInfo(pConnDesc,
											 pDfEntry,
											 Bitmap,
											 pPME,
											 pFDParm);
	} while (False);

	if (CleanupLock)
	{
		AfpSwmrRelease(pIdDbLock);
	}

#ifdef	PROFILING
	AfpGetPerfCounter(&TimeE);
	TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
	INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_PathMapTime,
								 TimeD,
								 &AfpStatisticsLock);
#endif
	return Status;
}

/***	afpGetMappedForLookupFDInfo
 *
 *	After a pathmap for LOOKUP operation, this routine is called to
 *	return various FileDir parm information about the mapped file/dir.
 *	The following FileDir information is always returned:
 *		AFP DirId/FileId
 *		Parent DirId
 *		DFE flags (indicating item is a directory, a file, or a file with an ID)
 *		Attributes (Inhibit bits and D/R Already open bits normalized with
 *					the NTFS attributes for RO, System, Hidden, Archive)
 *		BackupTime
 *		CreateTime
 *		ModifiedTime
 *
 *	The following FileDir information is returned according to the flags set
 *	in word 0 of the Bitmap parameter (these correspond to the AFP file/dir
 *	bitmap):
 *		Longname
 *		Shortname
 *		FinderInfo
 *		ProDosInfo
 *		Directory Access Rights (as stored in AFP_AfpInfo stream)
 *		Directory OwnerId/GroupId
 *		Directory Offspring count (file count and dir count are separate)
 *
 *	The open access is stored in word 1 of the Bitmap parameter.
 *	This is used by AfpOpenUserHandle (for NTFS volumes) or AfpIoOpen (for
 *	CDFS volumes) when opening the data stream of the file/dir (under
 *	impersonation for NTFS) who's handle will be returned within the
 *	pPME parameter if supplied.
 *
 *	LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared)
 *
 */
LOCAL
AFPSTATUS
afpGetMappedForLookupFDInfo(
	IN	PCONNDESC			pConnDesc,
	IN	PDFENTRY			pDfEntry,
	IN	DWORD				Bitmap,
	OUT	PPATHMAPENTITY		pPME	OPTIONAL,	// Supply for NTFS only if need a
												// handle in user's context, usually
												// for security checking purposes
	OUT	PFILEDIRPARM		pFDParm	OPTIONAL	// Supply if want returned FDInfo
)
{
	BOOLEAN			fNtfsVol;
	AFPSTATUS		Status = STATUS_SUCCESS;
	DWORD			OpenAccess = FILEIO_ACCESS_NONE;
	FILESYSHANDLE	fsh;
	PFILESYSHANDLE	pHandle = NULL;

	PAGED_CODE( );

	fNtfsVol = IS_VOLUME_NTFS(pConnDesc->cds_pVolDesc);
	if (ARGUMENT_PRESENT(pPME))
	{
		pHandle = &pPME->pme_Handle;
	}
	else if ((fNtfsVol &&
			(Bitmap & (FD_BITMAP_SHORTNAME | FD_BITMAP_PRODOSINFO))))
	{
		pHandle = &fsh;
	}

	if (pHandle != NULL)
	{
		if (!NT_SUCCESS(Status = afpOpenUserHandle(pConnDesc,
												   pDfEntry,
												   (ARGUMENT_PRESENT(pPME) &&
													(pPME->pme_FullPath.Buffer != NULL)) ?
														&pPME->pme_FullPath : NULL,
												   Bitmap,		// encode open/deny modes
												   pHandle)))
		{
			if ((Status == AFP_ERR_DENY_CONFLICT) &&
				ARGUMENT_PRESENT(pFDParm))
			{
				// For CreateId/ResolveId/DeleteId
				pFDParm->_fdp_AfpId = pDfEntry->dfe_AfpId;
				pFDParm->_fdp_Flags = (pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS);
			}
			return Status;
		}
	}

	do
	{
		if (ARGUMENT_PRESENT(pFDParm))
		{
			pFDParm->_fdp_AfpId = pDfEntry->dfe_AfpId;
			pFDParm->_fdp_ParentId = pDfEntry->dfe_Parent->dfe_AfpId;

			ASSERT(!((pDfEntry->dfe_Flags & DFE_FLAGS_DIR) &&
					 (pDfEntry->dfe_Flags & (DFE_FLAGS_FILE_WITH_ID | DFE_FLAGS_FILE_NO_ID))));

			pFDParm->_fdp_Flags = (pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS);

			if (Bitmap & FD_BITMAP_FINDERINFO)
			{
				pFDParm->_fdp_FinderInfo = pDfEntry->dfe_FinderInfo;
			}

			pFDParm->_fdp_Attr = pDfEntry->dfe_AfpAttr;
			AfpNormalizeAfpAttr(pFDParm, pDfEntry->dfe_NtAttr);

			// The Finder uses the Finder isInvisible flag over
			// the file system Invisible attribute to tell if the thing is
			// displayed or not.  If the PC turns off the hidden attribute
			// we should clear the Finder isInvisible flag
			if ((Bitmap & FD_BITMAP_FINDERINFO) &&
				!(pFDParm->_fdp_Attr & FD_BITMAP_ATTR_INVISIBLE))
			{
				pFDParm->_fdp_FinderInfo.fd_Attr1 &= ~FINDER_FLAG_INVISIBLE;
			}

			pFDParm->_fdp_BackupTime = pDfEntry->dfe_BackupTime;
			pFDParm->_fdp_CreateTime = pDfEntry->dfe_CreateTime;
			pFDParm->_fdp_ModifiedTime = AfpConvertTimeToMacFormat(&pDfEntry->dfe_LastModTime);

			if (Bitmap & FD_BITMAP_LONGNAME)
			{
				ASSERT((pFDParm->_fdp_LongName.Buffer != NULL) &&
					   (pFDParm->_fdp_LongName.MaximumLength >=
						pDfEntry->dfe_UnicodeName.Length/(USHORT)sizeof(WCHAR)));
				AfpConvertMungedUnicodeToAnsi(&pDfEntry->dfe_UnicodeName,
											  &pFDParm->_fdp_LongName);
			}

			if (Bitmap & FD_BITMAP_SHORTNAME)
			{
				ASSERT(pFDParm->_fdp_ShortName.Buffer != NULL);

				if (!fNtfsVol)
				{
					ASSERT(pFDParm->_fdp_ShortName.MaximumLength >=
										(pDfEntry->dfe_UnicodeName.Length/sizeof(WCHAR)));
					AfpConvertMungedUnicodeToAnsi(&pDfEntry->dfe_UnicodeName,
												  &pFDParm->_fdp_ShortName);

					// if asking for shortname on CDFS, we will fill in the pFDParm
					// shortname with the pDfEntry longname, ONLY if it is an 8.3 name
					if (!AfpIsLegalShortname(&pFDParm->_fdp_ShortName))
					{
						pFDParm->_fdp_ShortName.Length = 0;
					}
				}
				else
				{
					// get NTFS shortname
					ASSERT(pFDParm->_fdp_ShortName.MaximumLength >= AFP_SHORTNAME_LEN);
					ASSERT(pHandle != NULL);

					Status = AfpIoQueryShortName(pHandle,
												 &pFDParm->_fdp_ShortName);
					if (!NT_SUCCESS(Status))
					{
						pFDParm->_fdp_ShortName.Length = 0;
						break;
					}
				}
			}

			if (DFE_IS_FILE(pDfEntry))
			{
				if (pDfEntry->dfe_Flags & DFE_FLAGS_D_ALREADYOPEN)
					pFDParm->_fdp_Attr |= FILE_BITMAP_ATTR_DATAOPEN;
				if (pDfEntry->dfe_Flags & DFE_FLAGS_R_ALREADYOPEN)
					pFDParm->_fdp_Attr |= FILE_BITMAP_ATTR_RESCOPEN;
				if (Bitmap & FILE_BITMAP_RESCLEN)
				{
					pFDParm->_fdp_RescForkLen = pDfEntry->dfe_RescLen;
				}
				if (Bitmap & FILE_BITMAP_DATALEN)
				{
					pFDParm->_fdp_DataForkLen = pDfEntry->dfe_DataLen;
				}
			}

			if (Bitmap & FD_BITMAP_PRODOSINFO)
			{
				if (fNtfsVol)
				{
					ASSERT(pHandle != NULL);
					Status = AfpQueryProDos(pHandle,
											&pFDParm->_fdp_ProDosInfo);
					if (!NT_SUCCESS(Status))
					{
						break;
					}
				}
				else	// CDFS File or Directory
				{
					RtlZeroMemory(&pFDParm->_fdp_ProDosInfo, sizeof(PRODOSINFO));
					if (DFE_IS_FILE(pDfEntry))	// CDFS file
					{
						AfpProDosInfoFromFinderInfo(&pDfEntry->dfe_FinderInfo,
													&pFDParm->_fdp_ProDosInfo);
					}
					else	// CDFS Directory
					{
						pFDParm->_fdp_ProDosInfo.pd_FileType[0] = PRODOS_TYPE_DIR;
						pFDParm->_fdp_ProDosInfo.pd_AuxType[1] = PRODOS_AUX_DIR;
					}
				}
			}

			// check for dir here since enumerate ANDs the file and dir bitmaps
			if (DFE_IS_DIRECTORY(pDfEntry) &&
				(Bitmap & (DIR_BITMAP_ACCESSRIGHTS |
						   DIR_BITMAP_OWNERID |
						   DIR_BITMAP_GROUPID)))
			{
				if (fNtfsVol)
				{
					// Because the file and dir bitmaps are OR'd together,
					// and the OwnerId bit is overloaded with the RescLen bit,
					// we don't know if this bit was actually included in the
					// file bitmap or the dir bitmap.  The api would have
					// determined whether or not it needed a handle based on
					// these bitmaps, so based on the pPME we can tell if we
					// actually need to query for security or not.
					if (ARGUMENT_PRESENT(pPME))
					{
						pFDParm->_fdp_OwnerRights = DFE_OWNER_ACCESS(pDfEntry);
						pFDParm->_fdp_GroupRights = DFE_GROUP_ACCESS(pDfEntry);
						pFDParm->_fdp_WorldRights = DFE_WORLD_ACCESS(pDfEntry);

						// Query this user's rights
						Status = AfpQuerySecurityIdsAndRights(pConnDesc->cds_pSda,
															  pHandle,
															  Bitmap,
															  pFDParm);
						if (!NT_SUCCESS(Status))
						{
							break;
						}
					}
				}
				else
				{
					pFDParm->_fdp_OwnerRights =
					pFDParm->_fdp_GroupRights =
					pFDParm->_fdp_WorldRights =
					pFDParm->_fdp_UserRights  = (DIR_ACCESS_READ | DIR_ACCESS_SEARCH);
					pFDParm->_fdp_OwnerId = pFDParm->_fdp_GroupId = 0;
				}
			}

			// Must check for type directory since this Bitmap bit is overloaded
			if (DFE_IS_DIRECTORY(pDfEntry) && (Bitmap & DIR_BITMAP_OFFSPRINGS))
			{
#ifndef GET_CORRECT_OFFSPRING_COUNTS
				if (!DFE_CHILDREN_ARE_PRESENT(pDfEntry) &&
					(pDfEntry->dfe_DirOffspring == 0))
				{
					// If the files have not yet been cached in for this dir,
					// return non-zero filecount so that system 7.x view by
					// name will enumerate the directory if user clicks the
					// triangle for this dir.  If you return zero offspring
					// What might break from lying like this?
					pFDParm->_fdp_FileCount = 1;
                }
				else
#endif
					pFDParm->_fdp_FileCount = pDfEntry->dfe_FileOffspring;

				pFDParm->_fdp_DirCount  = pDfEntry->dfe_DirOffspring;
			}
		}
	} while (False);

	if (pHandle == &fsh)
	{
		// if we had to open a handle just to query shortname or ProDOS
		// close it
		AfpIoClose(&fsh);
	}

	return Status;
}


/***	afpMapAfpPathToMappedPath
 *
 *	Maps an AFP DirId/pathname pair to a MAPPEDPATH structure.
 *	The CALLER must have the DirId/FileId database locked for shared
 *	access (or Exclusive access if they need that level of lock for other
 *	operations on the IDDB, to map a path only requires shared lock)
 *
 *	LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared OR Exclusive)
 */
LOCAL
AFPSTATUS
afpMapAfpPathToMappedPath(
	IN		PVOLDESC		pVolDesc,
	IN		DWORD			DirId,
	IN		PANSI_STRING	Path,		// relative to DirId
	IN		BYTE			PathType,	// short names or long names
	IN		PATHMAP_TYPE	MapReason,  // for lookup or hard/soft create?
	IN		DWORD			DFflag,		// file, dir or don't know which
	IN		BOOLEAN			LockedExclusive,
	OUT		PMAPPEDPATH		pMappedPath

)
{
	PDFENTRY		pDFEntry, ptempDFEntry;
	CHAR			*position, *tempposition;
	int				length, templength;
	ANSI_STRING		acomponent;
	CHAR			component[AFP_FILENAME_LEN+1];
	BOOLEAN			checkEnumForParent = False, checkEnumForDir = False;

	PAGED_CODE( );

	ASSERT(pVolDesc != NULL);

#ifndef GET_CORRECT_OFFSPRING_COUNTS
	if (MapReason == LookupForEnumerate)
	{
		checkEnumForDir = True;
		MapReason = Lookup;
	}
#endif

	// Initialize the returned MappedPath structure
	pMappedPath->mp_pdfe = NULL;
	AfpSetEmptyUnicodeString(&pMappedPath->mp_Tail,
							 sizeof(pMappedPath->mp_Tailbuf),
							 pMappedPath->mp_Tailbuf);

	// Lookup the initial DirId in the index database, it better be valid
	if ((pDFEntry = AfpFindDfEntryById(pVolDesc,
									   DirId,
									   DFE_DIR)) == NULL)
	{
		return AFP_ERR_OBJECT_NOT_FOUND;
	}

	ASSERT(Path != NULL);
	tempposition = position = Path->Buffer;
	templength = length = Path->Length;

	do
	{
		// Lookup by DirId only?
		if (length == 0)				// no path was given
		{
			if (MapReason != Lookup)	// mapping is for create
			{
				return AFP_ERR_PARAM;	// missing the file or dirname
			}
			else if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
			{
				return AFP_ERR_OBJECT_NOT_FOUND;
			}
			else
			{
				pMappedPath->mp_pdfe = pDFEntry;
#ifdef GET_CORRECT_OFFSPRING_COUNTS
				checkEnumForParent = checkEnumForDir = True;
#endif
				break;
			}
		}

		//
		// Pre-scan path to munge for easier component breakdown
		//

		// Get rid of a leading null to make scanning easier
		if (*position == AFP_PATHSEP)
		{
			length--;
			position++;
			if (length == 0)	// The path consisted of just one null byte
			{
				if (MapReason != Lookup)
				{
					return AFP_ERR_PARAM;
				}
				else if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
				{
					return AFP_ERR_OBJECT_NOT_FOUND;
				}
				else if (((DFflag == DFE_DIR) && DFE_IS_FILE(pDFEntry)) ||
						 ((DFflag == DFE_FILE) && DFE_IS_DIRECTORY(pDFEntry)))
				{
					return AFP_ERR_OBJECT_TYPE;
				}
				else
				{
					pMappedPath->mp_pdfe = pDFEntry;
#ifdef GET_CORRECT_OFFSPRING_COUNTS
					checkEnumForParent = checkEnumForDir = True;
#endif
					break;
				}
			}
		}

		//
		// Get rid of a trailing null if it is not an "up" token --
		// i.e. preceded by another null.
		// The 2nd array access is ok because we know we have at
		// least 2 chars at that point
		//
		if ((position[length-1] == AFP_PATHSEP) &&
			(position[length-2] != AFP_PATHSEP))
		{
				length--;
		}


		// begin parsing out path components, stop when you find the last component
		while (1)
		{
			afpGetNextComponent(position,
								length,
								PathType,
								component,
								&templength);
			if (templength < 0)
			{
				// component was too long or an invalid AFP character was found
				return AFP_ERR_PARAM;
			}

			length -= templength;
			if (length == 0)
			{
				// we found the last component
				break;
			}

			position += templength;

			if (component[0] == AFP_PATHSEP)	// moving up?
			{	// make sure you don't go above parent of root!
				if (DFE_IS_PARENT_OF_ROOT(pDFEntry))
				{
					return AFP_ERR_OBJECT_NOT_FOUND;
				}
				else pDFEntry = pDFEntry->dfe_Parent;	//backup one level
			}
			else // Must be a directory component moving DOWN in tree
			{
				RtlInitString(&acomponent, component);
				AfpConvertStringToMungedUnicode(&acomponent, &pMappedPath->mp_Tail);
				if ((ptempDFEntry = AfpFindEntryByUnicodeName(pVolDesc,
															  &pMappedPath->mp_Tail,
															  PathType,
															  pDFEntry,
															  DFE_DIR)) == NULL)
				{
					return AFP_ERR_OBJECT_NOT_FOUND;
				}
				else
				{
					pDFEntry = ptempDFEntry;
				}
			}
		} // end while

		//
		// we have found the last component
		// is the last component an 'up' token?
		//
		if (component[0] == AFP_PATHSEP)
		{
			// don't bother walking up beyond the root
			switch (pDFEntry->dfe_AfpId)
			{
				case AFP_ID_PARENT_OF_ROOT:
					return AFP_ERR_OBJECT_NOT_FOUND;
				case AFP_ID_ROOT:
					return ((MapReason == Lookup) ? AFP_ERR_OBJECT_NOT_FOUND :
													AFP_ERR_PARAM);
				default: // backup one level
					pMappedPath->mp_pdfe = pDFEntry->dfe_Parent;
			}

			// this better be a lookup request
			if (MapReason != Lookup)
			{
				if (DFflag == DFE_DIR)
				{
					return AFP_ERR_OBJECT_EXISTS;
				}
				else
				{
					return AFP_ERR_OBJECT_TYPE;
				}
			}

			// had to have been a lookup operation
			if (DFflag == DFE_FILE)
			{
				return AFP_ERR_OBJECT_TYPE;
			}
			else
			{
#ifdef GET_CORRECT_OFFSPRING_COUNTS
				checkEnumForParent = checkEnumForDir = True;
#endif
				break;
			}
		} // endif last component was an 'up' token

		// the last component is a file or directory name
		RtlInitString(&acomponent, component);
		AfpConvertStringToMungedUnicode(&acomponent,
										&pMappedPath->mp_Tail);

		//
		// Before we search our database for the last component of the
		// path, make sure all the files have been cached in for this
		// directory
		//
		if (!DFE_CHILDREN_ARE_PRESENT(pDFEntry))
		{
			if (!LockedExclusive &&
				!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
			{
				return AFP_ERR_WRITE_LOCK_REQUIRED;
			}
			else
			{
				NTSTATUS status;
				LockedExclusive = True;
				status = AfpCacheDirectoryTree(pVolDesc,
											   pDFEntry,
											   GETFILES,
											   NULL,
											   NULL);
				if (!NT_SUCCESS(status))
				{
					DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
							("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
							 &(pDFEntry->dfe_UnicodeName), status) );
					return AFP_ERR_MISC;
				}
			}
		}

		ptempDFEntry = AfpFindEntryByUnicodeName(pVolDesc,
												 &pMappedPath->mp_Tail,
												 PathType,
												 pDFEntry,
												 DFE_ANY);

		if (MapReason == Lookup)	// its a lookup request
		{
			if (ptempDFEntry == NULL)
			{
				return AFP_ERR_OBJECT_NOT_FOUND;
			}
			else if (((DFflag == DFE_DIR) && DFE_IS_FILE(ptempDFEntry)) ||
					 ((DFflag == DFE_FILE) && DFE_IS_DIRECTORY(ptempDFEntry)))
			{
				return AFP_ERR_OBJECT_TYPE;
			}
			else
			{
				pMappedPath->mp_pdfe = ptempDFEntry;
#ifdef GET_CORRECT_OFFSPRING_COUNTS
				if (DFE_IS_DIRECTORY(ptempDFEntry))
					// we've already made sure this thing's parent was
					// enumerated already above.
					checkEnumForDir = True;
#endif
				break;
			}
		}
		else	// path mapping is for a create
		{
			ASSERT(DFflag != DFE_ANY); // Create must specify the exact type

			// Save the parent DFEntry
			pMappedPath->mp_pdfe = pDFEntry;

			if (ptempDFEntry != NULL)
			{
				// A file or dir by that name exists in the database
				// (and we will assume it exists on disk)
				if (MapReason == SoftCreate)
				{
					// Attempting create of a directory, or soft create of a file,
					// and dir OR file by that name exists,
					if ((DFflag == DFE_DIR) || DFE_IS_FILE(ptempDFEntry))
					{
						return AFP_ERR_OBJECT_EXISTS;
					}
					else
					{
						return AFP_ERR_OBJECT_TYPE;
					}
				}
				else if (DFE_IS_FILE(ptempDFEntry))
				{
					// Must be hard create and file by that name exists
					if (ptempDFEntry->dfe_Flags & DFE_FLAGS_OPEN_BITS)
					{
						return AFP_ERR_FILE_BUSY;
					}
					else
					{
						// note we return object_exists instead of no_err
						return AFP_ERR_OBJECT_EXISTS;
					}
				}
				else
				{
					// Attempting hard create of file, but found a directory
					return AFP_ERR_OBJECT_TYPE;
				}
			}
			else
			{
				return AFP_ERR_NONE;
			}
		}

	} while (False);

	// The only way we should have gotten here is if we successfully mapped
	// the path to a DFENTRY for lookup and would return AFP_ERR_NONE
	ASSERT((pMappedPath->mp_pdfe != NULL) && (MapReason == Lookup));

#ifdef GET_CORRECT_OFFSPRING_COUNTS
	if (checkEnumForParent)
	{
		if (!DFE_CHILDREN_ARE_PRESENT(pMappedPath->mp_pdfe->dfe_Parent))
		{
			if (!LockedExclusive &&
				!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
			{
				return AFP_ERR_WRITE_LOCK_REQUIRED;
			}
			else
			{
				NTSTATUS status;
				LockedExclusive = True;
				status = AfpCacheDirectoryTree(pVolDesc,
											   pMappedPath->mp_pdfe->dfe_Parent,
											   GETFILES,
											   NULL,
											   NULL);
				if (!NT_SUCCESS(status))
				{
					DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
							("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
							 &(pMappedPath->mp_pdfe->dfe_Parent->dfe_UnicodeName), status) );
					return AFP_ERR_MISC;
				}
			}
		}

	}
#endif

	if (checkEnumForDir)
	{
		if (!DFE_CHILDREN_ARE_PRESENT(pMappedPath->mp_pdfe))
		{
			if (!LockedExclusive &&
				!AfpSwmrUpgradeToExclusive(&pVolDesc->vds_IdDbAccessLock))
			{
				return AFP_ERR_WRITE_LOCK_REQUIRED;
			}
			else
			{
				NTSTATUS status;
				LockedExclusive = True;
				status = AfpCacheDirectoryTree(pVolDesc,
											   pMappedPath->mp_pdfe,
											   GETFILES,
											   NULL,
											   NULL);
				if (!NT_SUCCESS(status))
				{
					DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_ERR,
							("afpMapAfpPathToMappedPath: could not cache dir tree for %Z (0x%lx)\n",
							 &(pMappedPath->mp_pdfe->dfe_UnicodeName), status) );
					return AFP_ERR_MISC;
				}
			}
		}

	}


	return AFP_ERR_NONE;
}


/***	AfpHostPathFromDFEntry
 *
 *	This routine takes a pointer to a DFEntry and builds the full
 *	host path (in unicode) to that entity by ascending the ID database
 *	tree.
 *
 *	IN	pDFE	--	pointer to DFEntry of which host path is desired
 *	IN	taillen --	number of extra *bytes*, if any, the caller
 *					desires to have allocated for the host path,
 *					including room for any path separators
 *	OUT	ppPath	--	pointer to UNICODE string
 *
 *	The caller must have the DirID/FileID database locked for read
 *	before calling this routine. The caller can supply a buffer which will
 *	be used if sufficient. Caller must free the allocated (if any)
 * 	unicode string buffer.
 *
 *	LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Shared)
 */
AFPSTATUS
AfpHostPathFromDFEntry(
	IN		PDFENTRY		pDFE,
	IN		DWORD			taillen,
	OUT		PUNICODE_STRING	pPath
)
{
	AFPSTATUS		Status = AFP_ERR_NONE;
	DWORD			pathlen = taillen;
	PDFENTRY		*pdfelist = NULL, curpdfe = NULL;
	PDFENTRY		apdfelist[AVERAGE_NODE_DEPTH];
	int				counter;

	PAGED_CODE( );

	pPath->Length = 0;

	do
	{
		if (DFE_IS_FILE(pDFE))
		{
			counter = pDFE->dfe_Parent->dfe_DirDepth;
		}
		else // its a DIRECTORY entry
		{
			ASSERT(DFE_IS_DIRECTORY(pDFE));
			if (DFE_IS_ROOT(pDFE))
			{
				if ((pathlen > 0) && (pPath->MaximumLength < pathlen))
				{
					if ((pPath->Buffer = (PWCHAR)AfpAllocNonPagedMemory(pathlen)) == NULL)
					{
						Status = AFP_ERR_MISC;
						break;
					}
					pPath->MaximumLength = (USHORT)pathlen;
				}
				break;				// We are done
			}

			if (DFE_IS_PARENT_OF_ROOT(pDFE))
			{
				Status = AFP_ERR_OBJECT_NOT_FOUND;
				break;
			}

			ASSERT(pDFE->dfe_DirDepth >= 1);
			counter = pDFE->dfe_DirDepth - 1;
		}

		if (counter)
		{
			// if node is within average depth, use the array on the stack,
			// otherwise, allocate an array
			if (counter <= AVERAGE_NODE_DEPTH)
			{
				pdfelist = apdfelist;
			}
			else
			{
				pdfelist = (PDFENTRY *)AfpAllocNonPagedMemory(counter*sizeof(PDFENTRY));
				if (pdfelist == NULL)
				{
					Status = AFP_ERR_MISC;
					break;
				}
			}
			pathlen += counter * sizeof(WCHAR); // room for path separators
		}

		curpdfe = pDFE;
		pathlen += curpdfe->dfe_UnicodeName.Length;

		// walk up the tree till you find the root, collecting string lengths
		// and PDFENTRY values as you go...
		while (counter--)
		{
			pdfelist[counter] = curpdfe;
			curpdfe = curpdfe->dfe_Parent;
			pathlen += curpdfe->dfe_UnicodeName.Length;
		}

		// we are in the root, start building up the host path buffer
		if (pathlen > pPath->MaximumLength)
		{
			pPath->Buffer = (PWCHAR)AfpAllocNonPagedMemory(pathlen);
			if (pPath->Buffer == NULL)
			{
				Status = AFP_ERR_MISC;
				break;
			}
			DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_INFO,
					("AfpHostPathFromDFEntry: Allocated path buffer %lx\n",
					pPath->Buffer));
			pPath->MaximumLength = (USHORT)pathlen;
		}

		counter = 0;
		do
		{
			RtlAppendUnicodeStringToString(pPath, &curpdfe->dfe_UnicodeName);
			if (curpdfe != pDFE)
			{	// add a path separator
				pPath->Buffer[pPath->Length / sizeof(WCHAR)] = L'\\';
				pPath->Length += sizeof(WCHAR);
				curpdfe = pdfelist[counter++];
				continue;
			}
			break;
		} while (True);

		if (pdfelist && (pdfelist != apdfelist))
			AfpFreeMemory(pdfelist);
	} while (False);

	return Status;
}



/***	AfpCheckParentPermissions
 *
 *	Check if this user has the necessary SeeFiles or SeeFolders permissions
 *	to the parent directory of a file or dir we have just pathmapped.
 *
 *	LOCKS_ASSUMED: vds_IdDbAccessLock (SWMR, Exclusive or Shared)
 */
AFPSTATUS
AfpCheckParentPermissions(
	IN	PCONNDESC			pConnDesc,
	IN	DWORD				ParentDirId,
	IN	PUNICODE_STRING		pParentPath,	// path of dir to check
	IN	DWORD				RequiredPerms,	// seefiles,seefolders,makechanges mask
	OUT	PFILESYSHANDLE		pHandle OPTIONAL, // return open parent handle?
	OUT	PBYTE				pUserRights OPTIONAL // return user rights?
)
{
	NTSTATUS		Status = AFP_ERR_NONE;
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	PVOLDESC		pVolDesc = pConnDesc->cds_pVolDesc;
	PDFENTRY		pDfEntry;

	PAGED_CODE( );

	ASSERT(IS_VOLUME_NTFS(pVolDesc) && (ParentDirId != AFP_ID_PARENT_OF_ROOT));
	ASSERT(AfpSwmrLockedExclusive(&pVolDesc->vds_IdDbAccessLock) ||
		   AfpSwmrLockedShared(&pVolDesc->vds_IdDbAccessLock));

	do
	{
		PME.pme_Handle.fsh_FileHandle = NULL;
		if (ARGUMENT_PRESENT(pHandle))
		{
			pHandle->fsh_FileHandle = NULL;
		}
		ASSERT(ARGUMENT_PRESENT(pParentPath));
		AfpInitializePME(&PME, pParentPath->MaximumLength, pParentPath->Buffer);
		PME.pme_FullPath.Length = pParentPath->Length;

		if ((pDfEntry = AfpFindDfEntryById(pVolDesc,
											ParentDirId,
											DFE_DIR)) == NULL)
		{
			Status = AFP_ERR_OBJECT_NOT_FOUND;
			break;
		}

		ASSERT(DFE_IS_DIRECTORY(pDfEntry));
		AfpInitializeFDParms(&FDParm);

		Status = afpGetMappedForLookupFDInfo(pConnDesc,
											 pDfEntry,
											 DIR_BITMAP_ACCESSRIGHTS |
												FD_INTERNAL_BITMAP_OPENACCESS_READCTRL,
											 &PME,
											 &FDParm);

		if (!NT_SUCCESS(Status))
		{
			if (PME.pme_Handle.fsh_FileHandle != NULL)
			{
				AfpIoClose(&PME.pme_Handle);
			}
			break;
		}

		if ((FDParm._fdp_UserRights & RequiredPerms) != RequiredPerms)
		{
			Status = AFP_ERR_ACCESS_DENIED;
		}

		if (ARGUMENT_PRESENT(pHandle) && NT_SUCCESS(Status))
		{
			*pHandle = PME.pme_Handle;
		}
		else
		{
			AfpIoClose(&PME.pme_Handle);
		}

		if (ARGUMENT_PRESENT(pUserRights))
		{
			*pUserRights = FDParm._fdp_UserRights;
		}

	} while (False);

	return Status;
}

/***	afpOpenUserHandle
 *
 * Open a handle to data or resource stream of an entity in the user's
 * context.  Only called for NTFS volumes.
 *
 *	LOCKS_ASSUMED: vds_idDbAccessLock (SWMR, Shared)
 */
AFPSTATUS
afpOpenUserHandle(
	IN	PCONNDESC			pConnDesc,
	IN	PDFENTRY			pDfEntry,
	IN	PUNICODE_STRING		pPath		OPTIONAL,	// path of file/dir to open
	IN	DWORD				Bitmap,					// to extract the Open access mode
	OUT	PFILESYSHANDLE		pfshData				// Handle of data stream of object
)
{
	PVOLDESC		pVolDesc = pConnDesc->cds_pVolDesc;
	NTSTATUS		Status;
	DWORD			OpenAccess;
	DWORD			DenyMode;
	BOOLEAN			isdir, CheckAccess = False, Revert = False;
	WCHAR			HostPathBuf[BIG_PATH_LEN];
	UNICODE_STRING	uHostPath;

	PAGED_CODE( );

	pfshData->fsh_FileHandle = NULL;

	isdir = (DFE_IS_DIRECTORY(pDfEntry)) ? True : False;
	OpenAccess = AfpMapFDBitmapOpenAccess(Bitmap, isdir);

	// Extract the index into the AfpDenyModes array from Bitmap
	DenyMode = AfpDenyModes[(Bitmap & FD_INTERNAL_BITMAP_DENYMODE_ALL) >>
								FD_INTERNAL_BITMAP_DENYMODE_SHIFT];

	do
	{
		if (ARGUMENT_PRESENT(pPath))
		{
			uHostPath = *pPath;
		}
		else
		{
			AfpSetEmptyUnicodeString(&uHostPath,
									 sizeof(HostPathBuf),
									 HostPathBuf);
			ASSERT ((Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC) == 0);
			if (!NT_SUCCESS(AfpHostPathFromDFEntry(pDfEntry,
												   0,
												   &uHostPath)))
			{
				Status = AFP_ERR_MISC;
				break;
			}
		}

		CheckAccess = False;
		Revert = False;
		// Don't impersonate or check access if this is ADMIN calling
		// or if volume is CDFS. If this handle will be used for setting
		// permissions, impersonate the user token instead. The caller
		// should have determined by now that this chappie has access
		// to change permissions.
		if (Bitmap & FD_INTERNAL_BITMAP_OPENACCESS_RWCTRL)
		{
			Revert = True;
			AfpImpersonateClient(NULL);
		}

		else if (!(Bitmap & FD_INTERNAL_BITMAP_SKIP_IMPERSONATION) &&
				 (pConnDesc->cds_pSda->sda_ClientType != SDA_CLIENT_ADMIN) &&
				 IS_VOLUME_NTFS(pVolDesc))
		{
			CheckAccess = True;
			Revert = True;
			AfpImpersonateClient(pConnDesc->cds_pSda);
		}

		DBGPRINT(DBG_COMP_AFPINFO, DBG_LEVEL_INFO,
				("afpOpenUserHandle: OpenMode %lx, DenyMode %lx\n",
				OpenAccess, DenyMode));

		if (Bitmap & FD_INTERNAL_BITMAP_OPENFORK_RESC)
		{
			DWORD	crinfo;	// was the Resource fork opened or created?

			ASSERT(IS_VOLUME_NTFS(pVolDesc));
			ASSERT((uHostPath.MaximumLength - uHostPath.Length) >= AfpResourceStream.Length);
			RtlCopyMemory((PBYTE)(uHostPath.Buffer) + uHostPath.Length,
						  AfpResourceStream.Buffer,
						  AfpResourceStream.Length);
			uHostPath.Length += AfpResourceStream.Length;
			Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
								 AFP_STREAM_DATA,
								 &uHostPath,
								 OpenAccess,
								 DenyMode,
								 FILEIO_OPEN_FILE,
								 FILEIO_CREATE_INTERNAL,
								 FILE_ATTRIBUTE_NORMAL,
								 True,
								 NULL,
								 pfshData,
								 &crinfo,
								 NULL,
								 NULL,
								 NULL);
		}
		else
		{
			Status = AfpIoOpen(&pVolDesc->vds_hRootDir,
								AFP_STREAM_DATA,
								isdir ?
									FILEIO_OPEN_DIR : FILEIO_OPEN_FILE,
								&uHostPath,
								OpenAccess,
								DenyMode,
								CheckAccess,
								pfshData);
		}

		if (Revert)
			AfpRevertBack();

		if (!ARGUMENT_PRESENT(pPath))
		{
			if ((uHostPath.Buffer != NULL) && (uHostPath.Buffer != HostPathBuf))
				AfpFreeMemory(uHostPath.Buffer);
		}

		if (!NT_SUCCESS(Status))
		{
			DBGPRINT(DBG_COMP_IDINDEX, DBG_LEVEL_WARN,
					("afpOpenUserHandle: NtOpenFile/NtCreateFile (Open %lx, Deny %lx) %lx\n",
					OpenAccess, DenyMode, Status));
			Status = AfpIoConvertNTStatusToAfpStatus(Status);
			break;
		}

	} while (False);

	if (!NT_SUCCESS(Status) && (pfshData->fsh_FileHandle != NULL))
	{
		AfpIoClose(pfshData);
	}

	return Status;
}