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.
2161 lines
70 KiB
2161 lines
70 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smbsrch.c
|
|
|
|
Abstract:
|
|
|
|
This module contains routines for processing the Search SMB.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 13-Feb-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#include "smbsrch.tmh"
|
|
#pragma hdrstop
|
|
|
|
#define BugCheckFileId SRV_FILE_SMBSRCH
|
|
|
|
#define VOLUME_BUFFER_SIZE \
|
|
FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION,VolumeLabel) + \
|
|
MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR)
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, SrvSmbSearch )
|
|
#endif
|
|
|
|
|
|
SMB_PROCESSOR_RETURN_TYPE
|
|
SrvSmbSearch (
|
|
SMB_PROCESSOR_PARAMETERS
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the various search SMBs, including the core
|
|
Search and the LM 1.0 Find, Find Unique, and Find Close.
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_SEARCH request;
|
|
PRESP_SEARCH response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
SMB_STATUS SmbStatus = SmbStatusInProgress;
|
|
UNICODE_STRING fileName;
|
|
PSRV_DIRECTORY_INFORMATION directoryInformation = NULL;
|
|
CLONG availableSpace;
|
|
CLONG totalBytesWritten;
|
|
BOOLEAN calledQueryDirectory;
|
|
BOOLEAN findFirst;
|
|
BOOLEAN isUnicode;
|
|
BOOLEAN filterLongNames;
|
|
BOOLEAN isCoreSearch;
|
|
PTABLE_ENTRY entry = NULL;
|
|
SHORT sidIndex;
|
|
SHORT sequence;
|
|
PSMB_RESUME_KEY resumeKey = NULL;
|
|
PCCHAR s;
|
|
PSMB_DIRECTORY_INFORMATION smbDirInfo;
|
|
USHORT smbFileAttributes;
|
|
PSEARCH search = NULL;
|
|
PDIRECTORY_CACHE dirCache, dc;
|
|
USHORT count;
|
|
USHORT maxCount;
|
|
USHORT i;
|
|
USHORT resumeKeyLength;
|
|
UCHAR command;
|
|
CCHAR j;
|
|
CLONG nonPagedBufferSize;
|
|
ULONG resumeFileIndex;
|
|
WCHAR nameBuffer[8 + 1 + 3 + 1];
|
|
OEM_STRING oemString;
|
|
|
|
PTREE_CONNECT treeConnect;
|
|
PSESSION session;
|
|
PCONNECTION connection;
|
|
PPAGED_CONNECTION pagedConnection;
|
|
HANDLE RootDirectoryHandle;
|
|
|
|
WCHAR unicodeResumeName[ sizeof( dirCache->UnicodeResumeName ) / sizeof( WCHAR ) ];
|
|
USHORT unicodeResumeNameLength = 0;
|
|
|
|
PAGED_CODE( );
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_SEARCH;
|
|
SrvWmiStartContext(WorkContext);
|
|
|
|
connection = WorkContext->Connection;
|
|
pagedConnection = connection->PagedConnection;
|
|
|
|
//
|
|
// HackHack: Check the flags2 field if dos client. Some dos clients
|
|
// set flags2 to 0xffff.
|
|
//
|
|
|
|
isUnicode = SMB_IS_UNICODE( WorkContext );
|
|
|
|
if ( isUnicode && IS_DOS_DIALECT(connection->SmbDialect) ) {
|
|
WorkContext->RequestHeader->Flags2 = 0;
|
|
isUnicode = FALSE;
|
|
}
|
|
|
|
filterLongNames =
|
|
((SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
|
|
SMB_FLAGS2_KNOWS_LONG_NAMES) == 0) ||
|
|
IS_DOS_DIALECT(connection->SmbDialect);
|
|
|
|
IF_SMB_DEBUG(SEARCH1) {
|
|
SrvPrint2( "Search request header at 0x%p, response header at 0x%p\n",
|
|
WorkContext->RequestHeader, WorkContext->ResponseHeader );
|
|
SrvPrint2( "Search request params at 0x%p, response params%p\n",
|
|
WorkContext->RequestParameters,
|
|
WorkContext->ResponseParameters );
|
|
}
|
|
|
|
request = (PREQ_SEARCH)WorkContext->RequestParameters;
|
|
response = (PRESP_SEARCH)WorkContext->ResponseParameters;
|
|
command = WorkContext->RequestHeader->Command;
|
|
|
|
//
|
|
// Set up a pointer in the SMB buffer where we will write
|
|
// information about files. The +3 is to account for the
|
|
// SMB_FORMAT_VARIABLE and the word that holds the data length.
|
|
//
|
|
|
|
smbDirInfo = (PSMB_DIRECTORY_INFORMATION)(response->Buffer + 3);
|
|
|
|
fileName.Buffer = NULL;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
// Find tree connect corresponding to given TID if a tree connect
|
|
// pointer has not already been put in the WorkContext block by an
|
|
// AndX command.
|
|
//
|
|
|
|
status = SrvVerifyUidAndTid(
|
|
WorkContext,
|
|
&session,
|
|
&treeConnect,
|
|
ShareTypeDisk
|
|
);
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint0( "SrvSmbSearch: Invalid UID or TID\n" );
|
|
}
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
isCoreSearch = (BOOLEAN)(command == SMB_COM_SEARCH);
|
|
|
|
if( session->IsSessionExpired )
|
|
{
|
|
status = SESSION_EXPIRED_STATUS_CODE;
|
|
SrvSetSmbError( WorkContext, status );
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get necessary information from the request SMB before we start
|
|
// overwriting it.
|
|
//
|
|
|
|
maxCount = SmbGetUshort( &request->MaxCount );
|
|
|
|
//
|
|
// If they aren't asking for any files, then they are confused!
|
|
//
|
|
if( maxCount == 0 ) {
|
|
|
|
//
|
|
// We would log this, but certain linux clients mistakenly do this
|
|
// over and over and over...
|
|
//
|
|
status = STATUS_INVALID_SMB;
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// If this is a core search, we don't want to get too many files,
|
|
// as we have to cache information about them between requests.
|
|
//
|
|
//
|
|
// 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 than 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 ( isCoreSearch ) {
|
|
maxCount = MIN( maxCount, (USHORT)MAX_DIRECTORY_CACHE_SIZE );
|
|
nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
|
|
|
|
} else {
|
|
|
|
if ( maxCount > MAX_FILES_FOR_MED_SEARCH ) {
|
|
nonPagedBufferSize = MAX_SEARCH_BUFFER_SIZE;
|
|
} else if ( maxCount > MAX_FILES_FOR_MIN_SEARCH ) {
|
|
nonPagedBufferSize = MED_SEARCH_BUFFER_SIZE;
|
|
} else {
|
|
nonPagedBufferSize = MIN_SEARCH_BUFFER_SIZE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The response to a search is never unicode, though the request may be.
|
|
//
|
|
if( isUnicode ) {
|
|
USHORT flags2 = SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 );
|
|
flags2 &= ~SMB_FLAGS2_UNICODE;
|
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Flags2, flags2 );
|
|
}
|
|
|
|
//
|
|
// If there was a resume key, verify the SID. If there was no
|
|
// resume key, allocate a search block. The first two bytes after
|
|
// the format token are the length of the resume key. If they are
|
|
// both zero, then the request was a find first.
|
|
//
|
|
count = MIN( SmbGetUshort( &request->ByteCount ), (USHORT)(END_OF_REQUEST_SMB(WorkContext)-request->Buffer+1) );
|
|
|
|
if( isUnicode ) {
|
|
PWCHAR p;
|
|
|
|
// We will be starting at a WCHAR offset, so we'll skip the first byte. Remove it.
|
|
// Also, set count to an even length so we don't have any "half-char" style buffer overruns
|
|
count = count & ~1;
|
|
|
|
for( p = (PWCHAR)((PCCHAR)request->Buffer+1), i = 0;
|
|
i < count && SmbGetUshort(p) != UNICODE_NULL;
|
|
p++, i += sizeof(*p) );
|
|
|
|
s = (PCCHAR)(p + 1); // skip past the null to the next char
|
|
|
|
} else {
|
|
|
|
for ( s = (PCCHAR)request->Buffer, i = 0;
|
|
i < count && *s != (CCHAR)SMB_FORMAT_VARIABLE;
|
|
s++, i += sizeof(*s) );
|
|
}
|
|
|
|
//
|
|
// If there was no SMB_FORMAT_VARIABLE token in the buffer, fail.
|
|
//
|
|
|
|
if ( i == count || *s != (CCHAR)SMB_FORMAT_VARIABLE ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint0( "SrvSmbSearch: no SMB_FORMAT_VARIABLE token.\n" );
|
|
}
|
|
|
|
SrvLogInvalidSmb( WorkContext );
|
|
|
|
status = STATUS_INVALID_SMB;
|
|
goto error_exit;
|
|
}
|
|
|
|
resumeKeyLength = SmbGetUshort( (PSMB_USHORT)(s+1) );
|
|
|
|
if ( resumeKeyLength == 0 ) {
|
|
|
|
//
|
|
// There was no resume key, so either a Find First or a Find
|
|
// Unique was intended. If it was actually a Find Close, return
|
|
// an error to the client.
|
|
//
|
|
|
|
if ( command == SMB_COM_FIND_CLOSE ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint0( "SrvSmbSearch: Find Close sent w/o resume key.\n" );
|
|
}
|
|
|
|
status = STATUS_INVALID_SMB;
|
|
|
|
SrvLogInvalidSmb( WorkContext );
|
|
goto error_exit;
|
|
}
|
|
|
|
IF_SMB_DEBUG(SEARCH2) SrvPrint0( "FIND FIRST\n" );
|
|
|
|
findFirst = TRUE;
|
|
calledQueryDirectory = FALSE;
|
|
|
|
//
|
|
// Initialize the string containing the path name. The +1 is to
|
|
// account for the ASCII token in the Buffer field of the
|
|
// request SMB.
|
|
//
|
|
|
|
status = SrvCanonicalizePathName(
|
|
WorkContext,
|
|
treeConnect->Share,
|
|
NULL,
|
|
(PVOID)(request->Buffer + 1),
|
|
END_OF_REQUEST_SMB( WorkContext ),
|
|
FALSE,
|
|
isUnicode,
|
|
&fileName
|
|
);
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// If the volume attribute bit is set, just return the volume name.
|
|
//
|
|
|
|
if ( SmbGetUshort( &request->SearchAttributes )
|
|
== SMB_FILE_ATTRIBUTE_VOLUME ) {
|
|
|
|
//
|
|
// Use NtQueryVolumeInformationFile to get the volume name.
|
|
//
|
|
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PFILE_FS_VOLUME_INFORMATION volumeInformation;
|
|
|
|
//
|
|
// Allocate enough space to store the volume information structure
|
|
// and MAXIMUM_FILENAME_LENGTH bytes for the volume label name.
|
|
//
|
|
|
|
volumeInformation = ALLOCATE_HEAP( VOLUME_BUFFER_SIZE, BlockTypeVolumeInformation );
|
|
|
|
if ( volumeInformation == NULL ) {
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvSmbSearch: Unable to allocate memory from server heap",
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlZeroMemory( volumeInformation, VOLUME_BUFFER_SIZE );
|
|
|
|
//
|
|
// Get the Share root handle.
|
|
//
|
|
|
|
status = SrvGetShareRootHandle( treeConnect->Share );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
SrvPrint1( "SrvSmbSearch: SrvGetShareRootHandle failed %x.\n",
|
|
status );
|
|
}
|
|
|
|
FREE_HEAP( volumeInformation );
|
|
goto error_exit;
|
|
}
|
|
//
|
|
// Handle SnapShot case
|
|
//
|
|
status = SrvSnapGetRootHandle( WorkContext, &RootDirectoryHandle );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
FREE_HEAP( volumeInformation );
|
|
goto error_exit;
|
|
}
|
|
|
|
status = NtQueryVolumeInformationFile(
|
|
RootDirectoryHandle,
|
|
&ioStatusBlock,
|
|
volumeInformation,
|
|
VOLUME_BUFFER_SIZE,
|
|
FileFsVolumeInformation
|
|
);
|
|
|
|
//
|
|
// If the media was changed and we can come up with a new share root handle,
|
|
// then we should retry the operation
|
|
//
|
|
if( SrvRetryDueToDismount( treeConnect->Share, status ) ) {
|
|
|
|
status = SrvSnapGetRootHandle( WorkContext, &RootDirectoryHandle );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
FREE_HEAP( volumeInformation );
|
|
goto error_exit;
|
|
}
|
|
|
|
status = NtQueryVolumeInformationFile(
|
|
RootDirectoryHandle,
|
|
&ioStatusBlock,
|
|
volumeInformation,
|
|
VOLUME_BUFFER_SIZE,
|
|
FileFsVolumeInformation
|
|
);
|
|
}
|
|
|
|
//
|
|
// Release the share root handle
|
|
//
|
|
|
|
SrvReleaseShareRootHandle( WorkContext->TreeConnect->Share );
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_UNEXPECTED,
|
|
"SrvSmbSearch: NtQueryVolumeInformationFile returned %X",
|
|
status,
|
|
NULL
|
|
);
|
|
|
|
SrvLogServiceFailure( SRV_SVC_NT_QUERY_VOL_INFO_FILE, status );
|
|
|
|
FREE_HEAP( volumeInformation );
|
|
goto error_exit;
|
|
}
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint2( "NtQueryVolumeInformationFile succeeded, name = %ws, "
|
|
"length %ld\n", volumeInformation->VolumeLabel,
|
|
volumeInformation->VolumeLabelLength );
|
|
}
|
|
|
|
|
|
//
|
|
// Check if we have a volume label
|
|
//
|
|
|
|
if ( volumeInformation->VolumeLabelLength > 0 ) {
|
|
|
|
//
|
|
// Build the response SMB.
|
|
//
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, 1 );
|
|
SmbPutUshort(
|
|
&response->ByteCount,
|
|
3 + sizeof(SMB_DIRECTORY_INFORMATION)
|
|
);
|
|
response->Buffer[0] = SMB_FORMAT_VARIABLE;
|
|
SmbPutUshort(
|
|
(PSMB_USHORT)(response->Buffer+1),
|
|
sizeof(SMB_DIRECTORY_INFORMATION)
|
|
);
|
|
|
|
//
|
|
// *** Is there anything that we must set in the resume key?
|
|
//
|
|
|
|
smbDirInfo->FileAttributes = SMB_FILE_ATTRIBUTE_VOLUME;
|
|
SmbZeroTime( &smbDirInfo->LastWriteTime );
|
|
SmbZeroDate( &smbDirInfo->LastWriteDate );
|
|
SmbPutUlong( &smbDirInfo->FileSize, 0 );
|
|
|
|
{
|
|
UNICODE_STRING unicodeString;
|
|
OEM_STRING innerOemString;
|
|
|
|
//
|
|
// Volume labels may be longer than 11 bytes, but we
|
|
// truncate then to this length in order to make sure that
|
|
// the label will fit into the 8.3+NULL -byte space in the
|
|
// SMB_DIRECTORY_INFORMATION structure. Also, the LM 1.2
|
|
// ring 3 and Pinball servers do this.
|
|
//
|
|
|
|
unicodeString.Length =
|
|
(USHORT) MIN(
|
|
volumeInformation->VolumeLabelLength,
|
|
11 * sizeof(WCHAR) );
|
|
|
|
unicodeString.MaximumLength = 13;
|
|
unicodeString.Buffer = volumeInformation->VolumeLabel;
|
|
|
|
innerOemString.MaximumLength = 13;
|
|
innerOemString.Buffer = (PCHAR)smbDirInfo->FileName;
|
|
|
|
RtlUnicodeStringToOemString(
|
|
&innerOemString,
|
|
&unicodeString,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// If the volume label is greater than 8 characters, it
|
|
// needs to be turned into 8.3 format.
|
|
//
|
|
if( innerOemString.Length > 8 ) {
|
|
//
|
|
// Slide the last three characters one position to the
|
|
// right and insert a '.' to formulate an 8.3 name
|
|
//
|
|
smbDirInfo->FileName[11] = smbDirInfo->FileName[10];
|
|
smbDirInfo->FileName[10] = smbDirInfo->FileName[9];
|
|
smbDirInfo->FileName[9] = smbDirInfo->FileName[8];
|
|
smbDirInfo->FileName[8] = '.';
|
|
innerOemString.Length++;
|
|
}
|
|
|
|
smbDirInfo->FileName[innerOemString.Length] = '\0';
|
|
|
|
//
|
|
// Blank pad space after the volume label.
|
|
//
|
|
|
|
for ( i = (USHORT)(innerOemString.Length + 1);
|
|
i < 13;
|
|
i++ ) {
|
|
smbDirInfo->FileName[i] = ' ';
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Store the resume key in the response packet. DOS redirs
|
|
// actually use this!
|
|
//
|
|
|
|
{
|
|
|
|
UNICODE_STRING baseFileName;
|
|
|
|
SrvGetBaseFileName( &fileName, &baseFileName );
|
|
|
|
SrvUnicodeStringTo8dot3(
|
|
&baseFileName,
|
|
(PSZ)smbDirInfo->ResumeKey.FileName,
|
|
FALSE
|
|
);
|
|
|
|
//
|
|
// I set Sid = 1 because the next 5 bytes should
|
|
// be nonzero and we don't really have a sid.
|
|
//
|
|
|
|
smbDirInfo->ResumeKey.Sid = 0x01;
|
|
SmbPutUlong( &smbDirInfo->ResumeKey.FileIndex, 0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// There is no volume label.
|
|
//
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, 0 );
|
|
SmbPutUshort( &response->ByteCount, 3 );
|
|
response->Buffer[0] = SMB_FORMAT_VARIABLE;
|
|
SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
|
|
|
|
}
|
|
|
|
WorkContext->ResponseParameters =
|
|
NEXT_LOCATION(
|
|
response,
|
|
RESP_SEARCH,
|
|
SmbGetUshort( &response->ByteCount )
|
|
);
|
|
|
|
FREE_HEAP( volumeInformation );
|
|
|
|
if ( !isUnicode &&
|
|
fileName.Buffer != NULL &&
|
|
fileName.Buffer != nameBuffer ) {
|
|
RtlFreeUnicodeString( &fileName );
|
|
}
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If this is a core search without patterns, short circuit the
|
|
// whole thing right here.
|
|
//
|
|
if( isCoreSearch && fileName.Length <= sizeof( nameBuffer ) &&
|
|
( fileName.Length == 0 ||
|
|
!FsRtlDoesNameContainWildCards( &fileName )) ) {
|
|
|
|
IO_STATUS_BLOCK ioStatus;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
ULONG attributes;
|
|
ULONG inclusiveSearchAttributes, exclusiveSearchAttributes;
|
|
USHORT searchAttributes;
|
|
UNICODE_STRING baseFileName;
|
|
BOOLEAN returnDirectories, returnDirectoriesOnly;
|
|
FILE_NETWORK_OPEN_INFORMATION fileInformation;
|
|
PSZ dirInfoName;
|
|
SMB_DATE dosDate;
|
|
SMB_TIME dosTime;
|
|
UNICODE_STRING foundFileName;
|
|
|
|
UNICODE_STRING ObjectNameString;
|
|
PUNICODE_STRING filePathName;
|
|
BOOLEAN FreePathName = FALSE;
|
|
|
|
ObjectNameString.Buffer = fileName.Buffer;
|
|
ObjectNameString.Length = fileName.Length;
|
|
ObjectNameString.MaximumLength = fileName.Length;
|
|
|
|
if( fileName.Length == 0 ) {
|
|
|
|
//
|
|
// Since we are opening the root of the share, set the attribute to
|
|
// case insensitive, as this is how we opened the share when it was added
|
|
//
|
|
status = SrvSnapGetNameString( WorkContext, &filePathName, &FreePathName );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
goto error_exit;
|
|
}
|
|
|
|
ObjectNameString = *filePathName;
|
|
attributes = OBJ_CASE_INSENSITIVE;
|
|
|
|
} else {
|
|
|
|
attributes =
|
|
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
|
WorkContext->Session->UsingUppercasePaths ) ?
|
|
OBJ_CASE_INSENSITIVE : 0;
|
|
|
|
}
|
|
|
|
SrvInitializeObjectAttributes_U(
|
|
&objectAttributes,
|
|
&ObjectNameString,
|
|
attributes,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = IMPERSONATE( WorkContext );
|
|
|
|
if( NT_SUCCESS( status ) ) {
|
|
|
|
status = SrvGetShareRootHandle( treeConnect->Share );
|
|
|
|
if( NT_SUCCESS( status ) ) {
|
|
|
|
//
|
|
// The file name is always relative to the share root
|
|
//
|
|
status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
goto SnapError;
|
|
}
|
|
|
|
//
|
|
// Get the attributes of the object
|
|
//
|
|
if( IoFastQueryNetworkAttributes(
|
|
&objectAttributes,
|
|
FILE_READ_ATTRIBUTES,
|
|
0,
|
|
&ioStatus,
|
|
&fileInformation
|
|
) == FALSE ) {
|
|
|
|
SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
|
|
ioStatus.Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
status = ioStatus.Status;
|
|
|
|
//
|
|
// If the media was changed and we can come up with a new share root handle,
|
|
// then we should retry the operation
|
|
//
|
|
if( SrvRetryDueToDismount( treeConnect->Share, status ) ) {
|
|
|
|
status = SrvSnapGetRootHandle( WorkContext, &objectAttributes.RootDirectory );
|
|
if( !NT_SUCCESS(status) )
|
|
{
|
|
goto SnapError;
|
|
}
|
|
|
|
//
|
|
// Get the attributes of the object
|
|
//
|
|
if( IoFastQueryNetworkAttributes(
|
|
&objectAttributes,
|
|
FILE_READ_ATTRIBUTES,
|
|
0,
|
|
&ioStatus,
|
|
&fileInformation
|
|
) == FALSE ) {
|
|
|
|
SrvLogServiceFailure( SRV_SVC_IO_FAST_QUERY_NW_ATTRS, 0 );
|
|
ioStatus.Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
SrvReleaseShareRootHandle( treeConnect->Share );
|
|
}
|
|
|
|
SnapError:
|
|
|
|
REVERT();
|
|
}
|
|
|
|
// Free up the string
|
|
if( FreePathName )
|
|
{
|
|
FREE_HEAP( filePathName );
|
|
filePathName = NULL;
|
|
}
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
//
|
|
// Do error mapping to keep the DOS clients happy
|
|
//
|
|
if ( status == STATUS_NO_SUCH_FILE ||
|
|
status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
status = STATUS_NO_MORE_FILES;
|
|
}
|
|
goto error_exit;
|
|
}
|
|
|
|
searchAttributes = SmbGetUshort( &request->SearchAttributes );
|
|
searchAttributes &= SMB_FILE_ATTRIBUTE_READONLY |
|
|
SMB_FILE_ATTRIBUTE_HIDDEN |
|
|
SMB_FILE_ATTRIBUTE_SYSTEM |
|
|
SMB_FILE_ATTRIBUTE_VOLUME |
|
|
SMB_FILE_ATTRIBUTE_DIRECTORY |
|
|
SMB_FILE_ATTRIBUTE_ARCHIVE;
|
|
|
|
//
|
|
// The file or directory exists, now we need to see if it matches the
|
|
// criteria given by the client
|
|
//
|
|
SRV_SMB_ATTRIBUTES_TO_NT(
|
|
searchAttributes & 0xff,
|
|
&returnDirectories,
|
|
&inclusiveSearchAttributes
|
|
);
|
|
|
|
inclusiveSearchAttributes |= FILE_ATTRIBUTE_NORMAL |
|
|
FILE_ATTRIBUTE_ARCHIVE |
|
|
FILE_ATTRIBUTE_READONLY;
|
|
|
|
SRV_SMB_ATTRIBUTES_TO_NT(
|
|
searchAttributes >> 8,
|
|
&returnDirectoriesOnly,
|
|
&exclusiveSearchAttributes
|
|
);
|
|
|
|
exclusiveSearchAttributes &= ~FILE_ATTRIBUTE_NORMAL;
|
|
|
|
//
|
|
// See if the returned file meets our objectives
|
|
//
|
|
if(
|
|
//
|
|
// If we're only supposed to return directories, then we don't want it
|
|
//
|
|
returnDirectoriesOnly
|
|
||
|
|
|
|
//
|
|
// If there are bits set in FileAttributes that are
|
|
// not set in inclusiveSearchAttributes, then we don't want it
|
|
//
|
|
((fileInformation.FileAttributes << 24 ) |
|
|
( inclusiveSearchAttributes << 24 )) !=
|
|
( inclusiveSearchAttributes << 24 )
|
|
|
|
||
|
|
//
|
|
// If the file doesn't have attribute bits specified as exclusive
|
|
// bits, we don't want it
|
|
//
|
|
( ((fileInformation.FileAttributes << 24 ) |
|
|
(exclusiveSearchAttributes << 24 )) !=
|
|
(fileInformation.FileAttributes << 24) )
|
|
|
|
) {
|
|
//
|
|
// Just as though the file was never there!
|
|
//
|
|
status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// We want this entry!
|
|
// Fill in the response
|
|
//
|
|
|
|
//
|
|
// Switch over to a private name buffer, to avoid overwriting info
|
|
// in the SMB buffer.
|
|
//
|
|
RtlCopyMemory( nameBuffer, fileName.Buffer, fileName.Length );
|
|
foundFileName.Buffer = nameBuffer;
|
|
foundFileName.Length = fileName.Length;
|
|
foundFileName.MaximumLength = fileName.MaximumLength;
|
|
|
|
SrvGetBaseFileName( &foundFileName, &baseFileName );
|
|
SrvUnicodeStringTo8dot3(
|
|
&baseFileName,
|
|
(PSZ)smbDirInfo->ResumeKey.FileName,
|
|
TRUE
|
|
);
|
|
|
|
//
|
|
// Resume Key doesn't matter, since the client will not come back. But
|
|
// just in case it does, make sure we have a bum resume key
|
|
//
|
|
SET_RESUME_KEY_INDEX( (PSMB_RESUME_KEY)smbDirInfo, 0xff );
|
|
SET_RESUME_KEY_SEQUENCE( (PSMB_RESUME_KEY)smbDirInfo, 0xff );
|
|
SmbPutUlong( &((PSMB_RESUME_KEY)smbDirInfo)->FileIndex, 0 );
|
|
SmbPutUlong( (PSMB_ULONG)&((PSMB_RESUME_KEY)smbDirInfo)->Consumer[0], 0 );
|
|
|
|
//
|
|
// Fill in the name (even though they knew what it was!)
|
|
//
|
|
oemString.Buffer = smbDirInfo->FileName;
|
|
oemString.MaximumLength = sizeof( smbDirInfo->FileName );
|
|
RtlUpcaseUnicodeStringToOemString( &oemString, &baseFileName, FALSE );
|
|
|
|
//
|
|
// Null terminate and blank-pad the name
|
|
//
|
|
oemString.Buffer[ oemString.Length ] = '\0';
|
|
|
|
for( i=(USHORT)oemString.Length+1; i < sizeof( smbDirInfo->FileName); i++ ) {
|
|
oemString.Buffer[i] = ' ';
|
|
}
|
|
|
|
SRV_NT_ATTRIBUTES_TO_SMB(
|
|
fileInformation.FileAttributes,
|
|
fileInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
|
|
&smbFileAttributes
|
|
);
|
|
smbDirInfo->FileAttributes = (UCHAR)smbFileAttributes;
|
|
|
|
SrvTimeToDosTime(
|
|
&fileInformation.LastWriteTime,
|
|
&dosDate,
|
|
&dosTime
|
|
);
|
|
|
|
SmbPutDate( &smbDirInfo->LastWriteDate, dosDate );
|
|
SmbPutTime( &smbDirInfo->LastWriteTime, dosTime );
|
|
|
|
SmbPutUlong( &smbDirInfo->FileSize, fileInformation.EndOfFile.LowPart );
|
|
|
|
totalBytesWritten = sizeof(SMB_DIRECTORY_INFORMATION);
|
|
count = 1;
|
|
goto done_core;
|
|
}
|
|
|
|
directoryInformation = ALLOCATE_NONPAGED_POOL(
|
|
nonPagedBufferSize,
|
|
BlockTypeDirectoryInfo
|
|
);
|
|
|
|
if ( directoryInformation == NULL ) {
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvSmbSearch: unable to allocate nonpaged pool",
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
directoryInformation->DirectoryHandle = NULL;
|
|
directoryInformation->DownlevelTimewarp = FALSE;
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
|
|
nonPagedBufferSize, directoryInformation );
|
|
}
|
|
|
|
//
|
|
// Allocate a search block. The search block is used to retain
|
|
// state information between search or find SMBs. The search
|
|
// blocks for core and regular searches are slightly different,
|
|
// hence the BOOLEAN parameter to SrvAllocateSearch.
|
|
//
|
|
|
|
//
|
|
// If we've reached our max, start closing searches.
|
|
//
|
|
|
|
if ( SrvStatistics.CurrentNumberOfOpenSearches >= SrvMaxOpenSearches ) {
|
|
|
|
SrvForceTimeoutSearches( connection );
|
|
}
|
|
|
|
SrvAllocateSearch(
|
|
&search,
|
|
&fileName,
|
|
isCoreSearch
|
|
);
|
|
|
|
if ( search == NULL ) {
|
|
|
|
IF_DEBUG(ERRORS) {
|
|
SrvPrint0( "SrvSmbSearch: unable to allocate search block.\n" );
|
|
}
|
|
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
search->Pid = SmbGetAlignedUshort(
|
|
&WorkContext->RequestHeader->Pid
|
|
);
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
if ( isCoreSearch ) {
|
|
pagedConnection->CurrentNumberOfCoreSearches++;
|
|
}
|
|
|
|
search->Session = WorkContext->Session;
|
|
SrvReferenceSession( WorkContext->Session );
|
|
|
|
search->TreeConnect = WorkContext->TreeConnect;
|
|
SrvReferenceTreeConnect( WorkContext->TreeConnect );
|
|
|
|
//
|
|
// If this is not a find unique, put the search block in the
|
|
// search table. Otherwise, just set the sidIndex and sequence
|
|
// variables to 0 to distinguish between a valid resumable
|
|
// search block.
|
|
//
|
|
|
|
if ( command == SMB_COM_FIND_UNIQUE ) {
|
|
|
|
WorkContext->Session->CurrentSearchOpenCount++;
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
sequence = sidIndex = -1;
|
|
|
|
} else {
|
|
NTSTATUS TableStatus;
|
|
PTABLE_HEADER searchTable = &pagedConnection->SearchTable;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
if ( searchTable->FirstFreeEntry == -1
|
|
&&
|
|
SrvGrowTable(
|
|
searchTable,
|
|
SrvInitialSearchTableSize,
|
|
SrvMaxSearchTableSize,
|
|
&TableStatus ) == FALSE
|
|
&&
|
|
SrvTimeoutSearches(
|
|
NULL,
|
|
connection,
|
|
TRUE ) == 0
|
|
) {
|
|
|
|
IF_DEBUG(ERRORS)
|
|
SrvPrint0( "SrvSmbSearch: Connection searchTable full.\n" );
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
if( TableStatus == STATUS_INSUFF_SERVER_RESOURCES )
|
|
{
|
|
SrvLogTableFullError( SRV_TABLE_SEARCH );
|
|
status = STATUS_OS2_NO_MORE_SIDS;
|
|
}
|
|
else
|
|
{
|
|
status = TableStatus;
|
|
}
|
|
|
|
goto error_exit;
|
|
|
|
} else if ( GET_BLOCK_STATE( session ) != BlockStateActive ) {
|
|
|
|
//
|
|
//
|
|
// If the session is closing do not insert this search
|
|
// on the search list, because the list may already
|
|
// have been cleaned up.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
status = STATUS_SMB_BAD_UID;
|
|
goto error_exit;
|
|
|
|
} else if ( GET_BLOCK_STATE( treeConnect ) != BlockStateActive ) {
|
|
|
|
//
|
|
// Tree connect is closing. Don't insert the search block
|
|
// so the tree connect can be cleaned up immediately.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
status = STATUS_SMB_BAD_TID;
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
//
|
|
// increment the count of open searches
|
|
//
|
|
|
|
WorkContext->Session->CurrentSearchOpenCount++;
|
|
|
|
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;
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint3( "Allocated search block at 0x%p. Index = 0x%lx, sequence = 0x%lx\n", search, sidIndex, sequence );
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The resume key length was nonzero, so this should be a find
|
|
// next. Check the resume key length to be safe.
|
|
//
|
|
|
|
USHORT resumeSequence;
|
|
|
|
if ( resumeKeyLength != sizeof(SMB_RESUME_KEY) ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint2( "Resume key length was incorrect--was %ld instead "
|
|
"of %ld\n", resumeKeyLength, sizeof(SMB_RESUME_KEY) );
|
|
}
|
|
|
|
SrvLogInvalidSmb( WorkContext );
|
|
|
|
status = STATUS_INVALID_SMB;
|
|
goto error_exit;
|
|
}
|
|
|
|
findFirst = FALSE;
|
|
|
|
resumeKey = (PSMB_RESUME_KEY)(s + 3);
|
|
|
|
//
|
|
// Set up the sequence number and index. These are used for the
|
|
// return resume keys.
|
|
//
|
|
|
|
sequence = SID_SEQUENCE( resumeKey );
|
|
sidIndex = SID_INDEX( resumeKey );
|
|
|
|
directoryInformation = ALLOCATE_NONPAGED_POOL(
|
|
nonPagedBufferSize,
|
|
BlockTypeDirectoryInfo
|
|
);
|
|
|
|
if ( directoryInformation == NULL ) {
|
|
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvSmbSearch: unable to allocate nonpaged pool",
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
directoryInformation->DirectoryHandle = NULL;
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint2( "Allocated buffer space of %ld bytes at 0x%p\n",
|
|
nonPagedBufferSize, directoryInformation );
|
|
}
|
|
//
|
|
// Verify the SID in the resume key. SrvVerifySid also fills in
|
|
// fields of directoryInformation so it is ready to be used by
|
|
// SrvQueryDirectoryFile.
|
|
//
|
|
|
|
search = SrvVerifySid(
|
|
WorkContext,
|
|
sidIndex,
|
|
sequence,
|
|
directoryInformation,
|
|
nonPagedBufferSize
|
|
);
|
|
|
|
if ( search == NULL ) {
|
|
|
|
if (0) IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint2( "SrvSmbSearch: Invalid resume key (SID): index = "
|
|
"%lx, seq. = %lx\n",
|
|
sidIndex, sequence );
|
|
}
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// If this is a core search, take the search block off its last-use
|
|
// list. We will return it to the end of the list when we are
|
|
// done processing this SMB.
|
|
//
|
|
|
|
if ( isCoreSearch ) {
|
|
|
|
USHORT dirCacheIndex;
|
|
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
//
|
|
// If the reference count on the search block is not 2,
|
|
// somebody messed up and we could run into problems,
|
|
// because the timeout code assumes that dereferencing a
|
|
// search block will kill it. The reference count is 2--one
|
|
// for our pointer, one for the active status.
|
|
//
|
|
|
|
ASSERT( search->BlockHeader.ReferenceCount == 2 );
|
|
|
|
//
|
|
// If the search block has already been taken off the LRU
|
|
// list, then the client has attempted two simultaneous core
|
|
// searches with the same search block. This is an error on
|
|
// the part of the client.
|
|
//
|
|
|
|
if ( search->LastUseListEntry.Flink == NULL ) {
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
status = STATUS_INVALID_SMB;
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint0( "SrvSmbSearch: Attempt to do two simultaneuos core searches on same search block.\n" );
|
|
}
|
|
|
|
SrvLogInvalidSmb( WorkContext );
|
|
goto error_exit;
|
|
}
|
|
|
|
SrvRemoveEntryList(
|
|
&pagedConnection->CoreSearchList,
|
|
&search->LastUseListEntry
|
|
);
|
|
|
|
DECREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
|
|
|
|
//
|
|
// Set the entry pointer fields to NULL so that if another
|
|
// core search comes in for this search block we will know
|
|
// there is an error.
|
|
//
|
|
|
|
search->LastUseListEntry.Flink = NULL;
|
|
search->LastUseListEntry.Blink = NULL;
|
|
|
|
//
|
|
// Get the information from the directory cache
|
|
// corresponding to this file and put it into the resume key
|
|
// so that SrvQueryDirectoryFile has the proper information
|
|
// in the resume key. Core clients do not return the
|
|
// correct file name in the resume key, and have special
|
|
// requirements for the file index in the resume key.
|
|
//
|
|
|
|
resumeFileIndex = SmbGetUlong( &resumeKey->FileIndex );
|
|
resumeSequence = (USHORT)((resumeFileIndex & 0xFFFF0000) >> 16);
|
|
|
|
dirCacheIndex = (USHORT)(resumeFileIndex & (USHORT)0xFFFF);
|
|
|
|
//
|
|
// If the directory cache pointer in the search block
|
|
// indicates that there is no directory cache, then we
|
|
// returned no files last time, so return no files this
|
|
// time. This is due to DOS weirdness.
|
|
//
|
|
|
|
if ( search->DirectoryCache == NULL ||
|
|
dirCacheIndex >= search->NumberOfCachedFiles ) {
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint0( "Core request for rewind when no dircache exists.\n" );
|
|
}
|
|
|
|
//
|
|
// Put the search block back on the LRU list if the
|
|
// session and tree connect is still active.
|
|
//
|
|
|
|
if ((GET_BLOCK_STATE( session ) == BlockStateActive) &&
|
|
(GET_BLOCK_STATE( treeConnect ) == BlockStateActive)) {
|
|
|
|
KeQuerySystemTime( &search->LastUseTime );
|
|
SrvInsertTailList(
|
|
&pagedConnection->CoreSearchList,
|
|
&search->LastUseListEntry
|
|
);
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
} else {
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
//
|
|
// Not needed any more since session is closing.
|
|
//
|
|
|
|
SrvCloseSearch( search );
|
|
}
|
|
|
|
//
|
|
// Remove our pointer's reference.
|
|
//
|
|
|
|
SrvDereferenceSearch( search );
|
|
|
|
//
|
|
// Build the response SMB.
|
|
//
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, 0 );
|
|
SmbPutUshort( &response->ByteCount, 3 );
|
|
response->Buffer[0] = SMB_FORMAT_VARIABLE;
|
|
SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_SEARCH,
|
|
3
|
|
);
|
|
|
|
DEALLOCATE_NONPAGED_POOL( directoryInformation );
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dirCache = &search->DirectoryCache[dirCacheIndex];
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint3( "Accessing dircache, real file = %ws, index = 0x%lx, "
|
|
"cache index = %ld\n",
|
|
dirCache->UnicodeResumeName, dirCache->FileIndex,
|
|
dirCacheIndex );
|
|
}
|
|
|
|
SmbPutUlong( &resumeKey->FileIndex, dirCache->FileIndex );
|
|
unicodeResumeNameLength = dirCache->UnicodeResumeNameLength;
|
|
|
|
ASSERT( unicodeResumeNameLength <= sizeof( unicodeResumeName ) );
|
|
|
|
RtlCopyMemory( unicodeResumeName,
|
|
dirCache->UnicodeResumeName,
|
|
unicodeResumeNameLength );
|
|
|
|
//
|
|
// Free the directory cache--it is no longer needed.
|
|
//
|
|
|
|
FREE_HEAP( search->DirectoryCache );
|
|
search->DirectoryCache = NULL;
|
|
search->NumberOfCachedFiles = 0;
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
} else if ( command == SMB_COM_FIND_CLOSE ) {
|
|
|
|
//
|
|
// If this is a find close request, close the search block and
|
|
// dereference it. Close out the directory query, and send the
|
|
// response SMB.
|
|
//
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint1( "FIND CLOSE: Closing search block at 0x%p\n",
|
|
search );
|
|
}
|
|
|
|
SrvCloseQueryDirectory( directoryInformation );
|
|
search->DirectoryHandle = NULL;
|
|
|
|
SrvCloseSearch( search );
|
|
|
|
//
|
|
// Dereference the search block. SrvCloseSearch has already
|
|
// dereferenced it once, so it will be deallocated when we
|
|
// dereference it here.
|
|
//
|
|
|
|
SrvDereferenceSearch( search );
|
|
|
|
DEALLOCATE_NONPAGED_POOL( directoryInformation );
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->ByteCount, 3 );
|
|
response->Buffer[0] = SMB_FORMAT_VARIABLE;
|
|
SmbPutUshort( (PSMB_USHORT)(response->Buffer+1), 0 );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_SEARCH,
|
|
0
|
|
);
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint1( "FIND NEXT: Resuming search with file %s\n",
|
|
resumeKey->FileName );
|
|
}
|
|
|
|
//
|
|
// Set the filename string so that SrvQueryDirectoryFile knows
|
|
// what search to resume on.
|
|
//
|
|
|
|
|
|
if( unicodeResumeNameLength != 0 ) {
|
|
|
|
fileName.Buffer = unicodeResumeName;
|
|
fileName.Length = fileName.MaximumLength = unicodeResumeNameLength;
|
|
|
|
} else {
|
|
|
|
fileName.Buffer = nameBuffer;
|
|
|
|
Srv8dot3ToUnicodeString(
|
|
(PSZ)resumeKey->FileName,
|
|
&fileName
|
|
);
|
|
}
|
|
|
|
|
|
//
|
|
// Set calledQueryDirectory to TRUE will indicate to
|
|
// SrvQueryDirectoryFile that it does not need to parse the
|
|
// input name for the directory in which the search occurs, nor
|
|
// does it need to open the directory.
|
|
//
|
|
|
|
calledQueryDirectory = TRUE;
|
|
|
|
//
|
|
// Get the resume file index in an aligned field for later use.
|
|
//
|
|
|
|
resumeFileIndex = SmbGetUlong( &resumeKey->FileIndex );
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint1( "Found search block at 0x%p\n", search );
|
|
}
|
|
}
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint2( "Sequence # = %ld, index = %ld\n", sequence, sidIndex );
|
|
}
|
|
|
|
//
|
|
// Find the amount of space we have available for writing file
|
|
// entries into. The total buffer size available (includes space
|
|
// for the SMB header and parameters) is the minimum of our buffer
|
|
// size and the client's buffer size. The available space is the
|
|
// total buffer space less the amount we will need for the SMB
|
|
// header and parameters.
|
|
//
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint4( "BL = %ld, MBS = %ld, r->B = 0x%p, RB->Buffer = 0x%p\n",
|
|
WorkContext->ResponseBuffer->BufferLength,
|
|
session->MaxBufferSize, (PSZ)response->Buffer,
|
|
(PSZ)WorkContext->ResponseBuffer->Buffer );
|
|
}
|
|
|
|
availableSpace =
|
|
MIN(
|
|
WorkContext->ResponseBuffer->BufferLength,
|
|
(CLONG)session->MaxBufferSize
|
|
) -
|
|
PTR_DIFF(response->Buffer, WorkContext->ResponseBuffer->Buffer );
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint1( "Available buffer space: %ld\n", availableSpace );
|
|
}
|
|
|
|
//
|
|
// Simplify the search patterns if possible. This makes the filesystems more
|
|
// efficient, as they special case the '*' pattern
|
|
//
|
|
if ( (fileName.Length >= (12 * sizeof(WCHAR))) &&
|
|
(RtlEqualMemory(
|
|
&fileName.Buffer[fileName.Length/sizeof(WCHAR) - 12],
|
|
StrQuestionMarks,
|
|
12 * sizeof(WCHAR)))) {
|
|
|
|
if( fileName.Length == (12 * sizeof( WCHAR )) ||
|
|
fileName.Buffer[ fileName.Length/sizeof(WCHAR) - 13 ] == L'\\' ) {
|
|
|
|
//
|
|
// The search name ends in ????????.???, replace it with *
|
|
//
|
|
fileName.Buffer[fileName.Length/sizeof(WCHAR)-12] = L'*';
|
|
fileName.Length -= 11 * sizeof(WCHAR);
|
|
|
|
}
|
|
|
|
} else if ((fileName.Length >= (3 * sizeof(WCHAR))) &&
|
|
(RtlEqualMemory(
|
|
&fileName.Buffer[fileName.Length/sizeof(WCHAR) - 3],
|
|
StrStarDotStar,
|
|
3 * sizeof(WCHAR)))) {
|
|
|
|
if( fileName.Length == (3 * sizeof( WCHAR )) ||
|
|
fileName.Buffer[ fileName.Length/sizeof(WCHAR) - 4 ] == L'\\' ) {
|
|
|
|
//
|
|
// The search name ends in *.*, replace it with *
|
|
//
|
|
|
|
fileName.Length -= 2 * sizeof(WCHAR);
|
|
|
|
}
|
|
}
|
|
|
|
if( isCoreSearch ) {
|
|
dirCache = (PDIRECTORY_CACHE)ALLOCATE_HEAP(
|
|
maxCount * sizeof(DIRECTORY_CACHE),
|
|
BlockTypeDirCache
|
|
);
|
|
|
|
if( dirCache == NULL ) {
|
|
INTERNAL_ERROR(
|
|
ERROR_LEVEL_EXPECTED,
|
|
"SrvSmbSearch: Unable to allocate %d bytes from heap",
|
|
maxCount * sizeof(DIRECTORY_CACHE),
|
|
NULL
|
|
);
|
|
|
|
status = STATUS_INSUFF_SERVER_RESOURCES;
|
|
goto error_exit;
|
|
}
|
|
|
|
RtlZeroMemory( dirCache, maxCount * sizeof(DIRECTORY_CACHE) );
|
|
}
|
|
|
|
//
|
|
// Now we can start getting files. We do this until one of three
|
|
// conditions is met:
|
|
//
|
|
// 1) There are no more files to return.
|
|
// 2) We have obtained as many files as were requested.
|
|
// 3) The SMB buffer is full.
|
|
//
|
|
|
|
count = 0;
|
|
totalBytesWritten = 0;
|
|
dc = dirCache;
|
|
|
|
do {
|
|
|
|
PSZ dirInfoName;
|
|
UNICODE_STRING name;
|
|
PFILE_BOTH_DIR_INFORMATION bothDirInfo;
|
|
SMB_DATE dosDate;
|
|
SMB_TIME dosTime;
|
|
ULONG effectiveBufferSize;
|
|
|
|
//
|
|
// Since the file information returned by NtQueryDirectoryFile is
|
|
// about twice the size of the information we must return in the SMB
|
|
// (SMB_DIRECTORY_INFORMATION), use a buffer size equal to twice the
|
|
// available space if the available space is getting small. This
|
|
// optimization means that NtQueryDirectoryFile will return unused
|
|
// files less often. For example, if there is only space left in
|
|
// the SMB buffer for a single file entry, it does not make sense
|
|
// for NtQueryDirectoryFile to completely fill up the buffer--all it
|
|
// really needs to return is a single file.
|
|
//
|
|
|
|
effectiveBufferSize = MIN( nonPagedBufferSize, availableSpace * 2 );
|
|
|
|
//
|
|
// Make sure that there is at least enough room to hold a single
|
|
// file.
|
|
//
|
|
|
|
effectiveBufferSize = MAX( effectiveBufferSize, MIN_SEARCH_BUFFER_SIZE );
|
|
|
|
status = SrvQueryDirectoryFile(
|
|
WorkContext,
|
|
(BOOLEAN)!calledQueryDirectory,
|
|
TRUE, // filter long names
|
|
FALSE, // not for backup intent
|
|
FileBothDirectoryInformation,
|
|
0,
|
|
&fileName,
|
|
(PULONG)( (findFirst || count != 0) ?
|
|
NULL : &resumeFileIndex ),
|
|
search->SearchAttributes,
|
|
directoryInformation,
|
|
effectiveBufferSize
|
|
);
|
|
|
|
calledQueryDirectory = TRUE;
|
|
|
|
if ( status == STATUS_NO_SUCH_FILE ) {
|
|
status = STATUS_NO_MORE_FILES;
|
|
} else if ( status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
status = STATUS_OBJECT_PATH_NOT_FOUND;
|
|
}
|
|
|
|
if ( status == STATUS_NO_MORE_FILES ) {
|
|
|
|
if ( findFirst && count == 0 ) {
|
|
|
|
//
|
|
// If no files matched on the find first, close out
|
|
// the search.
|
|
//
|
|
|
|
IF_SMB_DEBUG(SEARCH1) {
|
|
SrvPrint1( "SrvSmbSearch: no matching files (%wZ).\n",
|
|
&fileName );
|
|
}
|
|
|
|
if( isCoreSearch ) {
|
|
FREE_HEAP( dirCache );
|
|
}
|
|
goto error_exit;
|
|
}
|
|
|
|
break;
|
|
|
|
} else if ( !NT_SUCCESS(status) ) {
|
|
IF_DEBUG(ERRORS) {
|
|
SrvPrint1(
|
|
"SrvSmbSearch: SrvQueryDirectoryFile returned %X\n",
|
|
status
|
|
);
|
|
}
|
|
|
|
if( isCoreSearch ) {
|
|
FREE_HEAP( dirCache );
|
|
}
|
|
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// Set the resumeKey pointer to NULL. If this is a find next, we
|
|
// have already resumed/rewound the search, so calls to
|
|
// NtQueryDirectoryFile during this search should continue where
|
|
// the last search left off.
|
|
//
|
|
|
|
resumeKey = NULL;
|
|
|
|
//
|
|
// If this is a Find command, then put the 8dot3 (no ".")
|
|
// representation of the file name into the resume key. If
|
|
// this is a search command, then put the 8dot3 representation of
|
|
// the search specification in the resume key.
|
|
//
|
|
|
|
bothDirInfo = (PFILE_BOTH_DIR_INFORMATION)
|
|
directoryInformation->CurrentEntry;
|
|
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
SrvPrint3( "SrvQueryDirectoryFile--name %ws, length = %ld, "
|
|
"status = %X\n",
|
|
bothDirInfo->FileName,
|
|
bothDirInfo->FileNameLength,
|
|
status );
|
|
SrvPrint1( "smbDirInfo = 0x%p\n", smbDirInfo );
|
|
}
|
|
|
|
//
|
|
// Use the FileName, unless it is not legal 8.3
|
|
//
|
|
name.Buffer = bothDirInfo->FileName;
|
|
name.Length = (SHORT)bothDirInfo->FileNameLength;
|
|
|
|
if( bothDirInfo->ShortNameLength != 0 ) {
|
|
|
|
if( !SrvIsLegalFatName( bothDirInfo->FileName,
|
|
bothDirInfo->FileNameLength ) ) {
|
|
|
|
//
|
|
// FileName is not legal 8.3, so switch to the
|
|
// ShortName
|
|
//
|
|
name.Buffer = bothDirInfo->ShortName;
|
|
name.Length = (SHORT)bothDirInfo->ShortNameLength;
|
|
}
|
|
}
|
|
|
|
name.MaximumLength = name.Length;
|
|
|
|
if ( isCoreSearch ) {
|
|
|
|
UNICODE_STRING baseFileName;
|
|
|
|
SrvGetBaseFileName( &search->SearchName, &baseFileName );
|
|
|
|
SrvUnicodeStringTo8dot3(
|
|
&baseFileName,
|
|
(PSZ)smbDirInfo->ResumeKey.FileName,
|
|
filterLongNames
|
|
);
|
|
|
|
//
|
|
// Save the unicode version of the 8.3 name in the dirCache
|
|
//
|
|
dc->UnicodeResumeNameLength = MIN(name.Length, 12*sizeof(WCHAR));
|
|
RtlCopyMemory( dc->UnicodeResumeName, name.Buffer, MIN(name.Length, 12*sizeof(WCHAR)) );
|
|
dc->FileIndex = bothDirInfo->FileIndex;
|
|
|
|
++dc;
|
|
|
|
} else {
|
|
|
|
SrvUnicodeStringTo8dot3(
|
|
&name,
|
|
(PSZ)smbDirInfo->ResumeKey.FileName,
|
|
filterLongNames
|
|
);
|
|
}
|
|
|
|
//
|
|
// Generate the resume key for this file.
|
|
//
|
|
// *** This must be done AFTER setting the file name in the resume
|
|
// key, as setting the resume key name would overwrite some
|
|
// of the sequence bytes which are stored in the high bits
|
|
// of the file name bytes.
|
|
//
|
|
|
|
SET_RESUME_KEY_INDEX( (PSMB_RESUME_KEY)smbDirInfo, sidIndex );
|
|
SET_RESUME_KEY_SEQUENCE( (PSMB_RESUME_KEY)smbDirInfo, sequence );
|
|
|
|
//
|
|
// Put the file index in the resume key.
|
|
//
|
|
|
|
SmbPutUlong(
|
|
&((PSMB_RESUME_KEY)smbDirInfo)->FileIndex,
|
|
bothDirInfo->FileIndex
|
|
);
|
|
|
|
SmbPutUlong(
|
|
(PSMB_ULONG)&((PSMB_RESUME_KEY)smbDirInfo)->Consumer[0],
|
|
0
|
|
);
|
|
|
|
//
|
|
// Load the file name into the SMB_DIRECTORY_INFORMATION structure.
|
|
//
|
|
|
|
dirInfoName = (PSZ)smbDirInfo->FileName;
|
|
|
|
oemString.Buffer = dirInfoName;
|
|
oemString.MaximumLength = (USHORT)RtlUnicodeStringToOemSize( &name );
|
|
|
|
if ( filterLongNames ) {
|
|
|
|
//
|
|
// If the client doesn't understand long names, upcase the file
|
|
// name. This is necessary for compatibility reasons. Note
|
|
// that the FAT file system returns upcased names anyway.
|
|
//
|
|
|
|
RtlUpcaseUnicodeStringToOemString( &oemString, &name, FALSE );
|
|
|
|
} else {
|
|
|
|
RtlUnicodeStringToOemString( &oemString, &name, FALSE );
|
|
|
|
}
|
|
|
|
//
|
|
// Blank-pad the end of the filename in order to be compatible with
|
|
// prior redirectors.
|
|
//
|
|
// !!! It is not certain whether this is required.
|
|
//
|
|
|
|
for ( i = (USHORT)(oemString.MaximumLength); i < 13; i++ ) {
|
|
dirInfoName[i] = ' ';
|
|
}
|
|
|
|
//
|
|
// Fill in other fields in the file entry.
|
|
//
|
|
|
|
SRV_NT_ATTRIBUTES_TO_SMB(
|
|
bothDirInfo->FileAttributes,
|
|
bothDirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY,
|
|
&smbFileAttributes
|
|
);
|
|
|
|
smbDirInfo->FileAttributes = (UCHAR)smbFileAttributes;
|
|
|
|
SrvTimeToDosTime(
|
|
&bothDirInfo->LastWriteTime,
|
|
&dosDate,
|
|
&dosTime
|
|
);
|
|
|
|
SmbPutDate( &smbDirInfo->LastWriteDate, dosDate );
|
|
SmbPutTime( &smbDirInfo->LastWriteTime, dosTime );
|
|
|
|
//
|
|
// *** NT file sizes are LARGE_INTEGERs (64 bits), SMB file sizes
|
|
// are longwords (32 bits). We just return the low 32 bits
|
|
// of the NT file size, because that is all we can do.
|
|
//
|
|
|
|
SmbPutUlong( &smbDirInfo->FileSize, bothDirInfo->EndOfFile.LowPart );
|
|
|
|
//
|
|
// Find the space left in the SMB buffer.
|
|
//
|
|
|
|
availableSpace -= sizeof(SMB_DIRECTORY_INFORMATION);
|
|
|
|
totalBytesWritten += sizeof(SMB_DIRECTORY_INFORMATION);
|
|
|
|
//
|
|
// Set up the smbDirInfo pointer for the next file. There is
|
|
// no padding for alignment between files, so just increment
|
|
// the pointer.
|
|
//
|
|
|
|
smbDirInfo++;
|
|
|
|
count++;
|
|
|
|
} while ( ( availableSpace > sizeof(SMB_DIRECTORY_INFORMATION) ) &&
|
|
( count < maxCount ) );
|
|
|
|
IF_SMB_DEBUG(SEARCH2) {
|
|
|
|
SrvPrint0( "Stopped putting entries in buffer. Reason:\n" );
|
|
|
|
if ( status == STATUS_NO_MORE_FILES ) {
|
|
SrvPrint0( "status = STATUS_NO_MORE_FILES\n" );
|
|
} else if ( count >= maxCount ) {
|
|
SrvPrint2( "count = %ld, maxCount = %ld\n", count, maxCount );
|
|
} else {
|
|
SrvPrint1( "Available space = %ld\n", availableSpace );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store information in the search block.
|
|
//
|
|
|
|
search->DirectoryHandle = directoryInformation->DirectoryHandle;
|
|
search->Wildcards = directoryInformation->Wildcards;
|
|
search->DownlevelTimewarp = directoryInformation->DownlevelTimewarp;
|
|
|
|
//
|
|
// If this was a core search, store information about the files that
|
|
// were returned in a directory cache. Also, modify the information
|
|
// in the SMB buffer, as it is slightly different for core searches.
|
|
//
|
|
|
|
if ( isCoreSearch ) {
|
|
|
|
if ( count == 0 ) {
|
|
|
|
FREE_HEAP( dirCache );
|
|
|
|
IF_SMB_DEBUG(SEARCH1) {
|
|
SrvPrint3( "SrvSmbSearch: prematurely closing search %p, index %lx sequence %lx\n",
|
|
search, sidIndex, sequence );
|
|
}
|
|
|
|
SrvCloseSearch( search );
|
|
goto done_core;
|
|
}
|
|
|
|
//
|
|
// Modify the CoreSequence field of the search block. This is
|
|
// done because core searches demand that the FileIndex field of
|
|
// the resume key always increase.
|
|
//
|
|
|
|
search->CoreSequence++;
|
|
|
|
//
|
|
// Set up the pointer to the file information now stored in the
|
|
// SMB buffer and save the location of the directory cache.
|
|
// Store the number of files in the directory cache.
|
|
//
|
|
|
|
smbDirInfo = (PSMB_DIRECTORY_INFORMATION)(response->Buffer + 3);
|
|
search->DirectoryCache = dirCache;
|
|
search->NumberOfCachedFiles = count;
|
|
|
|
//
|
|
// Loop through the files changing information about the files
|
|
// in the SMB buffer to conform to what the core client expects.
|
|
//
|
|
|
|
for ( i = 0; i < count; i++ ) {
|
|
|
|
SmbPutUlong(
|
|
&smbDirInfo->ResumeKey.FileIndex,
|
|
(search->CoreSequence << 16) + i
|
|
);
|
|
|
|
smbDirInfo++;
|
|
dirCache++;
|
|
}
|
|
|
|
//
|
|
// If this was a core search, put the search block back on the
|
|
// appropriate search block list. If no files were found for this
|
|
// SMB, put the search block on the complete list. Also, set the
|
|
// last use time field of the search block.
|
|
//
|
|
// If this is a find first to which we responded with
|
|
// one file AND either more than one file was requested or this
|
|
// is a unique search (no wildcards) AND there was space in the
|
|
// buffer for more, close out the search. This saves the memory
|
|
// associated with an open handle and frees up the search table
|
|
// entry. Also close the search if zero files are being returned.
|
|
//
|
|
// We can do this safely because we know that the client would
|
|
// not be able to do a rewind or resume with these conditions
|
|
// and get back anything other than NO_MORE_FILES, which is what
|
|
// we'll return if the client attempts to resume or rewind to an
|
|
// invalid SID.
|
|
//
|
|
|
|
if ( (count == 1
|
|
&&
|
|
findFirst
|
|
&&
|
|
( maxCount > 1 || !search->Wildcards )
|
|
&&
|
|
availableSpace > sizeof(SMB_DIRECTORY_INFORMATION) ) ) {
|
|
|
|
IF_SMB_DEBUG(SEARCH1) {
|
|
SrvPrint3( "SrvSmbSearch: prematurely closing search %p, index %lx sequence %lx\n",
|
|
search, sidIndex, sequence );
|
|
}
|
|
|
|
SrvCloseSearch( search );
|
|
|
|
} else {
|
|
|
|
PLIST_ENTRY hashEntry;
|
|
|
|
//
|
|
// Put the search on the core search list.
|
|
//
|
|
|
|
ACQUIRE_LOCK( &connection->Lock );
|
|
|
|
if ( GET_BLOCK_STATE( session ) != BlockStateActive ) {
|
|
|
|
//
|
|
// The session is closing. Do not insert this search
|
|
// on the search list, because the list may already
|
|
// have been cleaned up.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
status = STATUS_SMB_BAD_UID;
|
|
goto error_exit;
|
|
|
|
} else if ( GET_BLOCK_STATE( treeConnect ) != BlockStateActive ) {
|
|
|
|
//
|
|
// Tree connect is closing. Don't insert the search block
|
|
// so the tree connect can be cleaned up immediately.
|
|
//
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
status = STATUS_SMB_BAD_TID;
|
|
goto error_exit;
|
|
}
|
|
|
|
KeQuerySystemTime( &search->LastUseTime );
|
|
|
|
SrvInsertTailList(
|
|
&pagedConnection->CoreSearchList,
|
|
&search->LastUseListEntry
|
|
);
|
|
|
|
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.CoreSearches );
|
|
|
|
//
|
|
// Insert this into the hash table.
|
|
//
|
|
|
|
hashEntry = &search->HashTableEntry;
|
|
|
|
if ( hashEntry->Flink == NULL ) {
|
|
SrvAddToSearchHashTable(
|
|
pagedConnection,
|
|
search
|
|
);
|
|
} else {
|
|
|
|
PLIST_ENTRY listHead;
|
|
|
|
listHead = &pagedConnection->SearchHashTable[
|
|
search->HashTableIndex].ListHead;
|
|
|
|
if ( listHead->Flink != hashEntry ) {
|
|
|
|
//
|
|
// remove it and put it back on the front of the queue.
|
|
//
|
|
|
|
SrvRemoveEntryList(
|
|
listHead,
|
|
hashEntry
|
|
);
|
|
|
|
SrvInsertHeadList(
|
|
listHead,
|
|
hashEntry
|
|
);
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK( &connection->Lock );
|
|
|
|
//
|
|
// Make sure the reference count will be 2. 1 for out pointer,
|
|
// and one for the active status.
|
|
//
|
|
|
|
ASSERT( search->BlockHeader.ReferenceCount == 2 );
|
|
}
|
|
|
|
} else if ( command == SMB_COM_FIND_UNIQUE ) {
|
|
|
|
//
|
|
// If this was a find unique, get rid of the search block by
|
|
// closing the query directory and the search block.
|
|
//
|
|
|
|
search->DirectoryHandle = NULL;
|
|
SrvCloseQueryDirectory( directoryInformation );
|
|
SrvCloseSearch( search );
|
|
}
|
|
|
|
done_core:
|
|
|
|
//
|
|
// Set up the response SMB.
|
|
//
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, count );
|
|
SmbPutUshort( &response->ByteCount, (USHORT)(totalBytesWritten+3) );
|
|
response->Buffer[0] = SMB_FORMAT_VARIABLE;
|
|
SmbPutUshort(
|
|
(PSMB_USHORT)(response->Buffer+1),
|
|
(USHORT)totalBytesWritten
|
|
);
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_SEARCH,
|
|
SmbGetUshort( &response->ByteCount )
|
|
);
|
|
|
|
//
|
|
// Remove our pointer's reference.
|
|
//
|
|
|
|
if( search ) {
|
|
search->InUse = FALSE;
|
|
SrvDereferenceSearch( search );
|
|
}
|
|
|
|
if ( !isUnicode &&
|
|
fileName.Buffer != NULL &&
|
|
fileName.Buffer != nameBuffer &&
|
|
fileName.Buffer != unicodeResumeName ) {
|
|
|
|
RtlFreeUnicodeString( &fileName );
|
|
}
|
|
|
|
if( directoryInformation ) {
|
|
DEALLOCATE_NONPAGED_POOL( directoryInformation );
|
|
}
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
goto Cleanup;
|
|
|
|
error_exit:
|
|
|
|
if ( search != NULL ) {
|
|
|
|
//
|
|
// If findFirst == TRUE, then we allocated a search block which
|
|
// we have to close.
|
|
// If findFirst == TRUE and calledQueryDirectory == TRUE, then
|
|
// we also opened the directory handle and need to close it.
|
|
// If findFirst == FALSE, then then we got an existing search
|
|
// block with an existing directory handle.
|
|
//
|
|
|
|
if ( findFirst) {
|
|
if ( calledQueryDirectory ) {
|
|
SrvCloseQueryDirectory( directoryInformation );
|
|
search->DirectoryHandle = NULL;
|
|
}
|
|
SrvCloseSearch( search );
|
|
}
|
|
|
|
search->InUse = FALSE;
|
|
|
|
//
|
|
// Remove our pointer's reference.
|
|
//
|
|
|
|
SrvDereferenceSearch( search );
|
|
}
|
|
|
|
//
|
|
// Deallocate the directory information block. We do not need
|
|
// to close the directoryhandle here since we should have already
|
|
// closed it (if we need to) in the preceding code.
|
|
//
|
|
|
|
if ( directoryInformation != NULL ) {
|
|
DEALLOCATE_NONPAGED_POOL( directoryInformation );
|
|
}
|
|
|
|
if ( !isUnicode &&
|
|
fileName.Buffer != NULL &&
|
|
fileName.Buffer != nameBuffer &&
|
|
fileName.Buffer != unicodeResumeName ) {
|
|
|
|
RtlFreeUnicodeString( &fileName );
|
|
}
|
|
|
|
if( status == STATUS_PATH_NOT_COVERED ) {
|
|
SrvSetSmbError( WorkContext, status );
|
|
|
|
} else {
|
|
SrvSetSmbError(
|
|
WorkContext,
|
|
isCoreSearch && (status != STATUS_OBJECT_PATH_NOT_FOUND) ?
|
|
STATUS_NO_MORE_FILES : status
|
|
);
|
|
}
|
|
|
|
SmbStatus = SmbStatusSendResponse;
|
|
|
|
Cleanup:
|
|
SrvWmiEndContext(WorkContext);
|
|
return SmbStatus;
|
|
|
|
} // SrvSmbSearch
|