windows-nt/Source/XPSP1/NT/base/fs/srv/smbfind.c
2020-09-26 16:20:57 +08:00

3338 lines
104 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
smbsrch.c
Abstract:
This module contains routines for processing the find 2 SMBs:
Find 2 (First/Next/Rewind)
Find 2 Close
Author:
David Treadwell (davidtr) 13-Feb-1990
Revision History:
--*/
#include "precomp.h"
#include "smbfind.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBFIND
VOID SRVFASTCALL
BlockingFindFirst2 (
IN PWORK_CONTEXT WorkContext
);
SMB_TRANS_STATUS
DoFindFirst2 (
IN PWORK_CONTEXT WorkContext
);
VOID SRVFASTCALL
BlockingFindNext2 (
IN PWORK_CONTEXT WorkContext
);
SMB_TRANS_STATUS
DoFindNext2 (
IN PWORK_CONTEXT WorkContext
);
NTSTATUS
SrvFind2Loop (
IN PWORK_CONTEXT WorkContext,
IN BOOLEAN IsFirstCall,
IN PULONG ResumeFileIndex OPTIONAL,
IN USHORT Flags,
IN USHORT InformationLevel,
IN PTRANSACTION Transaction,
IN PSRV_DIRECTORY_INFORMATION DirectoryInformation,
IN CLONG BufferSize,
IN USHORT SearchAttributes,
IN PUNICODE_STRING FileName OPTIONAL,
IN USHORT MaxCount,
IN PRESP_FIND_NEXT2 Response,
OUT PSEARCH Search
);
VOID
ConvertFileInfo (
IN PFILE_DIRECTORY_INFORMATION File,
IN PWCH FileName,
IN BOOLEAN Directory,
IN BOOLEAN ClientIsUnicode,
OUT PSMB_FIND_BUFFER FindBuffer
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbFindFirst2 )
#pragma alloc_text( PAGE, BlockingFindFirst2 )
#pragma alloc_text( PAGE, DoFindFirst2 )
#pragma alloc_text( PAGE, SrvSmbFindNext2 )
#pragma alloc_text( PAGE, BlockingFindNext2 )
#pragma alloc_text( PAGE, DoFindNext2 )
#pragma alloc_text( PAGE, SrvFind2Loop )
#pragma alloc_text( PAGE, ConvertFileInfo )
#pragma alloc_text( PAGE, SrvSmbFindClose2 )
#endif
SMB_TRANS_STATUS
SrvSmbFindFirst2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find First2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred. See
smbtypes.h for a more complete description.
--*/
{
PREQ_FIND_FIRST2 request;
PTRANSACTION transaction;
SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_FIND_FIRST2;
SrvWmiStartContext(WorkContext);
//
// If the infomation level is QUERY_EAS_FROM_LIST, and we
// are not in a blocking thread, requeue the request to a blocking
// thread.
//
// We can't process the SMB in a non blocking thread because this
// info level requires opening the file, which may be oplocked,
// so the open operation may block.
//
transaction = WorkContext->Parameters.Transaction;
request = (PREQ_FIND_FIRST2)transaction->InParameters;
if ( transaction->ParameterCount >= sizeof(REQ_FIND_FIRST2) &&
SmbGetUshort( &request->InformationLevel ) == SMB_INFO_QUERY_EAS_FROM_LIST ) {
WorkContext->FspRestartRoutine = BlockingFindFirst2;
SrvQueueWorkToBlockingThread(WorkContext);
SmbStatus = SmbTransStatusInProgress;
}
else {
SmbStatus = DoFindFirst2(WorkContext);
}
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbFindFirst2
VOID SRVFASTCALL
BlockingFindFirst2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find First2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
None.
--*/
{
SMB_TRANS_STATUS smbStatus = SmbTransStatusInProgress;
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_FIND_FIRST2;
SrvWmiStartContext(WorkContext);
smbStatus = DoFindFirst2( WorkContext );
if ( smbStatus != SmbTransStatusInProgress ) {
SrvCompleteExecuteTransaction( WorkContext, smbStatus );
}
SrvWmiEndContext(WorkContext);
return;
} // BlockingFindFirst2
SMB_TRANS_STATUS
DoFindFirst2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find First2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred. See
smbtypes.h for a more complete description.
--*/
{
PREQ_FIND_FIRST2 request;
PRESP_FIND_FIRST2 response;
PTRANSACTION transaction;
PCONNECTION connection;
NTSTATUS status,TableStatus;
UNICODE_STRING fileName;
PTABLE_ENTRY entry = NULL;
PTABLE_HEADER searchTable;
SHORT sidIndex = 0;
USHORT sequence;
USHORT maxCount;
USHORT flags;
USHORT informationLevel;
BOOLEAN isUnicode;
PSRV_DIRECTORY_INFORMATION directoryInformation;
CLONG nonPagedBufferSize;
PSEARCH search = NULL;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(SEARCH1) {
SrvPrint1( "Find First2 entered; transaction 0x%p\n", transaction );
}
request = (PREQ_FIND_FIRST2)transaction->InParameters;
response = (PRESP_FIND_FIRST2)transaction->OutParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( (transaction->ParameterCount < sizeof(REQ_FIND_FIRST2)) ||
(transaction->MaxParameterCount < sizeof(RESP_FIND_FIRST2)) ) {
//
// Not enough parameter bytes were sent.
//
IF_SMB_DEBUG(SEARCH2) {
SrvPrint2( "DoFindFirst2: bad parameter byte counts: "
"%ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// Make sure this really is a disk type share
//
if( transaction->TreeConnect->Share->ShareType != ShareTypeDisk ) {
SrvSetSmbError( WorkContext, STATUS_ACCESS_DENIED );
return SmbTransStatusErrorWithoutData;
}
//
// Make sure the client is allowed to do this, if we have an Admin share
//
status = SrvIsAllowedOnAdminShare( WorkContext, transaction->TreeConnect->Share );
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// Initialize the string containing the search name specification.
//
isUnicode = SMB_IS_UNICODE( WorkContext );
status = SrvCanonicalizePathName(
WorkContext,
transaction->TreeConnect->Share,
NULL,
request->Buffer,
END_OF_TRANSACTION_PARAMETERS( transaction ),
FALSE,
isUnicode,
&fileName
);
if( !NT_SUCCESS( status ) ) {
SrvSetSmbError( WorkContext, status );
return SmbTransStatusErrorWithoutData;
}
//
// Get parameters from the request SMB.
//
maxCount = SmbGetUshort( &request->SearchCount );
flags = SmbGetUshort( &request->Flags );
//
// Make sure that the informationLevel is supported.
//
informationLevel = SmbGetUshort( &request->InformationLevel );
switch ( informationLevel ) {
case SMB_INFO_STANDARD:
case SMB_INFO_QUERY_EA_SIZE:
case SMB_INFO_QUERY_EAS_FROM_LIST:
case SMB_FIND_FILE_DIRECTORY_INFO:
case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
case SMB_FIND_FILE_NAMES_INFO:
case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
break;
default:
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "DoFindFirst2: Bad info level: %ld\n",
informationLevel );
}
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
return SmbTransStatusErrorWithoutData;
}
//
// Allocate a search block on the assumption that a search table
// entry will be available when needed.
//
SrvAllocateSearch( &search, &fileName, FALSE );
if ( search == NULL ) {
IF_DEBUG(ERRORS) {
SrvPrint0( "DoFindFirst2: unable to allocate search block.\n" );
}
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
return SmbTransStatusErrorWithoutData;
}
search->SearchStorageType = SmbGetUlong(&request->SearchStorageType);
//
// Allocate an SID for the search. The SID is used to locate the
// search block on FindNexts. If there are no free entries in the
// table, attempt to grow the table. If we are unable to grow the table,
// attempt to timeout a search block using the shorter timeout period.
// If this fails, reject the request.
//
connection = WorkContext->Connection;
searchTable = &connection->PagedConnection->SearchTable;
ACQUIRE_LOCK( &connection->Lock );
//
// Before inserting this search block, make sure the session and tree
// connect is still active. If this gets inserted after the session
// is closed, the search might not be cleaned up properly.
//
if (GET_BLOCK_STATE(WorkContext->Session) != BlockStateActive) {
IF_DEBUG(ERRORS) {
SrvPrint0( "DoFindFirst2: Session Closing.\n" );
}
RELEASE_LOCK( &connection->Lock );
FREE_HEAP( search );
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
return SmbTransStatusErrorWithoutData;
} else if (GET_BLOCK_STATE(WorkContext->TreeConnect) != BlockStateActive) {
IF_DEBUG(ERRORS) {
SrvPrint0( "DoFindFirst2: Tree Connect Closing.\n" );
}
RELEASE_LOCK( &connection->Lock );
FREE_HEAP( search );
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_TID );
return SmbTransStatusErrorWithoutData;
}
//
// Set up referenced session and tree connect pointers and increment
// the count of open files on the session. This prevents an idle
// session with an open search from being autodisconnected.
//
search->Session = WorkContext->Session;
SrvReferenceSession( WorkContext->Session );
search->TreeConnect = WorkContext->TreeConnect;
SrvReferenceTreeConnect( WorkContext->TreeConnect );
WorkContext->Session->CurrentSearchOpenCount++;
if ( searchTable->FirstFreeEntry == -1
&&
SrvGrowTable(
searchTable,
SrvInitialSearchTableSize,
SrvMaxSearchTableSize,
&TableStatus ) == FALSE
&&
SrvTimeoutSearches(
NULL,
connection,
TRUE ) == 0
) {
IF_DEBUG(ERRORS) {
SrvPrint0( "DoFindFirst2: Connection SearchTable full.\n" );
}
//
// Decrement the counts of open searches.
//
WorkContext->Session->CurrentSearchOpenCount--;
RELEASE_LOCK( &connection->Lock );
SrvDereferenceTreeConnect( search->TreeConnect );
SrvDereferenceSession( search->Session );
FREE_HEAP( search );
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
{
SrvLogTableFullError( SRV_TABLE_SEARCH);
SrvSetSmbError( WorkContext, STATUS_OS2_NO_MORE_SIDS );
}
else {
SrvSetSmbError( WorkContext, STATUS_INSUFFICIENT_RESOURCES );
}
return SmbTransStatusErrorWithoutData;
}
sidIndex = searchTable->FirstFreeEntry;
//
// A free SID was found. Remove it from the free list and set
// its owner and sequence number.
//
entry = &searchTable->Table[sidIndex];
searchTable->FirstFreeEntry = entry->NextFreeEntry;
DEBUG entry->NextFreeEntry = -2;
if ( searchTable->LastFreeEntry == sidIndex ) {
searchTable->LastFreeEntry = -1;
}
INCREMENT_SID_SEQUENCE( entry->SequenceNumber );
//
// SID = sequence | sidIndex == 0 is illegal. If this is
// the current value, increment the sequence.
//
if ( entry->SequenceNumber == 0 && sidIndex == 0 ) {
INCREMENT_SID_SEQUENCE( entry->SequenceNumber );
}
sequence = entry->SequenceNumber;
entry->Owner = search;
RELEASE_LOCK( &connection->Lock );
//
// Fill in other fields of the search block.
//
search->SearchAttributes = SmbGetUshort( &request->SearchAttributes );
search->TableIndex = sidIndex;
//
// Store the Flags2 field of the smb in the search block. This is
// used as a workaround for an OS/2 client side bug where the
// findfirst and findnext flags2 bits are inconsistent.
//
search->Flags2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 );
if ( search->Flags2 & SMB_FLAGS2_KNOWS_LONG_NAMES ) {
search->Flags2 |= SMB_FLAGS2_KNOWS_EAS;
}
//
// A buffer of nonpaged pool is required by SrvQueryDirectoryFile.
// We need to use the SMB buffer for found file names and information,
// so allocate a buffer from nonpaged pool.
//
// If we don't need to return many files, we don't need to allocate
// a large buffer. The buffer size is the configurable size or
// enough to hold two more then the number of files we need to
// return. We get space to hold two extra files in case some
// files do not meet the search criteria (eg directories).
//
if ( maxCount > MAX_FILES_FOR_MED_FIND2 ) {
nonPagedBufferSize = MAX_SEARCH_BUFFER_SIZE;
} else if ( maxCount > MAX_FILES_FOR_MIN_FIND2 ) {
nonPagedBufferSize = MED_SEARCH_BUFFER_SIZE;
} else {
nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
}
directoryInformation = ALLOCATE_NONPAGED_POOL(
nonPagedBufferSize,
BlockTypeDataBuffer
);
if ( directoryInformation == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"DoFindFirst2: could not allocate nonpaged pool.",
NULL,
NULL
);
SrvCloseSearch( search );
SrvDereferenceSearch( search );
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
return SmbTransStatusErrorWithoutData;
}
directoryInformation->DirectoryHandle = 0;
IF_SMB_DEBUG(SEARCH2) {
SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
nonPagedBufferSize, directoryInformation );
}
//
// Call SrvFind2Loop to fill the data section of the transaction with
// file entries. It writes into the response parameters section
// of the SMB information relating to the results of the search.
// The information is the same as the response parameters for
// a FindNext2, so that structure is used. The FindFirst2 parameters
// are identical to the FindNext2 parameters except for the Sid
// at the beginning of the FindFirst2 response.
//
status = SrvFind2Loop(
WorkContext,
TRUE,
NULL,
flags,
informationLevel,
transaction,
directoryInformation,
nonPagedBufferSize,
search->SearchAttributes,
&fileName,
maxCount,
(PRESP_FIND_NEXT2)( &response->SearchCount ),
search
);
if ( !isUnicode ) {
RtlFreeUnicodeString( &fileName );
}
//
// Map the error, if necessary
//
if ( !IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {
if ( status == STATUS_NO_SUCH_FILE ) {
status = STATUS_NO_MORE_FILES;
}
}
if ( !NT_SUCCESS(status) && SmbGetUshort( &response->SearchCount ) == 0 ) {
//
// If an error was encountered on a find first, we close the search
// block.
//
search->DirectoryHandle = NULL;
SrvCloseSearch( search );
SrvDereferenceSearch( search );
SrvCloseQueryDirectory( directoryInformation );
DEALLOCATE_NONPAGED_POOL( directoryInformation );
SrvSetSmbError2( WorkContext, status, TRUE );
transaction->SetupCount = 0;
transaction->ParameterCount = sizeof(RESP_FIND_FIRST2);
SmbPutUshort( &response->Sid, 0 );
return SmbTransStatusErrorWithData;
}
//
// If the client told us to close the search after this request, or
// close at end-of-search, or this no files were found, close the
// search block and call SrvCloseQueryDirectory. Otherwise, store
// information in the search block.
//
if ( ( flags & SMB_FIND_CLOSE_AFTER_REQUEST ) != 0 ||
( status == STATUS_NO_MORE_FILES &&
( flags & SMB_FIND_CLOSE_AT_EOS ) != 0 ) ) {
IF_SMB_DEBUG(SEARCH2) {
SrvPrint1( "Closing search at %p\n", search );
}
search->DirectoryHandle = NULL;
SrvCloseSearch( search );
SrvCloseQueryDirectory( directoryInformation );
} else {
search->DirectoryHandle = directoryInformation->DirectoryHandle;
search->Wildcards = directoryInformation->Wildcards;
}
//
// Free the buffer used for the search and dereference our pointer to
// the search block.
//
DEALLOCATE_NONPAGED_POOL( directoryInformation );
search->InUse = FALSE;
SrvDereferenceSearch( search );
//
// Build the output parameter and data structures.
//
transaction->SetupCount = 0;
transaction->ParameterCount = sizeof(RESP_FIND_FIRST2);
SmbPutUshort( &response->Sid, MAKE_SID( sidIndex, sequence ) );
return SmbTransStatusSuccess;
} // DoFindFirst2
SMB_TRANS_STATUS
SrvSmbFindNext2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find Next2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred. See
smbtypes.h for a more complete description.
--*/
{
SMB_TRANS_STATUS SmbStatus = SmbTransStatusInProgress;
PTRANSACTION transaction;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_FIND_NEXT2;
SrvWmiStartContext(WorkContext);
//
// If the infomation level is QUERY_EAS_FROM_LIST, and we
// are not in a blocking thread, requeue the request to a blocking
// thread.
//
// We can't process the SMB in a non blocking thread because this
// info level requires opening the file, which may be oplocked,
// so the open operation may block.
//
transaction = WorkContext->Parameters.Transaction;
if( transaction->ParameterCount >= sizeof(REQ_FIND_NEXT2) ) {
PREQ_FIND_NEXT2 request = (PREQ_FIND_NEXT2)transaction->InParameters;
USHORT informationLevel = SmbGetUshort( &request->InformationLevel );
if ( informationLevel == SMB_INFO_QUERY_EAS_FROM_LIST ) {
WorkContext->FspRestartRoutine = BlockingFindNext2;
SrvQueueWorkToBlockingThread( WorkContext );
SmbStatus = SmbTransStatusInProgress;
goto Cleanup;
}
}
SmbStatus = DoFindNext2( WorkContext );
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbFindNext2
VOID SRVFASTCALL
BlockingFindNext2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find Next2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
None.
--*/
{
SMB_TRANS_STATUS smbStatus = SmbTransStatusInProgress;
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_FIND_NEXT2;
SrvWmiStartContext(WorkContext);
smbStatus = DoFindNext2( WorkContext );
if ( smbStatus != SmbTransStatusInProgress ) {
SrvCompleteExecuteTransaction( WorkContext, smbStatus );
}
SrvWmiEndContext(WorkContext);
return;
} // BlockingFindNext2
SMB_TRANS_STATUS
DoFindNext2 (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Find First2 request. This request arrives in a
Transaction2 SMB.
Arguments:
WorkContext - Supplies the address of a Work Context Block
describing the current request. See smbtypes.h for a more
complete description of the valid fields.
Return Value:
SMB_TRANS_STATUS - Indicates whether an error occurred. See
smbtypes.h for a more complete description.
--*/
{
PREQ_FIND_NEXT2 request;
PRESP_FIND_NEXT2 response;
PTRANSACTION transaction;
NTSTATUS status;
USHORT i;
PCHAR ansiChar;
PWCH unicodeChar;
ULONG maxIndex;
BOOLEAN illegalPath;
BOOLEAN freeFileName;
UNICODE_STRING fileName;
PTABLE_ENTRY entry = NULL;
USHORT maxCount;
USHORT informationLevel;
PSRV_DIRECTORY_INFORMATION directoryInformation;
CLONG nonPagedBufferSize;
ULONG resumeFileIndex;
USHORT flags;
USHORT sid;
PSEARCH search = NULL;
PAGED_CODE( );
transaction = WorkContext->Parameters.Transaction;
IF_SMB_DEBUG(SEARCH1) {
SrvPrint1( "Find Next2 entered; transaction %p\n", transaction );
}
request = (PREQ_FIND_NEXT2)transaction->InParameters;
response = (PRESP_FIND_NEXT2)transaction->OutParameters;
//
// Verify that enough parameter bytes were sent and that we're allowed
// to return enough parameter bytes.
//
if ( (transaction->ParameterCount <
sizeof(REQ_FIND_NEXT2)) ||
(transaction->MaxParameterCount <
sizeof(RESP_FIND_NEXT2)) ) {
//
// Not enough parameter bytes were sent.
//
IF_DEBUG(SMB_ERRORS) {
SrvPrint2( "DoFindNext2: bad parameter byte counts: %ld %ld\n",
transaction->ParameterCount,
transaction->MaxParameterCount );
}
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
return SmbTransStatusErrorWithoutData;
}
//
// Get parameters from the request SMB.
//
maxCount = SmbGetUshort( &request->SearchCount );
resumeFileIndex = SmbGetUlong( &request->ResumeKey );
flags = SmbGetUshort( &request->Flags );
//
// Make sure that the informationLevel is supported.
//
informationLevel = SmbGetUshort( &request->InformationLevel );
switch ( informationLevel ) {
case SMB_INFO_STANDARD:
case SMB_INFO_QUERY_EA_SIZE:
case SMB_INFO_QUERY_EAS_FROM_LIST:
case SMB_FIND_FILE_DIRECTORY_INFO:
case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
case SMB_FIND_FILE_NAMES_INFO:
case SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO:
case SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO:
break;
default:
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "DoFindNext2: Bad info level: %ld\n",
informationLevel );
}
SrvSetSmbError( WorkContext, STATUS_OS2_INVALID_LEVEL );
return SmbTransStatusErrorWithoutData;
}
//
// A buffer of nonpaged pool is required by SrvQueryDirectoryFile.
// We need to use the SMB buffer for found file names and information,
// so allocate a buffer from nonpaged pool.
//
// If we don't need to return many files, we don't need to allocate
// a large buffer. The buffer size is the configurable size or
// enough to hold two more then the number of files we need to
// return. We get space to hold two extra files in case some
// files do not meet the search criteria (eg directories).
//
if ( maxCount > MAX_FILES_FOR_MED_FIND2 ) {
nonPagedBufferSize = MAX_SEARCH_BUFFER_SIZE;
} else if ( maxCount > MAX_FILES_FOR_MIN_FIND2 ) {
nonPagedBufferSize = MED_SEARCH_BUFFER_SIZE;
} else {
nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
}
directoryInformation = ALLOCATE_NONPAGED_POOL(
nonPagedBufferSize,
BlockTypeDataBuffer
);
if ( directoryInformation == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"DoFindFirst2: unable to allocate nonpaged pool.",
NULL,
NULL
);
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
return SmbTransStatusErrorWithoutData;
}
IF_SMB_DEBUG(SEARCH2) {
SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
nonPagedBufferSize, directoryInformation );
}
//
// Get the search block corresponding to this SID. SrvVerifySid
// references the search block and fills in fields of
// directoryInformation so it is ready to be used by
// SrvQueryDirectoryFile.
//
sid = SmbGetUshort( &request->Sid );
search = SrvVerifySid(
WorkContext,
SID_INDEX2( sid ),
SID_SEQUENCE2( sid ),
directoryInformation,
nonPagedBufferSize
);
if ( search == NULL ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "DoFindNext2: Invalid SID: %lx.\n", sid );
}
SrvSetSmbError( WorkContext, STATUS_INVALID_HANDLE );
DEALLOCATE_NONPAGED_POOL( directoryInformation );
return SmbTransStatusErrorWithoutData;
}
//
// Initialize the string containing the resume name specification.
// If the client requested that we resume from the last file returned,
// use the file name and index stored in the search block.
//
if ( ( flags & SMB_FIND_CONTINUE_FROM_LAST ) == 0 ) {
//
// Test and use the information passed by the client. A file
// name may not be longer than MAXIMUM_FILENAME_LENGTH characters,
// and it should not contain any directory information.
//
illegalPath = FALSE;
freeFileName = FALSE;
if ( SMB_IS_UNICODE( WorkContext ) ) {
fileName.Buffer = ALIGN_SMB_WSTR( (PWCH)request->Buffer );
maxIndex = (ULONG)((END_OF_REQUEST_SMB( WorkContext ) -
(PUCHAR)fileName.Buffer) / sizeof(WCHAR));
for ( i = 0, unicodeChar = fileName.Buffer;
(i < MAXIMUM_FILENAME_LENGTH) && (i < maxIndex);
i++, unicodeChar++ ) {
if ( *unicodeChar == '\0' ) {
break;
}
if ( IS_UNICODE_PATH_SEPARATOR( *unicodeChar ) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "DoFindNext2: illegal path name: %ws\n",
fileName.Buffer );
}
illegalPath = TRUE;
break;
}
}
fileName.Length = (USHORT) (i * sizeof(WCHAR));
fileName.MaximumLength = fileName.Length;
} else {
ansiChar = (PCHAR)request->Buffer;
maxIndex = (ULONG)(END_OF_REQUEST_SMB( WorkContext ) - ansiChar);
for ( i = 0;
(i < MAXIMUM_FILENAME_LENGTH) && (i < maxIndex);
i++, ansiChar++ ) {
if ( *ansiChar == '\0' ) {
break;
}
if ( IS_ANSI_PATH_SEPARATOR( *ansiChar ) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "DoFindNext2: illegal path name: %s\n",
request->Buffer );
}
illegalPath = TRUE;
break;
}
}
if ( !illegalPath ) {
status = SrvMakeUnicodeString(
FALSE,
&fileName,
request->Buffer,
&i
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint0( "DoFindNext2: unable to allocate Unicode string\n" );
}
search->InUse = FALSE;
SrvDereferenceSearch( search );
DEALLOCATE_NONPAGED_POOL( directoryInformation );
SrvSetSmbError2(
WorkContext,
STATUS_OBJECT_PATH_SYNTAX_BAD,
TRUE
);
return SmbTransStatusErrorWithoutData;
}
freeFileName = TRUE;
}
}
if ( illegalPath ) {
search->InUse = FALSE;
SrvDereferenceSearch( search );
DEALLOCATE_NONPAGED_POOL( directoryInformation );
SrvSetSmbError( WorkContext, STATUS_OBJECT_PATH_SYNTAX_BAD );
return SmbTransStatusErrorWithoutData;
}
} else {
//
// Use the information in the search block.
//
fileName = search->LastFileNameReturned;
freeFileName = FALSE;
resumeFileIndex = search->LastFileIndexReturned;
}
//
// Call SrvFind2Loop to fill the SMB buffer and set output parameters.
//
// !!! The NULL that might get passed for the resume file index is
// a real hack. I doubt it is necessary, but it could prevent
// a server crash if we somehow failed to store the resume file
// name.
status = SrvFind2Loop(
WorkContext,
FALSE,
fileName.Buffer != NULL ? &resumeFileIndex : NULL,
flags,
informationLevel,
transaction,
directoryInformation,
nonPagedBufferSize,
search->SearchAttributes,
&fileName,
maxCount,
response,
search
);
if ( freeFileName ) {
RtlFreeUnicodeString( &fileName );
}
if ( !NT_SUCCESS(status) && status != STATUS_NO_MORE_FILES ) {
search->InUse = FALSE;
SrvDereferenceSearch( search );
DEALLOCATE_NONPAGED_POOL( directoryInformation );
transaction->SetupCount = 0;
transaction->ParameterCount = sizeof(RESP_FIND_NEXT2);
SrvSetSmbError2( WorkContext, status, TRUE );
return SmbTransStatusErrorWithData;
}
//
// If the client told us to close the search after this request,
// or close at end-of-search, close the search block and call
// SrvCloseQueryDirectory.
//
if ( ( flags & SMB_FIND_CLOSE_AFTER_REQUEST ) != 0 ||
( status == STATUS_NO_MORE_FILES &&
( flags & SMB_FIND_CLOSE_AT_EOS ) != 0 ) ) {
search->DirectoryHandle = NULL;
SrvCloseSearch( search );
SrvCloseQueryDirectory( directoryInformation );
}
//
// Dereference our pointer to the search block and free the buffer.
//
DEALLOCATE_NONPAGED_POOL( directoryInformation );
search->InUse = FALSE;
SrvDereferenceSearch( search );
//
// Build the output parameter and data structures.
//
transaction->SetupCount = 0;
transaction->ParameterCount = sizeof(RESP_FIND_NEXT2);
return SmbTransStatusSuccess;
} // DoFindNext2
NTSTATUS
SrvFind2Loop (
IN PWORK_CONTEXT WorkContext,
IN BOOLEAN IsFirstCall,
IN PULONG ResumeFileIndex OPTIONAL,
IN USHORT Flags,
IN USHORT InformationLevel,
IN PTRANSACTION Transaction,
IN PSRV_DIRECTORY_INFORMATION DirectoryInformation,
IN CLONG BufferSize,
IN USHORT SearchAttributes,
IN PUNICODE_STRING FileName OPTIONAL,
IN USHORT MaxCount,
IN PRESP_FIND_NEXT2 Response,
OUT PSEARCH Search
)
/*++
Routine Description:
This routine does the looping necessary to get files and put them
into an SMB buffer for the Find First2 and Find Next2 transaction
protocols.
Arguments:
WorkContext -
IsFirstCall - TRUE if this is a Find First and this is the first call
to SrvQueryDirectoryFile.
ResumeFileIndex - if non-NULL, a pointer to the file index to resume
from.
Flags - the Flags field of the request SMB.
InformationLevel - the InformationLevel field of the request SMB. The
validity of this value should be verified by the calling routine.
Transaction - a pointer to the transaction block to use.
DirectoryInformation - a pointer to the SRV_DIRECTORY_INFORMATION
structure to use.
BufferSize - size of the DirectoryInformation buffer.
SearchAttributes - the SMB-style attributes to pass to
SrvQueryDirectoryFile.
FileName - if non-NULL the file name to resume the search from.
MaxCount - the maximum number of files to get.
Response - a pointer to the response field of the SMB. If this is
a Find First2, it is a pointer to the SearchCount field of the
response SMB--Find First2 and Find Next2 response formats are
identical from this point on.
Search - a pointer to the search block to use.
Return Value:
NTSTATUS indicating results.
--*/
{
NTSTATUS status;
PCHAR bufferLocation;
BOOLEAN resumeKeysRequested;
BOOLEAN allowExtraLongNames;
BOOLEAN isUnicode;
USHORT count = 0;
PCHAR lastEntry;
CLONG totalBytesWritten;
OEM_STRING oemString;
UNICODE_STRING unicodeString;
UNICODE_STRING lastFileName;
ULONG lastFileIndex = (ULONG)0xFFFFFFFF;
HANDLE fileHandle;
PFILE_GET_EA_INFORMATION ntGetEa;
ULONG ntGetEaLength;
USHORT eaErrorOffset = 0;
BOOLEAN filterLongNames;
BOOLEAN errorOnFileOpen;
BOOLEAN findWithBackupIntent;
IO_STATUS_BLOCK ioStatusBlock;
OBJECT_ATTRIBUTES objectAttributes;
BOOLEAN createNullEas;
PAGED_CODE( );
//
// If the client is requesting an NT info level for search information,
// do not return resume keys outside the actual file entry. Resume
// keys (aka FileIndex) are part of every NT info structure.
//
// Also, for NT info levels we can return file names longer than 255
// bytes, because the NT info levels have name length fields that
// are four bytes wide, whereas the downlevel info levels only have
// one-byte name length fields.
//
if ( InformationLevel == SMB_FIND_FILE_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_FULL_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_NAMES_INFO ||
InformationLevel == SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO
) {
resumeKeysRequested = FALSE;
allowExtraLongNames = TRUE;
} else {
resumeKeysRequested =
(BOOLEAN)((Flags & SMB_FIND_RETURN_RESUME_KEYS) != 0 ? TRUE : FALSE);
allowExtraLongNames = FALSE;
}
//
// Is this for backup intent?
//
if ( (Flags & SMB_FIND_WITH_BACKUP_INTENT) != 0 ) {
findWithBackupIntent = TRUE;
} else {
findWithBackupIntent = FALSE;
}
//
// Is this request in Unicode?
//
isUnicode = SMB_IS_UNICODE( WorkContext );
//
// Initialize count of files found.
//
SmbPutUshort( &Response->SearchCount, 0 );
//
// If this a request to return EAs, convert the OS/2 1.2 EA list
// to NT format. This routine allocates space for the NT list
// which must be deallocated before we exit.
//
if ( InformationLevel == SMB_INFO_QUERY_EAS_FROM_LIST ) {
PGEALIST geaList = (PGEALIST)Transaction->InData;
if (Transaction->DataCount < sizeof(GEALIST) ||
SmbGetUshort(&geaList->cbList) < sizeof(GEALIST) ||
SmbGetUshort(&geaList->cbList) > Transaction->DataCount) {
SmbPutUshort( &Response->SearchCount, 0 );
SmbPutUshort( &Response->EndOfSearch, 0 );
SmbPutUshort( &Response->EaErrorOffset, 0 );
SmbPutUshort( &Response->LastNameOffset, 0 );
Transaction->DataCount = 0;
return STATUS_OS2_EA_LIST_INCONSISTENT;
}
status = SrvOs2GeaListToNt(
geaList,
&ntGetEa,
&ntGetEaLength,
&eaErrorOffset
);
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "SrvFind2Loop: SrvOs2GeaListToNt failed, "
"status = %X\n", status );
}
SmbPutUshort( &Response->SearchCount, 0 );
SmbPutUshort( &Response->EndOfSearch, 0 );
SmbPutUshort( &Response->EaErrorOffset, eaErrorOffset );
SmbPutUshort( &Response->LastNameOffset, 0 );
Transaction->DataCount = 0;
return status;
}
}
//
// Determine whether long filenames (non-8.3) should be filtered out
// or returned to the client.
//
// There is a bug in the LanMan21 that makes the redir forget that
// he knows about long names.
if ( ( ( Search->Flags2 & SMB_FLAGS2_KNOWS_LONG_NAMES ) != 0 ) &&
!IS_DOS_DIALECT( WorkContext->Connection->SmbDialect ) ) {
filterLongNames = FALSE;
} else {
filterLongNames = TRUE;
}
//
// If the client says he doesn't know about long names and this is
// a request for any info level other than SMB_INFO_STANDARD, we
// need to fail the request.
//
if ( filterLongNames && InformationLevel != SMB_INFO_STANDARD ) {
IF_DEBUG(ERRORS) {
SrvPrint0( "SrvFind2Loop: client doesn't know long names.\n" );
}
SmbPutUshort( &Response->SearchCount, 0 );
SmbPutUshort( &Response->EndOfSearch, 0 );
SmbPutUshort( &Response->EaErrorOffset, 0 );
SmbPutUshort( &Response->LastNameOffset, 0 );
Transaction->DataCount = 0;
return STATUS_INVALID_PARAMETER;
}
//
// Loop calling SrvQueryDirectoryFile to get files. We do this until
// one of the following conditions is met:
//
// 1) There are no more files to return.
// 2) We have obtained as many files as were requested.
// 3) We have put in as much data as MaxDataCount allows.
//
bufferLocation = Transaction->OutData;
lastEntry = bufferLocation;
totalBytesWritten = 0;
do {
//
// The ff fields have the same offsets in the three directory
// information structures:
// NextEntryOffset
// FileIndex
// CreationTime
// LastAccessTime
// LastWriteTime
// ChangeTime
// EndOfFile
// AllocationSize
// FileAttributes
// FileNameLength
//
PFILE_DIRECTORY_INFORMATION fileBasic;
PFILE_FULL_DIR_INFORMATION fileFull;
PFILE_BOTH_DIR_INFORMATION fileBoth;
PFILE_ID_FULL_DIR_INFORMATION fileIdFull;
PFILE_ID_BOTH_DIR_INFORMATION fileIdBoth;
ULONG ntInformationLevel;
//
// Make sure these asserts hold.
//
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, NextEntryOffset ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, NextEntryOffset ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileIndex ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileIndex ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, CreationTime ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, CreationTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastAccessTime ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, LastAccessTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastWriteTime ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, LastWriteTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, ChangeTime ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, ChangeTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, EndOfFile ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, EndOfFile ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, AllocationSize ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, AllocationSize ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileAttributes ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileAttributes ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileNameLength ) ==
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileNameLength ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, NextEntryOffset ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, NextEntryOffset ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileIndex ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileIndex ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, CreationTime ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, CreationTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastAccessTime ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, LastAccessTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, LastWriteTime ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, LastWriteTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, ChangeTime ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, ChangeTime ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, EndOfFile ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, EndOfFile ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, AllocationSize ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, AllocationSize ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileAttributes ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileAttributes ) );
C_ASSERT( FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileNameLength ) ==
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileNameLength ) );
//
// Set the info level to be used for the NT call. If
// SMB_FIND_FILE_NAMES_INFO is the info level, use
// FileDirectoryInformation as it returns all the correct
// information and works with SrvQueryDirectoryFile.
//
if ( InformationLevel == SMB_INFO_QUERY_EA_SIZE ||
InformationLevel == SMB_FIND_FILE_FULL_DIRECTORY_INFO ) {
ntInformationLevel = FileFullDirectoryInformation;
} else if ( InformationLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO ||
InformationLevel == SMB_INFO_STANDARD ) {
ntInformationLevel = FileBothDirectoryInformation;
} else if ( InformationLevel == SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO ) {
ntInformationLevel = FileIdFullDirectoryInformation;
} else if ( InformationLevel == SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO ) {
ntInformationLevel = FileIdBothDirectoryInformation;
}
else {
//
// SMB_INFO_QUERY_EAS_FROM_LIST
// SMB_FIND_NAMES_INFO
// SMB_FIND_FILE_DIRECTORY_INFO
//
ntInformationLevel = FileDirectoryInformation;
}
//
// Call SrvQueryDirectoryFile to get a file.
//
status = SrvQueryDirectoryFile(
WorkContext,
IsFirstCall,
filterLongNames,
findWithBackupIntent,
ntInformationLevel,
Search->SearchStorageType,
FileName,
ResumeFileIndex,
SearchAttributes,
DirectoryInformation,
BufferSize // !!! optimizations?
);
//
// If the client requested EA information, open the file.
//
// If the found file is '.' (current directory) or '..' (parent
// directory) do not open the file. This is because we do not want
// to perform any operations on these files at this point (don't
// return EA size, etc.).
//
fileBasic = DirectoryInformation->CurrentEntry;
fileBoth = (PFILE_BOTH_DIR_INFORMATION)DirectoryInformation->CurrentEntry;
fileFull = (PFILE_FULL_DIR_INFORMATION)DirectoryInformation->CurrentEntry;
fileIdBoth = (PFILE_ID_BOTH_DIR_INFORMATION)DirectoryInformation->CurrentEntry;
fileIdFull = (PFILE_ID_FULL_DIR_INFORMATION)DirectoryInformation->CurrentEntry;
errorOnFileOpen = FALSE;
createNullEas = FALSE;
if ( NT_SUCCESS( status ) &&
InformationLevel == SMB_INFO_QUERY_EAS_FROM_LIST &&
!( ( fileBasic->FileNameLength == sizeof(WCHAR) &&
fileBasic->FileName[0] == '.' )
||
( fileBasic->FileNameLength == 2*sizeof(WCHAR) &&
fileBasic->FileName[0] == '.' &&
fileBasic->FileName[1] == '.' ) )
) {
UNICODE_STRING fileName;
//
// Set up local variables for the filename to open.
//
fileName.Length = (SHORT)fileBasic->FileNameLength;
fileName.MaximumLength = fileName.Length;
fileName.Buffer = (PWCH)fileBasic->FileName;
//
// Set up the object attributes structure for SrvIoCreateFile.
//
SrvInitializeObjectAttributes_U(
&objectAttributes,
&fileName,
(WorkContext->RequestHeader->Flags &
SMB_FLAGS_CASE_INSENSITIVE ||
WorkContext->Session->UsingUppercasePaths) ?
OBJ_CASE_INSENSITIVE : 0L,
DirectoryInformation->DirectoryHandle,
NULL
);
IF_DEBUG(SEARCH) {
SrvPrint1( "SrvQueryDirectoryFile: Opening file %wZ\n", &fileName );
}
//
// Attempt to open the file, using the client's security
// profile to check access. (We call SrvIoCreateFile, rather than
// NtOpenFile, in order to get user-mode access checking.)
//
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
status = SrvIoCreateFile(
WorkContext,
&fileHandle,
FILE_READ_EA,
&objectAttributes,
&ioStatusBlock,
NULL, // AllocationSize
0, // FileAttributes
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN, // Disposition
0, // FILE_COMPLETE_IF_OPLOCKED, // CreateOptions
NULL, // EaBuffer
0, // EaLength
CreateFileTypeNone, // File type
NULL, // ExtraCreateParameters
IO_FORCE_ACCESS_CHECK, // Options
NULL
);
if ( NT_SUCCESS(status) ) {
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 29, Search );
} else if( RtlCompareUnicodeString( &fileName, &SrvEaFileName, TRUE ) == 0 ) {
//
// They were trying to open up the EA data file. We expect this
// failure and skip past it. This file has no EAs
//
IF_DEBUG(SEARCH) {
SrvPrint1( "SrvQueryDirectoryFile: Skipping file %wZ\n", &fileName );
}
status = STATUS_SUCCESS;
goto skipit;
}
//
// If the user didn't have this permission, update the statistics
// database.
//
if ( status == STATUS_ACCESS_DENIED ) {
SrvStatistics.AccessPermissionErrors++;
}
//
// If the file is oplocked, wait for the oplock to break
// synchronously.
//
#if 1
ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
#else
if ( status == STATUS_OPLOCK_BREAK_IN_PROGRESS ) {
status = SrvWaitForOplockBreak( WorkContext, fileHandle );
if ( !NT_SUCCESS(status) ) {
SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 45, Search );
SrvNtClose( fileHandle, TRUE );
}
}
#endif
if ( !NT_SUCCESS(status) ) {
errorOnFileOpen = TRUE;
fileHandle = NULL;
IF_DEBUG(ERRORS) {
SrvPrint2( "Find2Loop: SrvIoCreateFile for file %wZ "
"failed: %X\n",
&fileName, status );
}
} else {
SrvStatistics.TotalFilesOpened++;
}
} else {
skipit:
createNullEas = TRUE;
fileHandle = NULL;
}
//
// If SrvQueryDirectoryFile returns an error, break out of the
// loop. If the error occurred in opening the file for one of
// the higher info levels, then we want to return the files we
// have obtained so far.
//
// If the error occurred on the file open *and* we haven't
// returned any files yet, then we want to return this file
// along with the code ERROR_EA_ACCESS_DENIED.
//
if ( !NT_SUCCESS(status) ) {
if ( count == 0 && errorOnFileOpen ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "EA access denied on first file of search (%x).\n",
status );
}
fileHandle = NULL;
status = STATUS_OS2_EA_ACCESS_DENIED;
break;
} else if ( status == STATUS_NO_MORE_FILES && count == 0 ) {
SmbPutUshort( &Response->SearchCount, 0 );
SmbPutUshort( &Response->EndOfSearch, 0 );
SmbPutUshort( &Response->EaErrorOffset, 0 );
SmbPutUshort( &Response->LastNameOffset, 0 );
Transaction->DataCount = 0;
return status;
} else {
break;
}
}
//
// Since it is no longer the first call to SrvQueryDirectoryFile,
// reset the isFirstCall local variable. If necessary, we already
// rewound the search, so set the ResumeFileIndex to NULL.
//
IsFirstCall = FALSE;
ResumeFileIndex = NULL;
IF_SMB_DEBUG(SEARCH2) {
UNICODE_STRING nameString;
switch (ntInformationLevel) {
case FileFullDirectoryInformation:
nameString.Buffer = fileFull->FileName;
nameString.Length = (USHORT)fileFull->FileNameLength;
break;
case FileBothDirectoryInformation:
nameString.Buffer = fileBoth->FileName;
nameString.Length = (USHORT)fileBoth->FileNameLength;
break;
default:
nameString.Buffer = fileBasic->FileName;
nameString.Length = (USHORT)fileBasic->FileNameLength;
break;
}
SrvPrint4( "SrvQueryDirectoryFile(%ld)-- %p, length=%ld, "
"status=%X\n", count,
&nameString,
nameString.Length,
status );
}
//
// Downlevel info levels have no provision for file names longer
// than 8 bits, while the NT info levels return 32 bits. If the
// file name is too long, skip it.
//
if ( !allowExtraLongNames ) {
if ( isUnicode ) {
if ( fileBasic->FileNameLength > 255 ) {
continue;
}
} else {
if ( fileBasic->FileNameLength > 255*sizeof(WCHAR) ) {
continue;
}
}
}
//
// If the client has requested that resume keys (really file
// indices for the purposes of this protocol), put in the
// four bytes just before the actual file information.
//
// Make sure that we don't write beyond the buffer when we do
// this. The fact that the buffer is full will be caught later.
//
if ( resumeKeysRequested &&
( (CLONG)( (bufferLocation+4) - Transaction->OutData ) <
Transaction->MaxDataCount ) ) {
SmbPutUlong( (PSMB_ULONG)bufferLocation, fileBasic->FileIndex );
bufferLocation += 4;
}
//
// Convert the information from NT style to the SMB protocol format,
// which is identical to the OS/2 1.2 semantics. Use an if
// statement rather than a switch so that a break will cause
// termination of the do loop.
//
if ( InformationLevel == SMB_INFO_STANDARD ) {
PSMB_FIND_BUFFER findBuffer = (PSMB_FIND_BUFFER)bufferLocation;
ULONG fileNameLength;
UNICODE_STRING fileName;
//
// Find the file name. If a short name is present, and the
// redirector ask for short names only, use it. Otherwise
// use the full file name.
//
if ( filterLongNames &&
fileBoth->ShortNameLength != 0 ) {
fileName.Buffer = fileBoth->ShortName;
fileName.Length = fileBoth->ShortNameLength;
fileName.MaximumLength = fileBoth->ShortNameLength;
} else {
fileName.Buffer = fileBoth->FileName;
fileName.Length = (USHORT)fileBoth->FileNameLength;
fileName.MaximumLength = (USHORT)fileBoth->FileNameLength;
}
//
// Find the new buffer location. This is not used until the
// next pass through the loop, but we do it here in order to
// check if there is enough space for the current file entry in
// the buffer. The +1 is for the zero terminator on the file
// name.
//
if ( isUnicode ) {
bufferLocation = ALIGN_SMB_WSTR( findBuffer->FileName );
bufferLocation += fileName.Length + sizeof(WCHAR);
} else {
unicodeString.Buffer = fileName.Buffer;
unicodeString.Length = fileName.Length;
unicodeString.MaximumLength = unicodeString.Length;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
bufferLocation = (PCHAR)(findBuffer->FileName + fileNameLength);
}
//
// Make sure that there is enough space in the buffer before
// writing the filename.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Put information about the file into the SMB buffer.
//
ConvertFileInfo(
fileBasic,
fileName.Buffer,
(BOOLEAN)((fileBoth->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0),
isUnicode,
findBuffer
);
//
// Put the file name in the buffer, in Unicode or ANSI
// depending what was negotiated.
//
if ( isUnicode ) {
PWCH buffer = ALIGN_SMB_WSTR( findBuffer->FileName );
//
// We need to upper case the name if the client does
// not understand long names. This is done for compatibility
// reasons (FAT upper cases names).
//
if ( filterLongNames ) {
(VOID)RtlUpcaseUnicodeString(
&fileName,
&fileName,
FALSE
);
}
RtlCopyMemory(
buffer,
fileName.Buffer,
fileName.Length
);
ASSERT(fileName.Length <= 255);
findBuffer->FileNameLength = (UCHAR)fileName.Length;
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PCHAR)findBuffer->FileName;
//
// We need to upper case the name if the client does
// not understand long names. This is done for compatibility
// reasons (FAT upper cases names).
//
if ( filterLongNames ) {
status = RtlUpcaseUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
} else {
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
}
ASSERT( NT_SUCCESS(status) );
ASSERT(oemString.Length <= 255);
findBuffer->FileNameLength = (UCHAR)oemString.Length;
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileName.Buffer;
lastFileName.Length = (USHORT)fileName.Length;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBoth->FileIndex;
} else if ( InformationLevel == SMB_INFO_QUERY_EA_SIZE ) {
PSMB_FIND_BUFFER2 findBuffer = (PSMB_FIND_BUFFER2)bufferLocation;
ULONG fileNameLength;
//
// Find the new buffer location. This is not used until the
// next pass through the loop, but we do it here in order to
// check if there is enough space for the current file entry in
// the buffer. The +1 is for the zero terminator on the file
// name.
//
if ( isUnicode ) {
bufferLocation =
(PCHAR)(findBuffer->FileName + fileFull->FileNameLength + 1);
} else {
unicodeString.Buffer = fileFull->FileName;
unicodeString.Length = (USHORT)fileFull->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
bufferLocation = (PCHAR)(findBuffer->FileName + fileNameLength);
}
//
// Make sure that there is enough space in the buffer before
// writing the filename.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Put information about the file into the SMB buffer.
//
ConvertFileInfo(
fileBasic,
fileFull->FileName,
(BOOLEAN)((fileFull->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0),
isUnicode,
(PSMB_FIND_BUFFER)findBuffer
);
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileFull->FileName,
fileFull->FileNameLength
);
ASSERT(fileFull->FileNameLength <= 255);
findBuffer->FileNameLength = (UCHAR)fileFull->FileNameLength;
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PCHAR)(findBuffer->FileName);
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
ASSERT(oemString.Length <= 255);
findBuffer->FileNameLength = (UCHAR)oemString.Length;
}
if ( fileFull->EaSize == 0) {
SmbPutUlong( &findBuffer->EaSize, 4 );
} else {
SmbPutUlong( &findBuffer->EaSize, fileFull->EaSize );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileFull->FileName;
lastFileName.Length = (USHORT)fileFull->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileFull->FileIndex;
} else if ( InformationLevel == SMB_INFO_QUERY_EAS_FROM_LIST ) {
PSMB_FIND_BUFFER2 findBuffer = (PSMB_FIND_BUFFER2)bufferLocation;
PFEALIST feaList;
PCHAR fileNameInfo;
ULONG fileNameLength;
//
// Find the new buffer location. This is not used until the
// next pass through the loop, but we do it here in order to
// check if there is enough space for the current file entry
// in the buffer. The +1 is for the zero terminator on the
// file name. A check is made later on to see if the EAs
// actually fit, and the bufferLocation variable is reset to
// account for the actual size of the EA.
//
if ( isUnicode ) {
bufferLocation =
(PCHAR)(findBuffer->FileName + fileBasic->FileNameLength + 1);
} else {
unicodeString.Buffer = fileBasic->FileName;
unicodeString.Length = (USHORT)fileBasic->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
bufferLocation =
(PCHAR)(findBuffer->FileName + fileNameLength + 1);
}
//
// Make sure that there is enough space in the buffer before
// writing the filename.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 46, Search );
SrvNtClose( fileHandle, TRUE );
fileHandle = NULL;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Put information about the file into the SMB buffer.
//
ConvertFileInfo(
fileBasic,
fileBasic->FileName,
(BOOLEAN)((fileBasic->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0),
isUnicode,
(PSMB_FIND_BUFFER)findBuffer
);
//
// Get the EAs corresponding to the GEA list passed by the
// client.
//
feaList = (PFEALIST)&findBuffer->EaSize;
if ( ( fileHandle != NULL ) || createNullEas ) {
if ( fileHandle != NULL ) {
//
// Get the file's EAs. The buffer space available is
// the space remaining in the buffer less enough space
// to write the file name, name length, and zero
// terminator.
//
status = SrvQueryOs2FeaList(
fileHandle,
NULL,
ntGetEa,
ntGetEaLength,
feaList,
(Transaction->MaxDataCount -
(ULONG)( (PCHAR)feaList - Transaction->OutData ) -
fileBasic->FileNameLength - 2),
&eaErrorOffset
);
SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 47, Search );
SrvNtClose( fileHandle, TRUE );
} else {
//
// if file is . or .. or "EA DATA. SF"
//
status = SrvConstructNullOs2FeaList(
ntGetEa,
feaList,
(Transaction->MaxDataCount -
(ULONG)( (PCHAR)feaList - Transaction->OutData ) -
fileBasic->FileNameLength - 2)
);
}
if ( !NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "SrvQueryOs2FeaList failed, status = %X\n",
status );
}
//
// If this is the first file, return it anyway with
// an error code.
//
if ( status == STATUS_INVALID_EA_NAME ) {
SmbPutUshort( &Response->SearchCount, 0 );
SmbPutUshort( &Response->EndOfSearch, 0 );
SmbPutUshort( &Response->EaErrorOffset, eaErrorOffset );
SmbPutUshort( &Response->LastNameOffset, 0 );
Transaction->DataCount = 0;
return status;
}
if ( count == 0 ) {
status = STATUS_OS2_EA_ACCESS_DENIED;
SmbPutUlong( &findBuffer->EaSize, 0 );
} else {
break;
}
}
//
// We already checked to see if the information other
// than EAs would fit in the buffer. If the EAs didn't
// fit as well, and this is the first file, then return
// information on this file but no EAs. Return
// STATUS_OS2_EAS_DIDNT_FIT. The EA size of the file
// should be in the EaSize field of the output buffer,
// put there by SrvQueryOs2FeaList.
//
// Also do this if we couldn't get at the file's EAs.
//
if ( count == 0 &&
( status == STATUS_BUFFER_OVERFLOW ||
status == STATUS_OS2_EA_ACCESS_DENIED ) ) {
IF_DEBUG(ERRORS) {
SrvPrint0( "First file's EAs would not fit.\n" );
}
count = 1;
//
// Write the file name information (length and name).
//
if ( isUnicode ) {
RtlCopyMemory(
(PVOID) (&findBuffer->FileNameLength + 1),
fileBasic->FileName,
fileBasic->FileNameLength
);
findBuffer->FileNameLength = (UCHAR)fileBasic->FileNameLength;
bufferLocation = (PCHAR)
(findBuffer->FileName + fileBasic->FileNameLength + 1);
} else {
NTSTATUS rtlStatus;
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer =
(PUCHAR)(&findBuffer->FileNameLength + 1);
rtlStatus = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(rtlStatus) );
findBuffer->FileNameLength = (UCHAR)oemString.Length;
bufferLocation = (PCHAR)
(findBuffer->FileName + oemString.Length + 1);
}
lastEntry = (PCHAR)findBuffer;
lastFileName.Buffer = fileBasic->FileName;
lastFileName.Length = (USHORT)fileBasic->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBasic->FileIndex;
if ( status == STATUS_BUFFER_OVERFLOW ) {
status = STATUS_OS2_EAS_DIDNT_FIT;
}
break;
}
} else {
SmbPutUlong( &feaList->cbList, sizeof(feaList->cbList) );
}
//
// Make sure that there is enough buffer space to write the
// file name and name size. The +2 is to account for the
// file name size field and the zero terminator.
//
fileNameInfo = (PCHAR)feaList->list +
SmbGetUlong( &feaList->cbList ) -
sizeof(feaList->cbList);
if ( isUnicode ) {
bufferLocation = fileNameInfo + fileBasic->FileNameLength + 2;
} else {
bufferLocation = fileNameInfo + fileNameLength + 1;
}
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Write the file name information (length and name).
//
if ( isUnicode ) {
RtlCopyMemory(
fileNameInfo + 1,
fileBasic->FileName,
fileBasic->FileNameLength
);
} else {
NTSTATUS rtlStatus;
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = fileNameInfo + 1;
rtlStatus = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(rtlStatus) );
}
*fileNameInfo++ = (UCHAR)oemString.Length;
IF_SMB_DEBUG(SEARCH2) {
SrvPrint1( "EA size is %ld\n", SmbGetUlong( &feaList->cbList ) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileBasic->FileName;
lastFileName.Length = (USHORT)fileBasic->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBasic->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_DIRECTORY_INFO ) {
FILE_DIRECTORY_INFORMATION UNALIGNED *findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileBasic->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileBasic->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileBasic->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName ) +
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
RtlCopyMemory(
findBuffer,
fileBasic,
FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName )
);
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileBasic->FileName,
fileBasic->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileBasic->FileName;
lastFileName.Length = (USHORT)fileBasic->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBasic->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_FULL_DIRECTORY_INFO ) {
FILE_FULL_DIR_INFORMATION UNALIGNED *findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileFull->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileFull->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileFull->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName)+
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
RtlCopyMemory(
findBuffer,
fileFull,
FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName )
);
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileFull->FileName,
fileFull->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileFull->FileName;
lastFileName.Length = (USHORT)fileFull->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileFull->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO ) {
FILE_BOTH_DIR_INFORMATION UNALIGNED *findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileBoth->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileBoth->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileBoth->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,FileName)+
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
RtlCopyMemory(
findBuffer,
fileBoth,
FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName )
);
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileBoth->FileName,
fileBoth->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileBoth->FileName;
lastFileName.Length = (USHORT)fileBoth->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBoth->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_NAMES_INFO ) {
PFILE_NAMES_INFORMATION findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileBasic->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileBasic->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileBasic->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET(FILE_NAMES_INFORMATION,FileName) +
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
findBuffer->FileIndex = fileBasic->FileIndex;
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileBasic->FileName,
fileBasic->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileBasic->FileName;
lastFileName.Length = (USHORT)fileBasic->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileBasic->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO ) {
FILE_ID_FULL_DIR_INFORMATION UNALIGNED *findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileIdFull->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileIdFull->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileIdFull->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET(FILE_ID_FULL_DIR_INFORMATION, FileName)+
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
RtlCopyMemory(
findBuffer,
fileIdFull,
FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION, FileName )
);
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileIdFull->FileName,
fileIdFull->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileIdFull->FileName;
lastFileName.Length = (USHORT)fileIdFull->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileIdFull->FileIndex;
} else if ( InformationLevel == SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO ) {
FILE_ID_BOTH_DIR_INFORMATION UNALIGNED *findBuffer = (PVOID)bufferLocation;
ULONG fileNameLength;
//
// If the client is not speaking Unicode, we need to convert
// the file name to ANSI.
//
if ( isUnicode ) {
fileNameLength = fileIdBoth->FileNameLength;
} else {
unicodeString.Length = (USHORT)fileIdBoth->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
unicodeString.Buffer = fileIdBoth->FileName;
fileNameLength = RtlUnicodeStringToOemSize( &unicodeString );
}
//
// Find the new buffer location. It won't be used until the
// next pass through the loop, but we need to make sure that
// this entry will fit.
//
bufferLocation = bufferLocation +
FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,FileName)+
fileNameLength;
bufferLocation = (PCHAR)(((ULONG_PTR)bufferLocation + 7) & ~7);
//
// Check whether this entry will fit in the output buffer.
//
if ( (CLONG)(bufferLocation - Transaction->OutData) >
Transaction->MaxDataCount ) {
status = STATUS_BUFFER_OVERFLOW;
bufferLocation = (PCHAR)findBuffer;
break;
}
//
// Copy over the information about the entry.
//
RtlCopyMemory(
findBuffer,
fileIdBoth,
FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION, FileName )
);
findBuffer->NextEntryOffset =
PTR_DIFF(bufferLocation, findBuffer);
findBuffer->FileNameLength = fileNameLength;
if ( isUnicode ) {
RtlCopyMemory(
findBuffer->FileName,
fileIdBoth->FileName,
fileIdBoth->FileNameLength
);
} else {
oemString.MaximumLength = (USHORT)fileNameLength;
oemString.Buffer = (PSZ)findBuffer->FileName;
status = RtlUnicodeStringToOemString(
&oemString,
&unicodeString,
FALSE
);
ASSERT( NT_SUCCESS(status) );
}
//
// The lastEntry variable holds a pointer to the last file entry
// that we wrote--an offset to this entry must be returned
// in the response SMB.
//
lastEntry = (PCHAR)findBuffer;
//
// The file name and index of the last file returned must be
// stored in the search block. Save the name pointer, length,
// and file index here.
//
lastFileName.Buffer = fileIdBoth->FileName;
lastFileName.Length = (USHORT)fileIdBoth->FileNameLength;
lastFileName.MaximumLength = lastFileName.Length;
lastFileIndex = fileIdBoth->FileIndex;
}
count++;
if ( status == STATUS_OS2_EA_ACCESS_DENIED ) {
break;
}
} while ( count < MaxCount );
IF_SMB_DEBUG(SEARCH2) {
SrvPrint0( "Stopped putting entries in buffer. Reason:\n" );
if ( !NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW ) {
SrvPrint1( " status = %X\n", status );
} else if ( count >= MaxCount ) {
SrvPrint2( " count = %ld, maxCount = %ld\n", count, MaxCount );
} else {
SrvPrint3( " buffer location = 0x%p, trans->OD = 0x%p, "
"trans->MaxOD = 0x%lx\n", bufferLocation,
Transaction->OutData, Transaction->MaxDataCount );
}
}
//
// Deallocate the pool used for the NT get EA list if this was the
// right information level.
//
if ( InformationLevel == SMB_INFO_QUERY_EAS_FROM_LIST ) {
DEALLOCATE_NONPAGED_POOL( ntGetEa );
}
//
// If we have not found any files and an error occurred, or the first
// file file we found had EAs to large to fit in the buffer, then return
// the error to the client. If an error occurred and we have found
// files, return what we have found.
//
if ( count == 0 && !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) {
SrvPrint1( "Find2 processing error; status = %X\n", status );
}
SrvSetSmbError( WorkContext, status );
return status;
} else if ( count == 1 &&
( status == STATUS_OS2_EAS_DIDNT_FIT ||
status == STATUS_OS2_EA_ACCESS_DENIED ) ) {
PVOID temp;
temp = WorkContext->ResponseParameters;
SrvSetSmbError( WorkContext, status );
WorkContext->ResponseParameters = temp;
status = STATUS_SUCCESS;
} else if ( !NT_SUCCESS(status) && status != STATUS_NO_MORE_FILES ) {
status = STATUS_SUCCESS;
}
//
// If this is a level for the SMB 4.0 protocol (NT), set the
// NextEntryOffset field of the last entry to zero.
//
if ( InformationLevel == SMB_FIND_FILE_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_FULL_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_BOTH_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_NAMES_INFO ||
InformationLevel == SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO ||
InformationLevel == SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO ) {
((PFILE_DIRECTORY_INFORMATION)lastEntry)->NextEntryOffset = 0;
}
//
// At the end of the loop, bufferLocation points to the first location
// AFTER the last entry we wrote, so it may be used to find the total
// number of data bytes that we intend to return.
//
totalBytesWritten = PTR_DIFF(bufferLocation, Transaction->OutData);
//
// Free the buffer that holds the last file name if it was in use,
// then allocate a new one and store the name and index of the last
// file returned in the search block so that it can resume the search
// if the client requests.
//
if ( Search->LastFileNameReturned.Buffer != NULL ) {
FREE_HEAP( Search->LastFileNameReturned.Buffer );
}
Search->LastFileNameReturned.Buffer =
ALLOCATE_HEAP_COLD(
lastFileName.Length,
BlockTypeDataBuffer
);
if ( Search->LastFileNameReturned.Buffer == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"SrvFind2Loop: unable to allocate %d bytes from heap.",
lastFileName.Length,
NULL
);
return STATUS_INSUFF_SERVER_RESOURCES;
}
Search->LastFileNameReturned.Length = lastFileName.Length;
Search->LastFileNameReturned.MaximumLength = lastFileName.Length;
RtlCopyMemory(
Search->LastFileNameReturned.Buffer,
lastFileName.Buffer,
lastFileName.Length
);
Search->LastFileIndexReturned = lastFileIndex;
//
// Put data in the response SMB.
//
SmbPutUshort( &Response->SearchCount, count );
SmbPutUshort(
&Response->EndOfSearch,
(USHORT)(status == STATUS_NO_MORE_FILES)
);
SmbPutUshort( &Response->EaErrorOffset, eaErrorOffset );
SmbPutUshort(
&Response->LastNameOffset,
(USHORT)(lastEntry - Transaction->OutData)
);
Transaction->DataCount = totalBytesWritten;
return status;
} // SrvFind2Loop
VOID
ConvertFileInfo (
IN PFILE_DIRECTORY_INFORMATION File,
IN PWCH FileName,
IN BOOLEAN Directory,
IN BOOLEAN ClientIsUnicode,
OUT PSMB_FIND_BUFFER FindBuffer
)
/*++
Routine Description:
This routine does the looping necessary to get files and put them
into an SMB buffer for the Find First2 and Find Next2 transaction
protocols.
Arguments:
File - a pointer to the structure containing the information about
the file.
FileName - name of the file.
Directory - a boolean indicating whether it is a file or directory.
The existence of this field allows File to point to a
FILE_FULL_DIR_INFORMATION structure if necessary.
FileBuffer - where to write the results in OS/2 format.
Return Value:
None
--*/
{
SMB_DATE smbDate;
SMB_TIME smbTime;
USHORT smbFileAttributes;
UNICODE_STRING unicodeString;
PAGED_CODE( );
//
// Convert the various times from NT format to SMB format.
//
SrvTimeToDosTime( &File->CreationTime, &smbDate, &smbTime );
SmbPutDate( &FindBuffer->CreationDate, smbDate );
SmbPutTime( &FindBuffer->CreationTime, smbTime );
SrvTimeToDosTime( &File->LastAccessTime, &smbDate, &smbTime );
SmbPutDate( &FindBuffer->LastAccessDate, smbDate );
SmbPutTime( &FindBuffer->LastAccessTime, smbTime );
SrvTimeToDosTime( &File->LastWriteTime, &smbDate, &smbTime );
SmbPutDate( &FindBuffer->LastWriteDate, smbDate );
SmbPutTime( &FindBuffer->LastWriteTime, smbTime );
//
// SMB protocol only allows 32-bit file sizes. Only return the low
// 32 bits, and too bad if the file is larger.
//
SmbPutUlong( &FindBuffer->DataSize, File->EndOfFile.LowPart );
SmbPutUlong(
&FindBuffer->AllocationSize,
File->AllocationSize.LowPart
);
SRV_NT_ATTRIBUTES_TO_SMB(
File->FileAttributes,
Directory,
&smbFileAttributes
);
SmbPutUshort( &FindBuffer->Attributes, smbFileAttributes );
if ( ClientIsUnicode ) {
FindBuffer->FileNameLength = (UCHAR)(File->FileNameLength);
} else {
unicodeString.Buffer = FileName;
unicodeString.Length = (USHORT)File->FileNameLength;
unicodeString.MaximumLength = unicodeString.Length;
FindBuffer->FileNameLength =
(UCHAR)RtlUnicodeStringToOemSize( &unicodeString );
}
return;
} // ConvertFileInfo
SMB_PROCESSOR_RETURN_TYPE
SrvSmbFindClose2 (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
This routine processes the Find Close2 SMB. This SMB is used to
close a search started by a Find First2 transaction.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
of the parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
--*/
{
PSEARCH search;
PSESSION session;
SRV_DIRECTORY_INFORMATION directoryInformation;
USHORT sid;
NTSTATUS status = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PREQ_FIND_CLOSE2 request;
PRESP_FIND_CLOSE2 response;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_FIND_CLOSE2;
SrvWmiStartContext(WorkContext);
IF_SMB_DEBUG(SEARCH1) {
SrvPrint2( "Find Close2 request header at 0x%p, response header at 0x%p\n",
WorkContext->RequestHeader, WorkContext->ResponseHeader );
SrvPrint2( "Find Close2 request params at 0x%p, response params%p\n",
WorkContext->RequestParameters,
WorkContext->ResponseParameters );
}
request = (PREQ_FIND_CLOSE2)WorkContext->RequestParameters;
response = (PRESP_FIND_CLOSE2)WorkContext->ResponseParameters;
//
// If a session block has not already been assigned to the current
// work context , verify the UID. If verified, the address of the
// session block corresponding to this user is stored in the WorkContext
// block and the session block is referenced.
//
session = SrvVerifyUid(
WorkContext,
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )
);
if ( session == NULL ) {
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "SrvSmbSearch: Invalid UID: 0x%lx\n",
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
}
SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID );
status = STATUS_SMB_BAD_UID;
SmbStatus = SmbStatusSendResponse;
}
//
// Get the search block corresponding to this SID. SrvVerifySid
// references the search block.
//
sid = SmbGetUshort( &request->Sid );
search = SrvVerifySid(
WorkContext,
SID_INDEX2( sid ),
SID_SEQUENCE2( sid ),
&directoryInformation,
sizeof(SRV_DIRECTORY_INFORMATION)
);
if ( search == NULL ) {
IF_DEBUG(SMB_ERRORS) SrvPrint0( "SrvSmbFindClose2: Invalid SID.\n" );
SrvSetSmbError( WorkContext, STATUS_INVALID_HANDLE );
status = STATUS_INVALID_HANDLE;
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
}
//
// Close the query directory and the search, then dereference our
// pointer to the search block.
//
search->DirectoryHandle = NULL;
SrvCloseSearch( search );
SrvCloseQueryDirectory( &directoryInformation );
SrvDereferenceSearch( search );
//
// Build the response SMB.
//
response->WordCount = 0;
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_FIND_CLOSE2,
0
);
SmbStatus = SmbStatusSendResponse;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbFindClose2