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