|
|
/*++
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
|