You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3887 lines
124 KiB
3887 lines
124 KiB
/*++
|
|
|
|
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
|
|
|
|
UNICODE_STRING SrvDownlevelTimewarpToken = { 12, 12, L"@GMT-*" };
|
|
|
|
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
|
|
);
|
|
|
|
NTSTATUS
|
|
SrvDownlevelTWarpFind2Loop (
|
|
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 )
|
|
#pragma alloc_text( PAGE, SrvDownlevelTWarpFind2Loop )
|
|
#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;
|
|
BOOLEAN isTimewarpSearch = FALSE;
|
|
|
|
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;
|
|
directoryInformation->DownlevelTimewarp = FALSE;
|
|
|
|
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.
|
|
//
|
|
if( !CLIENT_CAPABLE_OF( NT_STATUS, WorkContext->Connection ) &&
|
|
!SrvDisableDownlevelTimewarp )
|
|
{
|
|
UNICODE_STRING lastElement;
|
|
|
|
SrvGetBaseFileName( &fileName, &lastElement );
|
|
isTimewarpSearch = RtlEqualUnicodeString( &lastElement, &SrvDownlevelTimewarpToken, TRUE );
|
|
}
|
|
|
|
if( isTimewarpSearch )
|
|
{
|
|
search->DownlevelTimewarp = TRUE;
|
|
|
|
status = SrvSnapRefreshSnapShotsForShare( WorkContext->TreeConnect->Share );
|
|
if( NT_SUCCESS(status) )
|
|
{
|
|
status = SrvDownlevelTWarpFind2Loop(
|
|
WorkContext,
|
|
TRUE,
|
|
NULL,
|
|
flags,
|
|
informationLevel,
|
|
transaction,
|
|
directoryInformation,
|
|
nonPagedBufferSize,
|
|
search->SearchAttributes,
|
|
&fileName,
|
|
maxCount,
|
|
(PRESP_FIND_NEXT2)( &response->SearchCount ),
|
|
search
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
search->DownlevelTimewarp = directoryInformation->DownlevelTimewarp;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
directoryInformation->DownlevelTimewarp = search->DownlevelTimewarp;
|
|
|
|
//
|
|
// 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.
|
|
|
|
if( directoryInformation->DownlevelTimewarp )
|
|
{
|
|
status = SrvDownlevelTWarpFind2Loop(
|
|
WorkContext,
|
|
FALSE,
|
|
fileName.Buffer != NULL ? &resumeFileIndex : NULL,
|
|
flags,
|
|
informationLevel,
|
|
transaction,
|
|
directoryInformation,
|
|
nonPagedBufferSize,
|
|
search->SearchAttributes,
|
|
&fileName,
|
|
maxCount,
|
|
response,
|
|
search
|
|
);
|
|
}
|
|
else
|
|
{
|
|
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
|
|
|
|
NTSTATUS
|
|
SrvDownlevelTWarpFind2Loop (
|
|
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;
|
|
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_BOTH_DIRECTORY_INFO ) {
|
|
|
|
resumeKeysRequested = FALSE;
|
|
allowExtraLongNames = TRUE;
|
|
|
|
} else {
|
|
|
|
SmbPutUshort( &Response->SearchCount, 0 );
|
|
SmbPutUshort( &Response->EndOfSearch, 0 );
|
|
SmbPutUshort( &Response->EaErrorOffset, 0 );
|
|
SmbPutUshort( &Response->LastNameOffset, 0 );
|
|
Transaction->DataCount = 0;
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
isUnicode = SMB_IS_UNICODE( WorkContext );
|
|
filterLongNames = FALSE;
|
|
|
|
//
|
|
// Initialize count of files found.
|
|
//
|
|
|
|
SmbPutUshort( &Response->SearchCount, 0 );
|
|
|
|
//
|
|
// 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;
|
|
DirectoryInformation->DownlevelTimewarp = TRUE;
|
|
|
|
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_BOTH_DIR_INFORMATION fileBoth;
|
|
PFILE_DIRECTORY_INFORMATION fileBasic;
|
|
ULONG ntInformationLevel;
|
|
|
|
ntInformationLevel = FileBothDirectoryInformation;
|
|
|
|
//
|
|
// Call SrvQueryDirectoryFile to get a file.
|
|
//
|
|
|
|
status = SrvDownlevelTWarpQueryDirectoryFile(
|
|
WorkContext,
|
|
IsFirstCall,
|
|
filterLongNames,
|
|
FALSE,
|
|
ntInformationLevel,
|
|
Search->SearchStorageType,
|
|
FileName,
|
|
ResumeFileIndex,
|
|
SearchAttributes,
|
|
DirectoryInformation,
|
|
BufferSize // !!! optimizations?
|
|
);
|
|
|
|
fileBoth = (PFILE_BOTH_DIR_INFORMATION)DirectoryInformation->CurrentEntry;
|
|
fileBasic = DirectoryInformation->CurrentEntry;
|
|
|
|
errorOnFileOpen = FALSE;
|
|
createNullEas = FALSE;
|
|
|
|
//
|
|
// 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 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;
|
|
}
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
if ( status == STATUS_OS2_EA_ACCESS_DENIED ) {
|
|
break;
|
|
}
|
|
|
|
} while ( count < MaxCount );
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
((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, 0 );
|
|
SmbPutUshort(
|
|
&Response->LastNameOffset,
|
|
(USHORT)(lastEntry - Transaction->OutData)
|
|
);
|
|
Transaction->DataCount = totalBytesWritten;
|
|
|
|
return status;
|
|
|
|
} // SrvDownlevelTWarpFind2Loop
|
|
|
|
|
|
|
|
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
|