/*

Copyright (c) 1992  Microsoft Corporation

Module Name:

	fsp_file.c

Abstract:

	This module contains the entry points for the AFP file APIs queued to
	the FSP. These are all callable from FSP Only.

Author:

	Jameel Hyder (microsoft!jameelh)


Revision History:
	25 Apr 1992		Initial Version

Notes:	Tab stop: 4

--*/

#define	FILENUM	FILE_FSP_FILE

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpFspDispCreateFile)
#pragma alloc_text( PAGE, AfpFspDispSetFileParms)
#pragma alloc_text( PAGE, AfpFspDispCopyFile)
#pragma alloc_text( PAGE, AfpFspDispCreateId)
#pragma alloc_text( PAGE, AfpFspDispResolveId)
#pragma alloc_text( PAGE, AfpFspDispDeleteId)
#pragma alloc_text( PAGE, AfpFspDispExchangeFiles)
#endif

/***	AfpFspDispCreateFile
 *
 *	This is the worker routine for the AfpCreateFile API.
 *
 *	The request packet is represented below.
 *
 *	sda_AfpSubFunc	BYTE		Create option
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_Name1		ANSI_STRING	FileName
 */
AFPSTATUS FASTCALL
AfpFspDispCreateFile(
	IN	PSDA	pSda
)
{
	AFPSTATUS		Status = AFP_ERR_PARAM, PostStatus;
	PATHMAPENTITY	PME;
	PDFENTRY		pNewDfe;
	FILESYSHANDLE	hNewFile, hAfpInfo, hParent;
	AFPINFO			afpinfo;
	DWORD			crinfo;
	PATHMAP_TYPE	CreateOption;
	WCHAR			PathBuf[BIG_PATH_LEN];
	PVOLDESC		pVolDesc;		// For post-create processing
	BYTE			PathType;		// -- ditto --
	BOOLEAN			InRoot;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispCreateFile: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);

	do
	{
		hNewFile.fsh_FileHandle = NULL;
		hAfpInfo.fsh_FileHandle = NULL;
		hParent.fsh_FileHandle = NULL;
		CreateOption = (pSda->sda_AfpSubFunc == AFP_HARDCREATE_FLAG) ?
							HardCreate : SoftCreate;
		AfpInitializePME(&PME, sizeof(PathBuf), PathBuf);

		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_ParentId,
											   &pSda->sda_Name1,
											   PathType = pSda->sda_PathType,
											   CreateOption,
											   DFE_FILE,
											   0,
											   &PME,
											   NULL)))
		{
			break;
		}

		// check for seefiles on the parent directory if hard create
		if (CreateOption == HardCreate)
		{
			if (!NT_SUCCESS(Status = AfpCheckParentPermissions(
												pReqPkt->_pConnDesc,
												PME.pme_pDfeParent->dfe_AfpId,
												&PME.pme_ParentPath,
												DIR_ACCESS_READ,
												&hParent,
												NULL)))
			{
				break;
			}
		}

		AfpImpersonateClient(pSda);

		InRoot = (PME.pme_ParentPath.Length == 0) ? True : False;
		Status = AfpIoCreate(&pVolDesc->vds_hRootDir,
							AFP_STREAM_DATA,
							&PME.pme_FullPath,
							FILEIO_ACCESS_NONE | FILEIO_ACCESS_DELETE,
							FILEIO_DENY_NONE,
							FILEIO_OPEN_FILE,
							AfpCreateDispositions[pSda->sda_AfpSubFunc / AFP_HARDCREATE_FLAG],
							FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE,
							True,
							NULL,
							&hNewFile,
							&crinfo,
							pVolDesc,
							&PME.pme_FullPath,
// we don't get notified of parent mod time changing if there is no handle
// open for the parent dir at the time of create, which we cannot predict here.
							&PME.pme_ParentPath);

		AfpRevertBack();

		if (!NT_SUCCESS(Status))
		{
			Status = AfpIoConvertNTStatusToAfpStatus(Status);
			break;
		}

		// !!! HACK ALERT !!!
		// At this point we are pretty much done i.e. the create has succeeded
		// and we can return doing the rest of the work post-reply. Any errors
		// from now on SHOULD BE IGNORED. Also NO REFERENCE SHOULD BE MADE TO
		// the PSda & pConnDesc. Status should not be changed either. Also
		// reference the Volume for good measure. It cannot fail !!!
		AfpVolumeReference(pVolDesc);

		AfpCompleteApiProcessing(pSda, AFP_ERR_NONE);
		Status = AFP_ERR_EXTENDED;

		// Add this entry to the IdDb
		if (crinfo == FILE_CREATED)
		{
			pNewDfe = AfpAddDfEntry(pVolDesc,
									PME.pme_pDfeParent,
									&PME.pme_UTail,
									False,
									0);
		}
		else if (crinfo == FILE_SUPERSEDED)
		{
			ASSERT(CreateOption == HardCreate);
			pNewDfe = AfpFindEntryByUnicodeName(pVolDesc,
												&PME.pme_UTail,
												PathType,
												PME.pme_pDfeParent,
												DFE_FILE);
			if (pNewDfe == NULL)
			{
				pNewDfe = AfpAddDfEntry(pVolDesc,
										PME.pme_pDfeParent,
										&PME.pme_UTail,
										False,
										0);
			}

		}
		else ASSERTMSG("AfpFspDispCreateFile: unexpected create action", 0);
			
		if (pNewDfe != NULL)
		{
			afpinfo.afpi_Id = pNewDfe->dfe_AfpId;

			// Create the AfpInfo stream
			if (!NT_SUCCESS(AfpCreateAfpInfoStream(pVolDesc,
												   &hNewFile,
												   afpinfo.afpi_Id,
												   False,
												   &PME.pme_UTail,
												   &PME.pme_FullPath,
												   &afpinfo,
												   &hAfpInfo)))
			{
				// If we fail to add the AFP_AfpInfo stream, we must
				// rewind back to the original state.  i.e. delete
				// the file we just created, and remove it from
				// the Id database.
				AfpIoMarkFileForDelete(&hNewFile,
									   pVolDesc,
									   &PME.pme_FullPath,
									   InRoot ? NULL : &PME.pme_ParentPath);

				AfpDeleteDfEntry(pVolDesc, pNewDfe);
			}
			else
			{
				DWORD			Attr;

				// Get the rest of the File info, and cache it
				PostStatus = AfpIoQueryTimesnAttr(&hNewFile,
												  &pNewDfe->dfe_CreateTime,
												  &pNewDfe->dfe_LastModTime,
												  &Attr);
				
				if (NT_SUCCESS(PostStatus))
				{
					pNewDfe->dfe_NtAttr = (USHORT)Attr & FILE_ATTRIBUTE_VALID_FLAGS;
					pNewDfe->dfe_FinderInfo = afpinfo.afpi_FinderInfo;
					pNewDfe->dfe_BackupTime = afpinfo.afpi_BackupTime;
					pNewDfe->dfe_AfpAttr = afpinfo.afpi_Attributes;
					pNewDfe->dfe_DataLen = 0;
					pNewDfe->dfe_RescLen = 0;
					AfpVolumeSetModifiedTime(pVolDesc);
					AfpCacheParentModTime(pVolDesc,
										  (hParent.fsh_FileHandle == NULL) ? NULL : &hParent,
										  (hParent.fsh_FileHandle == NULL) ? &PME.pme_ParentPath : NULL,
										  PME.pme_pDfeParent,
										  0);
				}
				else
				{
					AfpIoMarkFileForDelete(&hNewFile,
										   pVolDesc,
										   &PME.pme_FullPath,
										   InRoot ? NULL : &PME.pme_ParentPath);
					AfpDeleteDfEntry(pVolDesc, pNewDfe);
				}
			}
		}

		AfpVolumeDereference(pVolDesc);
		ASSERT (Status == AFP_ERR_EXTENDED);
	} while (False);

	if (hNewFile.fsh_FileHandle != NULL)
		AfpIoClose(&hNewFile);

	if (hAfpInfo.fsh_FileHandle != NULL)
		AfpIoClose(&hAfpInfo);

	// If you release the lock before closing the handles,
	// for datahandle the FPOpenFork could get a sharing violation.
	// For AfpInfo stream CopyFile can get a sharing violation.
	AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);

	if (hParent.fsh_FileHandle != NULL)
		AfpIoClose(&hParent);

	if ((PME.pme_FullPath.Buffer != NULL) &&
		(PME.pme_FullPath.Buffer != PathBuf))
		AfpFreeMemory(PME.pme_FullPath.Buffer);

	return Status;
}


/***	AfpFspDispSetFileParms
 *
 *	This is the worker routine for the AfpSetFileParms API.
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_ReqBlock	DWORD		File Bitmap
 *	sda_Name1		ANSI_STRING	Path
 *	sda_Name2		BLOCK		File Parameters
 */
AFPSTATUS FASTCALL
AfpFspDispSetFileParms(
	IN	PSDA	pSda
)
{
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	PVOLDESC		pVolDesc;
	DWORD			Bitmap;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
		DWORD		_Bitmap;
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispSetFileParms: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	Bitmap = pReqPkt->_Bitmap;

	AfpInitializeFDParms(&FDParm);
	AfpInitializePME(&PME, 0, NULL);
	do
	{
		// Force the FD_BITMAP_LONGNAME in case a *file* is missing the afpinfo
		// stream we will be able to generate the correct type/creator in
		// AfpSetAfpInfo
		Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc,
										pReqPkt->_ParentId,
										&pSda->sda_Name1,
										pSda->sda_PathType,
										DFE_FILE,
										Bitmap | FD_INTERNAL_BITMAP_OPENACCESS_RW_ATTR |
												 FD_INTERNAL_BITMAP_RETURN_PMEPATHS |
												 FD_BITMAP_LONGNAME,
										&PME,
										&FDParm);

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

		if (Bitmap & (FD_BITMAP_ATTR |
					  FD_BITMAP_CREATETIME |
					  FD_BITMAP_MODIFIEDTIME))
		{
			DWORD	Attr;
			TIME	ModTime;

			if (!NT_SUCCESS(Status = AfpIoQueryTimesnAttr(&PME.pme_Handle,
														  &FDParm._fdp_CreateTime,
														  &ModTime,
														  &Attr)))
				break;
			
			FDParm._fdp_ModifiedTime = AfpConvertTimeToMacFormat(&ModTime);
			if (Bitmap & FD_BITMAP_ATTR)
				AfpNormalizeAfpAttr(&FDParm, Attr);
		}
		if ((Status = AfpUnpackFileDirParms(pSda->sda_Name2.Buffer,
											(LONG)pSda->sda_Name2.Length,
											&Bitmap,
											&FDParm)) != AFP_ERR_NONE)
			break;

		if (Bitmap != 0)
		{
			if ((Bitmap & FD_BITMAP_ATTR) &&
				(FDParm._fdp_Attr & (FILE_BITMAP_ATTR_DATAOPEN |
									 FILE_BITMAP_ATTR_RESCOPEN |
									 FILE_BITMAP_ATTR_COPYPROT)))
			{
				Status = AFP_ERR_PARAM;
				break;
			}
			AfpSetFileDirParms(pVolDesc, &PME, Bitmap, &FDParm);
		}
	} while (False);
	
	// Return before we close thus saving some time
	AfpCompleteApiProcessing(pSda, Status);

	if (PME.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PME.pme_Handle);

	if (PME.pme_FullPath.Buffer != NULL)
	{
		AfpFreeMemory(PME.pme_FullPath.Buffer);
	}

	return AFP_ERR_EXTENDED;
}


/***	AfpFspDispCopyFile
 *
 *	This is the worker routine for the AfpCopyFile API.
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	Source pConnDesc
 *	sda_ReqBlock	DWORD		Source ParentId
 *	sda_ReqBlock	DWORD		Dest VolId
 *	sda_ReqBlock	DWORD		Dest ParentId
 *	sda_Name1		ANSI_STRING	Source Path
 *	sda_Name2		ANSI_STRING	Dest Path
 *	sda_Name3		ANSI_STRING	New Name
 */
AFPSTATUS FASTCALL
AfpFspDispCopyFile(
	IN	PSDA	pSda
)
{
	PCONNDESC		pConnDescD;
	PATHMAPENTITY	PMESrc, PMEDst;
	FILEDIRPARM		FDParmSrc, FDParmDst;
	PANSI_STRING	pAnsiName;
	UNICODE_STRING	uNewName;
	WCHAR			wcbuf[AFP_FILENAME_LEN+1];
	PSWMR			pSwmr;
	PDFENTRY		pDfeParent, pNewDfe;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	BOOLEAN			DstLockTaken = False, Rename = True, InRoot;
	LONG			i;
	COPY_FILE_INFO	CopyFileInfo;
	PCOPY_FILE_INFO	pCopyFileInfo = &CopyFileInfo;
	DWORD			CreateTime = 0;
	AFPTIME			aModTime;
	TIME			ModTime;

	struct _RequestPacket
	{
		PCONNDESC	_pConnDescS;
		DWORD		_SrcParentId;
		DWORD		_DstVolId;
		DWORD		_DstParentId;
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispCopyFile: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDescS) &&
		   VALID_VOLDESC(pReqPkt->_pConnDescS->cds_pVolDesc));

	if	((pConnDescD =
			AfpConnectionReference(pSda, pReqPkt->_DstVolId)) != NULL)
	{
		ASSERT(VALID_CONNDESC(pConnDescD) &&
			   VALID_VOLDESC(pConnDescD->cds_pVolDesc));
	
		AfpInitializeFDParms(&FDParmSrc);
		AfpInitializeFDParms(&FDParmDst);
		AfpInitializePME(&PMESrc, 0, NULL);
		AfpInitializePME(&PMEDst, 0, NULL);
		AfpSetEmptyUnicodeString(&uNewName, sizeof(wcbuf), wcbuf);
        RtlZeroMemory(&CopyFileInfo, sizeof(COPY_FILE_INFO));
		PMESrc.pme_Handle.fsh_FileHandle = NULL;
		PMEDst.pme_Handle.fsh_FileHandle = NULL;

		do
		{
			if (pConnDescD->cds_pVolDesc->vds_Flags & AFP_VOLUME_READONLY)
			{
				Status = AFP_ERR_VOLUME_LOCKED;
				break;
			}

			// Make sure the new name is valid
			pAnsiName = &pSda->sda_Name3;
			if ((pSda->sda_Name3.Length > 0) &&
				((pSda->sda_Name3.Length > AFP_FILENAME_LEN) ||
				((pSda->sda_PathType == AFP_SHORTNAME) &&
				 !AfpIsLegalShortname(&pSda->sda_Name3)) ||
				(!NT_SUCCESS(AfpConvertStringToMungedUnicode(&pSda->sda_Name3,
															 &uNewName)))))
				break;

			Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDescS,
											pReqPkt->_SrcParentId,
											&pSda->sda_Name1,
											pSda->sda_PathType,
											DFE_FILE,
											FD_INTERNAL_BITMAP_OPENACCESS_READ |
												FD_BITMAP_ATTR |
												FD_BITMAP_LONGNAME |
												FD_BITMAP_FINDERINFO |
												FILE_BITMAP_RESCLEN |
												FILE_BITMAP_DATALEN |
												FD_INTERNAL_BITMAP_DENYMODE_WRITE,
											&PMESrc,
											&FDParmSrc);

			if (!NT_SUCCESS(Status))
			{
				break;
			}

			// Source opened ok. However we may have an internal deny conflict
			// Check that
			if (((Status = AfpCheckDenyConflict(pReqPkt->_pConnDescS->cds_pVolDesc,
												FDParmSrc._fdp_AfpId,
												False,
												FORK_OPEN_READ,
												FORK_DENY_WRITE,
												NULL)) != AFP_ERR_NONE) ||
				((Status = AfpCheckDenyConflict(pReqPkt->_pConnDescS->cds_pVolDesc,
												FDParmSrc._fdp_AfpId,
												True,
												FORK_OPEN_READ,
												FORK_DENY_WRITE,
												NULL)) != AFP_ERR_NONE))
			{
				Status = AFP_ERR_DENY_CONFLICT;
				break;
			}

			pSwmr = &pConnDescD->cds_pVolDesc->vds_IdDbAccessLock;
			AfpSwmrAcquireExclusive(pSwmr);
			DstLockTaken = True;

			// Map the destination directory for Lookup
			if (!NT_SUCCESS(Status = AfpMapAfpPath(pConnDescD,
												   pReqPkt->_DstParentId,
												   &pSda->sda_Name2,
												   pSda->sda_PathType,
												   Lookup,
												   DFE_DIR,
												   0,
												   &PMEDst,
												   &FDParmDst)))
			{
				break;
			}

			AfpImpersonateClient(pSda);
			
			// If no new name was supplied, we need to use the
			// current name
			if (pSda->sda_Name3.Length == 0)
			{
				Rename = False;
				pAnsiName = &FDParmSrc._fdp_LongName;
				AfpConvertStringToMungedUnicode(pAnsiName,
												&uNewName);
			}

			// since we really want the path of the thing we are about
			// to create, munge the strings in the PMEDst
			PMEDst.pme_ParentPath = PMEDst.pme_FullPath;
			if (PMEDst.pme_FullPath.Length > 0)
			{
				PMEDst.pme_FullPath.Buffer[PMEDst.pme_FullPath.Length / sizeof(WCHAR)] = L'\\';
				PMEDst.pme_FullPath.Length += sizeof(WCHAR);
			}
			Status = RtlAppendUnicodeStringToString(&PMEDst.pme_FullPath,
													&uNewName);
			ASSERT(NT_SUCCESS(Status));

			InRoot = (PMEDst.pme_ParentPath.Length == 0) ? True : False;
			Status = AfpIoCopyFile1(&PMESrc.pme_Handle,
									&PMEDst.pme_Handle,
									&uNewName,
									pConnDescD->cds_pVolDesc,
									&PMEDst.pme_FullPath,
									InRoot ? NULL : &PMEDst.pme_ParentPath,
									&CopyFileInfo);

			AfpRevertBack();

			if (!NT_SUCCESS(Status))
			{
				break;
			}

			// Add this entry to the IdDb. First find the parent directory
			pDfeParent = AfpFindDfEntryById(pConnDescD->cds_pVolDesc,
											FDParmDst._fdp_AfpId,
											DFE_DIR);
			ASSERT(pDfeParent != NULL);
			pNewDfe = AfpAddDfEntry(pConnDescD->cds_pVolDesc,
									pDfeParent,
									&uNewName,
									False,
									0);

			Status = AFP_ERR_MISC; // Assume failure
			if (pNewDfe != NULL)
			{
				// Put the new file's AFPId into the AfpInfo stream
				AfpInitializeFDParms(&FDParmDst);
				FDParmDst._fdp_Flags = DFE_FLAGS_FILE_NO_ID;
				FDParmDst._fdp_AfpId = pNewDfe->dfe_AfpId;
				FDParmDst._fdp_BackupTime = BEGINNING_OF_TIME;

	            // Copy the finderinfo from the source to the destination
				// Also clear the inited bit so that finder will assign
				// new coordinates for the new file.
				FDParmDst._fdp_FinderInfo = FDParmSrc._fdp_FinderInfo;
				FDParmDst._fdp_FinderInfo.fd_Attr1 &= ~FINDER_FLAG_SET;
				AfpConvertMungedUnicodeToAnsi(&pNewDfe->dfe_UnicodeName,
											  &FDParmDst._fdp_LongName);

				Status = AfpSetAfpInfo(&CopyFileInfo.cfi_DstStreamHandle[0],
									   FILE_BITMAP_FILENUM	|
									   FD_BITMAP_BACKUPTIME	|
									   FD_BITMAP_FINDERINFO,
									   &FDParmDst,
									   NULL,
									   NULL);

				if (NT_SUCCESS(Status))
				{
					// Get the rest of the File info, and cache it
					Status = AfpIoQueryTimesnAttr(&CopyFileInfo.cfi_SrcStreamHandle[0],
												  &pNewDfe->dfe_CreateTime,
												  &pNewDfe->dfe_LastModTime,
												  NULL);
					
					if (NT_SUCCESS(Status))
					{
			            // Copy the finderinfo into the destination DFE.
						// Use the FDParmDst version since it has the right
						// version - see above.
						pNewDfe->dfe_FinderInfo = FDParmDst._fdp_FinderInfo;
						pNewDfe->dfe_BackupTime = BEGINNING_OF_TIME;
						pNewDfe->dfe_AfpAttr = FDParmSrc._fdp_Attr &
														~(FD_BITMAP_ATTR_SET |
														  FILE_BITMAP_ATTR_DATAOPEN |
														  FILE_BITMAP_ATTR_RESCOPEN);
						pNewDfe->dfe_NtAttr =  (USHORT)AfpConvertAfpAttrToNTAttr(pNewDfe->dfe_AfpAttr);
						pNewDfe->dfe_DataLen = FDParmSrc._fdp_DataForkLen;
						pNewDfe->dfe_RescLen = FDParmSrc._fdp_RescForkLen;
	
						AfpCacheParentModTime(pConnDescD->cds_pVolDesc,
											  NULL,
											  &PMEDst.pme_ParentPath,
											  pNewDfe->dfe_Parent,
											  0);
					}

					// Set the attributes such that it matches the source
					Status = AfpIoSetTimesnAttr(&CopyFileInfo.cfi_DstStreamHandle[0],
												NULL,
												NULL,
												pNewDfe->dfe_NtAttr,
												0,
												pConnDescD->cds_pVolDesc,
												&PMEDst.pme_FullPath);
				}

				if (!NT_SUCCESS(Status))
				{
					// If we failed to write the correct AfpId onto the
					// new file, then delete the file, and remove it from
					// the Id database.
					AfpIoMarkFileForDelete(&CopyFileInfo.cfi_DstStreamHandle[0],
										   pConnDescD->cds_pVolDesc,
										   &PMEDst.pme_FullPath,
										   InRoot ? NULL : &PMEDst.pme_ParentPath);

					AfpDeleteDfEntry(pConnDescD->cds_pVolDesc, pNewDfe);
					Status = AFP_ERR_MISC;
				}
			}
		} while (False);

		if (DstLockTaken == True)
			AfpSwmrRelease(pSwmr);

		// If we have successfully come so far, go ahead and complete the copy
		if (Status == AFP_ERR_NONE)
		{
			Status = AfpIoCopyFile2(&CopyFileInfo,
									pConnDescD->cds_pVolDesc,
									&PMEDst.pme_FullPath,
									InRoot ? NULL : &PMEDst.pme_ParentPath);
			if (Status == AFP_ERR_NONE)
			{
				// We need to get the create and modified time from the source
				// file before we close it.
				AfpIoQueryTimesnAttr(&pCopyFileInfo->cfi_SrcStreamHandle[0],
									 &CreateTime,
									 &ModTime,
									 NULL);

				aModTime = AfpConvertTimeToMacFormat(&ModTime);

			} else {

				AfpSwmrAcquireExclusive(pSwmr);
				// Note that we cannot use pNewDfe. We need to remap. It could have
				// got deleted when we relinquished the Swmr.
				pNewDfe = AfpFindDfEntryById(pConnDescD->cds_pVolDesc,
											  FDParmDst._fdp_AfpId,
											  DFE_FILE);
				if (pNewDfe != NULL)
					AfpDeleteDfEntry(pConnDescD->cds_pVolDesc, pNewDfe);
				AfpSwmrRelease(pSwmr);
			}

            // update the disk quota for this user on the destination volume
            if (pConnDescD->cds_pVolDesc->vds_Flags & VOLUME_DISKQUOTA_ENABLED)
            {
                if (AfpConnectionReferenceByPointer(pConnDescD) != NULL)
                {
                    afpUpdateDiskQuotaInfo(pConnDescD);
                }
            }
		}

		// Close the source file and dest directory handles
		if (PMESrc.pme_Handle.fsh_FileHandle != NULL)
			AfpIoClose(&PMESrc.pme_Handle);
		if (PMEDst.pme_Handle.fsh_FileHandle != NULL)
			AfpIoClose(&PMEDst.pme_Handle);

		// Close all the handles, Free the handle space. We come here regardless
		// of success/error. MAKE SURE THE SOURCE HANDLE IS NOT CLOSED HERE SINCE
		// IT HAS BEEN CLOSED ABOVE.
		// MAKE SURE THE DESTINATION HANDLE IS NOT CLOSED HERE SINCE WE NEED IT TO
		// SET THE FILE TIME.
		for (i = 1; i < CopyFileInfo.cfi_NumStreams; i++)
		{
			if (CopyFileInfo.cfi_SrcStreamHandle[i].fsh_FileHandle != NULL)
			{
				AfpIoClose(&CopyFileInfo.cfi_SrcStreamHandle[i]);
			}
			if (CopyFileInfo.cfi_DstStreamHandle[i].fsh_FileHandle != NULL)
			{
				AfpIoClose(&CopyFileInfo.cfi_DstStreamHandle[i]);
			}
		}

		if ((CopyFileInfo.cfi_DstStreamHandle != NULL) &&
		    (CopyFileInfo.cfi_DstStreamHandle[0].fsh_FileHandle != NULL))
		{
			if (Status == AFP_ERR_NONE)
			{
				// Set the creation and modification date on the destination
				// file to match that of the source file
				AfpIoSetTimesnAttr(&pCopyFileInfo->cfi_DstStreamHandle[0],
								   &CreateTime,
								   &aModTime,
								   0,
								   0,
								   pConnDescD->cds_pVolDesc,
								   &PMEDst.pme_FullPath);
			}
			AfpIoClose(&CopyFileInfo.cfi_DstStreamHandle[0]);
		}

		if (PMEDst.pme_FullPath.Buffer != NULL)
			AfpFreeMemory(PMEDst.pme_FullPath.Buffer);

		if (CopyFileInfo.cfi_SrcStreamHandle != NULL)
			AfpFreeMemory(CopyFileInfo.cfi_SrcStreamHandle);
		if (CopyFileInfo.cfi_DstStreamHandle != NULL)
			AfpFreeMemory(CopyFileInfo.cfi_DstStreamHandle);

		AfpConnectionDereference(pConnDescD);
	}

	return Status;
}


/***	AfpFspDispCreateId
 *
 *	This is the worker routine for the AfpCreateId API.
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_Name1		ANSI_STRING	Path
 */
AFPSTATUS FASTCALL
AfpFspDispCreateId(
	IN	PSDA	pSda
)
{
	AFPSTATUS		Status = AFP_ERR_PARAM, Status2;
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
	};
	struct _ResponsePacket
	{
		BYTE	__FileId[4];
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispCreateId: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc) &&
		   VALID_VOLDESC(pReqPkt->_pConnDesc->cds_pVolDesc));
	
	do
	{
		AfpInitializePME(&PME, 0, NULL);
		Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc,
										pReqPkt->_ParentId,
										&pSda->sda_Name1,
										pSda->sda_PathType,
										DFE_FILE,
										FILE_BITMAP_FILENUM | FD_INTERNAL_BITMAP_OPENACCESS_READ,
										&PME,
										&FDParm);
        // if we get sharing violation we know we have read access to the file
		if (!NT_SUCCESS(Status) && (Status != AFP_ERR_DENY_CONFLICT))
			break;

		// Set the bit in DF Entry
		Status = AfpSetDFFileFlags(pReqPkt->_pConnDesc->cds_pVolDesc,
								   FDParm._fdp_AfpId,
								   0,
								   True,
								   False);
	} while (False);

	if ((Status == AFP_ERR_VOLUME_LOCKED) && (FDParm._fdp_Flags & DFE_FLAGS_FILE_WITH_ID))
	{
		// If the volume is locked, but an Id exists, return it
		Status = AFP_ERR_ID_EXISTS;
	}

	if ((Status == AFP_ERR_NONE) || (Status == AFP_ERR_ID_EXISTS))
	{
		pSda->sda_ReplySize = SIZE_RESPPKT;
		if ((Status2 = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
		{
			PUTDWORD2DWORD(pRspPkt->__FileId, FDParm._fdp_AfpId);
		}
		else
		{
			Status = Status2;
		}
	}

	// Return before we close thus saving some time
	AfpCompleteApiProcessing(pSda, Status);

	if (PME.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PME.pme_Handle);

	return AFP_ERR_EXTENDED;
}


/***	AfpFspDispResolveId
 *
 *	This is the worker routine for the AfpResolveId API.
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		FileId
 *	sda_ReqBlock	DWORD		Bitmap
 */
AFPSTATUS FASTCALL
AfpFspDispResolveId(
	IN	PSDA	pSda
)
{
	DWORD			Bitmap;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_FileId;
		DWORD		_Bitmap;
	};
	struct _ResponsePacket
	{
		BYTE	__Bitmap[2];
		// Rest of the parameters
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispResolveId: Entered\n"));
	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc) &&
		   VALID_VOLDESC(pReqPkt->_pConnDesc->cds_pVolDesc));
	
	Bitmap = pReqPkt->_Bitmap;

	do
	{
		AfpInitializeFDParms(&FDParm);
		AfpInitializePME(&PME, 0, NULL);

		// HACK: this is to make System 7.5 FindFile not grey out the first
		// item in the list of items found.  Normally we would check for
		// parameter non-zero in the api table in afpapi.c and return an
		// error there, but this is a special case.
		if (pReqPkt->_FileId == 0)
		{
			Status = AFP_ERR_ID_NOT_FOUND;
			break;
		}

		Status = AfpMapAfpIdForLookup(pReqPkt->_pConnDesc,
								pReqPkt->_FileId,
								DFE_FILE,
								Bitmap | FD_INTERNAL_BITMAP_OPENACCESS_READ,
								&PME,
								&FDParm);
		if (!NT_SUCCESS(Status) && (Status != AFP_ERR_DENY_CONFLICT))
		{								
			if (Status == AFP_ERR_OBJECT_NOT_FOUND)
			{
				Status = AFP_ERR_ID_NOT_FOUND;
			}
			break;
		}

		// a deny conflict means the user actually has access to the file, so
		// we need to open for nothing with no sharing modes to get the
		// bitmap parameters.
		if (Status == AFP_ERR_DENY_CONFLICT)
		{
			Status = AfpMapAfpIdForLookup(pReqPkt->_pConnDesc,
										  pReqPkt->_FileId,
										  DFE_FILE,
										  Bitmap,
										  &PME,
										  &FDParm);
			if (!NT_SUCCESS(Status))
			{
				if (Status == AFP_ERR_OBJECT_NOT_FOUND)
				{
					Status = AFP_ERR_ID_NOT_FOUND;
				}
				break;
			}

		}

		if (!(FDParm._fdp_Flags & DFE_FLAGS_FILE_WITH_ID))
		{
			Status = AFP_ERR_ID_NOT_FOUND;
			break;
		}

		pSda->sda_ReplySize = SIZE_RESPPKT +
						EVENALIGN(AfpGetFileDirParmsReplyLength(&FDParm, Bitmap));
	
		if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
		{
			AfpPackFileDirParms(&FDParm, Bitmap, pSda->sda_ReplyBuf + SIZE_RESPPKT);
			PUTDWORD2SHORT(pRspPkt->__Bitmap, Bitmap);
		}
	} while (False);

	if (PME.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PME.pme_Handle);

	return Status;
}


/***	AfpFspDispDeleteId
 *
 *	This is the worker routine for the AfpDeleteId API.
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		FileId
 */
AFPSTATUS FASTCALL
AfpFspDispDeleteId(
	IN	PSDA	pSda
)
{
	AFPSTATUS		Status = AFP_ERR_PARAM;
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_FileId;
	};

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
										("AfpFspDispDeleteId: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc) &&
		   VALID_VOLDESC(pReqPkt->_pConnDesc->cds_pVolDesc));
	
	do
	{
		AfpInitializePME(&PME, 0, NULL);
		Status = AfpMapAfpIdForLookup(pReqPkt->_pConnDesc,
		                              pReqPkt->_FileId,
									  DFE_FILE,
									  FILE_BITMAP_FILENUM |
									   FD_INTERNAL_BITMAP_OPENACCESS_READWRITE,
									  &PME,
									  &FDParm);
		if (!NT_SUCCESS(Status) && (Status != AFP_ERR_DENY_CONFLICT))
		{
			if (Status == AFP_ERR_OBJECT_NOT_FOUND)
			{
				Status = AFP_ERR_ID_NOT_FOUND;
			}
			break;
		}

		// Set the bit in DF Entry
		Status = AfpSetDFFileFlags(pReqPkt->_pConnDesc->cds_pVolDesc,
								   FDParm._fdp_AfpId,
								   0,
								   False,
								   True);
	} while (False);

	// Return before we close thus saving some time
	AfpCompleteApiProcessing(pSda, Status);

	if (PME.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PME.pme_Handle);

	return AFP_ERR_EXTENDED;
}


/***	AfpFspDispExchangeFiles
 *
 *	This is the worker routine for the AfpExchangeFiles API.
 *
 * Acquire the IdDb Swmr for Write and map both source and destination for
 * lookup (set to open the entities for DELETE access since we are going to
 * rename them). Check that we have the appropriate parent
 * permissions. Then rename source to destiation and vice versa (will need an
 * intermediate name - use a name > 31 chars so that it does not collide with
 * an existing name. Use characters that cannot be accessed by Win32 so that
 * side is taken care of as well (40 spaces should do it). Since we have the
 * Swmr held for WRITE, there is no issue of two different AfpExchangeFile
 * apis trying to rename to the same name). Then inter-change the file ids
 * and the FinderInfo in the AfpInfo streams. Also interchange the create
 * times (retain original ID and create time) on the files. Swap all the other
 * cached info in the 2 DFEntries.
 * Make sure the stuff is setup so that ChangeNotify filters are handled
 * appropriately.
 *
 * If either of the files are currently open, the name and ID in the
 * OpenForkDesc (that each OpenForkEntry points to) has to change to
 * the new name. Note that because the name and Id can now change in
 * the OpenForkDesc, we must make sure that everyone who accesses these
 * is taking appropriate locks.
 *
 *
 *	The request packet is represented below.
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		Srce. DirId
 *	sda_ReqBlock	DWORD		Dest. DirId
 *	sda_Name1		ANSI_STRING	Srce. Path
 *	sda_Name2		ANSI_STRING	Dest. Path
 */
AFPSTATUS FASTCALL
AfpFspDispExchangeFiles(
	IN	PSDA	pSda
)
{
	PATHMAPENTITY	PMESrc, PMEDst;
	PVOLDESC		pVolDesc;
	FILEDIRPARM		FDParmSrc, FDParmDst;
	AFPSTATUS		Status = AFP_ERR_NONE, Status2 = AFP_ERR_NONE;
	BOOLEAN			Move = True, RevertBack = False, SrcInRoot, DstInRoot;
	BOOLEAN			RestoreSrcRO = False, RestoreDstRO = False;
	FILESYSHANDLE	hSrcParent, hDstParent;
	DWORD			checkpoint = 0; // denotes what needs cleanup on error
	DWORD			NTAttrSrc = 0, NTAttrDst = 0;
	WCHAR			PathBuf[BIG_PATH_LEN];
	UNICODE_STRING	TempPath;		// temporary filename for renaming files
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_SrcParentId;
		DWORD		_DstParentId;
	};

#define _CHKPOINT_XCHG_DSTTOTEMP	1
#define _CHKPOINT_XCHG_SRCTODST		2
#define _CHKPOINT_XCHG_TEMPTOSRC	3

	PAGED_CODE( );

	DBGPRINT(DBG_COMP_AFPAPI_FILE, DBG_LEVEL_INFO,
			("AfpFspDispExchangeFiles: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc) &&
		   VALID_VOLDESC(pReqPkt->_pConnDesc->cds_pVolDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	AfpInitializeFDParms(&FDParmSrc);
	AfpInitializeFDParms(&FDParmDst);
	AfpInitializePME(&PMESrc, 0, NULL);
	AfpInitializePME(&PMEDst, 0, NULL);
	AfpSetEmptyUnicodeString(&TempPath, 0, NULL);
	hSrcParent.fsh_FileHandle = NULL;
	hDstParent.fsh_FileHandle = NULL;


	// Don't allow any fork operations that might access the FileId
	// in an OpenForkDesc that could get exchanged.
	AfpSwmrAcquireExclusive(&pVolDesc->vds_ExchangeFilesLock);

	AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);

	do
	{

		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_SrcParentId,
											   &pSda->sda_Name1,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_FILE,
											   FD_INTERNAL_BITMAP_OPENACCESS_DELETE |
												(FILE_BITMAP_MASK &
												~(FD_BITMAP_SHORTNAME | FD_BITMAP_PRODOSINFO)),
											   &PMESrc,
											   &FDParmSrc)))
		{
			break;
		}


		// Check for SeeFiles on the source parent dir
		if (!NT_SUCCESS(Status = AfpCheckParentPermissions(
												pReqPkt->_pConnDesc,
												FDParmSrc._fdp_ParentId,
												&PMESrc.pme_ParentPath,
												DIR_ACCESS_READ,
												&hSrcParent,
												NULL)))
		{
			break;
		}

		if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PMESrc.pme_Handle,
											  FD_BITMAP_ATTR_RENAMEINH,
											  FDParmSrc._fdp_Attr,
											  &NTAttrSrc)))
		{
			break;
		}
		
		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_DstParentId,
											   &pSda->sda_Name2,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_FILE,
											   FD_INTERNAL_BITMAP_OPENACCESS_DELETE |
												(FILE_BITMAP_MASK &
												~(FD_BITMAP_SHORTNAME | FD_BITMAP_PRODOSINFO)),
											   &PMEDst,
											   &FDParmDst)))
		{
			break;
		}

		if (FDParmSrc._fdp_AfpId == FDParmDst._fdp_AfpId)
		{
			// make sure the src and dst are not the same file
			Status = AFP_ERR_SAME_OBJECT;
			break;
		}

		if (FDParmSrc._fdp_ParentId == FDParmDst._fdp_ParentId)
		{
			// if the parent directories are the same, we are not
			// moving anything to a new directory, so the change
			// notify we expect will be a rename in the source dir.
			Move = False;
		}
		else
		{
			// Check for SeeFiles on the destination parent dir
			if (!NT_SUCCESS(Status = AfpCheckParentPermissions(
													pReqPkt->_pConnDesc,
													FDParmDst._fdp_ParentId,
													&PMEDst.pme_ParentPath,
													DIR_ACCESS_READ,
													&hDstParent,
													NULL)))
			{
				break;
			}
		}

		if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PMEDst.pme_Handle,
											  FD_BITMAP_ATTR_RENAMEINH,
											  FDParmDst._fdp_Attr,
											  &NTAttrDst)))
		{
			break;
		}


		//
		// Construct the path to the temporary filename for renaming during
		// the name exchange
		//
		TempPath.MaximumLength = PMEDst.pme_ParentPath.Length + sizeof(WCHAR) +
														AfpExchangeName.Length;
		TempPath.Buffer = PathBuf;
		if ((TempPath.MaximumLength > sizeof(PathBuf)) &&
			(TempPath.Buffer = (PWCHAR)AfpAllocNonPagedMemory(TempPath.MaximumLength)) == NULL)
		{
			Status = AFP_ERR_MISC;
			break;
		}

		AfpCopyUnicodeString(&TempPath, &PMEDst.pme_ParentPath);
		if (TempPath.Length != 0)
		{
			TempPath.Buffer[TempPath.Length / sizeof(WCHAR)] = L'\\';
			TempPath.Length += sizeof(WCHAR);
			ASSERT((TempPath.MaximumLength - TempPath.Length) >= AfpExchangeName.Length);
		}
		Status = RtlAppendUnicodeStringToString(&TempPath, &AfpExchangeName);
		ASSERT(NT_SUCCESS(Status));

		if (NTAttrSrc & FILE_ATTRIBUTE_READONLY)
		{
			// We must temporarily remove the ReadOnly attribute so that
			// we can rename the file/dir
			Status = AfpIoSetTimesnAttr(&PMESrc.pme_Handle,
										NULL,
										NULL,
										0,
										FILE_ATTRIBUTE_READONLY,
										pVolDesc,
										&PMESrc.pme_FullPath);
			if (!NT_SUCCESS(Status))
			{
				break;
			}
			else
				RestoreSrcRO = True;
		}

		if (NTAttrDst & FILE_ATTRIBUTE_READONLY)
		{
			// We must temporarily remove the ReadOnly attribute so that
			// we can rename the file/dir
			Status = AfpIoSetTimesnAttr(&PMEDst.pme_Handle,
										NULL,
										NULL,
										0,
										FILE_ATTRIBUTE_READONLY,
										pVolDesc,
										&PMEDst.pme_FullPath);
			if (!NT_SUCCESS(Status))
			{
				break;
			}
			else
				RestoreDstRO = True;
		}

		// We must impersonate to do the move since it is name based
		AfpImpersonateClient(pSda);
		RevertBack = True;

		SrcInRoot = (PMESrc.pme_ParentPath.Length == 0) ? True : False;
		DstInRoot = (PMEDst.pme_ParentPath.Length == 0) ? True : False;

		// First, rename the destination to a temporary name in the same
		// directory
		Status = AfpIoMoveAndOrRename(&PMEDst.pme_Handle,
									  NULL,
									  &AfpExchangeName,
									  pVolDesc,
									  &PMEDst.pme_FullPath,
									  DstInRoot ? NULL : &PMEDst.pme_ParentPath,
									  NULL,
									  NULL);

		if (NT_SUCCESS(Status))
		{
			checkpoint = _CHKPOINT_XCHG_DSTTOTEMP;
		}
		else
		{
			break;
		}

		// Next, rename the source to the destination name
		Status = AfpIoMoveAndOrRename(&PMESrc.pme_Handle,
									  Move ? &hDstParent : NULL,
									  &PMEDst.pme_UTail,
									  pVolDesc,
									  &PMESrc.pme_FullPath,
									  SrcInRoot ? NULL : &PMESrc.pme_ParentPath,
									  Move ? &PMEDst.pme_FullPath : NULL,
									  (Move && !DstInRoot) ? &PMEDst.pme_ParentPath : NULL);

		if (NT_SUCCESS(Status))
		{
			checkpoint = _CHKPOINT_XCHG_SRCTODST;
		}
		else
		{
			break;
		}


		// Finally, rename the temporary name to the source name
		Status = AfpIoMoveAndOrRename(&PMEDst.pme_Handle,
									  Move ? &hSrcParent : NULL,
									  &PMESrc.pme_UTail,
									  pVolDesc,
									  &TempPath,
									  DstInRoot ? NULL : &PMEDst.pme_ParentPath,
									  Move ? &PMESrc.pme_FullPath : NULL,
									  (Move && !SrcInRoot) ? &PMESrc.pme_ParentPath : NULL);

		if (NT_SUCCESS(Status))
		{
			checkpoint = _CHKPOINT_XCHG_TEMPTOSRC;
		}
		else
		{
			break;
		}

		AfpRevertBack();
		RevertBack = False;

		// Swap the FileIds and FinderInfo in the AfpInfo streams
		Status = AfpSetAfpInfo(&PMESrc.pme_Handle,
							   FILE_BITMAP_FILENUM | FD_BITMAP_FINDERINFO,
							   &FDParmDst,
							   NULL,
							   NULL);

		ASSERT(NT_SUCCESS(Status));
		Status = AfpSetAfpInfo(&PMEDst.pme_Handle,
							   FILE_BITMAP_FILENUM | FD_BITMAP_FINDERINFO,
							   &FDParmSrc,
							   NULL,
							   NULL);

		ASSERT(NT_SUCCESS(Status));
		// Swap the creation dates on the files
		Status = AfpIoSetTimesnAttr(&PMESrc.pme_Handle,
									&FDParmDst._fdp_CreateTime,
									NULL,
									0,
									0,
									pVolDesc,
									&PMEDst.pme_FullPath);
		ASSERT(NT_SUCCESS(Status));
		Status = AfpIoSetTimesnAttr(&PMEDst.pme_Handle,
									&FDParmSrc._fdp_CreateTime,
									NULL,
									0,
									0,
									pVolDesc,
									&PMESrc.pme_FullPath);
		ASSERT(NT_SUCCESS(Status));

		// All the physical file info that we *didn't* swap on the real
		// files, we need to swap in the DFEntries
		AfpExchangeIdEntries(pVolDesc,
							 FDParmSrc._fdp_AfpId,
							 FDParmDst._fdp_AfpId);

		// Now, if either of the 2 files is open, we have to update the
		// OpenForkDesc to contain the correct FileId (we don't bother
		// updating the path since we don't care if Admin shows the
		// original name of the file, even though it has been renamed)
		AfpExchangeForkAfpIds(pVolDesc,
							  FDParmSrc._fdp_AfpId,
							  FDParmDst._fdp_AfpId);

		// update the cached src and dest parent dir mod times
		AfpCacheParentModTime(pVolDesc,
							  &hSrcParent,
							  NULL,
							  NULL,
							  FDParmSrc._fdp_ParentId);

        if (Move)
		{
			AfpCacheParentModTime(pVolDesc,
								  &hDstParent,
								  NULL,
								  NULL,
								  FDParmDst._fdp_ParentId);
		}

	} while (False);

	// Use the checkpoint value to undo any renames that
	// need undoing if there was an error
	if (!NT_SUCCESS(Status))
	{
		switch(checkpoint)
		{
			case _CHKPOINT_XCHG_TEMPTOSRC:
			{
				// Need to rename the original dest back to temp name
				Status2 = AfpIoMoveAndOrRename(&PMEDst.pme_Handle,
											   Move ? &hDstParent : NULL,
											   &AfpExchangeName,
											   pVolDesc,
											   &PMESrc.pme_FullPath,
											   SrcInRoot ? NULL : &PMESrc.pme_ParentPath,
											   Move ? &TempPath : NULL,
											   (Move && !DstInRoot) ? &PMEDst.pme_ParentPath : NULL);
				if (!NT_SUCCESS(Status2))
				{
					break;
				}

				// fall thru;
			}
			case _CHKPOINT_XCHG_SRCTODST:
			{
				// Need to rename the dest back to original src name
				Status2 = AfpIoMoveAndOrRename(&PMESrc.pme_Handle,
											   Move ? &hSrcParent : NULL,
											   &PMESrc.pme_UTail,
											   pVolDesc,
											   &PMEDst.pme_FullPath,
											   DstInRoot ? NULL : &PMEDst.pme_ParentPath,
											   Move ? &PMESrc.pme_FullPath : NULL,
											   (Move && !SrcInRoot) ? &PMESrc.pme_ParentPath : NULL);
				
				if (!NT_SUCCESS(Status2))
				{
					break;
				}

				// fall thru;
			}
			case _CHKPOINT_XCHG_DSTTOTEMP:
			{
				// Need to rename the temp back to original dest name
				Status2 = AfpIoMoveAndOrRename(&PMEDst.pme_Handle,
											   NULL,
											   &PMEDst.pme_UTail,
											   pVolDesc,
											   &TempPath,
											   DstInRoot ? NULL : &PMEDst.pme_ParentPath,
											   NULL,
											   NULL);
				// update the cached src parent dir mod time
				AfpCacheParentModTime(pVolDesc,
									  &hSrcParent,
									  NULL,
									  NULL,
									  FDParmSrc._fdp_ParentId);
		
				if (Move)
				{
					// update the cached dest parent dir mod time
					AfpCacheParentModTime(pVolDesc,
										  &hDstParent,
										  NULL,
										  NULL,
										  FDParmDst._fdp_ParentId);
				}

				break;
			}
			default:
			{
				break;
			}

		} // end switch
	}

	// Set the ReadOnly attribute back on the files if need be
	// NOTE: will we get a notify for this since we havn't closed
	// the handle yet?
	if (RestoreSrcRO)
		AfpIoSetTimesnAttr(&PMESrc.pme_Handle,
							NULL,
							NULL,
							FILE_ATTRIBUTE_READONLY,
							0,
							NULL,
							NULL);
	if (RestoreDstRO)
		AfpIoSetTimesnAttr(&PMEDst.pme_Handle,
							NULL,
							NULL,
							FILE_ATTRIBUTE_READONLY,
							0,
							NULL,
							NULL);

	AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);
	AfpSwmrRelease(&pVolDesc->vds_ExchangeFilesLock);

	if (RevertBack)
		AfpRevertBack();

	if (PMESrc.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PMESrc.pme_Handle);

	if (PMEDst.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PMEDst.pme_Handle);

	if (hSrcParent.fsh_FileHandle != NULL)
		AfpIoClose(&hSrcParent);

	if (hDstParent.fsh_FileHandle != NULL)
		AfpIoClose(&hDstParent);

	if ((TempPath.Buffer != NULL) &&
		(TempPath.Buffer != PathBuf))
		AfpFreeMemory(TempPath.Buffer);

	if (PMESrc.pme_FullPath.Buffer != NULL)
		AfpFreeMemory(PMESrc.pme_FullPath.Buffer);

	if (PMEDst.pme_FullPath.Buffer != NULL)
		AfpFreeMemory(PMEDst.pme_FullPath.Buffer);

	return Status;
}