windows-nt/Source/XPSP1/NT/base/mvdm/dos/dem/demsrch.c

2610 lines
68 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/* 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 <vdm.h>
#include <softpc.h>
#include <mvdm.h>
#include <memory.h>
#include <nt_vdd.h>
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