2610 lines
68 KiB
C
2610 lines
68 KiB
C
/* 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
|