mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2265 lines
68 KiB
2265 lines
68 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dllfind.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the OS/2 V2.0 findfirst/next/close APIs
|
|
|
|
Author:
|
|
|
|
Therese Stowell (thereses) 5-Jan-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_ERRORS
|
|
#define INCL_OS2V20_FILESYS
|
|
#include "os2dll.h"
|
|
#ifdef DBCS
|
|
// MSKK Sep.29.1993 V-AkihiS
|
|
#include "conrqust.h"
|
|
#include "os2win.h"
|
|
#endif
|
|
|
|
|
|
APIRET
|
|
AllocateSearchHandle(
|
|
OUT PHDIR SearchHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a search handle and saves the data associated
|
|
with a search.
|
|
|
|
Arguments:
|
|
|
|
SearchHandle - where to store the OS/2 search handle
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Note:
|
|
|
|
As an optimization, we could remember the number of the last search
|
|
handle and start looking for a new one from there.
|
|
|
|
The caller must have the FileLock exclusively.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG NewTableLength;
|
|
PSEARCH_RECORD *NewTable;
|
|
|
|
for (i=2;i<SearchHandleTableLength;i++) {
|
|
if (SearchHandleTable[i] == SEARCH_HANDLE_FREE) {
|
|
try {
|
|
*SearchHandle = (HDIR) i;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
NewTableLength = SearchHandleTableLength + SEARCH_TABLE_HANDLE_INCREMENT;
|
|
NewTable = RtlAllocateHeap(Od2Heap,0,NewTableLength * sizeof(ULONG));
|
|
if (NewTable == NULL) {
|
|
#if DBG
|
|
KdPrint(( "OS2: AllocateSearchHandle, no memory in Od2Heap\n" ));
|
|
ASSERT(FALSE);
|
|
#endif
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
RtlMoveMemory(NewTable,SearchHandleTable,SearchHandleTableLength * sizeof(ULONG));
|
|
for (i=SearchHandleTableLength;i<NewTableLength;i++) {
|
|
NewTable[i] = SEARCH_HANDLE_FREE;
|
|
}
|
|
if (SearchHandleTableLength != INITIAL_SEARCH_HANDLES) {
|
|
RtlFreeHeap(Od2Heap,0,SearchHandleTable);
|
|
}
|
|
SearchHandleTable = NewTable;
|
|
try {
|
|
*SearchHandle = (HDIR) SearchHandleTableLength;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
SearchHandleTableLength = NewTableLength;
|
|
Od2ExitGP();
|
|
}
|
|
|
|
SearchHandleTableLength = NewTableLength;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
APIRET
|
|
FreeSearchHandle(
|
|
IN HDIR SearchHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a search handle.
|
|
|
|
Arguments:
|
|
|
|
SearchHandle - OS/2 search handle to free
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Note:
|
|
|
|
The caller must have the FileLock exclusively.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSEARCH_RECORD SearchRecord;
|
|
|
|
// we need to close the NT directory handle associated with the search
|
|
// and free the search record.
|
|
|
|
if (SearchHandleTable[(ULONG) SearchHandle] != SEARCH_HANDLE_FREE) {
|
|
SearchRecord = (PSEARCH_RECORD) SearchHandleTable[((ULONG)SearchHandle)];
|
|
NtClose(SearchRecord->NtHandle);
|
|
RtlFreeHeap(Od2Heap,0,SearchRecord);
|
|
SearchHandleTable[(ULONG) SearchHandle] = SEARCH_HANDLE_FREE;
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("FreeSeachHandle: invalid handle %x\n",SearchHandle);
|
|
}
|
|
#endif
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
APIRET
|
|
SaveSearchInfo(
|
|
IN BOOLEAN Rewind,
|
|
IN PFILE_DIRECTORY_INFORMATION RewindEntry,
|
|
IN PVOID FindBuffer,
|
|
IN ULONG BufferLength,
|
|
IN HANDLE DirectoryHandle,
|
|
IN ULONG Attributes,
|
|
IN OUT PHDIR UserSearchHandle,
|
|
IN ULONG FileInformationLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a search handle and saves the data associated
|
|
with a search.
|
|
|
|
Arguments:
|
|
|
|
Rewind - whether to rewind the search
|
|
|
|
RewindEntry - pointer to the entry to rewind from
|
|
|
|
FindBuffer - buffer containing entries returned by NtQueryDirectoryFile
|
|
|
|
BufferLength - length of FindBuffer
|
|
|
|
DirectoryHandle - the NT search handle
|
|
|
|
Attributes - The attributes used in the search
|
|
|
|
UserSearchHandle - where to store the OS/2 search handle (unprobed)
|
|
|
|
Return Value:
|
|
|
|
ERROR_INVALID_HANDLE - the specified search handle was invalid.
|
|
|
|
--*/
|
|
|
|
{
|
|
APIRET RetCode;
|
|
PSEARCH_RECORD SearchRecord;
|
|
ULONG SearchHandle;
|
|
#if DBG
|
|
PSZ RoutineName;
|
|
RoutineName = "SaveSearchInfo";
|
|
#endif
|
|
|
|
try {
|
|
SearchHandle = (ULONG) *UserSearchHandle;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
AcquireFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
if (SearchHandle == HDIR_CREATE) {
|
|
RetCode = AllocateSearchHandle(UserSearchHandle);
|
|
SearchHandle = (ULONG) *UserSearchHandle;
|
|
} else if (SearchHandle < SearchHandleTableLength) {
|
|
if (SearchHandleTable[SearchHandle] == SEARCH_HANDLE_FREE) {
|
|
if (SearchHandle != HDIR_SYSTEM) {
|
|
RetCode = ERROR_INVALID_HANDLE;
|
|
} else {
|
|
RetCode = NO_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
NtClose(SearchHandleTable[SearchHandle]->NtHandle); // close the old handle of DosFindFirst
|
|
if (SearchHandleTable[SearchHandle]->FindBuffer != NULL) { // free FindBuffer
|
|
RtlFreeHeap(Od2Heap,0,SearchHandleTable[SearchHandle]->FindBuffer);
|
|
}
|
|
RtlFreeHeap(Od2Heap,0,SearchHandleTable[SearchHandle]);
|
|
RetCode = NO_ERROR;
|
|
}
|
|
}
|
|
else {
|
|
RetCode = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
if (RetCode != NO_ERROR) {
|
|
ReleaseFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
return RetCode;
|
|
}
|
|
|
|
SearchHandleTable[SearchHandle] = RtlAllocateHeap(Od2Heap,0,sizeof(SEARCH_RECORD));
|
|
if (SearchHandleTable[SearchHandle] == NULL) {
|
|
#if DBG
|
|
KdPrint(( "OS2: AllocateSearchHandle, no memory in Od2Heap\n" ));
|
|
ASSERT(FALSE);
|
|
#endif
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
SearchRecord = (PSEARCH_RECORD) SearchHandleTable[SearchHandle];
|
|
SearchRecord->Attributes = Attributes;
|
|
SearchRecord->NtHandle = DirectoryHandle;
|
|
SearchRecord->InformationLevel = FileInformationLevel;
|
|
if (Rewind) {
|
|
SearchRecord->FindBuffer = FindBuffer;
|
|
SearchRecord->BufferLength = BufferLength;
|
|
SearchRecord->RewindEntry = RewindEntry;
|
|
}
|
|
else {
|
|
SearchRecord->FindBuffer = NULL;
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
}
|
|
ReleaseFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
|
|
APIRET
|
|
UpdateSearchInfo(
|
|
IN BOOLEAN Rewind,
|
|
IN PFILE_DIRECTORY_INFORMATION RewindEntry,
|
|
IN PVOID FindBuffer,
|
|
IN ULONG BufferLength,
|
|
IN HDIR SearchHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the search record after a FindNext.
|
|
|
|
Arguments:
|
|
|
|
Rewind - whether to rewind the search
|
|
|
|
RewindEntry - pointer to the entry to rewind from
|
|
|
|
SearchHandle - which search record to update
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
Note:
|
|
|
|
The caller must have the FileLock shared. we convert it to exclusive.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSEARCH_RECORD SearchRecord;
|
|
#if DBG
|
|
PSZ RoutineName;
|
|
RoutineName = "UpdateSearchInfo";
|
|
#endif
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("entering UpdateSearchInfo. rewind is %d\n",Rewind);
|
|
}
|
|
#endif
|
|
PromoteFileLocktoExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
if (SearchHandleTable[(ULONG) SearchHandle] == SEARCH_HANDLE_FREE) {
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
SearchRecord = (PSEARCH_RECORD) SearchHandleTable[((ULONG)SearchHandle)];
|
|
if (Rewind) {
|
|
SearchRecord->FindBuffer = FindBuffer;
|
|
SearchRecord->BufferLength = BufferLength;
|
|
SearchRecord->RewindEntry = RewindEntry;
|
|
}
|
|
else {
|
|
SearchRecord->FindBuffer = NULL;
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("leaving UpdateSearchInfo.\n");
|
|
}
|
|
#endif
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
MatchAttributes(
|
|
IN ULONG FoundAttributes,
|
|
IN ULONG RequestedAttributes,
|
|
IN BOOLEAN Directory
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether the FoundAttributes match the
|
|
RequestedAttributes, according to OS/2 matching rules.
|
|
|
|
Arguments:
|
|
|
|
FoundAttributes - the attributes, in NT format, associated with
|
|
the found directory entry.
|
|
|
|
RequestedAttributes - the desired attributes set, in OS/2 format.
|
|
|
|
Directory - the Directory field associated with the found directory
|
|
entry.
|
|
|
|
Return Value:
|
|
|
|
TRUE - the attributes match
|
|
|
|
FALSE - the attributes do not match
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
// turn off attributes that we don't base searches on (readonly, archived, and
|
|
// control.
|
|
|
|
FoundAttributes &= ~ATTR_IGNORE;
|
|
|
|
// map NT attributes to OS/2 attributes
|
|
|
|
if (Directory) {
|
|
FoundAttributes |= FILE_DIRECTORY;
|
|
}
|
|
else if (FoundAttributes & FILE_ATTRIBUTE_NORMAL) {
|
|
if (RequestedAttributes & ATTR_NOT_NORM)
|
|
return FALSE;
|
|
else
|
|
return TRUE;
|
|
}
|
|
//
|
|
// the following code is a direct port of OS/2 code.
|
|
//
|
|
// the result is non-zero if an attribute is not in the RequestedAttribute set
|
|
// and in the FoundAttribute set and in the {hidden, system, directory}
|
|
// important set. This means that we do not have a match.
|
|
//
|
|
if ((~RequestedAttributes & FoundAttributes) &
|
|
(FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY)) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
MapAttributesToOs2(
|
|
IN ULONG NtAttributes,
|
|
OUT PUSHORT Os2Attributes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts attributes from NT format to OS/2 format.
|
|
|
|
Arguments:
|
|
|
|
NtAttributes - the attributes in NT format.
|
|
|
|
Os2Attributes - where the converted attributes are returned.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
*Os2Attributes = (USHORT) NtAttributes;
|
|
if (*Os2Attributes & FILE_ATTRIBUTE_NORMAL) {
|
|
*Os2Attributes = 0;
|
|
}
|
|
}
|
|
|
|
// The purpose of the following CopyXXX routines is to isolate the transfer
|
|
// of data into the user buffer so that any changes in format
|
|
// are easily dealt with. this is in anticipation of Darryl's alignment
|
|
// DCR being passed.
|
|
|
|
// alignment note: we use the RtlStore/RetrieveUlong/short macros to
|
|
// read from and store to the naturally unaligned (packed) find and ea
|
|
// buffers. we do not check other pointers for alignment before accessing
|
|
// them.
|
|
|
|
APIRET
|
|
CopycbList(
|
|
IN ULONG cbList,
|
|
IN OUT PVOID *UserBuffer,
|
|
IN OUT PULONG UserBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the EA list length field to the user's buffer.
|
|
The buffer pointer is updated to point past the cblist field. It is
|
|
assumed that there is enough space for the cblist.
|
|
|
|
Arguments:
|
|
|
|
cbList - the EA list length.
|
|
|
|
UserBuffer - where to store the cblist.
|
|
|
|
UserBufferLength - Available space in the userbuffer.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
|
|
{
|
|
try {
|
|
RtlStoreUlong(*UserBuffer,cbList);
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + sizeof(ULONG);
|
|
*UserBufferLength = *UserBufferLength - sizeof(ULONG);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
APIRET
|
|
CopyFileName(
|
|
IN PSZ FileName,
|
|
IN ULONG FileNameLength,
|
|
IN OUT PVOID *UserBuffer,
|
|
IN OUT PULONG UserBufferLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the filenamelength and filename field to the user's
|
|
buffer. The buffer pointer is updated to point past the filename and
|
|
filenamelength fields.
|
|
|
|
Arguments:
|
|
|
|
FileName - The filename to copy.
|
|
|
|
FileNameLength - Length of the filename (not including NULL).
|
|
|
|
UserBuffer - Where to store the filename.
|
|
|
|
UserBufferLength - Available space in the userbuffer.
|
|
|
|
Return Value:
|
|
|
|
ERROR_BUFFER_OVERFLOW - there wasn't adequate space to store the filename
|
|
and filenamelength in the user buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
if (*UserBufferLength < (FileNameLength + (2 * sizeof(UCHAR)))) {
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
*UserBufferLength = *UserBufferLength -
|
|
(FileNameLength + (2 * sizeof(UCHAR)));
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("copying filename, %s\n",FileName);
|
|
}
|
|
#endif
|
|
try {
|
|
//
|
|
// store the filenamelength in the first byte of the buffer
|
|
//
|
|
*((PCHAR) (*UserBuffer)) = (UCHAR) FileNameLength;
|
|
//
|
|
// update the buffer pointer past the filenamelength
|
|
//
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + sizeof(UCHAR);
|
|
//
|
|
// copy the filename
|
|
//
|
|
RtlMoveMemory(*UserBuffer,FileName,FileNameLength);
|
|
//
|
|
// update the buffer pointer past the filename
|
|
//
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + FileNameLength;
|
|
//
|
|
// null terminate the filename
|
|
//
|
|
*((PCHAR) (*UserBuffer)) = 0;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
//
|
|
// update the buffer pointer past the filenamelength
|
|
//
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + sizeof(UCHAR);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
APIRET
|
|
CopyFileAttributes(
|
|
IN PFILE_FULL_DIR_INFORMATION NtEntry,
|
|
IN OUT PVOID *UserBuffer,
|
|
IN OUT PULONG UserBufferLength,
|
|
IN ULONG InfoLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the attributes fields to the user's
|
|
buffer. The buffer pointer is updated to point past the attributes.
|
|
|
|
Arguments:
|
|
|
|
NtEntry - The attributes in NT form.
|
|
|
|
UserBuffer - Where to store the filename.
|
|
|
|
UserBufferLength - Available space in the userbuffer.
|
|
|
|
InfoLevel - The level of file information requested.
|
|
|
|
Return Value:
|
|
|
|
ERROR_BUFFER_OVERFLOW - there wasn't adequate space to store the filename
|
|
and filenamelength in the user buffer.
|
|
|
|
Note:
|
|
|
|
Since this structure is dword-aligned, we could figure out at the
|
|
beginning whether the structure begins on a dword boundary and skip the
|
|
macros if it does.
|
|
|
|
--*/
|
|
|
|
{
|
|
PFILEFINDBUF4 BufPtr;
|
|
USHORT Attributes;
|
|
|
|
if (InfoLevel == FIL_STANDARD)
|
|
{
|
|
if (*UserBufferLength < ATTR_SIZE3) {
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
*UserBufferLength -= ATTR_SIZE3;
|
|
}
|
|
else
|
|
{
|
|
if (*UserBufferLength < ATTR_SIZE4) {
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
*UserBufferLength -= ATTR_SIZE4;
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("copying attributes\n");
|
|
}
|
|
#endif
|
|
BufPtr = (PFILEFINDBUF4) *UserBuffer;
|
|
|
|
try {
|
|
|
|
// fill in oNextEntryOffset later.
|
|
// convert creation time
|
|
|
|
NtTimeToFatTimeAndDate(&(BufPtr->ftimeCreation),
|
|
&(BufPtr->fdateCreation),
|
|
NtEntry->CreationTime);
|
|
|
|
// convert last access time
|
|
|
|
NtTimeToFatTimeAndDate(&(BufPtr->ftimeLastAccess),
|
|
&(BufPtr->fdateLastAccess),
|
|
NtEntry->LastAccessTime);
|
|
|
|
// convert last write time
|
|
|
|
NtTimeToFatTimeAndDate(&(BufPtr->ftimeLastWrite),
|
|
&(BufPtr->fdateLastWrite),
|
|
NtEntry->LastWriteTime);
|
|
|
|
// BUGBUG - Therese, what should we do here if .HighPart is non-zero
|
|
|
|
BufPtr->cbFile = NtEntry->EndOfFile.LowPart;
|
|
BufPtr->cbFileAlloc = NtEntry->AllocationSize.LowPart;
|
|
|
|
if (InfoLevel == FIL_STANDARD)
|
|
{
|
|
MapAttributesToOs2(
|
|
((PFILE_DIRECTORY_INFORMATION)NtEntry)->FileAttributes,
|
|
&Attributes);
|
|
}
|
|
else
|
|
{
|
|
MapAttributesToOs2(
|
|
NtEntry->FileAttributes,
|
|
&Attributes);
|
|
}
|
|
|
|
BufPtr->attrFile = (ULONG)(Attributes & ATTR_ALL);
|
|
if (InfoLevel == FIL_STANDARD)
|
|
{
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + ATTR_SIZE3; // BUGBUG check this
|
|
}
|
|
else if (InfoLevel == FIL_QUERYEASIZE)
|
|
{
|
|
BufPtr->cbList = NtEntry->EaSize;
|
|
/* Note that NT returns 0 when no EA's on file. OS/2 expects the
|
|
size of the .cbList field */
|
|
if (BufPtr->cbList == 0)
|
|
BufPtr->cbList = MINFEALISTSIZE;
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + ATTR_SIZE4; // BUGBUG check this
|
|
}
|
|
else /* InfoLevel == FIL_QUERYEASFROMLIST */
|
|
{
|
|
/* BUGBUG - I don't believe that anyone is looking at that field */
|
|
BufPtr->cbList = NtEntry->EaSize;
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + ATTR_SIZE4
|
|
- sizeof(BufPtr->cbList);
|
|
}
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
|
Od2ExitGP();
|
|
}
|
|
//
|
|
// update buffer pointer past attributes
|
|
//
|
|
return NO_ERROR;
|
|
}
|
|
|
|
APIRET
|
|
EditBuffer(
|
|
IN ULONG InfoLevel,
|
|
IN ULONG RequestedAttributes,
|
|
IN HANDLE NtDirectoryHandle,
|
|
IN OUT PULONG NumberOfEntries,
|
|
IN PVOID Buffer,
|
|
IN ULONG BufferLength,
|
|
IN OUT PFILEFINDBUF4 *LastEntry,
|
|
IN OUT PVOID *UserBuffer,
|
|
IN OUT PULONG UserBufferLength,
|
|
IN OUT PEAOP2 UserEaop,
|
|
OUT PBOOLEAN Rewind,
|
|
OUT PFILE_DIRECTORY_INFORMATION *RewindEntry
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the information returned by NtQueryDirectory to
|
|
the user's buffer and retrieves any additional information based on
|
|
the infolevel. It also removes any entries returned by NtQueryDirectory
|
|
if the attributes don't match the RequestedAttributes parameter.
|
|
|
|
Arguments:
|
|
|
|
InfoLevel - The level of file information requested.
|
|
|
|
RequestedAttributes - The attribute used in searching for FileName.
|
|
|
|
NtDirectoryHandle - handle to open directory
|
|
|
|
NumberOfEntries - on input, number of entries needed. on output,
|
|
number of entries found. this parameter has meaning regardless of
|
|
whether an error code was returned.
|
|
|
|
Buffer - entries returned by NtQueryDirectoryFile
|
|
|
|
BufferLength - length of data returned by NtQueryDirectoryFile
|
|
|
|
LastEntry - on input, the last entry stored in the user buffer by the
|
|
last call to EditBuffer. on exit, the last entry stored in the user
|
|
buffer. used to update oNextEntryOffset.
|
|
|
|
UserBuffer - where to store matching entries. this pointer is updated
|
|
to point past the returned entries on return. (unprobed)
|
|
|
|
UserBufferLength - remaining buffer length
|
|
|
|
UserEaop - user's EAOP, if infolevel == QUERYEAS (unprobed)
|
|
BUGBUG need to TRY this
|
|
|
|
Rewind - Whether search needs to be rewound.
|
|
|
|
RewindEntry - entry from which to continue scan
|
|
|
|
Return Value:
|
|
|
|
ERROR_BUFFER_OVERFLOW - there wasn't adequate space to store all the
|
|
requested entries in the user buffer.
|
|
|
|
ERROR_EAS_DIDNT_FIT - there wasn't adequate space to store the EAs for
|
|
one of the requested entries.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG NumberOfEntriesRequested;
|
|
PFILE_FULL_DIR_INFORMATION CurrentEntry;
|
|
STRING FileName;
|
|
UNICODE_STRING FileName_U;
|
|
BOOLEAN Directory;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
HANDLE FoundEntryHandle;
|
|
BOOLEAN Done; // whether we've hit the end of the NT buffer
|
|
APIRET RetCode;
|
|
NTSTATUS Status;
|
|
PFILEFINDBUF4 CurrentUserEntry, PreviousUserEntry;
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("entering EditBuffer with requestednumentries == %ld\n",*NumberOfEntries);
|
|
}
|
|
#endif
|
|
NumberOfEntriesRequested = *NumberOfEntries;
|
|
*NumberOfEntries = 0; // number of entries copied
|
|
CurrentEntry = (PFILE_FULL_DIR_INFORMATION) Buffer;
|
|
if (BufferLength == 0)
|
|
{
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
//
|
|
// we can break out of the while loop for three reasons:
|
|
// 1) we run out of user buffer space. if there are still entries left
|
|
// in the NT buffer, we need to save the rewind information. we
|
|
// return error_buffer_overflow if the last entry wouldn't fit. if
|
|
// infolevel == 3 and all but the EA data will fit for the last entry,
|
|
// we return error_eas_didnt_fit. we increment the returnedentry
|
|
// counter. we also return the rewind
|
|
// information because the only time we return an entry without its
|
|
// EAs is if it's the only entry returned. if there are other entries
|
|
// in the buffer, we need to rewind so that entry will be returned
|
|
// again. we leave it up to the caller to
|
|
// determine the correct error code, depending on whether any entries
|
|
// were returned.
|
|
// 2) we have copied the requested number of entries. if there are still
|
|
// entries left in the NT buffer, we need to save the rewind
|
|
// information.
|
|
// 3) we have exhausted the NT entries. this can happen if the entries
|
|
// returned by NT don't match the requested attributes. we return
|
|
// the number copied and no error.
|
|
//
|
|
Done = FALSE;
|
|
*RewindEntry = (PFILE_DIRECTORY_INFORMATION) CurrentEntry;
|
|
*Rewind = FALSE;
|
|
CurrentUserEntry = NULL;
|
|
while ((*UserBufferLength) &&
|
|
(*NumberOfEntries < NumberOfEntriesRequested) &&
|
|
(!Done))
|
|
{
|
|
if (InfoLevel == FIL_STANDARD)
|
|
{
|
|
//
|
|
// What we get back from Nt is Unicode
|
|
//
|
|
|
|
((PFILE_DIRECTORY_INFORMATION)CurrentEntry)->FileName
|
|
[(((PFILE_DIRECTORY_INFORMATION)CurrentEntry)->FileNameLength)/2] = 0;
|
|
RtlInitUnicodeString (&FileName_U,
|
|
((PFILE_DIRECTORY_INFORMATION)CurrentEntry)->FileName);
|
|
//
|
|
// Convert it to Ansi
|
|
//
|
|
RetCode = Od2UnicodeStringToMBString(
|
|
&FileName,
|
|
&FileName_U,
|
|
TRUE);
|
|
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("cannot convert to Unicode\n");
|
|
}
|
|
ASSERT( FALSE );
|
|
#endif
|
|
return(RetCode);
|
|
}
|
|
Directory = (BOOLEAN)((((PFILE_DIRECTORY_INFORMATION)CurrentEntry)->FileAttributes
|
|
& FILE_ATTRIBUTE_DIRECTORY) != 0);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// What we get back from Nt is Unicode
|
|
//
|
|
CurrentEntry->FileName[(CurrentEntry->FileNameLength)/2] = 0;
|
|
RtlInitUnicodeString (&FileName_U,
|
|
CurrentEntry->FileName);
|
|
//
|
|
// Convert it to Ansi
|
|
//
|
|
RetCode = Od2UnicodeStringToMBString(
|
|
&FileName,
|
|
&FileName_U,
|
|
TRUE);
|
|
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("cannot convert to Unicode\n");
|
|
}
|
|
ASSERT( FALSE );
|
|
#endif
|
|
return(RetCode);
|
|
}
|
|
Directory = (BOOLEAN)((CurrentEntry->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
|
}
|
|
|
|
if (MatchAttributes(CurrentEntry->FileAttributes,
|
|
RequestedAttributes,
|
|
Directory))
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("attributes match\n");
|
|
}
|
|
#endif
|
|
PreviousUserEntry = CurrentUserEntry;
|
|
CurrentUserEntry = *UserBuffer;
|
|
if (RetCode = CopyFileAttributes(CurrentEntry,
|
|
UserBuffer,
|
|
UserBufferLength,
|
|
InfoLevel))
|
|
{
|
|
if (RetCode == ERROR_BUFFER_OVERFLOW)
|
|
{
|
|
*Rewind = TRUE;
|
|
if (PreviousUserEntry)
|
|
{
|
|
PreviousUserEntry->oNextEntryOffset = 0;
|
|
}
|
|
}
|
|
return RetCode;
|
|
}
|
|
|
|
if (InfoLevel != FIL_QUERYEASFROMLIST)
|
|
{
|
|
if (RetCode = CopyFileName(FileName.Buffer,
|
|
FileName.Length,
|
|
UserBuffer,
|
|
UserBufferLength)) {
|
|
if (RetCode == ERROR_BUFFER_OVERFLOW) {
|
|
*Rewind = TRUE;
|
|
if (PreviousUserEntry) {
|
|
PreviousUserEntry->oNextEntryOffset = 0;
|
|
}
|
|
}
|
|
return RetCode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// if EA information is requested and the returned entry
|
|
// is '.' or '..', return an empty ea list.
|
|
//
|
|
|
|
if ((!strcmp(FileName.Buffer,".")) ||
|
|
(!strcmp(FileName.Buffer,"..")))
|
|
{
|
|
UserEaop->fpFEA2List = *UserBuffer;
|
|
UserEaop->fpFEA2List->cbList = MINFEALISTSIZE;
|
|
*UserBufferLength -= UserEaop->fpFEA2List->cbList;
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + UserEaop->fpFEA2List->cbList;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// we must be able to fit the name and the cblist in the buffer.
|
|
// otherwise it's error_buffer_overflow.
|
|
//
|
|
if (*UserBufferLength < (ULONG)(MINFEALISTSIZE + FileName.Length))
|
|
{
|
|
*Rewind = TRUE;
|
|
if (PreviousUserEntry)
|
|
{
|
|
PreviousUserEntry->oNextEntryOffset = 0;
|
|
}
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
InitializeObjectAttributes( &Obja,
|
|
&FileName_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NtDirectoryHandle,
|
|
NULL);
|
|
// BUGBUG test for sharing_violation when it's implemented
|
|
do {
|
|
Status = NtOpenFile(&FoundEntryHandle,
|
|
SYNCHRONIZE | FILE_READ_EA,
|
|
&Obja,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
} while (RetryIO(Status, &FoundEntryHandle));
|
|
if (!(NT_SUCCESS(Status)))
|
|
{
|
|
//BUGBUG fix this part
|
|
//
|
|
// if we don't have access to the file, we return an error
|
|
// and the filename. the search can continue past this
|
|
// entry. if there are other entries in the buffer, we
|
|
// return no error and don't return this entry. that way
|
|
// the entry is the only thing in the buffer when the
|
|
// error is returned.
|
|
//
|
|
if (Status == STATUS_ACCESS_DENIED) {
|
|
if (CurrentEntry->NextEntryOffset != 0) {
|
|
*Rewind = TRUE;
|
|
*RewindEntry = (PFILE_DIRECTORY_INFORMATION) ((PCHAR) CurrentEntry + CurrentEntry->NextEntryOffset);
|
|
}
|
|
*NumberOfEntries += 1;
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
return ERROR_FILE_NOT_FOUND; //BUGBUG bogus error
|
|
}
|
|
/* BUGBUG - Looks like a strange practise below to destroy
|
|
the user's fpFEA2List. In the case of the OS/2 ss, we call
|
|
DosFindFirst only with a temporary local buffer so leave
|
|
it for now */
|
|
UserEaop->fpFEA2List = *UserBuffer;
|
|
UserEaop->fpFEA2List->cbList = *UserBufferLength;
|
|
RetCode = GetGEAList(FoundEntryHandle,
|
|
(PBYTE)UserEaop,
|
|
sizeof(EAOP2)
|
|
);
|
|
if (RetCode != NO_ERROR)
|
|
{
|
|
if (RetCode==ERROR_BUFFER_OVERFLOW) {
|
|
|
|
//
|
|
// we have already verified that there is enough
|
|
// room in the buffer for the cblist.
|
|
//
|
|
|
|
CurrentUserEntry->cbList = CurrentEntry->EaSize;
|
|
CurrentUserEntry->oNextEntryOffset = 0;
|
|
*UserBuffer = (PCHAR)(*UserBuffer) + sizeof(CurrentUserEntry->cbList);
|
|
*NumberOfEntries += 1;
|
|
NtClose(FoundEntryHandle);
|
|
return ERROR_EAS_DIDNT_FIT;
|
|
}
|
|
else {
|
|
NtClose(FoundEntryHandle);
|
|
return RetCode;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NtClose(FoundEntryHandle);
|
|
|
|
*UserBufferLength -= UserEaop->fpFEA2List->cbList;
|
|
*UserBuffer = (PBYTE)(*UserBuffer) + UserEaop->fpFEA2List->cbList;
|
|
}
|
|
|
|
}
|
|
if (RetCode = CopyFileName(
|
|
FileName.Buffer,
|
|
FileName.Length,
|
|
UserBuffer,
|
|
UserBufferLength)) {
|
|
if (PreviousUserEntry) {
|
|
PreviousUserEntry->oNextEntryOffset = 0;
|
|
}
|
|
return RetCode;
|
|
}
|
|
}
|
|
*NumberOfEntries += 1;
|
|
|
|
//
|
|
// the first time through the loop, if editbuffer was already
|
|
// called for the call to the API, we set up the oNextEntryOffset
|
|
// of the last entry returned.
|
|
//
|
|
|
|
if (*LastEntry != NULL) {
|
|
ASSERT ((*LastEntry)->oNextEntryOffset == 0);
|
|
(*LastEntry)->oNextEntryOffset = RoundUpToUlong(((ULONG)CurrentUserEntry - (ULONG)*LastEntry));
|
|
*LastEntry = NULL;
|
|
}
|
|
CurrentUserEntry->oNextEntryOffset = RoundUpToUlong(((ULONG)*UserBuffer - (ULONG)CurrentUserEntry));
|
|
*UserBuffer = (PVOID) ((PCHAR)(CurrentUserEntry) + CurrentUserEntry->oNextEntryOffset);
|
|
}
|
|
if (CurrentEntry->NextEntryOffset == 0) {
|
|
Done = TRUE;
|
|
}
|
|
CurrentEntry = (PFILE_FULL_DIR_INFORMATION) ((PCHAR) CurrentEntry + CurrentEntry->NextEntryOffset);
|
|
*RewindEntry = (PFILE_DIRECTORY_INFORMATION) CurrentEntry;
|
|
Od2FreeMBString (&FileName);
|
|
}
|
|
|
|
//
|
|
// if we get here and the number of entries copied is zero, some were
|
|
// found but the attributes didn't match. we don't touch the user's
|
|
// buffer.
|
|
//
|
|
|
|
if (*NumberOfEntries > 0) {
|
|
CurrentUserEntry->oNextEntryOffset = 0;
|
|
*LastEntry = CurrentUserEntry;
|
|
}
|
|
|
|
//
|
|
// if we get here with NT entries left in the buffer, we've copied the
|
|
// requested number. we need to save the rewind information.
|
|
//
|
|
if (!Done) {
|
|
*Rewind = TRUE;
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
APIRET
|
|
DosFindFirst(
|
|
IN PSZ FileName,
|
|
IN OUT PHDIR DirectoryHandle,
|
|
IN ULONG FileAttributes,
|
|
IN PFILEFINDBUF3 Buffer,
|
|
IN ULONG Length,
|
|
IN OUT PULONG CountEntriesFound,
|
|
IN ULONG FileInformationLevel
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the OS/2 API DosFindFirst.
|
|
|
|
Arguments:
|
|
|
|
FileName - The name to be search for. may contain wildcard characters.
|
|
|
|
DirectoryHandle - The search handle associated with this particular
|
|
search. An input DirectoryHandle of 1 is specified to be always
|
|
available. An input DirectoryHandle of -1 indicates to allocate a
|
|
new handle. The handle is returned by overwriting the -1. Reuse of
|
|
this DirectoryHandle in another DosFindFirst closes the association
|
|
with the previously related DosFindFirst and opens a new association
|
|
with the current DosFindFirst.
|
|
|
|
FileAttributes - The attribute used in searching for FileName.
|
|
|
|
Buffer - Where to store the results of the search.
|
|
|
|
Length - Length of Buffer
|
|
|
|
CountEntriesFound - on input, this contains the number of entries
|
|
requested. on output, it contains the number of entries found.
|
|
|
|
FileInformationLevel - The level of file information requested.
|
|
|
|
|
|
Return Value:
|
|
|
|
see OS/2 spec.
|
|
|
|
--*/
|
|
|
|
{
|
|
STRING CanonicalNameString;
|
|
UNICODE_STRING CanonicalNameString_U;
|
|
ULONG FileType;
|
|
ULONG FileFlags;
|
|
PCHAR LastElement;
|
|
PCHAR CurrentChar;
|
|
HANDLE NtDirectoryHandle;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
ULONG BufferLength;
|
|
PVOID FindBuffer;
|
|
ULONG OneOs2EntrySize, OneNtEntrySize;
|
|
FILE_INFORMATION_CLASS InformationClass;
|
|
BOOLEAN RestartScan;
|
|
BOOLEAN Rewind;
|
|
PUNICODE_STRING pSearchString;
|
|
STRING SearchString;
|
|
UNICODE_STRING SearchString_U;
|
|
PFILE_DIRECTORY_INFORMATION RewindEntry;
|
|
ULONG EntriesNeeded;
|
|
ULONG EntriesRequested;
|
|
ULONG EntriesFound;
|
|
PEAOP2 UserEAOP=NULL;
|
|
PFILEFINDBUF3 UserBuffer;
|
|
APIRET RetCode;
|
|
NTSTATUS Status;
|
|
BOOLEAN OneEntry;
|
|
PFILEFINDBUF4 LastEntry;
|
|
|
|
if ((FileInformationLevel < FIL_STANDARD) ||
|
|
(FileInformationLevel > MAXFINDINFOLEVEL))
|
|
{
|
|
return ERROR_INVALID_LEVEL;
|
|
}
|
|
|
|
try
|
|
{
|
|
if ((EntriesRequested = *CountEntriesFound) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
Od2ProbeForWrite(CountEntriesFound, sizeof(ULONG), 1);
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ){
|
|
Od2ExitGP();
|
|
}
|
|
if (FileAttributes & ~(ATTR_ALL | ATTR_NOT_NORM))
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
RetCode = Od2Canonicalize(FileName,
|
|
CANONICALIZE_FILE_DEV_OR_PIPE,
|
|
&CanonicalNameString,
|
|
NULL,
|
|
&FileFlags,
|
|
&FileType
|
|
);
|
|
if (RetCode != NO_ERROR)
|
|
{
|
|
return RetCode;
|
|
}
|
|
|
|
if (FileType & FILE_TYPE_PSDEV)
|
|
{
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
return ERROR_ACCESS_DENIED;
|
|
}
|
|
|
|
if (FileFlags & CANONICALIZE_IS_ROOT_DIRECTORY)
|
|
{
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
return ERROR_NO_MORE_FILES;
|
|
}
|
|
|
|
|
|
// get pointer to last element in string. we do this by scanning from the
|
|
// beginning for \s. we must scan from the beginning to be dbcs correct.
|
|
|
|
//BUGBUG need to special case devices. what about named pipes? currently
|
|
// only UNC is distinguished
|
|
|
|
if (FileType & FILE_TYPE_UNC)
|
|
{
|
|
// add 10 for skipping '\OS2SS\UNC'
|
|
LastElement = CanonicalNameString.Buffer+10;
|
|
}
|
|
else
|
|
{
|
|
// skipping '\OS2SS\DRIVES\X:'
|
|
LastElement = CanonicalNameString.Buffer+FILE_PREFIX_LENGTH+FIRST_SLASH;
|
|
}
|
|
ASSERT (*LastElement == '\\');
|
|
CurrentChar = LastElement + 1;
|
|
while (*CurrentChar != 0)
|
|
{
|
|
while ((*CurrentChar != 0) && (*CurrentChar != '\\'))
|
|
{
|
|
#ifdef DBCS
|
|
// MSKK Apr.12.1993 V-AkihiS
|
|
// MSKK Sep.29.1993 V-AkihiS
|
|
// if (IsDBCSLeadByte(*CurrentChar))
|
|
if (Ow2NlsIsDBCSLeadByte(*CurrentChar, SesGrp->DosCP))
|
|
{
|
|
CurrentChar++;
|
|
}
|
|
if (*CurrentChar)
|
|
{
|
|
CurrentChar++;
|
|
}
|
|
#else
|
|
CurrentChar++;
|
|
#endif
|
|
}
|
|
if (*CurrentChar == '\\')
|
|
{
|
|
LastElement = CurrentChar++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// open the parent directory. if it's the root directory, we need to
|
|
// open "\OS2SS\DRIVES\d:\", otherwise we open "\OS2SS\DRIVES\d:\foo".
|
|
// note that the root directory ends in a '\' and the other cases don't.
|
|
//
|
|
|
|
if (LastElement == CanonicalNameString.Buffer+FILE_PREFIX_LENGTH+FIRST_SLASH) { // if root directory
|
|
if (!(FileType & FILE_TYPE_UNC)) {
|
|
CanonicalNameString.Length = FILE_PREFIX_LENGTH+ROOTDIRLENGTH; // we know the string length
|
|
CanonicalNameString.MaximumLength = CanonicalNameString.Length;
|
|
// and don't want to overwrite
|
|
}
|
|
} // the char.
|
|
else { // else
|
|
*LastElement = 0; // overwrite '\'
|
|
Od2InitMBString(&CanonicalNameString,CanonicalNameString.Buffer);
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("parent directory is %s\n",CanonicalNameString.Buffer);
|
|
}
|
|
#endif
|
|
//
|
|
// UNICODE conversion -
|
|
//
|
|
|
|
RetCode = Od2MBStringToUnicodeString(
|
|
&CanonicalNameString_U,
|
|
&CanonicalNameString,
|
|
TRUE);
|
|
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("DosFindFirst: no memory for Unicode Conversion\n");
|
|
}
|
|
#endif
|
|
return RetCode;
|
|
}
|
|
|
|
InitializeObjectAttributes(&Obja,
|
|
&CanonicalNameString_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
do {
|
|
Status = NtOpenFile(&NtDirectoryHandle,
|
|
SYNCHRONIZE | FILE_LIST_DIRECTORY,
|
|
&Obja,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE
|
|
);
|
|
} while (RetryIO(Status, NtDirectoryHandle));
|
|
RtlFreeUnicodeString (&CanonicalNameString_U);
|
|
if (!(NT_SUCCESS(Status))) {
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
return ERROR_PATH_NOT_FOUND;
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("opening parent directory succeeded\n");
|
|
}
|
|
#endif
|
|
|
|
// if level three is used, the user passed in an EAOP and we need to retrieve
|
|
// the real buffer pointer and length from it.
|
|
|
|
if (FileInformationLevel == FIL_QUERYEASFROMLIST)
|
|
{
|
|
if (Length < (sizeof (EAOP2)))
|
|
{
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
NtClose(NtDirectoryHandle);
|
|
*CountEntriesFound = 0;
|
|
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
Length = Length - sizeof(EAOP2);
|
|
UserEAOP = (PEAOP2)Buffer;
|
|
UserBuffer = (PFILEFINDBUF3) ((ULONG)Buffer + sizeof(EAOP2));
|
|
}
|
|
else
|
|
{
|
|
UserBuffer = Buffer;
|
|
}
|
|
|
|
if (FileInformationLevel == FIL_STANDARD)
|
|
{
|
|
OneOs2EntrySize = FIND_LEVEL_ONE_INFO_SIZE;
|
|
OneNtEntrySize = sizeof(FILE_DIRECTORY_INFORMATION);
|
|
InformationClass = FileDirectoryInformation;
|
|
}
|
|
else
|
|
{
|
|
OneOs2EntrySize = FIND_LEVEL_TWO_INFO_SIZE;
|
|
OneNtEntrySize = sizeof(FILE_FULL_DIR_INFORMATION);
|
|
InformationClass = FileFullDirectoryInformation;
|
|
}
|
|
|
|
/* BUGBUG - This check could be performed earlier (i.e. before opening the
|
|
directory). However, since under OS/2 when calling DosFindFirsts with both an
|
|
invalid path AND a too small buffer an error about the directory
|
|
is returned, we need to try and open the directory and only after
|
|
that check the length of the user-supplied buffer.
|
|
*/
|
|
if (Length < OneOs2EntrySize)
|
|
{
|
|
*CountEntriesFound = 0;
|
|
NtClose(NtDirectoryHandle);
|
|
// os2 1.x returns ERROR_NO_MORE_FILES and not ERROR_BUFFER_OVERFLOW
|
|
return ERROR_NO_MORE_FILES;
|
|
}
|
|
|
|
// allocate a buffer. the size of this buffer is
|
|
// big enough to contain the same number of entries (in NT format) that would
|
|
// fit into the user's buffer (in OS/2 format).
|
|
|
|
ASSERT(OneOs2EntrySize < OneNtEntrySize);
|
|
ASSERT((OneNtEntrySize & (sizeof(ULONG)-1))==0);
|
|
if (EntriesRequested == 1)
|
|
{
|
|
/* When calculating 'BufferLength', compute how many chars were
|
|
planned in the user-supplied buffer, then multiply by the size of
|
|
one char in the NT structure. The expression looks like:
|
|
size of NT structure w/o the name array
|
|
+ (number of chars allowed in Cruiser structure) *
|
|
(size of one char in NT structure)
|
|
The whole size should now be aligned to 4 bytes boundary
|
|
*/
|
|
if (FileInformationLevel == FIL_STANDARD) {
|
|
BufferLength = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName)
|
|
+ (Length - FIELD_OFFSET(FILEFINDBUF3, achName))
|
|
/ sizeof(((FILEFINDBUF3 *)0)->achName[0])
|
|
* sizeof(((FILE_DIRECTORY_INFORMATION *)0)->FileName[0]);
|
|
}
|
|
else {
|
|
BufferLength = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName)
|
|
+ (Length - FIELD_OFFSET(FILEFINDBUF4, achName))
|
|
/ sizeof(((FILEFINDBUF4 *)0)->achName[0])
|
|
* sizeof(((FILE_FULL_DIR_INFORMATION *)0)->FileName[0]);
|
|
}
|
|
// Align BufferLength to 4 bytes boundary
|
|
BufferLength = (BufferLength + 3) & 0xfffffffc;
|
|
}
|
|
else
|
|
BufferLength = OneNtEntrySize * (Length / OneOs2EntrySize);
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("user buffer size is %ld\n",Length);
|
|
DbgPrint("allocating a buffer of size %ld\n",BufferLength);
|
|
}
|
|
#endif
|
|
FindBuffer = RtlAllocateHeap(Od2Heap,0,BufferLength);
|
|
if (FindBuffer == NULL)
|
|
{
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
NtClose(NtDirectoryHandle);
|
|
return ERROR_NOT_ENOUGH_MEMORY; // BUGBUG bogus error
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("buffer allocation succeeded\n");
|
|
}
|
|
#endif
|
|
Od2InitMBString(&SearchString,LastElement+1);
|
|
|
|
RetCode = Od2MBStringToUnicodeString(
|
|
&SearchString_U,
|
|
&SearchString,
|
|
TRUE);
|
|
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("DosFindFirst: no memory for Unicode Conversion-2\n");
|
|
}
|
|
#endif
|
|
return RetCode;
|
|
}
|
|
|
|
pSearchString = &SearchString_U;
|
|
|
|
//
|
|
// Special case *.* to * since it is so common. Otherwise transmogrify
|
|
// the input name according to the following rules:
|
|
//
|
|
// - Change all ? to DOS_QM
|
|
// - Change all . followed by ? or * to DOS_DOT
|
|
// - Change an ending *. into DOS_STAR
|
|
//
|
|
// These transmogrifications are all done in place.
|
|
//
|
|
// This piece of code was heavily inspired by Windows base\client\filefind.c
|
|
//
|
|
|
|
if ( (SearchString_U.Length == 6) &&
|
|
(RtlCompareMemory(SearchString_U.Buffer, L"*.*", 6) == 6) ) {
|
|
|
|
SearchString_U.Length = sizeof(WCHAR); // leave only the first '*'
|
|
|
|
} else {
|
|
|
|
ULONG Index;
|
|
WCHAR *NameChar;
|
|
|
|
for ( Index = 0, NameChar = SearchString_U.Buffer;
|
|
Index < SearchString_U.Length/sizeof(WCHAR);
|
|
Index++, NameChar++) {
|
|
|
|
if ((*NameChar == L'?') || (*NameChar == L'*')) {
|
|
|
|
if (*NameChar == L'?') {
|
|
*NameChar = DOS_QM;
|
|
}
|
|
|
|
if (Index && *(NameChar - 1) == L'.') {
|
|
*(NameChar - 1) = DOS_DOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((*(NameChar - 2) == L'*') && (*(NameChar - 1) == L'.')) {
|
|
*(NameChar - 2) = DOS_STAR;
|
|
SearchString_U.Length -= sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
RestartScan = TRUE;
|
|
EntriesFound = 0;
|
|
LastEntry = NULL;
|
|
while (EntriesFound < EntriesRequested)
|
|
{
|
|
if ((EntriesRequested - EntriesFound) == 1) // if one entry needed
|
|
OneEntry = TRUE;
|
|
else
|
|
OneEntry = FALSE;
|
|
do {
|
|
Status = NtQueryDirectoryFile(NtDirectoryHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
FindBuffer,
|
|
BufferLength,
|
|
InformationClass,
|
|
OneEntry,
|
|
pSearchString,
|
|
RestartScan);
|
|
} while (RetryIO(Status, NtDirectoryHandle));
|
|
//
|
|
// if the call to NtQueryDirectoryFile fails, we end the search.
|
|
// if it failed because there were no more matching files and we
|
|
// found at least one match, we return the match and no error and
|
|
// save the search information. otherwise we don't save the search
|
|
// information.
|
|
//
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( TEMP )
|
|
{
|
|
DbgPrint("NtQueryDirectoryFile failed, Status=%x\n",
|
|
Status);
|
|
}
|
|
#endif
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
try
|
|
{
|
|
*CountEntriesFound = EntriesFound;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ){
|
|
NtClose(NtDirectoryHandle);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
Od2ExitGP();
|
|
}
|
|
if (((Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_NO_MORE_FILES)) &&
|
|
(EntriesFound > 0))
|
|
{
|
|
Rewind = FALSE;
|
|
RetCode = SaveSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
NtDirectoryHandle,
|
|
FileAttributes,
|
|
DirectoryHandle,
|
|
FileInformationLevel);
|
|
if (RetCode != NO_ERROR)
|
|
{
|
|
NtClose(NtDirectoryHandle);
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( TEMP )
|
|
{
|
|
DbgPrint("SaveSearchInfo returned RetCode=%d\n",
|
|
RetCode);
|
|
}
|
|
#endif
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return RetCode;
|
|
}
|
|
else {
|
|
NtClose(NtDirectoryHandle);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
if (Status == ERROR_NO_MORE_FILES) {
|
|
return ERROR_NO_MORE_FILES;
|
|
}
|
|
else {
|
|
return ERROR_FILE_NOT_FOUND; // BUGBUG bogus error
|
|
}
|
|
}
|
|
}
|
|
RestartScan = FALSE;
|
|
EntriesNeeded = EntriesRequested - EntriesFound;
|
|
|
|
|
|
RetCode = EditBuffer(FileInformationLevel,
|
|
FileAttributes,
|
|
NtDirectoryHandle,
|
|
&EntriesNeeded,
|
|
FindBuffer,
|
|
IoStatus.Information,
|
|
&LastEntry,
|
|
(PVOID *) &UserBuffer,
|
|
&Length,
|
|
UserEAOP,
|
|
&Rewind,
|
|
&RewindEntry
|
|
);
|
|
EntriesFound += EntriesNeeded;
|
|
|
|
// we use the return code from EditBuffer and the number of
|
|
// entries returned to determine what to return to the user.
|
|
// the basic rules are:
|
|
// if infolevel 1 and not enough room for any entries
|
|
// ERROR_BUFFER_OVERFLOW
|
|
// if infolevel 2 or 3 and not enough room for entry + cblist of first entry
|
|
// ERROR_BUFFER_OVERFLOW
|
|
// if infolevel 3
|
|
// if not enough room for EAs for first entry
|
|
// copy entry + cblist
|
|
// return ERROR_EAS_DIDNT_FIT
|
|
//
|
|
// this translates into the following pseudocode:
|
|
//
|
|
// if (RetCode)
|
|
// if (entries found == 0)
|
|
// don't create search handle
|
|
// return retcode
|
|
// if (retcode == ERROR_EAS_DIDNT_FIT)
|
|
// if (entries found == 1)
|
|
// create search handle
|
|
// return retcode
|
|
// else
|
|
// create search handle
|
|
// decrement found count
|
|
// else if (retcode == ERROR_ACCESS_DENIED)
|
|
// if (entries found == 1)
|
|
// create search handle
|
|
// copy filename to front of buffer
|
|
// point rewind entry to next entry
|
|
// return retcode
|
|
// else
|
|
// create search handle
|
|
// decrement found count
|
|
// else if (retcode == ERROR_BUFFER_OVERFLOW | ERROR_NO_MORE_FILES)
|
|
// create search handle
|
|
// return no error
|
|
// else
|
|
// don't create search handle
|
|
// return retcode
|
|
|
|
// if we get a return code, we stop getting more entries.
|
|
// first, we free up the memory we've allocated.
|
|
// then, if no entries have been found, we bag the search
|
|
// entirely by closing the directory handle and returning the
|
|
// error. if some entries have been found and ERROR_EAS_DIDNT_FIT
|
|
// was returned, we need to find out whether the error applied to
|
|
// the first (and only) entry in the buffer. if it did, we return
|
|
// the error. otherwise we don't. in any case, we save the search
|
|
// information and return a valid handle and search count.
|
|
|
|
if (RetCode != NO_ERROR) {
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
if (EntriesFound == 0) {
|
|
NtClose(NtDirectoryHandle);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return RetCode;
|
|
}
|
|
if (RetCode == ERROR_EAS_DIDNT_FIT) {
|
|
try {
|
|
*CountEntriesFound = EntriesFound;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
Od2ExitGP();
|
|
}
|
|
RetCode = SaveSearchInfo( Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
NtDirectoryHandle,
|
|
FileAttributes,
|
|
DirectoryHandle,
|
|
FileInformationLevel);
|
|
if (RetCode != NO_ERROR) {
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return RetCode;
|
|
}
|
|
if (EntriesFound == 1) {
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return ERROR_EAS_DIDNT_FIT;
|
|
}
|
|
else {
|
|
*CountEntriesFound--;
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// if the return code is error_buffer_overflow or error_no_more_files,
|
|
// and one or more entries has successfully been stored in the user's
|
|
// buffer, we return success.
|
|
//
|
|
else if ((RetCode == ERROR_BUFFER_OVERFLOW) ||
|
|
(RetCode == ERROR_NO_MORE_FILES)) {
|
|
try {
|
|
*CountEntriesFound = EntriesFound;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
Od2ExitGP();
|
|
}
|
|
RetCode = SaveSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
NtDirectoryHandle,
|
|
FileAttributes,
|
|
DirectoryHandle,
|
|
FileInformationLevel);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return RetCode;
|
|
}
|
|
//
|
|
// we get here if one or more entries has been successfully stored
|
|
// in the user's buffer, but we encountered an error other than
|
|
// buffer_overflow, no_more_files, or eas_didnt_fit. we fail the
|
|
// search and return the error code.
|
|
//
|
|
else {
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
NtClose(NtDirectoryHandle);
|
|
return RetCode;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we get here, EditBuffer returned NO_ERROR.
|
|
//
|
|
|
|
pSearchString = NULL;
|
|
}
|
|
//
|
|
// if we get here, we've found all the requested entries. save the
|
|
// search information and return.
|
|
//
|
|
RetCode = SaveSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
NtDirectoryHandle,
|
|
FileAttributes,
|
|
DirectoryHandle,
|
|
FileInformationLevel);
|
|
RtlFreeHeap(Od2Heap,0,CanonicalNameString.Buffer);
|
|
RtlFreeUnicodeString(&SearchString_U);
|
|
return RetCode;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used by the OS/2 1.x subsystem to inquire what type of search
|
|
is performed with the specified directory handle so that we can make the
|
|
required transformations according to the search level.
|
|
|
|
Return value:
|
|
|
|
The search's info level or -1 if handle is invalid.
|
|
--*/
|
|
|
|
ULONG
|
|
Internal_return_search_level(
|
|
IN HDIR DirectoryHandle
|
|
)
|
|
{
|
|
if ((((ULONG)DirectoryHandle) >= SearchHandleTableLength) ||
|
|
(((ULONG) SearchHandleTable[((ULONG)DirectoryHandle)]) == SEARCH_HANDLE_FREE)) {
|
|
return (ULONG)(-1);
|
|
}
|
|
|
|
return (SearchHandleTable[((ULONG)DirectoryHandle)]->InformationLevel);
|
|
}
|
|
|
|
APIRET
|
|
DosFindNext(
|
|
IN HDIR DirectoryHandle,
|
|
IN PFILEFINDBUF3 Buffer,
|
|
IN ULONG Length,
|
|
IN OUT PULONG CountEntriesFound
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the OS/2 API DosFindNext.
|
|
|
|
Arguments:
|
|
|
|
DirectoryHandle - The search handle associated with this particular
|
|
search.
|
|
|
|
Buffer - Where to store the results of the search.
|
|
|
|
Length - Length of Buffer
|
|
|
|
CountEntriesFound - on input, this contains the number of entries
|
|
requested. on output, it contains the number of entries found.
|
|
|
|
Return Value:
|
|
|
|
see OS/2 spec.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSEARCH_RECORD SearchRecord;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
ULONG BufferLength;
|
|
PVOID FindBuffer;
|
|
ULONG OneOs2EntrySize, OneNtEntrySize;
|
|
FILE_INFORMATION_CLASS InformationClass;
|
|
BOOLEAN Rewind;
|
|
PFILE_DIRECTORY_INFORMATION RewindEntry;
|
|
ULONG EntriesNeeded;
|
|
ULONG EntriesFound;
|
|
ULONG EntriesRequested;
|
|
PEAOP2 UserEAOP=NULL;
|
|
PFILEFINDBUF3 UserBuffer;
|
|
APIRET RetCode;
|
|
NTSTATUS Status;
|
|
BOOLEAN OneEntry;
|
|
PFILEFINDBUF4 LastEntry;
|
|
#if DBG
|
|
PSZ RoutineName;
|
|
RoutineName = "DosFindNext";
|
|
#endif
|
|
|
|
try {
|
|
if ((EntriesRequested = *CountEntriesFound) == 0) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
AcquireFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
if ((((ULONG)DirectoryHandle) >= SearchHandleTableLength) ||
|
|
(((ULONG) SearchHandleTable[((ULONG)DirectoryHandle)]) == SEARCH_HANDLE_FREE)) {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
SearchRecord = SearchHandleTable[((ULONG)DirectoryHandle)];
|
|
|
|
EntriesFound = 0;
|
|
|
|
// if level three is used, the user passed in an EAOP and we need to retrieve
|
|
// the real buffer pointer and length from it.
|
|
|
|
if (SearchRecord->InformationLevel == FIL_QUERYEASFROMLIST)
|
|
{
|
|
if (Length < (sizeof (EAOP2)))
|
|
{
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
Length = Length - sizeof(EAOP2);
|
|
UserEAOP = (PEAOP2)Buffer;
|
|
UserBuffer = (PFILEFINDBUF3) ((ULONG)Buffer + sizeof(EAOP2));
|
|
}
|
|
else
|
|
{
|
|
UserBuffer = Buffer;
|
|
}
|
|
|
|
//
|
|
// if we have some entries left over from last time, use them first.
|
|
//
|
|
|
|
LastEntry = NULL;
|
|
if (SearchRecord->FindBuffer != NULL)
|
|
{
|
|
EntriesNeeded = EntriesRequested - EntriesFound;
|
|
FindBuffer = SearchRecord->FindBuffer;
|
|
BufferLength = SearchRecord->BufferLength;
|
|
RetCode = EditBuffer(SearchRecord->InformationLevel,
|
|
SearchRecord->Attributes,
|
|
SearchRecord->NtHandle,
|
|
&EntriesNeeded,
|
|
SearchRecord->RewindEntry,
|
|
(ULONG)SearchRecord->FindBuffer+BufferLength-(ULONG)SearchRecord->RewindEntry,
|
|
&LastEntry,
|
|
(PVOID *) &UserBuffer,
|
|
&Length,
|
|
UserEAOP,
|
|
&Rewind,
|
|
&RewindEntry
|
|
);
|
|
EntriesFound += EntriesNeeded;
|
|
if (RetCode != NO_ERROR)
|
|
goto EditBufferError;
|
|
RetCode = UpdateSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
DirectoryHandle);
|
|
if (EntriesFound == EntriesRequested)
|
|
goto Done;
|
|
//
|
|
// if we get here, all the entries in the buffer were used.
|
|
//
|
|
}
|
|
if (SearchRecord->InformationLevel == FIL_STANDARD)
|
|
{
|
|
OneOs2EntrySize = FIND_LEVEL_ONE_INFO_SIZE;
|
|
OneNtEntrySize = sizeof(FILE_DIRECTORY_INFORMATION);
|
|
InformationClass = FileDirectoryInformation;
|
|
}
|
|
else
|
|
{
|
|
OneOs2EntrySize = FIND_LEVEL_TWO_INFO_SIZE;
|
|
OneNtEntrySize = sizeof(FILE_FULL_DIR_INFORMATION);
|
|
InformationClass = FileFullDirectoryInformation;
|
|
}
|
|
|
|
// allocate a buffer. the size of this buffer is
|
|
// big enough to contain the same number of entries (in NT format) that would
|
|
// fit into the user's buffer (in OS/2 format).
|
|
|
|
ASSERT(OneOs2EntrySize < OneNtEntrySize);
|
|
ASSERT((OneNtEntrySize & (sizeof(ULONG)-1))==0);
|
|
BufferLength = OneNtEntrySize * (Length / OneOs2EntrySize);
|
|
FindBuffer = RtlAllocateHeap(Od2Heap,0,BufferLength);
|
|
if (FindBuffer == NULL)
|
|
{
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return ERROR_NOT_ENOUGH_MEMORY; // BUGBUG bogus error
|
|
}
|
|
while (EntriesFound < EntriesRequested)
|
|
{
|
|
if ((EntriesRequested - EntriesFound) == 1) // if one entry needed
|
|
OneEntry = TRUE;
|
|
else
|
|
OneEntry = FALSE;
|
|
do {
|
|
Status = NtQueryDirectoryFile(SearchRecord->NtHandle,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatus,
|
|
FindBuffer,
|
|
BufferLength,
|
|
InformationClass,
|
|
OneEntry,
|
|
NULL,
|
|
FALSE); // multiple files for testing
|
|
} while (RetryIO(Status, SearchRecord->NtHandle));
|
|
//
|
|
// if the call to NtQueryDirectoryFile fails, we end the search.
|
|
// if it failed because there were no more matching files and we
|
|
// found at least one match, we return the match and no error and
|
|
// save the search information. otherwise we don't save the search
|
|
// information.
|
|
//
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
try
|
|
{
|
|
*CountEntriesFound = EntriesFound;
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
Od2ExitGP();
|
|
}
|
|
if (((Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_NO_MORE_FILES)) &&
|
|
(EntriesFound > 0)) {
|
|
Rewind = FALSE;
|
|
RetCode = UpdateSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
DirectoryHandle);
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return RetCode;
|
|
}
|
|
else {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
if (Status == STATUS_NO_MORE_FILES) {
|
|
return ERROR_NO_MORE_FILES;
|
|
}
|
|
else {
|
|
return ERROR_FILE_NOT_FOUND; //BUGBUG bogus erro
|
|
}
|
|
}
|
|
}
|
|
EntriesNeeded = EntriesRequested - EntriesFound;
|
|
RetCode = EditBuffer(SearchRecord->InformationLevel,
|
|
SearchRecord->Attributes,
|
|
SearchRecord->NtHandle,
|
|
&EntriesNeeded,
|
|
FindBuffer,
|
|
IoStatus.Information,
|
|
&LastEntry,
|
|
(PVOID *) &UserBuffer,
|
|
&Length,
|
|
UserEAOP,
|
|
&Rewind,
|
|
&RewindEntry
|
|
);
|
|
EntriesFound += EntriesNeeded;
|
|
|
|
// we use the return code from EditBuffer and the number of
|
|
// entries returned to determine what to return to the user.
|
|
// the basic rules are:
|
|
// if infolevel 1 and not enough room for any entries
|
|
// ERROR_BUFFER_OVERFLOW
|
|
// if infolevel 2 or 3 and not enough room for entry + cblist of first entry
|
|
// ERROR_BUFFER_OVERFLOW
|
|
// if infolevel 3
|
|
// if not enough room for EAs for first entry
|
|
// copy entry + cblist
|
|
// return ERROR_EAS_DIDNT_FIT
|
|
//
|
|
// this translates into the following pseudocode:
|
|
//
|
|
// if (RetCode)
|
|
// if (entries found == 0)
|
|
// return retcode
|
|
// if (retcode == ERROR_EAS_DIDNT_FIT)
|
|
// if (entries found == 1)
|
|
// return retcode
|
|
// else
|
|
// decrement found count
|
|
//
|
|
// else if (retcode == ERROR_BUFFER_OVERFLOW | ERROR_NO_MORE_FILES)
|
|
// if (entries > 0)
|
|
// return no error
|
|
// else
|
|
// return retcode
|
|
// else
|
|
// return retcode
|
|
|
|
// if we get a return code, we stop getting more entries.
|
|
// first, we free up the memory we've allocated.
|
|
// then, if no entries have been found, we bag the search
|
|
// entirely by closing the directory handle and returning the
|
|
// error. if some entries have been found and ERROR_EAS_DIDNT_FIT
|
|
// was returned, we need to find out whether the error applied to
|
|
// the first (and only) entry in the buffer. if it did, we return
|
|
// the error. otherwise we don't. in any case, we save the search
|
|
// information and return a valid handle and search count.
|
|
|
|
if (RetCode != NO_ERROR) {
|
|
EditBufferError:
|
|
if (EntriesFound == 0) {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
return RetCode;
|
|
}
|
|
if (RetCode == ERROR_EAS_DIDNT_FIT) {
|
|
try {
|
|
*CountEntriesFound = EntriesFound;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
Od2ExitGP();
|
|
}
|
|
RetCode = UpdateSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
DirectoryHandle);
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
if (RetCode != NO_ERROR) {
|
|
return RetCode;
|
|
}
|
|
if (EntriesFound == 1) {
|
|
return ERROR_EAS_DIDNT_FIT;
|
|
}
|
|
else {
|
|
*CountEntriesFound--;
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
//
|
|
// if the return code is error_buffer_overflow or error_no_more_files,
|
|
// and one or more entries has successfully been stored in the user's
|
|
// buffer, we return success.
|
|
//
|
|
else if ((RetCode == ERROR_BUFFER_OVERFLOW) ||
|
|
(RetCode == ERROR_NO_MORE_FILES)) {
|
|
if (EntriesFound > 0) {
|
|
try {
|
|
*CountEntriesFound = EntriesFound;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
Od2ExitGP();
|
|
}
|
|
RetCode = UpdateSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
DirectoryHandle);
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return RetCode;
|
|
}
|
|
else {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
return RetCode;
|
|
}
|
|
}
|
|
//
|
|
// we get here if one or more entries has been successfully stored
|
|
// in the user's buffer, but we encountered an error other than
|
|
// buffer_overflow, no_more_files, or eas_didnt_fit. we fail the
|
|
// search and return the error code.
|
|
//
|
|
else {
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
RtlFreeHeap(Od2Heap,0,FindBuffer);
|
|
return RetCode;
|
|
}
|
|
ASSERT (FALSE); // we should never get here
|
|
}
|
|
//
|
|
// if we get here, EditBuffer returned NO_ERROR.
|
|
//
|
|
}
|
|
|
|
//
|
|
// if we get here, we've found all the requested entries. save the
|
|
// search information and return.
|
|
//
|
|
RetCode = UpdateSearchInfo(Rewind,
|
|
RewindEntry,
|
|
FindBuffer,
|
|
BufferLength,
|
|
DirectoryHandle);
|
|
Done:
|
|
ReleaseFileLockShared(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return RetCode;
|
|
|
|
}
|
|
|
|
APIRET
|
|
DosFindClose(
|
|
IN HDIR DirectoryHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the OS/2 API DosFindClose.
|
|
|
|
Arguments:
|
|
|
|
DirectoryHandle - The search handle associated with this particular
|
|
search.
|
|
|
|
Return Value:
|
|
|
|
see OS/2 spec.
|
|
|
|
--*/
|
|
|
|
{
|
|
APIRET RetCode;
|
|
PSEARCH_RECORD SearchRecord;
|
|
#if DBG
|
|
PSZ RoutineName;
|
|
RoutineName = "DosFindClose";
|
|
#endif
|
|
|
|
AcquireFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
if ((((ULONG)DirectoryHandle) >= SearchHandleTableLength) ||
|
|
(((ULONG) SearchHandleTable[((ULONG)DirectoryHandle)]) == SEARCH_HANDLE_FREE)) {
|
|
ReleaseFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
SearchRecord = (PSEARCH_RECORD) SearchHandleTable[((ULONG)DirectoryHandle)];
|
|
if (SearchRecord->FindBuffer != NULL) {
|
|
RtlFreeHeap(Od2Heap,0,SearchRecord->FindBuffer);
|
|
}
|
|
RetCode = FreeSearchHandle(DirectoryHandle);
|
|
ReleaseFileLockExclusive(
|
|
#if DBG
|
|
RoutineName
|
|
#endif
|
|
);
|
|
return RetCode;
|
|
}
|