/*

Copyright (c) 1992  Microsoft Corporation

Module Name:

	fsp_fd.c

Abstract:

	This module contains the entry points for the AFP file-dir 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_FD

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

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, AfpFspDispGetFileDirParms)
#pragma alloc_text( PAGE, AfpFspDispSetFileDirParms)
#pragma alloc_text( PAGE, AfpFspDispDelete)
#pragma alloc_text( PAGE, AfpFspDispRename)
#pragma alloc_text( PAGE, AfpFspDispMoveAndRename)
#pragma alloc_text( PAGE, AfpFspDispCatSearch)
#endif

/***	AfpFspDispGetFileDirParms
 *
 *	This is the worker routine for the AfpGetFileDirParms API.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_ReqBlock	DWORD		File Bitmap
 *	sda_ReqBlock	DWORD		Dir Bitmap
 *	sda_Name1		ANSI_STRING	Path
 */
AFPSTATUS FASTCALL
AfpFspDispGetFileDirParms(
	IN	PSDA	pSda
)
{
	FILEDIRPARM		FDParm;
	PATHMAPENTITY	PME;
	BOOLEAN			NeedHandle = False;
	PVOLDESC		pVolDesc;
	DWORD			BitmapF, BitmapD, BitmapI = 0;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
		DWORD		_FileBitmap;
		DWORD		_DirBitmap;
	};
	struct _ResponsePacket
	{
		BYTE	__FileBitmap[2];
		BYTE	__DirBitmap[2];
		BYTE	__FileDirFlag;
		BYTE	__Pad;
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
			("AfpFspDispGetFileDirParms: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	BitmapF = pReqPkt->_FileBitmap;
	BitmapD = pReqPkt->_DirBitmap;

	do
	{
		AfpInitializeFDParms(&FDParm);
		AfpInitializePME(&PME, 0, NULL);
		if (IS_VOLUME_NTFS(pVolDesc) &&
			(BitmapD & (DIR_BITMAP_ACCESSRIGHTS |
					    DIR_BITMAP_OWNERID |
					    DIR_BITMAP_GROUPID)))
		{
			NeedHandle = True;
		}

		if (BitmapD & DIR_BITMAP_ACCESSRIGHTS)
		{
			BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_READCTRL;
		}


		if ((Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc,
											 pReqPkt->_ParentId,
											 &pSda->sda_Name1,
											 pSda->sda_PathType,
											 DFE_ANY,
											 BitmapF | BitmapD | BitmapI,
											 NeedHandle ? &PME : NULL,
											 &FDParm)) != AFP_ERR_NONE)
		{
			PME.pme_Handle.fsh_FileHandle = NULL;
			break;
		}

		pSda->sda_ReplySize = SIZE_RESPPKT +
					  EVENALIGN(AfpGetFileDirParmsReplyLength(&FDParm,
									IsDir(&FDParm) ? BitmapD : BitmapF));
	
		if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE)
		{
			AfpPackFileDirParms(&FDParm,
								IsDir(&FDParm) ? BitmapD : BitmapF,
								pSda->sda_ReplyBuf + SIZE_RESPPKT);
			PUTDWORD2SHORT(&pRspPkt->__FileBitmap, BitmapF);
			PUTDWORD2SHORT(&pRspPkt->__DirBitmap, BitmapD);
			pRspPkt->__FileDirFlag = IsDir(&FDParm) ?
										FILEDIR_FLAG_DIR : FILEDIR_FLAG_FILE;
			pRspPkt->__Pad = 0;
		}
	} while (False);
	
	// Return before we close thus saving some time
	AfpCompleteApiProcessing(pSda, Status);

	if (NeedHandle && (PME.pme_Handle.fsh_FileHandle != NULL))
		AfpIoClose(&PME.pme_Handle);	// Close the handle to the entity

	return AFP_ERR_EXTENDED;
}



/***	AfpFspDispSetFileDirParms
 *
 *	This is the worker routine for the AfpSetFileDirParms API.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_ReqBlock	DWORD		File or Directory Bitmap
 *	sda_Name1		ANSI_STRING	Path
 *	sda_Name2		BLOCK		File or Directory parameters
 */
AFPSTATUS FASTCALL
AfpFspDispSetFileDirParms(
	IN	PSDA	pSda
)
{
	PATHMAPENTITY	PME;
	PVOLDESC		pVolDesc;
	FILEDIRPARM		FDParm;
	DWORD			Bitmap, BitmapI;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	WCHAR			PathBuf[BIG_PATH_LEN];
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
		DWORD		_Bitmap;
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
			("AfpFspDispSetFileDirParms: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

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

	// 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
	BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_RW_ATTR |
			  FD_BITMAP_LONGNAME |
			  FD_INTERNAL_BITMAP_RETURN_PMEPATHS;

	// For a directory only the owner can change certain attributes like the
	// various inhibit bits. Check for access if an attempt is made to modify
	// any of these bits. We do not know at this point whether any of these
	// attributes are being set/cleared yet !!!
	if (Bitmap & FD_BITMAP_ATTR)
		BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_READCTRL|
				  FD_BITMAP_LONGNAME 					|
				  DIR_BITMAP_OWNERID 					|
				  DIR_BITMAP_GROUPID 					|
				  DIR_BITMAP_ACCESSRIGHTS				|
				  FD_INTERNAL_BITMAP_RETURN_PMEPATHS;

	AfpInitializeFDParms(&FDParm);
	AfpInitializePME(&PME, sizeof(PathBuf), PathBuf);

	do
	{
		Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc,
										pReqPkt->_ParentId,
										&pSda->sda_Name1,
										pSda->sda_PathType,
										DFE_ANY,
										Bitmap | BitmapI,
										&PME,
										&FDParm);

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

		if ((Status = AfpUnpackFileDirParms(pSda->sda_Name2.Buffer,
										   (LONG)pSda->sda_Name2.Length,
										   &Bitmap,
										   &FDParm)) != AFP_ERR_NONE)
			break;

		if (Bitmap != 0)
		{
			// Make sure they are not trying to set/clear any attributes
			// that are not common to both files and directories
			if ((Bitmap & FD_BITMAP_ATTR) &&
				(FDParm._fdp_Attr & ~(FD_BITMAP_ATTR_SET		|
									  FD_BITMAP_ATTR_INVISIBLE	|
									  FD_BITMAP_ATTR_DELETEINH	|
									  FILE_BITMAP_ATTR_WRITEINH	|
									  FD_BITMAP_ATTR_RENAMEINH	|
									  FD_BITMAP_ATTR_SYSTEM)))
			{
				Status = AFP_ERR_PARAM;
				break;
			}
			Status = 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) &&
		(PME.pme_FullPath.Buffer != PathBuf))
	{
		AfpFreeMemory(PME.pme_FullPath.Buffer);
	}

	return AFP_ERR_EXTENDED;
}



/***	AfpFspDispDelete
 *
 *	This is the worker routine for the AfpDelete API.  Deleting an open file
 *  or a directory that is not empty is not permitted under AFP.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_Name1		ANSI_STRING	Path
 */
AFPSTATUS FASTCALL
AfpFspDispDelete(
	IN	PSDA	pSda
)
{
	PATHMAPENTITY	PME;
	PVOLDESC		pVolDesc;
	FILEDIRPARM		FDParm;
	DWORD			Bitmap, NTAttr;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	FILESYSHANDLE	hParent;
	WCHAR			PathBuf[BIG_PATH_LEN];
	BOOLEAN			InRoot;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
										("AfpFspDispDelete: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	AfpInitializeFDParms(&FDParm);
	AfpInitializePME(&PME, sizeof(PathBuf), PathBuf);
	Bitmap = FD_BITMAP_ATTR | FD_INTERNAL_BITMAP_OPENACCESS_DELETE;

	AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);

	do
	{
		hParent.fsh_FileHandle = NULL;
		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_ParentId,
											   &pSda->sda_Name1,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_ANY,
											   Bitmap,
											   &PME,
											   &FDParm)))
		{
			PME.pme_Handle.fsh_FileHandle = NULL;
			break;
		}

		if ((FDParm._fdp_AfpId == AFP_ID_ROOT) ||
			(FDParm._fdp_AfpId == AFP_ID_NETWORK_TRASH))
		{
			Status = AFP_ERR_ACCESS_DENIED;
			break;
		}

		if (FDParm._fdp_Attr & (FILE_BITMAP_ATTR_DATAOPEN | FILE_BITMAP_ATTR_RESCOPEN))
		{
			ASSERT(!(FDParm._fdp_Flags & DFE_FLAGS_DIR));
			Status = AFP_ERR_FILE_BUSY;	// Cannot delete an open file
			break;
		}

		if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PME.pme_Handle,
													FD_BITMAP_ATTR_DELETEINH,
													FDParm._fdp_Attr,
													&NTAttr)))
		{
			break;
		}

		// Check for SeeFiles or SeeFolders on the parent dir
		if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc,
														   FDParm._fdp_ParentId,
														   &PME.pme_ParentPath,
														   (FDParm._fdp_Flags & DFE_FLAGS_DIR) ?
														   		DIR_ACCESS_SEARCH : DIR_ACCESS_READ,
														   &hParent,
														   NULL)))
		{
			break;
		}

		if (NTAttr & FILE_ATTRIBUTE_READONLY)
		{
			// We must remove the ReadOnly attribute to delete the file/dir
			Status = AfpIoSetTimesnAttr(&PME.pme_Handle,
										NULL,
										NULL,
										0,
										FILE_ATTRIBUTE_READONLY,
										pVolDesc,
										&PME.pme_FullPath);
		}

		if (NT_SUCCESS(Status))
		{
			InRoot = (PME.pme_ParentPath.Length == 0) ? True : False;
			Status = AfpIoMarkFileForDelete(&PME.pme_Handle,
											pVolDesc,
											&PME.pme_FullPath,
											InRoot ? NULL : &PME.pme_ParentPath);

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

			// !!! HACK ALERT !!!
			// At this point we are pretty much done i.e. the delete has either
			// succeeded or failed 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, Status);

			if (NT_SUCCESS(Status)) // Delete succeeded
			{
				ASSERT(VALID_DFE(PME.pme_pDfEntry));
				ASSERT(PME.pme_pDfEntry->dfe_AfpId == FDParm._fdp_AfpId);
				AfpDeleteDfEntry(pVolDesc, PME.pme_pDfEntry);
				AfpIoClose(&PME.pme_Handle);
				AfpCacheParentModTime(pVolDesc,
									  &hParent,
									  NULL,
									  NULL,
									  FDParm._fdp_ParentId);
			}
			else if (NTAttr & FILE_ATTRIBUTE_READONLY) // Delete failed
			{
				// Set the ReadOnly attribute back on the file/dir if need be
				Status = AfpIoSetTimesnAttr(&PME.pme_Handle,
											NULL,
											NULL,
											FILE_ATTRIBUTE_READONLY,
											0,
											pVolDesc,
											&PME.pme_FullPath);
				ASSERT(NT_SUCCESS(Status));
			}
			Status = AFP_ERR_EXTENDED;
		}
		ASSERT (Status == AFP_ERR_EXTENDED);
		AfpVolumeDereference(pVolDesc);
	} while (False);

	// Close file handle so file really gets deleted before mac can come
	// back in with another request using same filename (like create)
	if (PME.pme_Handle.fsh_FileHandle != NULL)
		AfpIoClose(&PME.pme_Handle);
	
	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;
}



/***	AfpFspDispRename
 *
 *	This is the worker routine for the AfpRename API.  Renaming a file does
 *  NOT provoke a new Extension-Type/Creator mapping.  Renaming an open file
 *  is permitted under AFP.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		ParentId
 *	sda_Name1		ANSI_STRING	Path
 *	sda_Name2		ANSI_STRING	New name
 */
AFPSTATUS FASTCALL
AfpFspDispRename(
	IN	PSDA	pSda
)
{
	PATHMAPENTITY	PME;
	PVOLDESC		pVolDesc;
	FILEDIRPARM		FDParm;
	DWORD			Bitmap, NTAttr;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	UNICODE_STRING	uNewName;
	WCHAR			wcbuf[AFP_FILENAME_LEN+1];
	WCHAR			PathBuf[BIG_PATH_LEN];
	PDFENTRY		pDfEntry;
	FILESYSHANDLE 	hParent;
	BOOLEAN			InRoot;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_ParentId;
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
			("AfpFspDispRename: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	AfpInitializeFDParms(&FDParm);
	AfpInitializePME(&PME, sizeof(PathBuf), PathBuf);
	AfpSetEmptyUnicodeString(&uNewName, sizeof(wcbuf), wcbuf);

	Bitmap = FD_BITMAP_ATTR | FD_INTERNAL_BITMAP_OPENACCESS_DELETE;
	hParent.fsh_FileHandle = NULL;
		
	AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);

	do
	{
		// Make sure the new name is not a null string or too long
		if ((pSda->sda_Name2.Length == 0) ||
			(pSda->sda_Name2.Length > AFP_FILENAME_LEN) ||
			((pSda->sda_PathType == AFP_SHORTNAME) &&
			 !AfpIsLegalShortname(&pSda->sda_Name2)) ||
			(!NT_SUCCESS(AfpConvertStringToMungedUnicode(&pSda->sda_Name2,
														 &uNewName))))
			break;

		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_ParentId,
											   &pSda->sda_Name1,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_ANY,
											   Bitmap,
											   &PME,
											   &FDParm)))
		{
			PME.pme_Handle.fsh_FileHandle = NULL;
			break;
		}

		if ((FDParm._fdp_AfpId == AFP_ID_ROOT) ||
			(FDParm._fdp_AfpId == AFP_ID_NETWORK_TRASH))
		{
			Status = AFP_ERR_CANT_RENAME;
			break;
		}

		// Check if the RO bit is on & retain the mod time
		if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PME.pme_Handle,
													FD_BITMAP_ATTR_RENAMEINH,
													FDParm._fdp_Attr,
													&NTAttr)))
		{
			break;
		}

		// Check for SeeFiles or SeeFolders on the parent dir
		if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc,
														   FDParm._fdp_ParentId,
														   &PME.pme_ParentPath,
														   (FDParm._fdp_Flags & DFE_FLAGS_DIR) ?
														   DIR_ACCESS_SEARCH : DIR_ACCESS_READ,
														   &hParent,
														   NULL)))
		{
			break;
		}

		if (NTAttr & FILE_ATTRIBUTE_READONLY)
		{
			// We must temporarily remove the ReadOnly attribute so that
			// we can rename the file/dir
			Status = AfpIoSetTimesnAttr(&PME.pme_Handle,
										NULL,
										NULL,
										0,
										FILE_ATTRIBUTE_READONLY,
										pVolDesc,
										&PME.pme_FullPath);
		}

		if (NT_SUCCESS(Status))
		{
			// We must impersonate to do the rename since it is name based
			AfpImpersonateClient(pSda);

			InRoot = (PME.pme_ParentPath.Length == 0) ? True : False;
			Status = AfpIoMoveAndOrRename(&PME.pme_Handle,
										  NULL,
										  &uNewName,
										  pVolDesc,
										  &PME.pme_FullPath,
										  InRoot ? NULL : &PME.pme_ParentPath,
										  NULL,
										  NULL);

			AfpRevertBack();

			if (NT_SUCCESS(Status))	// Rename succeeded
			{
				if ((pDfEntry = AfpFindDfEntryById(pVolDesc,
												   FDParm._fdp_AfpId,
												   DFE_ANY)) != NULL)
				{
					ASSERT(((pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS) &
							FDParm._fdp_Flags) != 0);
					pDfEntry = AfpRenameDfEntry(pVolDesc,
												pDfEntry,
												&uNewName);
					if (pDfEntry == NULL)
					{
						// We could not rename the id entry, so
						// just delete it, and hope the parent dir
						// gets enumerated again
						// NOTE: How will the parent directory
						//	 get re-enumerated now ?
						ASSERT(VALID_DFE(PME.pme_pDfEntry));
						ASSERT(PME.pme_pDfEntry->dfe_AfpId == FDParm._fdp_AfpId);
						AfpDeleteDfEntry(pVolDesc, PME.pme_pDfEntry);
						Status = AFP_ERR_MISC;	// Out of memory
					}
					else
					{
						AfpCacheParentModTime(pVolDesc,
											  &hParent,
											  NULL,
											  pDfEntry->dfe_Parent,
											  0);
					}
				}
			}
		}
		else
		{
			Status = AFP_ERR_MISC;	// Could not delete ReadOnly attribute
			break;
		}

		// Set the ReadOnly attribute back on the file/dir if need be.
		if (NTAttr & FILE_ATTRIBUTE_READONLY)
			AfpIoSetTimesnAttr(&PME.pme_Handle,
								NULL,
								NULL,
								FILE_ATTRIBUTE_READONLY,
								0,
								pVolDesc,
								&PME.pme_FullPath);
	} 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 (hParent.fsh_FileHandle != NULL)
	{
		AfpIoClose(&hParent);
	}

	AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);

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

	return AFP_ERR_EXTENDED;
}



/***	AfpFspDispMoveAndRename
 *
 *	This is the worker routine for the AfpMoveAndRename API.  Note that
 *  in AFP 2.x, a FILE (not a dir) CAN BE MOVED when its RenameInhibit bit
 *  is set if it is NOT BEING RENAMED.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		Source ParentId
 *	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
AfpFspDispMoveAndRename(
	IN	PSDA	pSda
)
{
	PATHMAPENTITY	PMEsrc, PMEdst;
	PVOLDESC		pVolDesc;
	FILEDIRPARM		FDParmsrc, FDParmdst;
	DWORD			Bitmap, NTAttr;
	AFPSTATUS		Status = AFP_ERR_PARAM;
	UNICODE_STRING	uNewName;
	WCHAR			wcbuf[AFP_FILENAME_LEN+1];
	BOOLEAN			Rename = True, Move = True, SrcInRoot, DstInRoot;
	PDFENTRY		pDfesrc, pDfedst, pDfeParentsrc;
	FILESYSHANDLE	hSrcParent;
	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_SrcParentId;
		DWORD		_DstParentId;
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
										("AfpFspDispMoveAndRename: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc;

	ASSERT(VALID_VOLDESC(pVolDesc));
	
	AfpInitializeFDParms(&FDParmsrc);
	AfpInitializeFDParms(&FDParmdst);
	AfpInitializePME(&PMEsrc, 0, NULL);
	AfpInitializePME(&PMEdst, 0, NULL);

	Bitmap = FD_BITMAP_ATTR | FD_BITMAP_LONGNAME | FD_INTERNAL_BITMAP_OPENACCESS_DELETE;
	AfpSetEmptyUnicodeString(&uNewName, sizeof(wcbuf), wcbuf);
	hSrcParent.fsh_FileHandle = NULL;

	AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock);

	do
	{
		// Make sure the new name is not too long
		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;

		// Map source path for lookup (could be file or dir).
		// We ask for the finderinfo in case the user is moving an
		// application file to another directory, we can update its
		// parent dirid in the APPL desktop database
		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_SrcParentId,
											   &pSda->sda_Name1,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_ANY,
											   Bitmap | FD_BITMAP_FINDERINFO,
											   &PMEsrc,
											   &FDParmsrc)))
		{
			PMEsrc.pme_Handle.fsh_FileHandle = NULL;
			break;
		}

		// Map the destination parent directory path for lookup
		if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc,
											   pReqPkt->_DstParentId,
											   &pSda->sda_Name2,
											   pSda->sda_PathType,
											   Lookup,
											   DFE_DIR,
											   0,
											   &PMEdst,
											   &FDParmdst)))
		{
			PMEdst.pme_Handle.fsh_FileHandle = NULL;
			break;
		}

		if ((FDParmsrc._fdp_AfpId == AFP_ID_ROOT) ||
			(FDParmsrc._fdp_AfpId == AFP_ID_NETWORK_TRASH))
		{
			Status = AFP_ERR_CANT_MOVE;
			break;
		}

		if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PMEsrc.pme_Handle,
													FD_BITMAP_ATTR_RENAMEINH,
													FDParmsrc._fdp_Attr,
													&NTAttr)))
		{
			// Files (not dirs) marked RenameInhibit that are NOT being
			// renamed are allowed to be moved in AFP 2.x
			if (!((Status == AFP_ERR_OBJECT_LOCKED) &&
				 (!IsDir(&FDParmsrc)) &&
				 (pSda->sda_Name3.Length == 0)))
			{
				break;
			}
		}

		if (FDParmsrc._fdp_ParentId == FDParmdst._fdp_AfpId)
		{
			// 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;

			//
			// Trying to move a file onto itself.  Just return success.
			// (some apps are braindead and actually move files onto
			// themselves for who knows what reason)
			//
			if ((pSda->sda_Name3.Length == 0) ||
				 RtlEqualString(&pSda->sda_Name3,
								&FDParmsrc._fdp_LongName,
								False))
			{
				Status = AFP_ERR_NONE;
				break;
			}

		}

		// Check for SeeFiles or SeeFolders on the source parent dir
		if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc,
														   FDParmsrc._fdp_ParentId,
														   &PMEsrc.pme_ParentPath,
														   (FDParmsrc._fdp_Flags & DFE_FLAGS_DIR) ?
																DIR_ACCESS_SEARCH : DIR_ACCESS_READ,
														   &hSrcParent,
														   NULL)))
		{
			break;
		}

		if (NTAttr & FILE_ATTRIBUTE_READONLY)
		{
			// We must temporarily remove the ReadOnly attribute so that
			// we can move the file/dir
			Status = AfpIoSetTimesnAttr(&PMEsrc.pme_Handle,
										NULL,
										NULL,
										0,
										FILE_ATTRIBUTE_READONLY,
										pVolDesc,
										&PMEsrc.pme_FullPath);
		}

		if (NT_SUCCESS(Status))
		{
			// If no new name was supplied, we need to use the
			// current name
			if (pSda->sda_Name3.Length == 0)
			{
				Rename = False;
				uNewName = PMEsrc.pme_UTail;
			}
			
			// We must impersonate to do the move since it is name based
			AfpImpersonateClient(pSda);

            if (Move)
			{
				// if we are moving, we will also get an ADDED notification
				// for the destination directory.  Since we have the path
				// of the parent dir, but we really want the name of the
				// thing we are about to move and/or rename, munge the
				// destination paths to reflect the new name of the thing
				// we are moving/renaming

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

			SrcInRoot = (PMEsrc.pme_ParentPath.Length == 0) ? True : False;
			DstInRoot = (PMEdst.pme_ParentPath.Length == 0) ? True : False;
			Status = AfpIoMoveAndOrRename(&PMEsrc.pme_Handle,
										  Move ? &PMEdst.pme_Handle : NULL,
										  &uNewName,
										  pVolDesc,
										  &PMEsrc.pme_FullPath,
										  SrcInRoot ? NULL : &PMEsrc.pme_ParentPath,
										  Move ? &PMEdst.pme_FullPath : NULL,
										  (Move && !DstInRoot) ? &PMEdst.pme_ParentPath : NULL);
			AfpRevertBack();

			if (NT_SUCCESS(Status))	// Move succeeded
			{
				if (((pDfesrc = AfpFindDfEntryById(pVolDesc,
												   FDParmsrc._fdp_AfpId,
												   DFE_ANY)) != NULL) &&
					((pDfedst = AfpFindDfEntryById(pVolDesc,
												   FDParmdst._fdp_AfpId,
												   DFE_DIR)) != NULL))

				{
					ASSERT(((pDfesrc->dfe_Flags & DFE_FLAGS_DFBITS) &
							FDParmsrc._fdp_Flags) != 0);
					pDfeParentsrc = pDfesrc->dfe_Parent;
					pDfesrc = AfpMoveDfEntry(pVolDesc,
											 pDfesrc,
											 pDfedst,
											 Rename ? &uNewName : NULL);
					if (pDfesrc == NULL)
					{
						// We could not move the id entry, so
						// just delete it.
						ASSERT(VALID_DFE(PMEsrc.pme_pDfEntry));
						ASSERT(PMEsrc.pme_pDfEntry->dfe_AfpId == FDParmsrc._fdp_AfpId);
						AfpDeleteDfEntry(pVolDesc, PMEsrc.pme_pDfEntry);
						Status = AFP_ERR_MISC;	// Out of memory
					}

					// update cached mod time of source parent directory
					AfpCacheParentModTime(pVolDesc,
										  &hSrcParent,
										  NULL,
										  pDfeParentsrc,
										  0);
					if (Move)
					{
						// update cached mod time of destination directory
						AfpCacheParentModTime(pVolDesc,
											  &PMEdst.pme_Handle,
											  NULL,
											  pDfedst,
											  0);
						//
						// if we just moved an application program, update
						// the parentID in the corresponding APPL mapping.
						//
						if ((!IsDir(&FDParmsrc)) &&
							(FDParmsrc._fdp_FinderInfo.fd_TypeD == *(PDWORD)"APPL"))
						{
							AfpAddAppl(pVolDesc,
									   FDParmsrc._fdp_FinderInfo.fd_CreatorD,
									   0,
									   FDParmsrc._fdp_AfpId,
									   True,
									   FDParmdst._fdp_AfpId);
						}
					}
				}
			}
		}
		else
		{
			Status = AFP_ERR_MISC;	// Could not delete ReadOnly attribute
			break;
		}

		// Set the ReadOnly attribute back on the file/dir if need be
		if (NTAttr & FILE_ATTRIBUTE_READONLY)
			AfpIoSetTimesnAttr(&PMEsrc.pme_Handle,
								NULL,
								NULL,
								FILE_ATTRIBUTE_READONLY,
								0,
								pVolDesc,
								&PMEsrc.pme_FullPath);
	} while (False);

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

	AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock);

	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 (PMEsrc.pme_FullPath.Buffer != NULL)
		AfpFreeMemory(PMEsrc.pme_FullPath.Buffer);

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

	return AFP_ERR_EXTENDED;
}


/***	AfpFspDispCatSearch
 *
 *	This is the worker routine for the AfpCatSearch API.
 *
 *	The request packet is represented below
 *
 *	sda_ReqBlock	PCONNDESC	pConnDesc
 *	sda_ReqBlock	DWORD		RequestedMatches
 *	sda_Name1		ANSI_STRING	Catalog Position - 16 bytes
 *	sda_Name2		ANSI_STRING	Everything else - needs unmarshalling
 *
 *	The reason we could not unmarshall all the parameters is because this
 *  API's parameters do not conform to the common way all the other APIs'
 *  parameters do, and therefore we cannot use the common code and table
 *  structures in afpapi.c.
 */
AFPSTATUS FASTCALL
AfpFspDispCatSearch(
	IN	PSDA	pSda
)
{
	AFPSTATUS		Status = AFP_ERR_PARAM;
	PBYTE			pEndOfBuffer;
	USHORT			Flags;
	SHORT			SizeLeft = 0;
	PVOLDESC		pVolDesc;
	DWORD			FileResultBitmap;
	DWORD			DirResultBitmap;
	DWORD			RequestBitmap;
	DWORD			Count;
	BOOLEAN			fPartialName = False, FreeReplyBuf = False;
    FILEDIRPARM		FDPLowerAndValue, FDPUpperAndMask;
	PCATSEARCHSPEC	pSpec1, pSpec2;
	UNICODE_STRING	MatchString;
	WCHAR			strbuf[AFP_LONGNAME_LEN+1];

	struct _RequestPacket
	{
		PCONNDESC	_pConnDesc;
		DWORD		_RequestedMatches;
	};

	// The part of the request buffer that could not be unmarshalled into
	// fields in the Sda because they don't conform to any of the other APIs.
	// These will be unmarshalled here into local variables, the sda_Name2
	// can be cast to this structure for easy access.
	struct _RestOfRawRequest
	{
		USHORT	_FileResultBitmap;
		USHORT	_DirResultBitmap;
		BYTE	_fPartialName;
		BYTE	_Pad1;
		USHORT	_RequestBitmap;
		// Spec1 and Spec2 follow
	};

#define pRawPkt	((struct _RestOfRawRequest *)(pSda->sda_Name2.Buffer))

	struct _ResponsePacket
	{
		 BYTE		__CatPosition[16];
		 BYTE		__FileBitmap[2];
		 BYTE		__DirBitmap[2];
		 BYTE		__ActualCount[4];
	};

	PAGED_CODE();

	DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO,
										("AfpFspDispCatSearch: Entered\n"));

	ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc));

	do
	{
		if (pSda->sda_Name2.Length < (sizeof(struct _RestOfRawRequest) +
									  (2 * sizeof(CATSEARCHSPEC))))
		{
			// The request buffer must be at least as big as the rest of the
			// parameters that weren't yet unmarshalled, plus 2 Spec structs
			break;
		}
		
		GETSHORT2DWORD(&FileResultBitmap, &pRawPkt->_FileResultBitmap);
		GETSHORT2DWORD(&DirResultBitmap, &pRawPkt->_DirResultBitmap);
		GETSHORT2DWORD(&RequestBitmap, &pRawPkt->_RequestBitmap);
	
		if ( (pRawPkt->_fPartialName & 0x80) != 0 )
		{
			fPartialName = True;
		}

		//
		// Validate the bitmaps
		//
		if (((FileResultBitmap | DirResultBitmap) == 0) ||
			((FileResultBitmap | DirResultBitmap) & ~FD_VALID_SEARCH_RESULT) ||
			(RequestBitmap == 0))
		{
			Status = AFP_ERR_BITMAP;
			break;
		}

		AfpInitializeFDParms(&FDPLowerAndValue);
        AfpInitializeFDParms(&FDPUpperAndMask);

		if (DirResultBitmap == 0)
		{
			FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_FILE_WITH_ID;
			if (RequestBitmap & ~FILE_VALID_SEARCH_CRITERIA)
			{
				Status = AFP_ERR_BITMAP;
				break;
			}
		}
		else if (FileResultBitmap == 0)
		{
			FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_DIR;
			if (RequestBitmap & ~DIR_VALID_SEARCH_CRITERIA)
			{
				Status = AFP_ERR_BITMAP;
				break;
			}
		}
		else
		{
			FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_FILE_WITH_ID | DFE_FLAGS_DIR;
			if (RequestBitmap & ~FD_VALID_SEARCH_CRITERIA)
			{
				Status = AFP_ERR_BITMAP;
				break;
			}
		}

		Flags = ((PCATALOGPOSITION)pSda->sda_Name1.Buffer)->cp_Flags;
		// The caller should not muck with the catalog position at all
		if ((Flags & ~CATFLAGS_VALID) ||
			// Writelock should only be required if we were about to search files
			((Flags & CATFLAGS_WRITELOCK_REQUIRED) && !(Flags & CATFLAGS_SEARCHING_FILES)))
			// NOTE: also should make sure ONLY ONE of the SEARCHING bits is on
			break;

		//
		// Now unpack the search criteria
		//
		MatchString.Length = 0;
		MatchString.MaximumLength = sizeof(strbuf);
		MatchString.Buffer = strbuf;

        Status = AfpUnpackCatSearchSpecs((PBYTE)pSda->sda_Name2.Buffer + sizeof(struct _RestOfRawRequest),
										 (USHORT)(pSda->sda_Name2.Length - sizeof(struct _RestOfRawRequest)),
										 RequestBitmap,
										 &FDPLowerAndValue,
										 &FDPUpperAndMask,
										 &MatchString);
		if (!NT_SUCCESS(Status))
		{
			break;
		}

		//
		// Allocate the reply buffer.  Estimate the required size by using
		// the maximum possible filename length plus potential pad bytes for
		// even alignment of each entry plus the length of the parent dirid.
		//
		pSda->sda_ReplySize = SIZE_RESPPKT + (pReqPkt->_RequestedMatches *
				((2 * sizeof(BYTE)) + sizeof(DWORD) + sizeof(USHORT) + sizeof(BYTE) + AFP_LONGNAME_LEN + 1));

		if (pSda->sda_ReplySize > MAX_CATSEARCH_REPLY)
		{
			pSda->sda_ReplySize = MAX_CATSEARCH_REPLY;
		}
		if ((pSda->sda_ReplyBuf = AfpIOAllocBuffer(pSda->sda_ReplySize)) == NULL)
		{
			pSda->sda_ReplySize = 0;
			Status = AFP_ERR_MISC;
			break;
		}

		FreeReplyBuf = True;

		//
		// Perform the search
		//

		FDPUpperAndMask._fdp_fPartialName = fPartialName;

		Count = pReqPkt->_RequestedMatches;
		Status = AfpCatSearch(pReqPkt->_pConnDesc,
						      (PCATALOGPOSITION)pSda->sda_Name1.Buffer, // CatalogPosition
						      RequestBitmap,
							  FileResultBitmap,
							  DirResultBitmap,
							  &FDPLowerAndValue,
							  &FDPUpperAndMask,
							  &MatchString,
							  &Count,					// IN OUT
							  (SHORT)(pSda->sda_ReplySize - SIZE_RESPPKT),
							  &SizeLeft,
							  pSda->sda_ReplyBuf + SIZE_RESPPKT,
							  (PCATALOGPOSITION)pSda->sda_ReplyBuf);

         if (!NT_SUCCESS(Status) && ((Status != AFP_ERR_EOF) &&
									 (Status != AFP_ERR_CATALOG_CHANGED)))
		 {
			 break;
		 }

		 PUTSHORT2SHORT(&pRspPkt->__FileBitmap, FileResultBitmap);
		 PUTSHORT2SHORT(&pRspPkt->__DirBitmap, DirResultBitmap);
		 PUTDWORD2DWORD(&pRspPkt->__ActualCount, Count);
		 pSda->sda_ReplySize -= SizeLeft;
		 ASSERT(pSda->sda_ReplySize <= MAX_CATSEARCH_REPLY);
		 FreeReplyBuf = False;



	} while (False);

	if (FreeReplyBuf)
	{
		AfpFreeReplyBuf(pSda);
	}

	return Status;
}