3338 lines
104 KiB
C
3338 lines
104 KiB
C
/*++
|
||
|
||
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
|