/* demsrch.c - SVC handlers for calls to search files * * demFindFirst * demFindNext * demFindFirstFCB * demFindNextFCB * * Modification History: * * Sudeepb 06-Apr-1991 Created * */ #include "dem.h" #include "demmsg.h" #include "winbasep.h" #include #include #include #include #include extern BOOL IsFirstCall; /* * Internal globals, function prototypes */ #define FINDFILE_DEVICE (HANDLE)0xffffffff typedef struct _PSP_FILEFINDLIST { LIST_ENTRY PspFFindEntry; // Next psp LIST_ENTRY FFindHeadList; // File Find list for this psp ULONG usPsp; // PSP id } PSP_FFINDLIST, *PPSP_FFINDLIST; typedef struct _FFINDDOSDATA { ULONG FileIndex; ULONG FileNameLength; WCHAR FileName[MAXIMUM_FILENAME_LENGTH + 1]; FILETIME ftLastWriteTime; DWORD dwFileSizeLow; UCHAR uchFileAttributes; CHAR cFileName[14]; } FFINDDOSDATA, *PFFINDDOSDATA; typedef struct _FILEFINDLIST { LIST_ENTRY FFindEntry; ULONG FFindId; NTSTATUS LastQueryStatus; LARGE_INTEGER FindFileTics; HANDLE DirectoryHandle; PVOID FindBufferBase; PVOID FindBufferNext; ULONG FindBufferLength; FFINDDOSDATA DosData; USHORT usSrchAttr; BOOLEAN SupportReset; UNICODE_STRING PathName; UNICODE_STRING FileName; BOOL SearchOnCD; }FFINDLIST, *PFFINDLIST; LIST_ENTRY PspFFindHeadList= {&PspFFindHeadList, &PspFFindHeadList}; #define FFINDID_BASE 0x80000000 ULONG NextFFindId = FFINDID_BASE; BOOLEAN FFindIdWrap = FALSE; #define MAX_DIRECTORYHANDLE 64 #define MAX_FINDBUFFER 128 ULONG NumDirectoryHandle = 0; ULONG NumFindBuffer=0; LARGE_INTEGER FindFileTics = {0,0}; LARGE_INTEGER NextFindFileTics = {0,0}; char szStartDotStar[]="????????.???"; PFFINDLIST SearchFile( PWCHAR pwcFile, USHORT SearchAttr, PFFINDLIST pFFindEntry, PFFINDDOSDATA pFFindDDOut ); NTSTATUS FileFindNext( PFFINDDOSDATA pFFindDD, PFFINDLIST pFFindEntry ); NTSTATUS FileFindLast( PFFINDLIST pFFindEntry ); VOID FileFindClose( PFFINDLIST pFFindEntry ); NTSTATUS FileFindOpen( PWCHAR pwcFile, PFFINDLIST pFFindEntry, ULONG BufferSize ); NTSTATUS FileFindReset( PFFINDLIST pFFindEntry ); HANDLE FileFindFirstDevice( PWCHAR FileName, PFILE_BOTH_DIR_INFORMATION DirectoryInfo ); void CloseOldestFileFindBuffer( void ); BOOL CopyDirInfoToDosData( HANDLE DirectoryHandle, PFFINDDOSDATA pFFindDD, PFILE_BOTH_DIR_INFORMATION DirectoryInfo, USHORT SearchAttr ); BOOL DemOemToUni( PUNICODE_STRING pUnicode, LPSTR lpstr ); VOID FillFcbVolume( PSRCHBUF pSrchBuf, CHAR *pFileName, USHORT SearchAttr ); BOOL FillDtaVolume( CHAR *pFileName, PSRCHDTA pDta, USHORT SearchAttr ); BOOL MatchVolLabel( CHAR * pVolLabel, CHAR * pBaseName ); VOID NtVolumeNameToDosVolumeName( CHAR * pDosName, CHAR * pNtName ); VOID FillFCBSrchBuf( PFFINDDOSDATA pFFindDD, PSRCHBUF pSrchBuf, BOOL IsOnCD ); VOID FillSrchDta( PFFINDDOSDATA pFFindDD, PSRCHDTA pDta, BOOL IsOnCD ); PFFINDLIST AddFFindEntry( PWCHAR pwcFile, PFFINDLIST pFFindEntrySrc ); PPSP_FFINDLIST GetPspFFindList( USHORT CurrPsp ); PFFINDLIST GetFFindEntryByFindId( ULONG NextFFindId ); VOID FreeFFindEntry( PFFINDLIST pFFindEntry ); VOID FreeFFindList( PLIST_ENTRY pFFindHeadList ); #if defined(NEC_98) // BUG fix for DBCS small alphabet converted to large it. void demCharUpper(char *); #endif // NEC_98 /* demFindFirst - Path-Style Find First File * * Entry - Client (DS:DX) - File Path with wildcard * Client (CX) - Search Attributes * * Exit - Success * Client (CF) = 0 * DTA updated * * Failure * Client (CF) = 1 * Client (AX) = Error Code * * NOTES * Search Rules: Ignore Read_only and Archive bits. * If CX == ATTR_NORMAL Search only for normal files * If CX == ATTR_HIDDEN Search Hidden or normal files * If CX == ATTR_SYSTEM Search System or normal files * If CX == ATTR_DIRECTORY Search directory or normal files * If CX == ATTR_VOLUME_ID Search Volume_ID * if CX == -1 return everytiing you find * * Limitations - 21-Sep-1992 Jonle * cannot return label from a UNC name,just like dos. * Apps which keep many find handles open can cause * serious trouble, we must rewrite so that we can * close the handles * */ VOID demFindFirst (VOID) { DWORD dwRet; PVOID pDta; #ifdef DBCS /* demFindFirst() for CSNW */ CHAR achPath[MAX_PATH]; #endif /* DBCS */ LPSTR lpFile = (LPSTR) GetVDMAddr (getDS(),getDX()); pDta = (PVOID) GetVDMAddr (*((PUSHORT)pulDTALocation + 1), *((PUSHORT)pulDTALocation)); #ifdef DBCS /* demFindFirst() for CSNW */ /* * convert Netware path to Dos path */ ConvNwPathToDosPath(achPath,lpFile); lpFile = achPath; #endif /* DBCS */ dwRet = demFileFindFirst (pDta, lpFile, getCX()); if (dwRet == -1) { dwRet = GetLastError(); demClientError(INVALID_HANDLE_VALUE, *lpFile); return; } if (dwRet != 0) { setAX((USHORT) dwRet); setCF (1); } else { setCF (0); } return; } DWORD demFileFindFirst ( PVOID pvDTA, LPSTR lpFile, USHORT SearchAttr) { PSRCHDTA pDta = (PSRCHDTA)pvDTA; PFFINDLIST pFFindEntry; FFINDDOSDATA FFindDD; UNICODE_STRING FileUni; WCHAR wcFile[MAX_PATH + sizeof(WCHAR)]; BOOL IsOnCD; #if DBG if (SIZEOF_DOSSRCHDTA != sizeof(SRCHDTA)) { sprintf(demDebugBuffer, "demsrch: FFirst SIZEOF_DOSSRCHDTA %ld != sizeof(SRCHDTA) %ld\n", SIZEOF_DOSSRCHDTA, sizeof(SRCHDTA)); OutputDebugStringOem(demDebugBuffer); } if (fShowSVCMsg & DEMFILIO){ sprintf(demDebugBuffer,"demsrch: FindFirst<%s>\n", lpFile); OutputDebugStringOem(demDebugBuffer); } #endif STOREDWORD(pDta->FFindId,0); STOREDWORD(pDta->pFFindEntry,0); FileUni.Buffer = wcFile; FileUni.MaximumLength = sizeof(wcFile); DemOemToUni(&FileUni, lpFile); IsOnCD = IsCdRomFile(lpFile); // // Do volume label first. // if (SearchAttr & ATTR_VOLUME_ID) { if (FillDtaVolume(lpFile, pDta, SearchAttr)) { // got vol label match // do look ahead before returning if (SearchAttr != ATTR_VOLUME_ID) { pFFindEntry = SearchFile(wcFile, SearchAttr, NULL, NULL); if (pFFindEntry) { pFFindEntry->SearchOnCD = IsOnCD; STOREDWORD(pDta->pFFindEntry,pFFindEntry); STOREDWORD(pDta->FFindId,pFFindEntry->FFindId); } } return 0; } // no vol match, if asking for more than vol label // fall thru to file search code, otherwise ret error else if (SearchAttr == ATTR_VOLUME_ID) { return GetLastError(); } } // // Search the dir // pFFindEntry = SearchFile(wcFile, SearchAttr, NULL, &FFindDD); if (!FFindDD.cFileName[0]) { // search.asm in doskrnl never returns ERROR_FILE_NOT_FOUND // only ERROR_PATH_NOT_FOUND, ERROR_NO_MORE_FILES DWORD dw; dw = GetLastError(); if (dw == ERROR_FILE_NOT_FOUND) { SetLastError(ERROR_NO_MORE_FILES); } else if (dw == ERROR_BAD_PATHNAME || dw == ERROR_DIRECTORY ) { SetLastError(ERROR_PATH_NOT_FOUND); } return (DWORD)-1; } FillSrchDta(&FFindDD, pDta, IsOnCD); if (pFFindEntry) { pFFindEntry->SearchOnCD = IsOnCD; STOREDWORD(pDta->pFFindEntry,pFFindEntry); STOREDWORD(pDta->FFindId,pFFindEntry->FFindId); } return 0; } /* * DemOemToUni * * returns TRUE\FALSE for success, sets last error if fail * */ BOOL DemOemToUni(PUNICODE_STRING pUnicode, LPSTR lpstr) { NTSTATUS Status; OEM_STRING OemString; RtlInitString(&OemString,lpstr); Status = RtlOemStringToUnicodeString(pUnicode,&OemString,FALSE); if (!NT_SUCCESS(Status)) { if (Status == STATUS_BUFFER_OVERFLOW) { SetLastError(ERROR_FILENAME_EXCED_RANGE); } else { SetLastError(RtlNtStatusToDosError(Status)); } return FALSE; } *(PWCHAR)((PUCHAR)pUnicode->Buffer + pUnicode->Length) = UNICODE_NULL; return TRUE; } /* demFindNext - Path-Style Find Next File * * Entry - None * * Exit - Success * Client (CF) = 0 * DTA updated * * Failure * Client (CF) = 1 * Client (AX) = Error Code */ VOID demFindNext (VOID) { DWORD dwRet; PVOID pDta; pDta = (PVOID) GetVDMAddr(*((PUSHORT)pulDTALocation + 1), *((PUSHORT)pulDTALocation)); dwRet = demFileFindNext (pDta); if (dwRet != 0) { setAX((USHORT) dwRet); setCF (1); return; } setCF (0); return; } DWORD demFileFindNext ( PVOID pvDta) { PSRCHDTA pDta = (PSRCHDTA)pvDta; USHORT SearchAttr; PFFINDLIST pFFindEntry; FFINDDOSDATA FFindDD; BOOL IsOnCD; pFFindEntry = GetFFindEntryByFindId(FETCHDWORD(pDta->FFindId)); if (!pFFindEntry || FETCHDWORD(pDta->pFFindEntry) != (DWORD)pFFindEntry ) { STOREDWORD(pDta->FFindId,0); STOREDWORD(pDta->pFFindEntry,0); // DOS has only one error (no_more_files) for all causes. return(ERROR_NO_MORE_FILES); } #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "demFileFindNext<%ws>\n", pFFindEntry->PathName.Buffer); OutputDebugStringOem(demDebugBuffer); } #endif SearchAttr = pFFindEntry->usSrchAttr; IsOnCD = pFFindEntry->SearchOnCD; // // Search the dir // pFFindEntry = SearchFile(NULL, SearchAttr, pFFindEntry, &FFindDD ); if (!FFindDD.cFileName[0]) { STOREDWORD(pDta->FFindId,0); STOREDWORD(pDta->pFFindEntry,0); return GetLastError(); } FillSrchDta(&FFindDD, pDta, IsOnCD); if (!pFFindEntry) { STOREDWORD(pDta->FFindId,0); STOREDWORD(pDta->pFFindEntry,0); } return 0; } /* demFindFirstFCB - FCB based Find First file * * Entry - Client (DS:SI) - SRCHBUF where the information will be returned * Client (ES:DI) - Full path file name with possibly wild cards * Client (Al) - 0 if not an extended FCB * Client (DL) - Search Attributes * * Exit - Success * Client (CF) = 0 * SRCHBUF is filled in * * Failure * Client (AL) = -1 * * NOTES * Search Rules: Ignore Read_only and Archive bits. * If DL == ATTR_NORMAL Search only for normal files * If DL == ATTR_HIDDEN Search Hidden or normal files * If DL == ATTR_SYSTEM Search System or normal files * If DL == ATTR_DIRECTORY Search directory or normal files * If DL == ATTR_VOLUME_ID Search only Volume_ID * if DL == -1 return everytiing you find */ VOID demFindFirstFCB (VOID) { LPSTR lpFile; USHORT SearchAttr; PSRCHBUF pFCBSrchBuf; PDIRENT pDirEnt; PFFINDLIST pFFindEntry; FFINDDOSDATA FFindDD; UNICODE_STRING FileUni; WCHAR wcFile[MAX_PATH]; BOOL IsOnCD; lpFile = (LPSTR) GetVDMAddr (getES(),getDI()); #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "demFindFirstFCB<%s>\n", lpFile); OutputDebugStringOem(demDebugBuffer); } #endif pFCBSrchBuf = (PSRCHBUF) GetVDMAddr (getDS(),getSI()); pDirEnt = &pFCBSrchBuf->DirEnt; STOREDWORD(pDirEnt->pFFindEntry,0); STOREDWORD(pDirEnt->FFindId,0); if (getDL() == ATTR_VOLUME_ID) { FillFcbVolume(pFCBSrchBuf,lpFile, ATTR_VOLUME_ID); return; } FileUni.Buffer = wcFile; FileUni.MaximumLength = sizeof(wcFile); if (!DemOemToUni(&FileUni ,lpFile)) { setCF(1); return; } SearchAttr = getAL() ? getDL() : 0; pFFindEntry = SearchFile(wcFile, SearchAttr, NULL, &FFindDD); if (!FFindDD.cFileName[0]){ demClientError(INVALID_HANDLE_VALUE, *lpFile); return; } IsOnCD = IsCdRomFile(lpFile); FillFCBSrchBuf(&FFindDD, pFCBSrchBuf, IsOnCD); if (pFFindEntry) { pFFindEntry->SearchOnCD = IsOnCD; STOREDWORD(pDirEnt->pFFindEntry,pFFindEntry); STOREDWORD(pDirEnt->FFindId,pFFindEntry->FFindId); } setCF(0); return; } /* demFindNextFCB - FCB based Find Next file * * Entry - Client (DS:SI) - SRCHBUF where the information will be returned * Client (Al) - 0 if not an extended FCB * Client (DL) - Search Attributes * * Exit - Success * Client (CF) = 0 * SRCHBUF is filled in * * Failure * Client (AL) = -1 * * NOTES * Search Rules: Ignore Read_only and Archive bits. * If DL == ATTR_NORMAL Search only for normal files * If DL == ATTR_HIDDEN Search Hidden or normal files * If DL == ATTR_SYSTEM Search System or normal files * If DL == ATTR_DIRECTORY Search directory or normal files * If DL == ATTR_VOLUME_ID Search only Volume_ID */ VOID demFindNextFCB (VOID) { USHORT SearchAttr; PSRCHBUF pSrchBuf; PDIRENT pDirEnt; PFFINDLIST pFFindEntry; FFINDDOSDATA FFindDD; BOOL IsOnCD; pSrchBuf = (PSRCHBUF) GetVDMAddr (getDS(),getSI()); pDirEnt = &pSrchBuf->DirEnt; pFFindEntry = GetFFindEntryByFindId(FETCHDWORD(pDirEnt->FFindId)); if (!pFFindEntry || FETCHDWORD(pDirEnt->pFFindEntry) != (DWORD)pFFindEntry || getDL() == ATTR_VOLUME_ID ) { if (pFFindEntry && FETCHDWORD(pDirEnt->pFFindEntry) != (DWORD)pFFindEntry) { FreeFFindEntry(pFFindEntry); } STOREDWORD(pDirEnt->pFFindEntry,0); STOREDWORD(pDirEnt->FFindId,0); // DOS has only one error (no_more_files) for all causes. setAX(ERROR_NO_MORE_FILES); setCF(1); return; } #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "demFindNextFCB<%ws>\n", pFFindEntry->PathName.Buffer); OutputDebugStringOem(demDebugBuffer); } #endif SearchAttr = getAL() ? getDL() : 0; IsOnCD = pFFindEntry->SearchOnCD; // // Search the dir // pFFindEntry = SearchFile(NULL, SearchAttr, pFFindEntry, &FFindDD ); if (!FFindDD.cFileName[0]) { STOREDWORD(pDirEnt->pFFindEntry,0); STOREDWORD(pDirEnt->FFindId,0); setAX((USHORT) GetLastError()); setCF(1); return; } FillFCBSrchBuf(&FFindDD, pSrchBuf,IsOnCD); if (!pFFindEntry) { STOREDWORD(pDirEnt->FFindId,0); STOREDWORD(pDirEnt->pFFindEntry,0); } setCF(0); return; } /* demTerminatePDB - PDB Terminate Notification * * Entry - Client (BX) - Terminating PDB * * Exit - None * */ VOID demTerminatePDB (VOID) { PPSP_FFINDLIST pPspFFindEntry; USHORT PSP; PSP = getBX (); if(!IsFirstCall) VDDTerminateUserHook(PSP); /* let host knows a process is terminating */ HostTerminatePDB(PSP); pPspFFindEntry = GetPspFFindList(PSP); if (!pPspFFindEntry) return; if (!IsListEmpty(&pPspFFindEntry->FFindHeadList)) { FreeFFindList( &pPspFFindEntry->FFindHeadList); } RemoveEntryList(&pPspFFindEntry->PspFFindEntry); free(pPspFFindEntry); return; } /* SearchFile - Common routine for FIND_FRST and FIND_NEXT * * Entry - * PCHAR pwcFile file name to search for * USHORT SearchAttr file attributes to match * PFFINDLIST pFFindEntry, current list entry * if new search FFindId is expected to be zero * PFFINDDOSDATA pFFindDDOut, filled with the next file in search * * Exit - if no more files pFFindDDOut is filled with zeros * returns PFFINDLIST if buffered entries exist, else NULL */ PFFINDLIST SearchFile( PWCHAR pwcFile, USHORT SearchAttr, PFFINDLIST pFFindEntry, PFFINDDOSDATA pFFindDDOut) { NTSTATUS Status; ULONG BufferSize; FFINDLIST FFindEntry; PFFINDLIST pFFEntry = NULL; SearchAttr &= ~(ATTR_READ_ONLY | ATTR_ARCHIVE | ATTR_DEVICE); Status = STATUS_NO_MORE_FILES; if (pFFindDDOut) { memset(pFFindDDOut, 0, sizeof(FFINDDOSDATA)); } try { if (pFFindEntry) { pFFEntry = pFFindEntry; Status = pFFindEntry->LastQueryStatus; if (pFFindDDOut) { *pFFindDDOut = pFFEntry->DosData; pFFEntry->DosData.cFileName[0] = '\0'; } else { return pFFEntry; } if (pFFEntry->FindBufferNext || pFFEntry->DirectoryHandle) { NTSTATUS st; st = FileFindNext(&pFFEntry->DosData, pFFEntry ); if (NT_SUCCESS(st)) { return pFFEntry; } if (pFFEntry->DirectoryHandle) { Status = st; } } // // Check Last Known Status before retrying // if (!NT_SUCCESS(Status)) { return NULL; } // // Reopen the FileFind Handle with a large buffer size // Status = FileFindOpen(NULL, pFFEntry, 4096 ); if (!NT_SUCCESS(Status)) { return NULL; } // // reset the search to the last known search pos // Status = FileFindReset(pFFEntry); if (!NT_SUCCESS(Status)) { return NULL; } } else { pFFEntry = &FFindEntry; memset(pFFEntry, 0, sizeof(FFINDLIST)); pFFEntry->SupportReset = TRUE; pFFEntry->usSrchAttr = SearchAttr; Status = FileFindOpen(pwcFile, pFFEntry, 1024 ); if (!NT_SUCCESS(Status)) { return NULL; } // // Fill up pFFindDDOut // if (pFFindDDOut) { Status = FileFindNext(pFFindDDOut, pFFEntry); if (!NT_SUCCESS(Status)) { return NULL; } } } // // Fill up pFFEntry->DosData // Status = FileFindNext(&pFFEntry->DosData, pFFEntry); if (!NT_SUCCESS(Status)) { return NULL; } // // if findfirst, fill in the static entries, and add the find entry // if (!pFFindEntry) { pFFEntry->FFindId = NextFFindId++; if (NextFFindId == 0xffffffff) { NextFFindId = FFINDID_BASE; FFindIdWrap = TRUE; } if (FFindIdWrap) { pFFindEntry = GetFFindEntryByFindId(NextFFindId); if (pFFindEntry) { FreeFFindEntry(pFFindEntry); pFFindEntry = NULL; } } pFFEntry = AddFFindEntry(pwcFile, pFFEntry); if (!pFFEntry) { pFFEntry = &FFindEntry; pFFEntry->DosData.cFileName[0] = '\0'; Status = STATUS_NO_MEMORY; return NULL; } } // // Try to fill one more entry. If the NtQuery for this search // is complete we can set the LastQueryStatus, and close dir handles. // Status = FileFindLast(pFFEntry); } finally { if (pFFEntry) { pFFEntry->LastQueryStatus = Status; // // if nothing is buffered, cleanup look aheads // if (!pFFEntry->DosData.cFileName[0] || pFFEntry->DirectoryHandle == FINDFILE_DEVICE) { if (pFFEntry == &FFindEntry) { FileFindClose(pFFEntry); RtlFreeUnicodeString(&pFFEntry->FileName); RtlFreeUnicodeString(&pFFEntry->PathName); } else { FreeFFindEntry(pFFEntry); } SetLastError(RtlNtStatusToDosError(Status)); pFFEntry = NULL; } } if (pFFEntry) { if (pFFEntry->DirectoryHandle) { if (!pFFindEntry || !NT_SUCCESS(pFFEntry->LastQueryStatus)) { NumDirectoryHandle--; NtClose(pFFEntry->DirectoryHandle); pFFEntry->DirectoryHandle = 0; } } if (NumFindBuffer > MAX_FINDBUFFER || NumDirectoryHandle > MAX_DIRECTORYHANDLE) { CloseOldestFileFindBuffer(); } // // Set HeartBeat timer to close find buffers, directory handle // Tics = 8(min) * 60(sec/min) * 18(tic/sec) // pFFEntry->FindFileTics.QuadPart = 8640 + FindFileTics.QuadPart; if (!FindFileTics.QuadPart) { NextFindFileTics.QuadPart = pFFEntry->FindFileTics.QuadPart; } } } return pFFEntry; } NTSTATUS FileFindOpen( PWCHAR pwcFile, PFFINDLIST pFFindEntry, ULONG BufferSize ) { NTSTATUS Status; BOOLEAN bStatus; BOOLEAN bReturnSingleEntry; PWCHAR pwc; OBJECT_ATTRIBUTES Obja; PUNICODE_STRING FileName; PUNICODE_STRING PathName; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; Status = STATUS_SUCCESS; PathName = &pFFindEntry->PathName; FileName = &pFFindEntry->FileName; try { if (pFFindEntry->DirectoryHandle == FINDFILE_DEVICE) { Status = STATUS_NO_MORE_FILES; goto FFOFinallyExit; } if (BufferSize <= sizeof(FILE_BOTH_DIR_INFORMATION) + MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)) { Status = STATUS_BUFFER_TOO_SMALL; goto FFOFinallyExit; } if (pwcFile) { bStatus = RtlDosPathNameToNtPathName_U(pwcFile, PathName, &pwc, NULL ); if (!bStatus ) { Status = STATUS_OBJECT_PATH_NOT_FOUND; goto FFOFinallyExit; } // // Copy out the PathName, FileName // if (pwc) { bStatus = RtlCreateUnicodeString(FileName, pwc ); if (!bStatus) { Status = STATUS_NO_MEMORY; goto FFOFinallyExit; } PathName->Length = (USHORT)((ULONG)pwc - (ULONG)PathName->Buffer); if (PathName->Buffer[(PathName->Length>>1)-2] != (WCHAR)':' ) { PathName->Length -= sizeof(UNICODE_NULL); } } else { FileName->Length = 0; FileName->MaximumLength = 0; } bReturnSingleEntry = FALSE; } else { bReturnSingleEntry = pFFindEntry->SupportReset; } // // Prepare Find Buffer for NtQueryDirectory // if (BufferSize != pFFindEntry->FindBufferLength) { if (pFFindEntry->FindBufferBase) { RtlFreeHeap(RtlProcessHeap(), 0, pFFindEntry->FindBufferBase); } else { NumFindBuffer++; } pFFindEntry->FindBufferBase = RtlAllocateHeap(RtlProcessHeap(), 0, BufferSize ); if (!pFFindEntry->FindBufferBase) { Status = STATUS_NO_MEMORY; goto FFOFinallyExit; } } pFFindEntry->FindBufferNext = NULL; pFFindEntry->FindBufferLength = BufferSize; DirectoryInfo = pFFindEntry->FindBufferBase; // // Open the directory for list access // if (!pFFindEntry->DirectoryHandle) { InitializeObjectAttributes( &Obja, PathName, OBJ_CASE_INSENSITIVE, NULL, NULL ); Status = NtOpenFile( &pFFindEntry->DirectoryHandle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if (!NT_SUCCESS(Status)) { if (pwcFile) { pFFindEntry->DirectoryHandle = FileFindFirstDevice(pwcFile, DirectoryInfo ); } else { pFFindEntry->DirectoryHandle = NULL; } if (pFFindEntry->DirectoryHandle) { Status = STATUS_SUCCESS; goto FFOFinallyExit; } if (Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_TYPE_MISMATCH ) { Status = STATUS_OBJECT_PATH_NOT_FOUND; } goto FFOFinallyExit; } NumDirectoryHandle++; } // // Prepare the filename for NtQueryDirectory // if (pwcFile) { WCHAR wchCurr, wchPrev; int Len = FileName->Length/sizeof(WCHAR); // // If there is no file part, but we are not looking at a device exit // if (!Len) { // // At this point, pwcFile has been parsed to PathName and FileName. If PathName // does not exist, the NtOpen() above will have failed and we will not be here. // PathName is formatted to \??\c:\xxx\yyy\zzz // DOS had this "feature" that if you looked for something like c:\foobar\, you'd // get PATH_NOT_FOUND, but if you looked for c:\ or \ you'd get NO_MORE_FILES, // so we special case this here. If the caller is only looking for c:\ or \ // PathName will be \??\c:\ If the caller is looking for ANY other string, // the PathName string will be longer than strlen("\??\c:\") because the text of // any dir will be added to the end. That's why a simple check of the string len // works at this time. // if ( PathName->Length > (sizeof( L"\\??\\c:\\")-sizeof(WCHAR)) ) { Status = STATUS_OBJECT_PATH_NOT_FOUND; } else { Status = STATUS_NO_MORE_FILES; } goto FFOFinallyExit; } // // ntio expects the following transmogrifications: // // - Change all ? to DOS_QM // - Change all . followed by ? or * to DOS_DOT // - Change all * followed by a . into DOS_STAR // // However, the doskrnl and wow32 have expanded '*'s to '?'s // so the * rules can be ignored. // pwc = FileName->Buffer; wchPrev = 0; while (Len--) { wchCurr = *pwc; if (wchCurr == L'?') { if (wchPrev == L'.') { *(pwc - 1) = DOS_DOT; } *pwc = DOS_QM; } wchPrev = wchCurr; pwc++; } } #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "FFOpen %x %ws (%ws)\n", pFFindEntry->DirectoryHandle, FileName->Buffer, pwcFile ); OutputDebugStringOem(demDebugBuffer); } #endif // // Do an initial query to fill the buffers, and verify everything is ok // Status = NtQueryDirectoryFile( pFFindEntry->DirectoryHandle, NULL, NULL, NULL, &IoStatusBlock, DirectoryInfo, BufferSize, FileBothDirectoryInformation, bReturnSingleEntry, FileName, FALSE ); FFOFinallyExit:; } finally { if (!NT_SUCCESS(Status)) { #if DBG if ((fShowSVCMsg & DEMFILIO) && !NT_SUCCESS(Status)) { sprintf(demDebugBuffer, "FFOpen Status %x\n", Status); OutputDebugStringOem(demDebugBuffer); } #endif FileFindClose(pFFindEntry); RtlFreeUnicodeString(PathName); PathName->Buffer = NULL; RtlFreeUnicodeString(FileName); FileName->Buffer = NULL; } else { pFFindEntry->FindBufferNext = pFFindEntry->FindBufferBase; } } return Status; } /* * Closes a FileFindHandle */ VOID FileFindClose( PFFINDLIST pFFindEntry ) { NTSTATUS Status; HANDLE DirectoryHandle; DirectoryHandle = pFFindEntry->DirectoryHandle; if (DirectoryHandle && DirectoryHandle != FINDFILE_DEVICE) { NtClose(DirectoryHandle); --NumDirectoryHandle; } pFFindEntry->DirectoryHandle = 0; if (pFFindEntry->FindBufferBase) { RtlFreeHeap(RtlProcessHeap(), 0, pFFindEntry->FindBufferBase); --NumFindBuffer; } pFFindEntry->FindBufferBase = NULL; pFFindEntry->FindBufferNext = NULL; pFFindEntry->FindBufferLength = 0; pFFindEntry->FindFileTics.QuadPart = 0; if (!NumDirectoryHandle && !NumFindBuffer) { FindFileTics.QuadPart = 0; NextFindFileTics.QuadPart = 0; } } /* * FileFindReset * * Resets search pos according to FileName, FileIndex. * The FindBuffers will point to the next file in the search * order. Assumes that the remembered search pos, has not been * reached yet for the current search. * */ NTSTATUS FileFindReset( PFFINDLIST pFFindEntry ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; UNICODE_STRING LastFileName; UNICODE_STRING CurrFileName; BOOLEAN bSlowReset; if (pFFindEntry->DirectoryHandle == FINDFILE_DEVICE) { return STATUS_NO_MORE_FILES; } Status = STATUS_UNSUCCESSFUL; LastFileName.Length = (USHORT)pFFindEntry->DosData.FileNameLength; LastFileName.MaximumLength = (USHORT)pFFindEntry->DosData.FileNameLength; LastFileName.Buffer = pFFindEntry->DosData.FileName; RtlInitUnicodeString(&CurrFileName, L"."); if (!RtlCompareUnicodeString(&LastFileName, &CurrFileName, TRUE)) { bSlowReset = TRUE; } else { RtlInitUnicodeString(&CurrFileName, L".."); if (!RtlCompareUnicodeString(&LastFileName, &CurrFileName, TRUE)) { bSlowReset = TRUE; } else { bSlowReset = FALSE; } } // // if the last file name, wasn't Dots and the volume supports reset // functionality call nt file sysetm to do the reset. // if (!bSlowReset && pFFindEntry->SupportReset) { VDMQUERYDIRINFO VdmQueryDirInfo; UNICODE_STRING UnicodeString; DirectoryInfo = (PFILE_BOTH_DIR_INFORMATION) pFFindEntry->FindBufferBase; VdmQueryDirInfo.FileHandle = pFFindEntry->DirectoryHandle; VdmQueryDirInfo.FileInformation = DirectoryInfo; VdmQueryDirInfo.Length = pFFindEntry->FindBufferLength; VdmQueryDirInfo.FileIndex = pFFindEntry->DosData.FileIndex; UnicodeString.Length = (USHORT)pFFindEntry->DosData.FileNameLength; UnicodeString.MaximumLength = UnicodeString.Length; UnicodeString.Buffer = pFFindEntry->DosData.FileName; VdmQueryDirInfo.FileName = &UnicodeString; Status = NtVdmControl(VdmQueryDir, &VdmQueryDirInfo); if (NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES || Status == STATUS_NO_SUCH_FILE) { return Status; } pFFindEntry->SupportReset = TRUE; } // // Reset the slow way by comparing FileName directly. // // WARNING: if the "remembered" File has been deleted we will // fail, is there something else we can do ? // Status = STATUS_NO_MORE_FILES; while (TRUE) { // // If there is no data in the find file buffer, call NtQueryDir // DirectoryInfo = pFFindEntry->FindBufferNext; if (!DirectoryInfo) { DirectoryInfo = pFFindEntry->FindBufferBase; Status = NtQueryDirectoryFile( pFFindEntry->DirectoryHandle, NULL, // no event NULL, // no apcRoutine NULL, // no apcContext &IoStatusBlock, DirectoryInfo, pFFindEntry->FindBufferLength, FileBothDirectoryInformation, FALSE, // single entry NULL, // no file name FALSE ); if (!NT_SUCCESS(Status)) { #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "FFReset Status %x\n", Status); OutputDebugStringOem(demDebugBuffer); } #endif return Status; } } if ( DirectoryInfo->NextEntryOffset ) { pFFindEntry->FindBufferNext = (PVOID)((ULONG)DirectoryInfo + DirectoryInfo->NextEntryOffset); } else { pFFindEntry->FindBufferNext = NULL; } if (DirectoryInfo->FileIndex == pFFindEntry->DosData.FileIndex) { CurrFileName.Length = (USHORT)DirectoryInfo->FileNameLength; CurrFileName.MaximumLength = (USHORT)DirectoryInfo->FileNameLength; CurrFileName.Buffer = DirectoryInfo->FileName; if (!RtlCompareUnicodeString(&LastFileName, &CurrFileName, TRUE)) { return STATUS_SUCCESS; } } } return Status; } /* * FileFindLast - Attempts to fill the FindFile buffer completely. * * * PFFINDLIST pFFindEntry - * * Returns - Status of NtQueryDir operation if invoked, otherwise * STATUS_SUCCESS. * */ NTSTATUS FileFindLast( PFFINDLIST pFFindEntry ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirInfo, LastDirInfo; LONG BytesLeft; if (pFFindEntry->DirectoryHandle == FINDFILE_DEVICE) { return STATUS_NO_MORE_FILES; } if (pFFindEntry->FindBufferNext) { ULONG BytesOffset; BytesOffset = (ULONG)pFFindEntry->FindBufferNext - (ULONG)pFFindEntry->FindBufferBase; if (BytesOffset) { RtlMoveMemory(pFFindEntry->FindBufferBase, pFFindEntry->FindBufferNext, pFFindEntry->FindBufferLength - BytesOffset ); } pFFindEntry->FindBufferNext = pFFindEntry->FindBufferBase; DirInfo = pFFindEntry->FindBufferBase; while (DirInfo->NextEntryOffset) { DirInfo = (PVOID)((ULONG)DirInfo + DirInfo->NextEntryOffset); } LastDirInfo = DirInfo; DirInfo = (PVOID)&DirInfo->FileName[DirInfo->FileNameLength>>1]; DirInfo = (PVOID) (((ULONG) DirInfo + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1)); BytesLeft = pFFindEntry->FindBufferLength - ((ULONG)DirInfo - (ULONG)pFFindEntry->FindBufferBase); } else { DirInfo = pFFindEntry->FindBufferBase; LastDirInfo = NULL; BytesLeft = pFFindEntry->FindBufferLength; } // the size of the dirinfo structure including the name must be a longlong. while (BytesLeft > sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(LONGLONG) + MAXIMUM_FILENAME_LENGTH*sizeof(WCHAR)) { Status = NtQueryDirectoryFile( pFFindEntry->DirectoryHandle, NULL, // no event NULL, // no apcRoutine NULL, // no apcContext &IoStatusBlock, DirInfo, BytesLeft, FileBothDirectoryInformation, FALSE, // single entry ? NULL, // no file name FALSE ); if (Status == STATUS_NO_MORE_FILES || Status == STATUS_NO_SUCH_FILE) { #if DBG if ((fShowSVCMsg & DEMFILIO)) { sprintf(demDebugBuffer, "FFLast Status %x\n", Status); OutputDebugStringOem(demDebugBuffer); } #endif return Status; } if (!NT_SUCCESS(Status)) { break; } if (LastDirInfo) { LastDirInfo->NextEntryOffset =(ULONG)DirInfo - (ULONG)LastDirInfo; } else { pFFindEntry->FindBufferNext = pFFindEntry->FindBufferBase; } while (DirInfo->NextEntryOffset) { DirInfo = (PVOID)((ULONG)DirInfo + DirInfo->NextEntryOffset); } LastDirInfo = DirInfo; DirInfo = (PVOID)&DirInfo->FileName[DirInfo->FileNameLength>>1]; DirInfo = (PVOID) (((ULONG) DirInfo + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1)); BytesLeft = pFFindEntry->FindBufferLength - ((ULONG)DirInfo - (ULONG)pFFindEntry->FindBufferBase); } return STATUS_SUCCESS; } /* * FileFindNext - retrieves the next file in the current search order, * * PFFINDDOSDATA pFFindDD * Receives File info returned by the nt FileSystem * * PFFINDLIST pFFindEntry - * Contains the DirectoryInfo (FileName,FileIndex) necessary to reset a * search pos. For operations other than QDIR_RESET_SCAN, this is ignored. * * Returns - * If Got a DirectoryInformation Entry, STATUS_SUCCESS * If no Open Directory handle and is unknown if there are more files * returns STATUS_IN`VALID_HANDLE * */ NTSTATUS FileFindNext( PFFINDDOSDATA pFFindDD, PFFINDLIST pFFindEntry ) { NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; PFILE_BOTH_DIR_INFORMATION DirectoryInfo; if (pFFindEntry->DirectoryHandle == FINDFILE_DEVICE) { return STATUS_NO_MORE_FILES; } Status = STATUS_UNSUCCESSFUL; do { // // If there is no data in the find file buffer, call NtQueryDir // DirectoryInfo = pFFindEntry->FindBufferNext; if (!DirectoryInfo) { if (!pFFindEntry->DirectoryHandle) { return STATUS_INVALID_HANDLE; } DirectoryInfo = pFFindEntry->FindBufferBase; Status = NtQueryDirectoryFile( pFFindEntry->DirectoryHandle, NULL, // no event NULL, // no apcRoutine NULL, // no apcContext &IoStatusBlock, DirectoryInfo, pFFindEntry->FindBufferLength, FileBothDirectoryInformation, FALSE, // single entry ? NULL, // no file name FALSE ); if (!NT_SUCCESS(Status)) { #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "FFNext Status %x\n", Status); OutputDebugStringOem(demDebugBuffer); } #endif return Status; } } if ( DirectoryInfo->NextEntryOffset ) { pFFindEntry->FindBufferNext = (PVOID)((ULONG)DirectoryInfo + DirectoryInfo->NextEntryOffset); } else { pFFindEntry->FindBufferNext = NULL; } } while (!CopyDirInfoToDosData(pFFindEntry->DirectoryHandle, pFFindDD, DirectoryInfo, pFFindEntry->usSrchAttr )); return STATUS_SUCCESS; } BOOL IsVolumeNtfs( HANDLE DirectoryHandle) { union { FILE_FS_ATTRIBUTE_INFORMATION AttributeInfo; BYTE rgBuffer[sizeof(FILE_FS_ATTRIBUTE_INFORMATION) + MAX_PATH*sizeof(WCHAR)]; } Attrib; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; BOOL fNtfsVolume = TRUE; Status = NtQueryVolumeInformationFile(DirectoryHandle, &IoStatusBlock, &Attrib, sizeof(Attrib), FileFsAttributeInformation); if (NT_SUCCESS(Status)) { fNtfsVolume = !_wcsicmp(Attrib.AttributeInfo.FileSystemName, L"Ntfs"); } return(fNtfsVolume); } /* * CopyDirInfoToDosData * */ BOOL CopyDirInfoToDosData( HANDLE DirectoryHandle, PFFINDDOSDATA pFFindDD, PFILE_BOTH_DIR_INFORMATION DirInfo, USHORT SearchAttr ) { NTSTATUS Status; OEM_STRING OemString; UNICODE_STRING UnicodeString; DWORD dwAttr; BOOLEAN SpacesInName = FALSE; BOOLEAN NameValid8Dot3; // // match the attributes // See DOS5.0 sources (dir2.asm, MatchAttributes) // ignores READONLY and ARCHIVE bits // if (FILE_ATTRIBUTE_NORMAL == DirInfo->FileAttributes) { DirInfo->FileAttributes = 0; } else { DirInfo->FileAttributes &= DOS_ATTR_MASK; } dwAttr = DirInfo->FileAttributes; dwAttr &= ~(FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY); if (((~(ULONG)SearchAttr) & dwAttr) & ATTR_ALL) return FALSE; // // set up the destination oem string buffer // OemString.Buffer = pFFindDD->cFileName; OemString.MaximumLength = 14; // // see if the name is legal fat // UnicodeString.Buffer = DirInfo->FileName; UnicodeString.Length = (USHORT)DirInfo->FileNameLength; UnicodeString.MaximumLength = (USHORT)DirInfo->FileNameLength; NameValid8Dot3 = RtlIsNameLegalDOS8Dot3( &UnicodeString, &OemString, &SpacesInName ); // // if failed (incompatible codepage or illegal FAT name), // use the short name // if (!NameValid8Dot3 || (SpacesInName && (DirInfo->ShortName[0] != UNICODE_NULL))) { if (DirInfo->ShortName[0] == UNICODE_NULL) { pFFindDD->cFileName[0] = '\0'; return FALSE; } UnicodeString.Buffer = DirInfo->ShortName; UnicodeString.Length = (USHORT)DirInfo->ShortNameLength; UnicodeString.MaximumLength = (USHORT)DirInfo->ShortNameLength; if (!NT_SUCCESS(RtlUpcaseUnicodeStringToCountedOemString(&OemString, &UnicodeString, FALSE))) { pFFindDD->cFileName[0] = '\0'; return FALSE; } } OemString.Buffer[OemString.Length] = '\0'; // fill in time, size and attributes // // bjm-11/10/97 - for directories, FAT does not update lastwritten time // when things actually happen in the directory. NTFS does. This causes // a problem for Encore 3.0 (when running on NTFS) which, at install time, // gets the lastwritten time for it's directory, then compares it, at app // run time, to the "current" last written time and will bail (with a "Not // correctly installed" message) if they're different. So, 16 bit apps // (which can only reasonably expect FAT info), should only get creation // time for this file if it's a directory. // // VadimB: 11/20/98 -- this hold true ONLY for apps running on NTFS and // not FAT -- since older FAT partitions are then given an incorrect // creation time if ((FILE_ATTRIBUTE_DIRECTORY & DirInfo->FileAttributes) && IsVolumeNtfs(DirectoryHandle)) { pFFindDD->ftLastWriteTime = *(LPFILETIME)&DirInfo->CreationTime; } else { pFFindDD->ftLastWriteTime = *(LPFILETIME)&DirInfo->LastWriteTime; } pFFindDD->dwFileSizeLow = DirInfo->EndOfFile.LowPart; pFFindDD->uchFileAttributes = (UCHAR)DirInfo->FileAttributes; // Save File Name, Index for restarting searches pFFindDD->FileIndex = DirInfo->FileIndex; pFFindDD->FileNameLength = DirInfo->FileNameLength; RtlCopyMemory(pFFindDD->FileName, DirInfo->FileName, DirInfo->FileNameLength ); pFFindDD->FileName[DirInfo->FileNameLength >> 1] = UNICODE_NULL; return TRUE; } HANDLE FileFindFirstDevice( PWCHAR FileName, PFILE_BOTH_DIR_INFORMATION DirectoryInfo ) /*++ Routine Description: Determines if the FileName is a device, and copies out the device name found if it is. Arguments: FileName - Supplies the device name of the file to find. pQueryDirInfo - On a successful find, this parameter returns information about the located file. Return Value: --*/ { ULONG DeviceNameData; PWSTR DeviceName; DeviceNameData = RtlIsDosDeviceName_U(FileName); if (DeviceNameData) { RtlZeroMemory(DirectoryInfo, sizeof(FILE_BOTH_DIR_INFORMATION)); DirectoryInfo->FileAttributes = FILE_ATTRIBUTE_ARCHIVE; DeviceName = (PWSTR)((ULONG)FileName + (DeviceNameData >> 16)); DeviceNameData &= 0xffff; DirectoryInfo->FileNameLength = DeviceNameData; DirectoryInfo->ShortNameLength = (CCHAR)DeviceNameData; RtlCopyMemory(DirectoryInfo->FileName, DeviceName, DeviceNameData ); RtlCopyMemory(DirectoryInfo->ShortName, DeviceName, DeviceNameData ); return FINDFILE_DEVICE; } return NULL; } /* FillFcbVolume - fill Volume info in the FCB * * Entry - pSrchBuf FCB Search buffer to be filled in * FileName File Name (interesting part is the drive letter) * * Exit - SUCCESS * Client (CF) - 0 * pSrchBuf is filled with volume info * * FAILURE * Client (CF) - 1 * Client (AX) = Error Code */ VOID FillFcbVolume( PSRCHBUF pSrchBuf, CHAR *pFileName, USHORT SearchAttr ) { CHAR *pch; PDIRENT pDirEnt = &pSrchBuf->DirEnt; CHAR FullPathBuffer[MAX_PATH]; CHAR achBaseName[DOS_VOLUME_NAME_SIZE + 2]; // 11 chars, '.', and null CHAR achVolumeName[NT_VOLUME_NAME_SIZE]; // // form a path without base name // this makes sure only on root directory will get the // volume label(the GetVolumeInformationOem will fail // if the given path is not root directory) // strcpy(FullPathBuffer, pFileName); pch = strrchr(FullPathBuffer, '\\'); if (pch) { pch++; // truncate to dos file name length (including period) pch[DOS_VOLUME_NAME_SIZE + 1] = '\0'; strcpy(achBaseName, pch); #ifdef DBCS #if defined(NEC_98) // BUG fix for DBCS small alphabet converted to large it. demCharUpper(achBaseName); #else // !NEC_98 CharUpper(achBaseName); #endif // !NEC_98 #else // !DBCS _strupr(achBaseName); #endif // !DBCS *pch = '\0'; } else { achBaseName[0] = '\0'; } // // if searching for volume only the DOS uses first 3 letters for // root drive path ignoring the rest of the path // as long as the full pathname is valid. // if (SearchAttr == ATTR_VOLUME_ID && (pch = strchr(FullPathBuffer, '\\')) && GetFileAttributes(FullPathBuffer) != 0xffffffff ) { pch++; *pch = '\0'; strcpy(achBaseName, szStartDotStar); } if (GetVolumeInformationOem(FullPathBuffer, achVolumeName, NT_VOLUME_NAME_SIZE, NULL, NULL, NULL, NULL, 0) == FALSE) { demClientError(INVALID_HANDLE_VALUE, *pFileName); return; } // truncate to dos volumen max size (no period) achVolumeName[DOS_VOLUME_NAME_SIZE] = '\0'; if (!achVolumeName[0] || !MatchVolLabel(achVolumeName, achBaseName)) { SetLastError(ERROR_NO_MORE_FILES); demClientError(INVALID_HANDLE_VALUE, *pFileName); return; } // warning !!! this assumes the FileExt follows FileName immediately memset(pSrchBuf->FileName, ' ', DOS_VOLUME_NAME_SIZE); strncpy(pSrchBuf->FileName, achVolumeName, strlen(achVolumeName)); // Now copy the directory entry strncpy(pDirEnt->FileName,pSrchBuf->FileName,8); strncpy(pDirEnt->FileExt,pSrchBuf->FileExt,3); setCF (0); return; } /* FillDtaVolume - fill Volume info in the DTA * * Entry - CHAR lpSearchName - name to match with volume name * * * Exit - SUCCESS * Returns - TRUE * pSrchBuf is filled with volume info * * FAILURE * Returns - FALSE * sets last error code */ BOOL FillDtaVolume( CHAR *pFileName, PSRCHDTA pDta, USHORT SearchAttr ) { CHAR *pch; CHAR FullPathBuffer[MAX_PATH]; CHAR achBaseName[DOS_VOLUME_NAME_SIZE + 2]; // 11 chars, '.' and null CHAR achVolumeName[NT_VOLUME_NAME_SIZE]; // // form a path without base name // this makes sure only on root directory will get the // volume label(the GetVolumeInformationOem will fail // if the given path is not root directory) // strcpy(FullPathBuffer, pFileName); pch = strrchr(FullPathBuffer, '\\'); if (pch) { pch++; pch[DOS_VOLUME_NAME_SIZE + 1] = '\0'; // max len (including period) strcpy(achBaseName, pch); #ifdef DBCS #if defined(NEC_98) // BUG fix for DBCS small alphabet converted to large it. demCharUpper(achBaseName); #else // !NEC_98 CharUpper(achBaseName); #endif // !NEC_98 #else // !DBCS _strupr(achBaseName); #endif // !DBCS *pch = '\0'; } else { achBaseName[0] = '\0'; } // // if searching for volume only the DOS uses first 3 letters for // root drive path ignoring the rest of the path, if there is no basename assume *.* // if (SearchAttr == ATTR_VOLUME_ID && (pch = strchr(FullPathBuffer, '\\')) && GetFileAttributes(FullPathBuffer) != 0xffffffff ) { pch++; if(!*pch) { strcpy(achBaseName, szStartDotStar); } *pch = '\0'; } if (GetVolumeInformationOem(FullPathBuffer, achVolumeName, NT_VOLUME_NAME_SIZE, NULL, NULL, NULL, NULL, 0) == FALSE) { return FALSE; } // truncate to dos file name length (no period) achVolumeName[DOS_VOLUME_NAME_SIZE] = '\0'; if (!achVolumeName[0] || !MatchVolLabel(achVolumeName, achBaseName)) { SetLastError(ERROR_NO_MORE_FILES); return FALSE; } // // DOS Dta search returns volume label in 8.3 format. But if label is // more than 8 characters long than NT just returns that as it is // without adding a ".". So here we have to add a "." in volume // labels, if needed. But note that FCB based volume search does'nt // add the "." So nothing need to be done there. // NtVolumeNameToDosVolumeName(pDta->achFileName, achVolumeName); pDta->uchFileAttr = ATTR_VOLUME_ID; STOREWORD(pDta->usLowSize,0); STOREWORD(pDta->usHighSize,0); // Zero out dates as we can not fetch dates for volume labels. STOREWORD(pDta->usTimeLastWrite,0); STOREWORD(pDta->usDateLastWrite,0); return TRUE; } /* * MatchVolLabel * Does a string compare to see if the vol label matches * a FAT search string. The search string is expected to * have the '*' character already expanded into '?' characters. * * WARNING: maintanes dos5.0 quirk of not caring about characters past * the defined len of each part of the vol label. * 12345678.123 * ^ ^ * * foovol foovol1 (srch string) * foo.vol foo.vol1 (srch string) * * entry: CHAR *pVol -- NT volume name * CHAR *pSrch -- dos volume name * * exit: TRUE for a match */ BOOL MatchVolLabel(CHAR *pVol, CHAR *pSrch ) { WORD w; CHAR achDosVolumeName[DOS_VOLUME_NAME_SIZE + 2]; // 11 chars, '.' and null NtVolumeNameToDosVolumeName(achDosVolumeName, pVol); pVol = achDosVolumeName; w = 8; while (w--) { if (*pVol == *pSrch) { if (!*pVol && !*pSrch) return TRUE; } else if (*pSrch == '.') { if (*pVol) return FALSE; } else if (*pSrch != '?') { return FALSE; } // move on to the next character // but not past second component part if (*pVol && *pVol != '.') pVol++; if (*pSrch && *pSrch != '.') pSrch++; } // skip trailing part of search string, in the first comp while (*pSrch && *pSrch != '.') pSrch++; w = 4; while (w--) { if (*pVol == *pSrch) { if (!*pVol && !*pSrch) return TRUE; } else if (*pSrch == '.') { if (*pVol) return FALSE; } else if (*pSrch != '?') { return FALSE; } // move on to the next character if (*pVol) pVol++; if (*pSrch) pSrch++; } return TRUE; } VOID NtVolumeNameToDosVolumeName(CHAR * pDosName, CHAR * pNtName) { char NtNameBuffer[NT_VOLUME_NAME_SIZE]; int i; char char8, char9, char10; // make a local copy so that the caller can use the same // buffer strcpy(NtNameBuffer, pNtName); if (strlen(NtNameBuffer) > 8) { char8 = NtNameBuffer[8]; char9 = NtNameBuffer[9]; char10 = NtNameBuffer[10]; // eat spaces from first 8 characters i = 7; while (NtNameBuffer[i] == ' ') i--; NtNameBuffer[i+1] = '.'; NtNameBuffer[i+2] = char8; NtNameBuffer[i+3] = char9; NtNameBuffer[i+4] = char10; NtNameBuffer[i+5] = '\0'; } strcpy(pDosName, NtNameBuffer); } /* FillFCBSrchBuf - Fill the FCB Search buffer. * * Entry - pSrchBuf FCB Search buffer to be filled in * hFind Search Handle * fFirst TRUE if call from FindFirstFCB * * Exit - None (pSrchBuf filled in) * */ VOID FillFCBSrchBuf( PFFINDDOSDATA pFFindDD, PSRCHBUF pSrchBuf, BOOL IsOnCD) { PDIRENT pDirEnt = &pSrchBuf->DirEnt; PCHAR pDot; USHORT usDate,usTime,i; FILETIME ftLocal; #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "FillFCBSrchBuf<%s>\n", pFFindDD->cFileName); OutputDebugStringOem(demDebugBuffer); } #endif // Copy file name (Max Name = 8 and Max ext = 3) if ((pDot = strchr(pFFindDD->cFileName,'.')) == NULL) { strncpy(pSrchBuf->FileName,pFFindDD->cFileName,8); _strnset(pSrchBuf->FileExt,'\x020',3); } else if (pDot == pFFindDD->cFileName) { strncpy(pSrchBuf->FileName,pFFindDD->cFileName,8); _strnset(pSrchBuf->FileExt,'\x020',3); } else { *pDot = '\0'; strncpy(pSrchBuf->FileName,pFFindDD->cFileName,8); *pDot++ = '\0'; strncpy(pSrchBuf->FileExt,pDot,3); } for (i=0;i<8;i++) { if (pSrchBuf->FileName[i] == '\0') pSrchBuf->FileName[i]='\x020'; } for (i=0;i<3;i++) { if (pSrchBuf->FileExt[i] == '\0') pSrchBuf->FileExt[i]='\x020'; } STOREWORD(pSrchBuf->usCurBlkNumber,0); STOREWORD(pSrchBuf->usRecordSize,0); STOREDWORD(pSrchBuf->ulFileSize, pFFindDD->dwFileSizeLow); // Convert NT File time/date to DOS time/date FileTimeToLocalFileTime (&pFFindDD->ftLastWriteTime,&ftLocal); FileTimeToDosDateTime (&ftLocal, &usDate, &usTime); // Now copy the directory entry strncpy(pDirEnt->FileName,pSrchBuf->FileName,8); strncpy(pDirEnt->FileExt,pSrchBuf->FileExt,3); // SudeepB - 28-Jul-1997 // // For CDFS, Win3.1/DOS/Win95, only return FILE_ATTRIBUTE_DIRECTORY (10) // for directories while WinNT returns // FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY (11). // Some VB controls that app setups use, depend on getting // FILE_ATTRIBUTE_DIRECTORY (10) only or otherwise are broken. // An example of this is Cliffs StudyWare series. // if (IsOnCD && pFFindDD->uchFileAttributes == (ATTR_DIRECTORY | ATTR_READ_ONLY)) pDirEnt->uchAttributes = ATTR_DIRECTORY; else pDirEnt->uchAttributes = pFFindDD->uchFileAttributes; STOREWORD(pDirEnt->usTime,usTime); STOREWORD(pDirEnt->usDate,usDate); STOREDWORD(pDirEnt->ulFileSize,pFFindDD->dwFileSizeLow); return; } /* FillSrchDta - Fill DTA for FIND_FIRST,FIND_NEXT operations. * * Entry - pW32FindData Buffer containing file data * hFind - Handle returned by FindFirstFile * PSRCHDTA pDta * * Exit - None * * Note : It is guranteed that file name adhers to 8:3 convention. * demSrchFile makes sure of that condition. * */ VOID FillSrchDta( PFFINDDOSDATA pFFindDD, PSRCHDTA pDta, BOOL IsOnCD) { USHORT usDate,usTime; FILETIME ftLocal; // SudeepB - 28-Jul-1997 // // For CDFS, Win3.1/DOS/Win95, only return FILE_ATTRIBUTE_DIRECTORY (10) // for directories while WinNT returns // FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_READONLY (11). // Some VB controls that app setups use, depend on getting // FILE_ATTRIBUTE_DIRECTORY (10) only or otherwise are broken. // An example of this is Cliffs StudyWare series. // if (IsOnCD && pFFindDD->uchFileAttributes == (ATTR_DIRECTORY | ATTR_READ_ONLY)) pDta->uchFileAttr = ATTR_DIRECTORY; else pDta->uchFileAttr = pFFindDD->uchFileAttributes; // Convert NT File time/date to DOS time/date FileTimeToLocalFileTime (&pFFindDD->ftLastWriteTime,&ftLocal); FileTimeToDosDateTime (&ftLocal, &usDate, &usTime); STOREWORD(pDta->usTimeLastWrite,usTime); STOREWORD(pDta->usDateLastWrite,usDate); STOREWORD(pDta->usLowSize,(USHORT)pFFindDD->dwFileSizeLow); STOREWORD(pDta->usHighSize,(USHORT)(pFFindDD->dwFileSizeLow >> 16)); #if DBG if (fShowSVCMsg & DEMFILIO) { sprintf(demDebugBuffer, "FillSrchDta<%s>\n", pFFindDD->cFileName); OutputDebugStringOem(demDebugBuffer); } #endif strncpy(pDta->achFileName,pFFindDD->cFileName, 13); return; } VOID demCloseAllPSPRecords (VOID) { PLIST_ENTRY Next; PPSP_FFINDLIST pPspFFindEntry; Next = PspFFindHeadList.Flink; while (Next != &PspFFindHeadList) { pPspFFindEntry = CONTAINING_RECORD(Next,PSP_FFINDLIST,PspFFindEntry); FreeFFindList( &pPspFFindEntry->FFindHeadList); Next= Next->Flink; RemoveEntryList(&pPspFFindEntry->PspFFindEntry); free(pPspFFindEntry); } } void DemHeartBeat(void) { PLIST_ENTRY Next; PLIST_ENTRY pFFindHeadList; PPSP_FFINDLIST pPspFFindEntry; PFFINDLIST pFFindEntry; if (!NumFindBuffer || NextFindFileTics.QuadPart > ++FindFileTics.QuadPart) { return; } pPspFFindEntry = GetPspFFindList(FETCHWORD(pusCurrentPDB[0])); if (!pPspFFindEntry) { return; } pFFindHeadList = &pPspFFindEntry->FFindHeadList; Next = pFFindHeadList->Blink; while (Next != pFFindHeadList) { pFFindEntry = CONTAINING_RECORD(Next,FFINDLIST, FFindEntry); if (pFFindEntry->FindFileTics.QuadPart) { if (pFFindEntry->FindFileTics.QuadPart <= FindFileTics.QuadPart) { FileFindClose(pFFindEntry); } else { NextFindFileTics.QuadPart = pFFindEntry->FindFileTics.QuadPart; return; } } Next = Next->Blink; } NextFindFileTics.QuadPart = 0; FindFileTics.QuadPart = 0; } // // CloseOldestFileFindBuffer // walks the psp file find list backwards to find the oldest // entry with FindBuffers, directory handles and closes it. // void CloseOldestFileFindBuffer( void ) { PLIST_ENTRY Next, NextPsp; PLIST_ENTRY pFFindHeadList; PPSP_FFINDLIST pPspFFindEntry; PFFINDLIST pFFEntry; NextPsp = PspFFindHeadList.Blink; while (NextPsp != &PspFFindHeadList) { pPspFFindEntry = CONTAINING_RECORD(NextPsp,PSP_FFINDLIST,PspFFindEntry); pFFindHeadList = &pPspFFindEntry->FFindHeadList; Next = pFFindHeadList->Blink; while (Next != pFFindHeadList) { pFFEntry = CONTAINING_RECORD(Next,FFINDLIST, FFindEntry); if (NumFindBuffer >= MAX_FINDBUFFER) { FileFindClose(pFFEntry); } else if (pFFEntry->DirectoryHandle && NumDirectoryHandle >= MAX_DIRECTORYHANDLE) { NumDirectoryHandle--; NtClose(pFFEntry->DirectoryHandle); pFFEntry->DirectoryHandle = 0; } if (NumFindBuffer < MAX_FINDBUFFER && NumDirectoryHandle < MAX_DIRECTORYHANDLE) { return; } Next = Next->Blink; } NextPsp= NextPsp->Blink; } } /* * GetFFindEntryByFindId */ PFFINDLIST GetFFindEntryByFindId(ULONG NextFFindId) { PLIST_ENTRY NextPsp; PLIST_ENTRY Next; PPSP_FFINDLIST pPspFFindEntry; PFFINDLIST pFFindEntry; PLIST_ENTRY pFFindHeadList; NextPsp = PspFFindHeadList.Flink; while (NextPsp != &PspFFindHeadList) { pPspFFindEntry = CONTAINING_RECORD(NextPsp,PSP_FFINDLIST,PspFFindEntry); pFFindHeadList = &pPspFFindEntry->FFindHeadList; Next = pFFindHeadList->Flink; while (Next != pFFindHeadList) { pFFindEntry = CONTAINING_RECORD(Next, FFINDLIST, FFindEntry); if (pFFindEntry->FFindId == NextFFindId) { return pFFindEntry; } Next= Next->Flink; } NextPsp= NextPsp->Flink; } return NULL; } /* AddFFindEntry - Adds a new File Find entry to the current * PSP's PspFileFindList * * Entry - * * Exit - PFFINDLIST pFFindList; */ PFFINDLIST AddFFindEntry( PWCHAR pwcFile, PFFINDLIST pFFindEntrySrc ) { PPSP_FFINDLIST pPspFFindEntry; PFFINDLIST pFFindEntry; ULONG Len; pPspFFindEntry = GetPspFFindList(FETCHWORD(pusCurrentPDB[0])); // // if a Psp entry doesn't exist // Allocate one, initialize it and insert it into the list // if (!pPspFFindEntry) { pPspFFindEntry = (PPSP_FFINDLIST) malloc(sizeof(PSP_FFINDLIST)); if (!pPspFFindEntry) return NULL; pPspFFindEntry->usPsp = FETCHWORD(pusCurrentPDB[0]); InitializeListHead(&pPspFFindEntry->FFindHeadList); InsertHeadList(&PspFFindHeadList, &pPspFFindEntry->PspFFindEntry); } // // Create the FileFindEntry and add to the FileFind list // pFFindEntry = (PFFINDLIST) malloc(sizeof(FFINDLIST)); if (!pFFindEntry) { return pFFindEntry; } // // Fill in FFindList // *pFFindEntry = *pFFindEntrySrc; // // Insert at the head of this psp list // InsertHeadList(&pPspFFindEntry->FFindHeadList, &pFFindEntry->FFindEntry); return pFFindEntry; } /* FreeFFindEntry * * Entry - PFFINDLIST pFFindEntry * * Exit - None * */ VOID FreeFFindEntry(PFFINDLIST pFFindEntry) { RemoveEntryList(&pFFindEntry->FFindEntry); FileFindClose(pFFindEntry); RtlFreeUnicodeString(&pFFindEntry->FileName); RtlFreeUnicodeString(&pFFindEntry->PathName); free(pFFindEntry); return; } /* FreeFFindList * * Entry - Frees the entire list * * Exit - None * */ VOID FreeFFindList(PLIST_ENTRY pFFindHeadList) { PLIST_ENTRY Next; PFFINDLIST pFFindEntry; Next = pFFindHeadList->Flink; while (Next != pFFindHeadList) { pFFindEntry = CONTAINING_RECORD(Next,FFINDLIST, FFindEntry); Next= Next->Flink; FreeFFindEntry(pFFindEntry); } return; } /* GetPspFFindList * * Entry - USHORT CurrPsp * * Exit - Success - PPSP_FFINDLIST * Failure - NULL * */ PPSP_FFINDLIST GetPspFFindList(USHORT CurrPsp) { PLIST_ENTRY Next; PPSP_FFINDLIST pPspFFindEntry; Next = PspFFindHeadList.Flink; while (Next != &PspFFindHeadList) { pPspFFindEntry = CONTAINING_RECORD(Next,PSP_FFINDLIST,PspFFindEntry); if (CurrPsp == pPspFFindEntry->usPsp) { return pPspFFindEntry; } Next= Next->Flink; } return NULL; } #if defined(NEC_98) // BUG fix for DBCS small alphabet in file name converted to large it. extern int dbcs_first[]; void demCharUpper(char * pszStr) { for(;*pszStr;) { if(dbcs_first[*pszStr&0xFF]) { pszStr++; if(*pszStr == '\0') break; } else { if(*pszStr >= 'a' && *pszStr <= 'z') *pszStr -= 0x20; } pszStr++; } } #endif // NEC_98