Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1553 lines
40 KiB

/*
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;
}