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