VOID SrvCloseCachedDirectoryEntries( IN PCONNECTION Connection ) /*++ Routine Description: This routine closes all the cached directory entries on the connection Arguments: Connection - Pointer to the connection structure having the cache ++*/ { KIRQL oldIrql; PCACHED_DIRECTORY cd; ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &oldIrql ); while( Connection->CachedDirectoryCount > 0 ) { cd = CONTAINING_RECORD( Connection->CachedDirectoryList.Flink, CACHED_DIRECTORY, ListEntry ); RemoveEntryList( &cd->ListEntry ); Connection->CachedDirectoryCount--; DEALLOCATE_NONPAGED_POOL( cd ); } RELEASE_SPIN_LOCK( &Connection->SpinLock, oldIrql ); }
VOID SrvFreeEndpoint ( IN PENDPOINT Endpoint ) /*++ Routine Description: This function returns an Endpoint Block to the system nonpaged pool. Arguments: Endpoint - Address of endpoint Return Value: None. --*/ { PAGED_CODE( ); DEBUG SET_BLOCK_TYPE_STATE_SIZE( Endpoint, BlockTypeGarbage, BlockStateDead, -1 ); DEBUG Endpoint->BlockHeader.ReferenceCount = (ULONG)-1; TERMINATE_REFERENCE_HISTORY( Endpoint ); if ( Endpoint->IpxMaxPacketSizeArray != NULL ) { FREE_HEAP( Endpoint->IpxMaxPacketSizeArray ); } if ( Endpoint->ConnectionTable.Table != NULL ) { SrvFreeTable( &Endpoint->ConnectionTable ); } DEALLOCATE_NONPAGED_POOL( Endpoint ); IF_DEBUG(HEAP) SrvPrint1( "SrvFreeEndpoint: Freed endpoint block at %lx\n", Endpoint ); INCREMENT_DEBUG_STAT( SrvDbgStatistics.EndpointInfo.Frees ); return; } // SrvFreeEndpoint
NOT PAGEABLE -- WalkConnectionTable #endif VOID SrvAllocateEndpoint ( OUT PENDPOINT *Endpoint, IN PUNICODE_STRING NetworkName, IN PUNICODE_STRING TransportName, IN PANSI_STRING TransportAddress, IN PUNICODE_STRING DomainName ) /*++ Routine Description: This function allocates an Endpoint Block from the system nonpaged pool. Arguments: Endpoint - Returns a pointer to the endpoint block, or NULL if no pool was available. NetworkName - Supplies a pointer to the network name (e.g., NET1). TransportName - The fully qualified name of the transport device. For example, "\Device\Nbf". TransportAddress - The fully qualified address (or name ) of the server's endpoint. This name is used exactly as specified. For NETBIOS-compatible networks, the caller must upcase and blank-fill the name. E.g., "\Device\Nbf\NTSERVERbbbbbbbb". DomainName - the domain being serviced by this endpoint Return Value: None. --*/ { CLONG length; PENDPOINT endpoint; NTSTATUS status; PAGED_CODE( ); // // Attempt to allocate from nonpaged pool. // length = sizeof(ENDPOINT) + NetworkName->Length + sizeof(*NetworkName->Buffer) + TransportName->Length + sizeof(*TransportName->Buffer) + TransportAddress->Length + sizeof(*TransportAddress->Buffer) + DomainName->Length + sizeof(*DomainName->Buffer) + RtlUnicodeStringToOemSize( DomainName ) + sizeof(CHAR); endpoint = ALLOCATE_NONPAGED_POOL( length, BlockTypeEndpoint ); *Endpoint = endpoint; if ( endpoint == NULL ) { INTERNAL_ERROR ( ERROR_LEVEL_EXPECTED, "SrvAllocateEndpoint: Unable to allocate %d bytes from nonpaged " "pool.", length, NULL ); return; } IF_DEBUG(HEAP) { SrvPrint1( "SrvAllocateEndpoint: Allocated endpoint at %lx\n", endpoint ); } // // Initialize the endpoint block. Zero it first. // RtlZeroMemory( endpoint, length ); SET_BLOCK_TYPE_STATE_SIZE( endpoint, BlockTypeEndpoint, BlockStateActive, length ); endpoint->BlockHeader.ReferenceCount = 2; // allow for Active status // and caller's pointer // // Allocate connection table. // SrvAllocateTable( &endpoint->ConnectionTable, 6, // !!! TRUE ); if ( endpoint->ConnectionTable.Table == NULL ) { DEALLOCATE_NONPAGED_POOL( endpoint ); *Endpoint = NULL; return; } InitializeListHead( &endpoint->FreeConnectionList ); #if SRVDBG29 UpdateConnectionHistory( "INIT", endpoint, NULL ); #endif // // Copy the network name, transport name, and server address, and domain // name into the block. // endpoint->NetworkName.Length = NetworkName->Length; endpoint->NetworkName.MaximumLength = (SHORT)(NetworkName->Length + sizeof(*NetworkName->Buffer)); endpoint->NetworkName.Buffer = (PWCH)(endpoint + 1); RtlCopyMemory( endpoint->NetworkName.Buffer, NetworkName->Buffer, NetworkName->Length ); endpoint->TransportName.Length = TransportName->Length; endpoint->TransportName.MaximumLength = (SHORT)(TransportName->Length + sizeof(*TransportName->Buffer)); endpoint->TransportName.Buffer = (PWCH)((PCHAR)endpoint->NetworkName.Buffer + endpoint->NetworkName.MaximumLength); RtlCopyMemory( endpoint->TransportName.Buffer, TransportName->Buffer, TransportName->Length ); endpoint->TransportAddress.Length = TransportAddress->Length; endpoint->TransportAddress.MaximumLength = (SHORT)(TransportAddress->Length + 1); endpoint->TransportAddress.Buffer = (PCHAR)endpoint->TransportName.Buffer + endpoint->TransportName.MaximumLength; RtlCopyMemory( endpoint->TransportAddress.Buffer, TransportAddress->Buffer, TransportAddress->Length ); endpoint->DomainName.Length = DomainName->Length; endpoint->DomainName.MaximumLength = (SHORT)(DomainName->Length + sizeof(*DomainName->Buffer)); endpoint->DomainName.Buffer = (PWCH)((PCHAR)endpoint->TransportAddress.Buffer + TransportAddress->MaximumLength); RtlCopyMemory( endpoint->DomainName.Buffer, DomainName->Buffer, DomainName->Length ); endpoint->OemDomainName.Length = (SHORT)RtlUnicodeStringToOemSize( DomainName ); endpoint->OemDomainName.MaximumLength = endpoint->OemDomainName.Length + sizeof( CHAR ); endpoint->OemDomainName.Buffer = (PCHAR)endpoint->DomainName.Buffer + DomainName->MaximumLength; status = RtlUnicodeStringToOemString( &endpoint->OemDomainName, &endpoint->DomainName, FALSE // Do not allocate the OEM string ); ASSERT( NT_SUCCESS(status) ); // // Initialize the network address field. // endpoint->NetworkAddress.Buffer = endpoint->NetworkAddressData; endpoint->NetworkAddress.Length = sizeof( endpoint->NetworkAddressData ) - sizeof(endpoint->NetworkAddressData[0]); endpoint->NetworkAddress.MaximumLength = sizeof( endpoint->NetworkAddressData ); // // Increment the count of endpoints in the server. // ACQUIRE_LOCK( &SrvEndpointLock ); SrvEndpointCount++; RELEASE_LOCK( &SrvEndpointLock ); INITIALIZE_REFERENCE_HISTORY( endpoint ); INCREMENT_DEBUG_STAT( SrvDbgStatistics.EndpointInfo.Allocations ); return; } // SrvAllocateEndpoint
VOID SrvRemoveCachedDirectoryName( IN PWORK_CONTEXT WorkContext, IN PUNICODE_STRING DirectoryName ) { PLIST_ENTRY listEntry; PCACHED_DIRECTORY cd; ULONG directoryNameHashValue; PCONNECTION connection = WorkContext->Connection; KIRQL oldIrql; USHORT tid; if( connection->CachedDirectoryCount == 0 ) { return; } // // DirectoryName must point to memory in nonpaged pool, else we can't touch // it under spinlock control. If the incomming SMB is UNICODE, we know that // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise // we can't trust it and we're better off just not trying to cache it. // if( !SMB_IS_UNICODE( WorkContext ) ) { return; } COMPUTE_STRING_HASH( DirectoryName, &directoryNameHashValue ); tid = WorkContext->TreeConnect->Tid; ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql ); for ( listEntry = connection->CachedDirectoryList.Flink; listEntry != &connection->CachedDirectoryList; listEntry = listEntry->Flink ) { cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry ); // // See if this entry is an exact match for what was requested // if( cd->DirectoryName.Length == DirectoryName->Length && cd->Tid == tid && RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer, DirectoryName->Length ) == DirectoryName->Length ) { // // Remove this entry from the list and adjust the count // RemoveEntryList( &cd->ListEntry ); connection->CachedDirectoryCount--; ASSERT( (LONG)connection->CachedDirectoryCount >= 0 ); RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); DEALLOCATE_NONPAGED_POOL( cd ); return; } } RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return; }
BOOLEAN SrvIsDirectoryCached ( IN PWORK_CONTEXT WorkContext, IN PUNICODE_STRING DirectoryName ) { PLIST_ENTRY listEntry; PCACHED_DIRECTORY cd; ULONG directoryNameHashValue; PCONNECTION connection = WorkContext->Connection; KIRQL oldIrql; LARGE_INTEGER timeNow; // // DirectoryName must point to memory in nonpaged pool, else we can't touch // it under spinlock control. If the incomming SMB is UNICODE, we know that // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise // we can't trust it and we're better off just not trying to cache it. // if( connection->CachedDirectoryCount == 0 || !SMB_IS_UNICODE( WorkContext ) ) { return FALSE; } KeQueryTickCount( &timeNow ); timeNow.LowPart -= (SrvFiveSecondTickCount >> 1 ); ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql ); top: for ( listEntry = connection->CachedDirectoryList.Flink; listEntry != &connection->CachedDirectoryList; listEntry = listEntry->Flink ) { cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry ); // // Is this element too old? // if( cd->TimeStamp < timeNow.LowPart ) { // // This element is more than 2.5 seconds old. Toss it out // RemoveEntryList( listEntry ); connection->CachedDirectoryCount--; DEALLOCATE_NONPAGED_POOL( cd ); goto top; } if( cd->Tid != WorkContext->TreeConnect->Tid ) { continue; } // // Is the requested entry a subdir of this cache entry? // if( DirectoryName->Length < cd->DirectoryName.Length && RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer, DirectoryName->Length ) == DirectoryName->Length && cd->DirectoryName.Buffer[ DirectoryName->Length / sizeof( WCHAR ) ] == L'\\' ) { RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return TRUE; // // Not a subdir -- is it an exact match? // } else if( DirectoryName->Length == cd->DirectoryName.Length && RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer, DirectoryName->Length ) == DirectoryName->Length ) { RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return TRUE; } } RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return FALSE; }
VOID SrvCacheDirectoryName ( IN PWORK_CONTEXT WorkContext, IN PUNICODE_STRING DirectoryName ) /*++ Routine Description: This routine remembers 'DirectoryName' for further fast processing of the CheckPath SMB Arguments: WorkContext - Pointer to the work context block DirectoryName - Fully canonicalized name of the directory we're caching ++*/ { CLONG blockLength; PCACHED_DIRECTORY cd; KIRQL oldIrql; PCONNECTION connection = WorkContext->Connection; PLIST_ENTRY listEntry; LARGE_INTEGER timeNow; USHORT tid; if( SrvMaxCachedDirectory == 0 ) { return; } // // DirectoryName must point to memory in nonpaged pool, else we can't touch // it under spinlock control. If the incomming SMB is UNICODE, we know that // the name is in the smb buffer, and is therefore in nonpaged pool. Otherwise // we can't trust it and we're better off just not trying to cache it. // if( !SMB_IS_UNICODE( WorkContext ) ) { return; } KeQueryTickCount( &timeNow ); timeNow.LowPart -= ( SrvFiveSecondTickCount >> 1 ); tid = WorkContext->TreeConnect->Tid; ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql ); // // Search the directory cache and see if this directory is already cached. If so, // don't cache it again. // top: for ( listEntry = connection->CachedDirectoryList.Flink; listEntry != &connection->CachedDirectoryList; listEntry = listEntry->Flink ) { cd = CONTAINING_RECORD( listEntry, CACHED_DIRECTORY, ListEntry ); // // Is this element too old? // if( cd->TimeStamp < timeNow.LowPart ) { // // This element is more than 2.5 seconds old. Toss it out // RemoveEntryList( listEntry ); connection->CachedDirectoryCount--; DEALLOCATE_NONPAGED_POOL( cd ); goto top; } if( cd->Tid != tid ) { continue; } // // Is the new entry a subdir of this cache entry? // if( DirectoryName->Length < cd->DirectoryName.Length && RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer, DirectoryName->Length ) == DirectoryName->Length && cd->DirectoryName.Buffer[ DirectoryName->Length / sizeof( WCHAR ) ] == L'\\' ) { // // It is a subdir -- no need to cache it again // RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return; } // // Is the cache entry a subdir of the new entry? // if( cd->DirectoryName.Length < DirectoryName->Length && RtlCompareMemory( DirectoryName->Buffer, cd->DirectoryName.Buffer, cd->DirectoryName.Length ) == cd->DirectoryName.Length && DirectoryName->Buffer[ cd->DirectoryName.Length / sizeof( WCHAR ) ] == L'\\' ) { // // We can remove this entry // RemoveEntryList( listEntry ); connection->CachedDirectoryCount--; DEALLOCATE_NONPAGED_POOL( cd ); // // We want to cache this new longer entry // break; } // // Not a subdir -- is it an exact match? // if( cd->DirectoryName.Length == DirectoryName->Length && RtlCompareMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer, DirectoryName->Length ) == DirectoryName->Length ) { // // This entry is already in the cache -- no need to recache // RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return; } } // // This directory name is not already in the cache. So add it. // blockLength = sizeof( CACHED_DIRECTORY ) + DirectoryName->Length + sizeof(WCHAR); cd = ALLOCATE_NONPAGED_POOL( blockLength, BlockTypeCachedDirectory ); if( cd == NULL ) { RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvCacheDirectoryName: Unable to allocate %d bytes from pool", blockLength, NULL ); return; } cd->Type = BlockTypeCachedDirectory; cd->State = BlockStateActive; cd->Size = (USHORT)blockLength; // cd->ReferenceCount = 1; // not used // // Set the timestamp of this entry. Remember, we subtracted // ticks up above from timeNow -- put them back in now. // cd->TimeStamp = timeNow.LowPart + ( SrvFiveSecondTickCount >> 1 ); // // Store the directory name as it was passed into us // cd->DirectoryName.Length = DirectoryName->Length; cd->DirectoryName.MaximumLength = (USHORT)DirectoryName->MaximumLength; cd->DirectoryName.Buffer = (PWCH)(cd + 1); RtlCopyMemory( cd->DirectoryName.Buffer, DirectoryName->Buffer, DirectoryName->Length ); cd->Tid = tid; InsertHeadList( &connection->CachedDirectoryList, &cd->ListEntry ); // // Check the number of elements in the cache. If getting too large, close oldest one. // if( connection->CachedDirectoryCount++ < SrvMaxCachedDirectory ) { RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); return; } // // Remove the last entry from the cache // cd = CONTAINING_RECORD( connection->CachedDirectoryList.Blink, CACHED_DIRECTORY, ListEntry ); RemoveEntryList( &cd->ListEntry ); connection->CachedDirectoryCount--; RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql ); DEALLOCATE_NONPAGED_POOL( cd ); return; }