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
42 KiB
1553 lines
42 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;
|
|
}
|
|
|
|
|