/* Copyright (c) 1992 Microsoft Corporation Module Name: fsp_fd.c Abstract: This module contains the entry points for the AFP file-dir APIs queued to the FSP. These are all callable from FSP Only. Author: Jameel Hyder (microsoft!jameelh) Revision History: 25 Apr 1992 Initial Version Notes: Tab stop: 4 --*/ #define FILENUM FILE_FSP_FD #include #include #include #include #include #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, AfpFspDispGetFileDirParms) #pragma alloc_text( PAGE, AfpFspDispSetFileDirParms) #pragma alloc_text( PAGE, AfpFspDispDelete) #pragma alloc_text( PAGE, AfpFspDispRename) #pragma alloc_text( PAGE, AfpFspDispMoveAndRename) #pragma alloc_text( PAGE, AfpFspDispCatSearch) #endif /*** AfpFspDispGetFileDirParms * * This is the worker routine for the AfpGetFileDirParms API. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD ParentId * sda_ReqBlock DWORD File Bitmap * sda_ReqBlock DWORD Dir Bitmap * sda_Name1 ANSI_STRING Path */ AFPSTATUS FASTCALL AfpFspDispGetFileDirParms( IN PSDA pSda ) { FILEDIRPARM FDParm; PATHMAPENTITY PME; BOOLEAN NeedHandle = False; PVOLDESC pVolDesc; DWORD BitmapF, BitmapD, BitmapI = 0; AFPSTATUS Status = AFP_ERR_PARAM; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _ParentId; DWORD _FileBitmap; DWORD _DirBitmap; }; struct _ResponsePacket { BYTE __FileBitmap[2]; BYTE __DirBitmap[2]; BYTE __FileDirFlag; BYTE __Pad; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispGetFileDirParms: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc; ASSERT(VALID_VOLDESC(pVolDesc)); BitmapF = pReqPkt->_FileBitmap; BitmapD = pReqPkt->_DirBitmap; do { AfpInitializeFDParms(&FDParm); AfpInitializePME(&PME, 0, NULL); if (IS_VOLUME_NTFS(pVolDesc) && (BitmapD & (DIR_BITMAP_ACCESSRIGHTS | DIR_BITMAP_OWNERID | DIR_BITMAP_GROUPID))) { NeedHandle = True; } if (BitmapD & DIR_BITMAP_ACCESSRIGHTS) { BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_READCTRL; } if ((Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc, pReqPkt->_ParentId, &pSda->sda_Name1, pSda->sda_PathType, DFE_ANY, BitmapF | BitmapD | BitmapI, NeedHandle ? &PME : NULL, &FDParm)) != AFP_ERR_NONE) { PME.pme_Handle.fsh_FileHandle = NULL; break; } pSda->sda_ReplySize = SIZE_RESPPKT + EVENALIGN(AfpGetFileDirParmsReplyLength(&FDParm, IsDir(&FDParm) ? BitmapD : BitmapF)); if ((Status = AfpAllocReplyBuf(pSda)) == AFP_ERR_NONE) { AfpPackFileDirParms(&FDParm, IsDir(&FDParm) ? BitmapD : BitmapF, pSda->sda_ReplyBuf + SIZE_RESPPKT); PUTDWORD2SHORT(&pRspPkt->__FileBitmap, BitmapF); PUTDWORD2SHORT(&pRspPkt->__DirBitmap, BitmapD); pRspPkt->__FileDirFlag = IsDir(&FDParm) ? FILEDIR_FLAG_DIR : FILEDIR_FLAG_FILE; pRspPkt->__Pad = 0; } } while (False); // Return before we close thus saving some time AfpCompleteApiProcessing(pSda, Status); if (NeedHandle && (PME.pme_Handle.fsh_FileHandle != NULL)) AfpIoClose(&PME.pme_Handle); // Close the handle to the entity return AFP_ERR_EXTENDED; } /*** AfpFspDispSetFileDirParms * * This is the worker routine for the AfpSetFileDirParms API. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD ParentId * sda_ReqBlock DWORD File or Directory Bitmap * sda_Name1 ANSI_STRING Path * sda_Name2 BLOCK File or Directory parameters */ AFPSTATUS FASTCALL AfpFspDispSetFileDirParms( IN PSDA pSda ) { PATHMAPENTITY PME; PVOLDESC pVolDesc; FILEDIRPARM FDParm; DWORD Bitmap, BitmapI; AFPSTATUS Status = AFP_ERR_PARAM; WCHAR PathBuf[BIG_PATH_LEN]; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _ParentId; DWORD _Bitmap; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispSetFileDirParms: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc; ASSERT(VALID_VOLDESC(pVolDesc)); Bitmap = pReqPkt->_Bitmap; // Force the FD_BITMAP_LONGNAME in case a *file* is missing the afpinfo // stream we will be able to generate the correct type/creator in // AfpSetAfpInfo BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_RW_ATTR | FD_BITMAP_LONGNAME | FD_INTERNAL_BITMAP_RETURN_PMEPATHS; // For a directory only the owner can change certain attributes like the // various inhibit bits. Check for access if an attempt is made to modify // any of these bits. We do not know at this point whether any of these // attributes are being set/cleared yet !!! if (Bitmap & FD_BITMAP_ATTR) BitmapI = FD_INTERNAL_BITMAP_OPENACCESS_READCTRL| FD_BITMAP_LONGNAME | DIR_BITMAP_OWNERID | DIR_BITMAP_GROUPID | DIR_BITMAP_ACCESSRIGHTS | FD_INTERNAL_BITMAP_RETURN_PMEPATHS; AfpInitializeFDParms(&FDParm); AfpInitializePME(&PME, sizeof(PathBuf), PathBuf); do { Status = AfpMapAfpPathForLookup(pReqPkt->_pConnDesc, pReqPkt->_ParentId, &pSda->sda_Name1, pSda->sda_PathType, DFE_ANY, Bitmap | BitmapI, &PME, &FDParm); if (!NT_SUCCESS(Status)) { PME.pme_Handle.fsh_FileHandle = NULL; break; } if ((Status = AfpUnpackFileDirParms(pSda->sda_Name2.Buffer, (LONG)pSda->sda_Name2.Length, &Bitmap, &FDParm)) != AFP_ERR_NONE) break; if (Bitmap != 0) { // Make sure they are not trying to set/clear any attributes // that are not common to both files and directories if ((Bitmap & FD_BITMAP_ATTR) && (FDParm._fdp_Attr & ~(FD_BITMAP_ATTR_SET | FD_BITMAP_ATTR_INVISIBLE | FD_BITMAP_ATTR_DELETEINH | FILE_BITMAP_ATTR_WRITEINH | FD_BITMAP_ATTR_RENAMEINH | FD_BITMAP_ATTR_SYSTEM))) { Status = AFP_ERR_PARAM; break; } Status = AfpSetFileDirParms(pVolDesc, &PME, Bitmap, &FDParm); } } while (False); // Return before we close thus saving some time AfpCompleteApiProcessing(pSda, Status); if (PME.pme_Handle.fsh_FileHandle != NULL) AfpIoClose(&PME.pme_Handle); if ((PME.pme_FullPath.Buffer != NULL) && (PME.pme_FullPath.Buffer != PathBuf)) { AfpFreeMemory(PME.pme_FullPath.Buffer); } return AFP_ERR_EXTENDED; } /*** AfpFspDispDelete * * This is the worker routine for the AfpDelete API. Deleting an open file * or a directory that is not empty is not permitted under AFP. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD ParentId * sda_Name1 ANSI_STRING Path */ AFPSTATUS FASTCALL AfpFspDispDelete( IN PSDA pSda ) { PATHMAPENTITY PME; PVOLDESC pVolDesc; PCONNDESC pConnDesc; FILEDIRPARM FDParm; DWORD Bitmap, NTAttr; AFPSTATUS Status = AFP_ERR_PARAM; FILESYSHANDLE hParent; WCHAR PathBuf[BIG_PATH_LEN]; BOOLEAN InRoot; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _ParentId; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispDelete: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); pConnDesc = pReqPkt->_pConnDesc; pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc; ASSERT(VALID_VOLDESC(pVolDesc)); AfpInitializeFDParms(&FDParm); AfpInitializePME(&PME, sizeof(PathBuf), PathBuf); Bitmap = FD_BITMAP_ATTR | FD_INTERNAL_BITMAP_OPENACCESS_DELETE; AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock); do { hParent.fsh_FileHandle = NULL; if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc, pReqPkt->_ParentId, &pSda->sda_Name1, pSda->sda_PathType, Lookup, DFE_ANY, Bitmap, &PME, &FDParm))) { PME.pme_Handle.fsh_FileHandle = NULL; break; } if ((FDParm._fdp_AfpId == AFP_ID_ROOT) || (FDParm._fdp_AfpId == AFP_ID_NETWORK_TRASH)) { Status = AFP_ERR_ACCESS_DENIED; break; } if (FDParm._fdp_Attr & (FILE_BITMAP_ATTR_DATAOPEN | FILE_BITMAP_ATTR_RESCOPEN)) { ASSERT(!(FDParm._fdp_Flags & DFE_FLAGS_DIR)); Status = AFP_ERR_FILE_BUSY; // Cannot delete an open file break; } if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PME.pme_Handle, FD_BITMAP_ATTR_DELETEINH, FDParm._fdp_Attr, &NTAttr))) { break; } // Check for SeeFiles or SeeFolders on the parent dir if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc, FDParm._fdp_ParentId, &PME.pme_ParentPath, (FDParm._fdp_Flags & DFE_FLAGS_DIR) ? DIR_ACCESS_SEARCH : DIR_ACCESS_READ, &hParent, NULL))) { break; } if (NTAttr & FILE_ATTRIBUTE_READONLY) { // We must remove the ReadOnly attribute to delete the file/dir Status = AfpIoSetTimesnAttr(&PME.pme_Handle, NULL, NULL, 0, FILE_ATTRIBUTE_READONLY, pVolDesc, &PME.pme_FullPath); } if (NT_SUCCESS(Status)) { InRoot = (PME.pme_ParentPath.Length == 0) ? True : False; Status = AfpIoMarkFileForDelete(&PME.pme_Handle, pVolDesc, &PME.pme_FullPath, InRoot ? NULL : &PME.pme_ParentPath); if (!NT_SUCCESS(Status)) { Status = AfpIoConvertNTStatusToAfpStatus(Status); } // !!! HACK ALERT !!! // At this point we are pretty much done i.e. the delete has either // succeeded or failed and we can return doing the rest of the work // post-reply. Any errors from now on SHOULD BE IGNORED. Also NO // REFERENCE SHOULD BE MADE TO the pSda & pConnDesc. Status should // not be changed either. Also reference the Volume for good measure. // It cannot fail !!! AfpVolumeReference(pVolDesc); AfpCompleteApiProcessing(pSda, Status); if (NT_SUCCESS(Status)) // Delete succeeded { ASSERT(VALID_DFE(PME.pme_pDfEntry)); ASSERT(PME.pme_pDfEntry->dfe_AfpId == FDParm._fdp_AfpId); AfpDeleteDfEntry(pVolDesc, PME.pme_pDfEntry); AfpIoClose(&PME.pme_Handle); AfpCacheParentModTime(pVolDesc, &hParent, NULL, NULL, FDParm._fdp_ParentId); } else if (NTAttr & FILE_ATTRIBUTE_READONLY) // Delete failed { // Set the ReadOnly attribute back on the file/dir if need be Status = AfpIoSetTimesnAttr(&PME.pme_Handle, NULL, NULL, FILE_ATTRIBUTE_READONLY, 0, pVolDesc, &PME.pme_FullPath); ASSERT(NT_SUCCESS(Status)); } Status = AFP_ERR_EXTENDED; } ASSERT (Status == AFP_ERR_EXTENDED); AfpVolumeDereference(pVolDesc); } while (False); // Close file handle so file really gets deleted before mac can come // back in with another request using same filename (like create) if (PME.pme_Handle.fsh_FileHandle != NULL) AfpIoClose(&PME.pme_Handle); AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock); if (hParent.fsh_FileHandle != NULL) AfpIoClose(&hParent); if ((PME.pme_FullPath.Buffer != NULL) && (PME.pme_FullPath.Buffer != PathBuf)) { AfpFreeMemory(PME.pme_FullPath.Buffer); } return Status; } /*** AfpFspDispRename * * This is the worker routine for the AfpRename API. Renaming a file does * NOT provoke a new Extension-Type/Creator mapping. Renaming an open file * is permitted under AFP. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD ParentId * sda_Name1 ANSI_STRING Path * sda_Name2 ANSI_STRING New name */ AFPSTATUS FASTCALL AfpFspDispRename( IN PSDA pSda ) { PATHMAPENTITY PME; PVOLDESC pVolDesc; FILEDIRPARM FDParm; DWORD Bitmap, NTAttr; AFPSTATUS Status = AFP_ERR_PARAM; UNICODE_STRING uNewName; WCHAR wcbuf[AFP_FILENAME_LEN+1]; WCHAR PathBuf[BIG_PATH_LEN]; PDFENTRY pDfEntry; FILESYSHANDLE hParent; BOOLEAN InRoot; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _ParentId; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispRename: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc; ASSERT(VALID_VOLDESC(pVolDesc)); AfpInitializeFDParms(&FDParm); AfpInitializePME(&PME, sizeof(PathBuf), PathBuf); AfpSetEmptyUnicodeString(&uNewName, sizeof(wcbuf), wcbuf); Bitmap = FD_BITMAP_ATTR | FD_INTERNAL_BITMAP_OPENACCESS_DELETE; hParent.fsh_FileHandle = NULL; AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock); do { // Make sure the new name is not a null string or too long if ((pSda->sda_Name2.Length == 0) || (pSda->sda_Name2.Length > AFP_FILENAME_LEN) || ((pSda->sda_PathType == AFP_SHORTNAME) && !AfpIsLegalShortname(&pSda->sda_Name2)) || (!NT_SUCCESS(AfpConvertStringToMungedUnicode(&pSda->sda_Name2, &uNewName)))) break; if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc, pReqPkt->_ParentId, &pSda->sda_Name1, pSda->sda_PathType, Lookup, DFE_ANY, Bitmap, &PME, &FDParm))) { PME.pme_Handle.fsh_FileHandle = NULL; break; } if ((FDParm._fdp_AfpId == AFP_ID_ROOT) || (FDParm._fdp_AfpId == AFP_ID_NETWORK_TRASH)) { Status = AFP_ERR_CANT_RENAME; break; } // Check if the RO bit is on & retain the mod time if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PME.pme_Handle, FD_BITMAP_ATTR_RENAMEINH, FDParm._fdp_Attr, &NTAttr))) { break; } // Check for SeeFiles or SeeFolders on the parent dir if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc, FDParm._fdp_ParentId, &PME.pme_ParentPath, (FDParm._fdp_Flags & DFE_FLAGS_DIR) ? DIR_ACCESS_SEARCH : DIR_ACCESS_READ, &hParent, NULL))) { break; } if (NTAttr & FILE_ATTRIBUTE_READONLY) { // We must temporarily remove the ReadOnly attribute so that // we can rename the file/dir Status = AfpIoSetTimesnAttr(&PME.pme_Handle, NULL, NULL, 0, FILE_ATTRIBUTE_READONLY, pVolDesc, &PME.pme_FullPath); } if (NT_SUCCESS(Status)) { // We must impersonate to do the rename since it is name based AfpImpersonateClient(pSda); InRoot = (PME.pme_ParentPath.Length == 0) ? True : False; Status = AfpIoMoveAndOrRename(&PME.pme_Handle, NULL, &uNewName, pVolDesc, &PME.pme_FullPath, InRoot ? NULL : &PME.pme_ParentPath, NULL, NULL); AfpRevertBack(); if (NT_SUCCESS(Status)) // Rename succeeded { if ((pDfEntry = AfpFindDfEntryById(pVolDesc, FDParm._fdp_AfpId, DFE_ANY)) != NULL) { ASSERT(((pDfEntry->dfe_Flags & DFE_FLAGS_DFBITS) & FDParm._fdp_Flags) != 0); pDfEntry = AfpRenameDfEntry(pVolDesc, pDfEntry, &uNewName); if (pDfEntry == NULL) { // We could not rename the id entry, so // just delete it, and hope the parent dir // gets enumerated again // NOTE: How will the parent directory // get re-enumerated now ? ASSERT(VALID_DFE(PME.pme_pDfEntry)); ASSERT(PME.pme_pDfEntry->dfe_AfpId == FDParm._fdp_AfpId); AfpDeleteDfEntry(pVolDesc, PME.pme_pDfEntry); Status = AFP_ERR_MISC; // Out of memory } else { AfpCacheParentModTime(pVolDesc, &hParent, NULL, pDfEntry->dfe_Parent, 0); } } } } else { Status = AFP_ERR_MISC; // Could not delete ReadOnly attribute break; } // Set the ReadOnly attribute back on the file/dir if need be. if (NTAttr & FILE_ATTRIBUTE_READONLY) AfpIoSetTimesnAttr(&PME.pme_Handle, NULL, NULL, FILE_ATTRIBUTE_READONLY, 0, pVolDesc, &PME.pme_FullPath); } while (False); // Return before we close thus saving some time AfpCompleteApiProcessing(pSda, Status); if (PME.pme_Handle.fsh_FileHandle != NULL) AfpIoClose(&PME.pme_Handle); if (hParent.fsh_FileHandle != NULL) { AfpIoClose(&hParent); } AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock); if ((PME.pme_FullPath.Buffer != NULL) && (PME.pme_FullPath.Buffer != PathBuf)) { AfpFreeMemory(PME.pme_FullPath.Buffer); } return AFP_ERR_EXTENDED; } /*** AfpFspDispMoveAndRename * * This is the worker routine for the AfpMoveAndRename API. Note that * in AFP 2.x, a FILE (not a dir) CAN BE MOVED when its RenameInhibit bit * is set if it is NOT BEING RENAMED. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD Source ParentId * sda_ReqBlock DWORD Dest ParentId * sda_Name1 ANSI_STRING Source Path * sda_Name2 ANSI_STRING Dest Path * sda_Name3 ANSI_STRING New Name */ AFPSTATUS FASTCALL AfpFspDispMoveAndRename( IN PSDA pSda ) { PATHMAPENTITY PMEsrc, PMEdst; PVOLDESC pVolDesc; FILEDIRPARM FDParmsrc, FDParmdst; DWORD Bitmap, NTAttr; AFPSTATUS Status = AFP_ERR_PARAM; UNICODE_STRING uNewName; WCHAR wcbuf[AFP_FILENAME_LEN+1]; BOOLEAN Rename = True, Move = True, SrcInRoot, DstInRoot; PDFENTRY pDfesrc, pDfedst, pDfeParentsrc; FILESYSHANDLE hSrcParent; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _SrcParentId; DWORD _DstParentId; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispMoveAndRename: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); pVolDesc = pReqPkt->_pConnDesc->cds_pVolDesc; ASSERT(VALID_VOLDESC(pVolDesc)); AfpInitializeFDParms(&FDParmsrc); AfpInitializeFDParms(&FDParmdst); AfpInitializePME(&PMEsrc, 0, NULL); AfpInitializePME(&PMEdst, 0, NULL); Bitmap = FD_BITMAP_ATTR | FD_BITMAP_LONGNAME | FD_INTERNAL_BITMAP_OPENACCESS_DELETE; AfpSetEmptyUnicodeString(&uNewName, sizeof(wcbuf), wcbuf); hSrcParent.fsh_FileHandle = NULL; AfpSwmrAcquireExclusive(&pVolDesc->vds_IdDbAccessLock); do { // Make sure the new name is not too long if ((pSda->sda_Name3.Length > 0) && ((pSda->sda_Name3.Length > AFP_FILENAME_LEN) || ((pSda->sda_PathType == AFP_SHORTNAME) && !AfpIsLegalShortname(&pSda->sda_Name3)) || (!NT_SUCCESS(AfpConvertStringToMungedUnicode(&pSda->sda_Name3, &uNewName))))) break; // Map source path for lookup (could be file or dir). // We ask for the finderinfo in case the user is moving an // application file to another directory, we can update its // parent dirid in the APPL desktop database if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc, pReqPkt->_SrcParentId, &pSda->sda_Name1, pSda->sda_PathType, Lookup, DFE_ANY, Bitmap | FD_BITMAP_FINDERINFO, &PMEsrc, &FDParmsrc))) { PMEsrc.pme_Handle.fsh_FileHandle = NULL; break; } // Map the destination parent directory path for lookup if (!NT_SUCCESS(Status = AfpMapAfpPath(pReqPkt->_pConnDesc, pReqPkt->_DstParentId, &pSda->sda_Name2, pSda->sda_PathType, Lookup, DFE_DIR, 0, &PMEdst, &FDParmdst))) { PMEdst.pme_Handle.fsh_FileHandle = NULL; break; } if ((FDParmsrc._fdp_AfpId == AFP_ID_ROOT) || (FDParmsrc._fdp_AfpId == AFP_ID_NETWORK_TRASH)) { Status = AFP_ERR_CANT_MOVE; break; } if (!NT_SUCCESS(Status = AfpCheckForInhibit(&PMEsrc.pme_Handle, FD_BITMAP_ATTR_RENAMEINH, FDParmsrc._fdp_Attr, &NTAttr))) { // Files (not dirs) marked RenameInhibit that are NOT being // renamed are allowed to be moved in AFP 2.x if (!((Status == AFP_ERR_OBJECT_LOCKED) && (!IsDir(&FDParmsrc)) && (pSda->sda_Name3.Length == 0))) { break; } } if (FDParmsrc._fdp_ParentId == FDParmdst._fdp_AfpId) { // if the parent directories are the same, we are not // moving anything to a new directory, so the change // notify we expect will be a rename in the source dir. Move = False; // // Trying to move a file onto itself. Just return success. // (some apps move files onto // themselves for who knows what reason) // if ((pSda->sda_Name3.Length == 0) || RtlEqualString(&pSda->sda_Name3, &FDParmsrc._fdp_LongName, False)) { Status = AFP_ERR_NONE; break; } } // Check for SeeFiles or SeeFolders on the source parent dir if (!NT_SUCCESS(Status = AfpCheckParentPermissions(pReqPkt->_pConnDesc, FDParmsrc._fdp_ParentId, &PMEsrc.pme_ParentPath, (FDParmsrc._fdp_Flags & DFE_FLAGS_DIR) ? DIR_ACCESS_SEARCH : DIR_ACCESS_READ, &hSrcParent, NULL))) { break; } if (NTAttr & FILE_ATTRIBUTE_READONLY) { // We must temporarily remove the ReadOnly attribute so that // we can move the file/dir Status = AfpIoSetTimesnAttr(&PMEsrc.pme_Handle, NULL, NULL, 0, FILE_ATTRIBUTE_READONLY, pVolDesc, &PMEsrc.pme_FullPath); } if (NT_SUCCESS(Status)) { // If no new name was supplied, we need to use the // current name if (pSda->sda_Name3.Length == 0) { Rename = False; uNewName = PMEsrc.pme_UTail; } // We must impersonate to do the move since it is name based AfpImpersonateClient(pSda); if (Move) { // if we are moving, we will also get an ADDED notification // for the destination directory. Since we have the path // of the parent dir, but we really want the name of the // thing we are about to move and/or rename, munge the // destination paths to reflect the new name of the thing // we are moving/renaming PMEdst.pme_ParentPath = PMEdst.pme_FullPath; if (PMEdst.pme_FullPath.Length > 0) { PMEdst.pme_FullPath.Buffer[PMEdst.pme_FullPath.Length / sizeof(WCHAR)] = L'\\'; PMEdst.pme_FullPath.Length += sizeof(WCHAR); } Status = RtlAppendUnicodeStringToString(&PMEdst.pme_FullPath, &uNewName); ASSERT(NT_SUCCESS(Status)); } SrcInRoot = (PMEsrc.pme_ParentPath.Length == 0) ? True : False; DstInRoot = (PMEdst.pme_ParentPath.Length == 0) ? True : False; Status = AfpIoMoveAndOrRename(&PMEsrc.pme_Handle, Move ? &PMEdst.pme_Handle : NULL, &uNewName, pVolDesc, &PMEsrc.pme_FullPath, SrcInRoot ? NULL : &PMEsrc.pme_ParentPath, Move ? &PMEdst.pme_FullPath : NULL, (Move && !DstInRoot) ? &PMEdst.pme_ParentPath : NULL); AfpRevertBack(); if (NT_SUCCESS(Status)) // Move succeeded { if (((pDfesrc = AfpFindDfEntryById(pVolDesc, FDParmsrc._fdp_AfpId, DFE_ANY)) != NULL) && ((pDfedst = AfpFindDfEntryById(pVolDesc, FDParmdst._fdp_AfpId, DFE_DIR)) != NULL)) { ASSERT(((pDfesrc->dfe_Flags & DFE_FLAGS_DFBITS) & FDParmsrc._fdp_Flags) != 0); pDfeParentsrc = pDfesrc->dfe_Parent; pDfesrc = AfpMoveDfEntry(pVolDesc, pDfesrc, pDfedst, Rename ? &uNewName : NULL); if (pDfesrc == NULL) { // We could not move the id entry, so // just delete it. ASSERT(VALID_DFE(PMEsrc.pme_pDfEntry)); ASSERT(PMEsrc.pme_pDfEntry->dfe_AfpId == FDParmsrc._fdp_AfpId); AfpDeleteDfEntry(pVolDesc, PMEsrc.pme_pDfEntry); Status = AFP_ERR_MISC; // Out of memory } // update cached mod time of source parent directory AfpCacheParentModTime(pVolDesc, &hSrcParent, NULL, pDfeParentsrc, 0); if (Move) { // update cached mod time of destination directory AfpCacheParentModTime(pVolDesc, &PMEdst.pme_Handle, NULL, pDfedst, 0); // // if we just moved an application program, update // the parentID in the corresponding APPL mapping. // if ((!IsDir(&FDParmsrc)) && (FDParmsrc._fdp_FinderInfo.fd_TypeD == *(PDWORD)"APPL")) { AfpAddAppl(pVolDesc, FDParmsrc._fdp_FinderInfo.fd_CreatorD, 0, FDParmsrc._fdp_AfpId, True, FDParmdst._fdp_AfpId); } } } } } else { Status = AFP_ERR_MISC; // Could not delete ReadOnly attribute break; } // Set the ReadOnly attribute back on the file/dir if need be if (NTAttr & FILE_ATTRIBUTE_READONLY) AfpIoSetTimesnAttr(&PMEsrc.pme_Handle, NULL, NULL, FILE_ATTRIBUTE_READONLY, 0, pVolDesc, &PMEsrc.pme_FullPath); } while (False); // Return before we close thus saving some time AfpCompleteApiProcessing(pSda, Status); AfpSwmrRelease(&pVolDesc->vds_IdDbAccessLock); if (PMEsrc.pme_Handle.fsh_FileHandle != NULL) AfpIoClose(&PMEsrc.pme_Handle); if (PMEdst.pme_Handle.fsh_FileHandle != NULL) AfpIoClose(&PMEdst.pme_Handle); if (hSrcParent.fsh_FileHandle != NULL) AfpIoClose(&hSrcParent); if (PMEsrc.pme_FullPath.Buffer != NULL) AfpFreeMemory(PMEsrc.pme_FullPath.Buffer); if (PMEdst.pme_FullPath.Buffer != NULL) AfpFreeMemory(PMEdst.pme_FullPath.Buffer); return AFP_ERR_EXTENDED; } /*** AfpFspDispCatSearch * * This is the worker routine for the AfpCatSearch API. * * The request packet is represented below * * sda_ReqBlock PCONNDESC pConnDesc * sda_ReqBlock DWORD RequestedMatches * sda_Name1 ANSI_STRING Catalog Position - 16 bytes * sda_Name2 ANSI_STRING Everything else - needs unmarshalling * * The reason we could not unmarshall all the parameters is because this * API's parameters do not conform to the common way all the other APIs' * parameters do, and therefore we cannot use the common code and table * structures in afpapi.c. */ AFPSTATUS FASTCALL AfpFspDispCatSearch( IN PSDA pSda ) { AFPSTATUS Status = AFP_ERR_PARAM; PBYTE pEndOfBuffer; USHORT Flags; SHORT SizeLeft = 0; PVOLDESC pVolDesc; DWORD FileResultBitmap; DWORD DirResultBitmap; DWORD RequestBitmap; DWORD Count; BOOLEAN fPartialName = False, FreeReplyBuf = False; FILEDIRPARM FDPLowerAndValue, FDPUpperAndMask; PCATSEARCHSPEC pSpec1, pSpec2; UNICODE_STRING MatchString; WCHAR strbuf[AFP_LONGNAME_LEN+1]; struct _RequestPacket { PCONNDESC _pConnDesc; DWORD _RequestedMatches; }; // The part of the request buffer that could not be unmarshalled into // fields in the Sda because they don't conform to any of the other APIs. // These will be unmarshalled here into local variables, the sda_Name2 // can be cast to this structure for easy access. struct _RestOfRawRequest { USHORT _FileResultBitmap; USHORT _DirResultBitmap; BYTE _fPartialName; BYTE _Pad1; USHORT _RequestBitmap; // Spec1 and Spec2 follow }; #define pRawPkt ((struct _RestOfRawRequest *)(pSda->sda_Name2.Buffer)) struct _ResponsePacket { BYTE __CatPosition[16]; BYTE __FileBitmap[2]; BYTE __DirBitmap[2]; BYTE __ActualCount[4]; }; PAGED_CODE(); DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_INFO, ("AfpFspDispCatSearch: Entered\n")); ASSERT(VALID_CONNDESC(pReqPkt->_pConnDesc)); do { if (pSda->sda_Name2.Length < (sizeof(struct _RestOfRawRequest) + (2 * sizeof(CATSEARCHSPEC)))) { // The request buffer must be at least as big as the rest of the // parameters that weren't yet unmarshalled, plus 2 Spec structs break; } GETSHORT2DWORD(&FileResultBitmap, &pRawPkt->_FileResultBitmap); GETSHORT2DWORD(&DirResultBitmap, &pRawPkt->_DirResultBitmap); GETSHORT2DWORD(&RequestBitmap, &pRawPkt->_RequestBitmap); if ( (pRawPkt->_fPartialName & 0x80) != 0 ) { fPartialName = True; } // // Validate the bitmaps // if (((FileResultBitmap | DirResultBitmap) == 0) || ((FileResultBitmap | DirResultBitmap) & ~FD_VALID_SEARCH_RESULT) || (RequestBitmap == 0)) { Status = AFP_ERR_BITMAP; break; } // make sure CatSearch is enabled: if it's disabled, reject the call if (!(pReqPkt->_pConnDesc->cds_pVolDesc->vds_Flags & AFP_VOLUME_SUPPORTS_CATSRCH)) { DBGPRINT(DBG_COMP_AFPAPI_FD, DBG_LEVEL_ERR, ("AfpFspDispCatSearch: CATSRCH not supported by volume\n")); Status = AFP_ERR_CALL_NOT_SUPPORTED; break; } AfpInitializeFDParms(&FDPLowerAndValue); AfpInitializeFDParms(&FDPUpperAndMask); if (DirResultBitmap == 0) { FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_FILE_WITH_ID; if (RequestBitmap & ~FILE_VALID_SEARCH_CRITERIA) { Status = AFP_ERR_BITMAP; break; } } else if (FileResultBitmap == 0) { FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_DIR; if (RequestBitmap & ~DIR_VALID_SEARCH_CRITERIA) { Status = AFP_ERR_BITMAP; break; } } else { FDPLowerAndValue._fdp_Flags = FDPUpperAndMask._fdp_Flags = DFE_FLAGS_FILE_WITH_ID | DFE_FLAGS_DIR; if (RequestBitmap & ~FD_VALID_SEARCH_CRITERIA) { Status = AFP_ERR_BITMAP; break; } } Flags = ((PCATALOGPOSITION)pSda->sda_Name1.Buffer)->cp_Flags; // The caller should not muck with the catalog position at all if ((Flags & ~CATFLAGS_VALID) || // Writelock should only be required if we were about to search files ((Flags & CATFLAGS_WRITELOCK_REQUIRED) && !(Flags & CATFLAGS_SEARCHING_FILES))) // NOTE: also should make sure ONLY ONE of the SEARCHING bits is on break; // // Now unpack the search criteria // MatchString.Length = 0; MatchString.MaximumLength = sizeof(strbuf); MatchString.Buffer = strbuf; Status = AfpUnpackCatSearchSpecs((PBYTE)pSda->sda_Name2.Buffer + sizeof(struct _RestOfRawRequest), (USHORT)(pSda->sda_Name2.Length - sizeof(struct _RestOfRawRequest)), RequestBitmap, &FDPLowerAndValue, &FDPUpperAndMask, &MatchString); if (!NT_SUCCESS(Status)) { break; } // // Allocate the reply buffer. Estimate the required size by using // the maximum possible filename length plus potential pad bytes for // even alignment of each entry plus the length of the parent dirid. // pSda->sda_ReplySize = (USHORT)(SIZE_RESPPKT + (pReqPkt->_RequestedMatches * ((2 * sizeof(BYTE)) + sizeof(DWORD) + sizeof(USHORT) + sizeof(BYTE) + AFP_LONGNAME_LEN + 1))); if (pSda->sda_ReplySize > MAX_CATSEARCH_REPLY) { pSda->sda_ReplySize = MAX_CATSEARCH_REPLY; } AfpIOAllocBackFillBuffer(pSda); if (pSda->sda_ReplyBuf == NULL) { pSda->sda_ReplySize = 0; Status = AFP_ERR_MISC; break; } #if DBG AfpPutGuardSignature(pSda); #endif FreeReplyBuf = True; // // Perform the search // FDPUpperAndMask._fdp_fPartialName = fPartialName; Count = pReqPkt->_RequestedMatches; Status = AfpCatSearch(pReqPkt->_pConnDesc, (PCATALOGPOSITION)pSda->sda_Name1.Buffer, // CatalogPosition RequestBitmap, FileResultBitmap, DirResultBitmap, &FDPLowerAndValue, &FDPUpperAndMask, &MatchString, &Count, // IN OUT (SHORT)(pSda->sda_ReplySize - SIZE_RESPPKT), &SizeLeft, pSda->sda_ReplyBuf + SIZE_RESPPKT, (PCATALOGPOSITION)pSda->sda_ReplyBuf); if (!NT_SUCCESS(Status) && ((Status != AFP_ERR_EOF) && (Status != AFP_ERR_CATALOG_CHANGED))) { break; } PUTSHORT2SHORT(&pRspPkt->__FileBitmap, FileResultBitmap); PUTSHORT2SHORT(&pRspPkt->__DirBitmap, DirResultBitmap); PUTDWORD2DWORD(&pRspPkt->__ActualCount, Count); pSda->sda_ReplySize -= SizeLeft; ASSERT(pSda->sda_ReplySize <= MAX_CATSEARCH_REPLY); FreeReplyBuf = False; } while (False); if (FreeReplyBuf) { AfpIOFreeBackFillBuffer(pSda); } return Status; }