544 lines
13 KiB
C
544 lines
13 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
share.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines for adding, deleting, and enumerating
|
||
shared resources.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 15-Nov-1989
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvVerifyShare )
|
||
#pragma alloc_text( PAGE, SrvFindShare )
|
||
#pragma alloc_text( PAGE, SrvRemoveShare )
|
||
#pragma alloc_text( PAGE, SrvAddShare )
|
||
#pragma alloc_text( PAGE, SrvShareEnumApiHandler )
|
||
#endif
|
||
|
||
|
||
PSHARE
|
||
SrvVerifyShare (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PSZ ShareName,
|
||
IN PSZ ShareTypeString,
|
||
IN BOOLEAN ShareNameIsUnicode,
|
||
IN BOOLEAN IsNullSession,
|
||
OUT PNTSTATUS Status
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempts to find a share that matches a given name and share type.
|
||
|
||
Arguments:
|
||
|
||
ShareName - name of share to verify, including the server name.
|
||
(I.e., of the form "\\server\share", as received in the SMB.)
|
||
|
||
ShareTypeString - type of the share (A:, LPT1:, COMM, IPC, or ?????).
|
||
|
||
ShareNameIsUnicode - if TRUE, the share name is Unicode.
|
||
|
||
IsNullSession - Is this the NULL session?
|
||
|
||
Status - Reason why this call failed. Not used if a share is returned.
|
||
|
||
Return Value:
|
||
|
||
A pointer to a share matching the given name and share type, or NULL
|
||
if none exists.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARE share;
|
||
BOOLEAN anyShareType = FALSE;
|
||
SHARE_TYPE shareType;
|
||
PWCH nameOnly;
|
||
UNICODE_STRING nameOnlyString;
|
||
UNICODE_STRING shareName;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// First ensure that the share type string is valid.
|
||
//
|
||
|
||
if ( _stricmp( StrShareTypeNames[ShareTypeDisk], ShareTypeString ) == 0 ) {
|
||
shareType = ShareTypeDisk;
|
||
} else if ( _stricmp( StrShareTypeNames[ShareTypePipe], ShareTypeString ) == 0 ) {
|
||
shareType = ShareTypePipe;
|
||
} else if ( _stricmp( StrShareTypeNames[ShareTypePrint], ShareTypeString ) == 0 ) {
|
||
shareType = ShareTypePrint;
|
||
} else if ( _stricmp( StrShareTypeNames[ShareTypeComm], ShareTypeString ) == 0 ) {
|
||
shareType = ShareTypeComm;
|
||
} else if ( _stricmp( StrShareTypeNames[ShareTypeWild], ShareTypeString ) == 0 ) {
|
||
anyShareType = TRUE;
|
||
} else {
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvVerifyShare: Invalid share type: %Z\n",
|
||
ShareTypeString );
|
||
}
|
||
*Status = STATUS_BAD_DEVICE_TYPE;
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// If the passed-in server\share conbination is not Unicode, convert
|
||
// it to Unicode.
|
||
//
|
||
|
||
if ( ShareNameIsUnicode ) {
|
||
ShareName = ALIGN_SMB_WSTR( ShareName );
|
||
}
|
||
|
||
if ( !NT_SUCCESS(SrvMakeUnicodeString(
|
||
ShareNameIsUnicode,
|
||
&shareName,
|
||
ShareName,
|
||
NULL
|
||
)) ) {
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint0( "SrvVerifyShare: Unable to allocate heap for "
|
||
"Unicode share name string\n" );
|
||
}
|
||
*Status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Skip past the "\\server\" part of the input string. If there is
|
||
// no leading "\\", assume that the input string contains the share
|
||
// name only. If there is a "\\", but no subsequent "\", assume
|
||
// that the input string contains just a server name, and points to
|
||
// the end of that name, thus fabricating a null share name.
|
||
//
|
||
|
||
nameOnly = shareName.Buffer;
|
||
|
||
if ( (*nameOnly == DIRECTORY_SEPARATOR_CHAR) &&
|
||
(*(nameOnly+1) == DIRECTORY_SEPARATOR_CHAR) ) {
|
||
|
||
PWSTR nextSlash;
|
||
|
||
nameOnly += 2;
|
||
nextSlash = wcschr( nameOnly, DIRECTORY_SEPARATOR_CHAR );
|
||
|
||
if ( nextSlash == NULL ) {
|
||
nameOnly = NULL;
|
||
} else {
|
||
nameOnly = nextSlash + 1;
|
||
}
|
||
}
|
||
|
||
RtlInitUnicodeString( &nameOnlyString, nameOnly );
|
||
|
||
//
|
||
// Try to match share name against available share names.
|
||
//
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
share = SrvFindShare( &nameOnlyString );
|
||
|
||
if ( share == NULL ) {
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvVerifyShare: Unknown share name: %s\n",
|
||
nameOnly );
|
||
}
|
||
|
||
if ( !ShareNameIsUnicode ) {
|
||
RtlFreeUnicodeString( &shareName );
|
||
}
|
||
*Status = STATUS_BAD_NETWORK_NAME;
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// If this is the null session, allow it to connect only to IPC$ or
|
||
// to shares specified in the NullSessionShares list.
|
||
//
|
||
|
||
if ( IsNullSession &&
|
||
SrvRestrictNullSessionAccess &&
|
||
( share->ShareType != ShareTypePipe ) ) {
|
||
|
||
BOOLEAN matchFound = FALSE;
|
||
ULONG i;
|
||
|
||
ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
|
||
|
||
for ( i = 0; SrvNullSessionShares[i] != NULL ; i++ ) {
|
||
|
||
if ( _wcsicmp(
|
||
SrvNullSessionShares[i],
|
||
nameOnlyString.Buffer
|
||
) == 0 ) {
|
||
|
||
matchFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvConfigurationLock );
|
||
|
||
if ( !matchFound ) {
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint0( "SrvVerifyShare: Illegal null session access.\n");
|
||
}
|
||
|
||
if ( !ShareNameIsUnicode ) {
|
||
RtlFreeUnicodeString( &shareName );
|
||
}
|
||
|
||
*Status = STATUS_ACCESS_DENIED;
|
||
return(NULL);
|
||
}
|
||
}
|
||
|
||
if ( !ShareNameIsUnicode ) {
|
||
RtlFreeUnicodeString( &shareName );
|
||
}
|
||
|
||
if ( anyShareType || (share->ShareType == shareType) ) {
|
||
|
||
//
|
||
// Put share in work context block and reference it.
|
||
//
|
||
|
||
SrvReferenceShare( share );
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
WorkContext->Share = share;
|
||
return share;
|
||
|
||
} else {
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvVerifyShare: incorrect share type: %s\n",
|
||
ShareTypeString );
|
||
}
|
||
|
||
*Status = STATUS_BAD_DEVICE_TYPE;
|
||
return NULL;
|
||
|
||
}
|
||
|
||
} // SrvVerifyShare
|
||
|
||
|
||
PSHARE
|
||
SrvFindShare (
|
||
IN PUNICODE_STRING ShareName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Attempts to find a share that matches a given name.
|
||
|
||
*** This routine must be called with the share lock (SrvShareLock)
|
||
held.
|
||
|
||
Arguments:
|
||
|
||
ShareName - name of share to Find.
|
||
|
||
Return Value:
|
||
|
||
A pointer to a share matching the given name, or NULL if none exists.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARE share;
|
||
PLIST_ENTRY listEntryRoot, listEntry;
|
||
ULONG hashValue;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Try to match share name against available share names.
|
||
//
|
||
|
||
COMPUTE_STRING_HASH( ShareName, &hashValue );
|
||
listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( hashValue ) ];
|
||
|
||
for( listEntry = listEntryRoot->Flink;
|
||
listEntry != listEntryRoot;
|
||
listEntry = listEntry->Flink ) {
|
||
|
||
share = CONTAINING_RECORD( listEntry, SHARE, GlobalShareList );
|
||
|
||
if( share->ShareNameHashValue == hashValue &&
|
||
RtlCompareUnicodeString(
|
||
&share->ShareName,
|
||
ShareName,
|
||
TRUE
|
||
) == 0 ) {
|
||
|
||
//
|
||
// Found a matching share. If it is active return its
|
||
// address.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE( share ) == BlockStateActive ) {
|
||
return share;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Couldn't find a matching share that was active.
|
||
//
|
||
|
||
return NULL;
|
||
|
||
} // SrvFindShare
|
||
|
||
VOID
|
||
SrvRemoveShare(
|
||
PSHARE Share
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
RemoveEntryList( &Share->GlobalShareList );
|
||
}
|
||
|
||
VOID
|
||
SrvAddShare(
|
||
PSHARE Share
|
||
)
|
||
{
|
||
PLIST_ENTRY listEntryRoot, listEntry;
|
||
ULONG hashValue;
|
||
|
||
PAGED_CODE();
|
||
|
||
COMPUTE_STRING_HASH( &Share->ShareName, &hashValue );
|
||
Share->ShareNameHashValue = hashValue;
|
||
listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( hashValue ) ];
|
||
|
||
InsertTailList( listEntryRoot, &Share->GlobalShareList );
|
||
}
|
||
|
||
NTSTATUS
|
||
SrvShareEnumApiHandler (
|
||
IN PSERVER_REQUEST_PACKET Srp,
|
||
IN PVOID OutputBuffer,
|
||
IN ULONG BufferLength,
|
||
IN PENUM_FILTER_ROUTINE FilterRoutine,
|
||
IN PENUM_SIZE_ROUTINE SizeRoutine,
|
||
IN PENUM_FILL_ROUTINE FillRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
All share Enum and GetInfo APIs are handled by this routine in the server
|
||
FSD. It takes the ResumeHandle in the SRP to find the first
|
||
appropriate share, then calls the passed-in filter routine to check
|
||
if the share should be filled in. If it should, we call the filter
|
||
routine, then try to get another shar. This continues until the
|
||
entire list has been walked.
|
||
|
||
Arguments:
|
||
|
||
Srp - a pointer to the SRP for the operation.
|
||
|
||
OutputBuffer - the buffer in which to fill output information.
|
||
|
||
BufferLength - the length of the buffer.
|
||
|
||
FilterRoutine - a pointer to a function that will check a share entry
|
||
against information in the SRP to determine whether the
|
||
information in the share should be placed in the output
|
||
buffer.
|
||
|
||
SizeRoutine - a pointer to a function that will find the total size
|
||
a single share will take up in the output buffer. This routine
|
||
is used to check whether we should bother to call the fill
|
||
routine.
|
||
|
||
FillRoutine - a pointer to a function that will fill in the output
|
||
buffer with information from a share.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - results of operation.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSHARE share;
|
||
ULONG totalEntries;
|
||
ULONG entriesRead;
|
||
ULONG bytesRequired;
|
||
|
||
PCHAR fixedStructurePointer;
|
||
PCHAR variableData;
|
||
ULONG blockSize;
|
||
|
||
BOOLEAN bufferOverflow = FALSE;
|
||
BOOLEAN entryReturned = FALSE;
|
||
|
||
PLIST_ENTRY listEntryRoot, listEntry;
|
||
ULONG oldSkipCount;
|
||
ULONG newResumeKey;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Set up local variables.
|
||
//
|
||
|
||
fixedStructurePointer = OutputBuffer;
|
||
variableData = fixedStructurePointer + BufferLength;
|
||
variableData = (PCHAR)((ULONG)variableData & ~1);
|
||
|
||
entriesRead = 0;
|
||
totalEntries = 0;
|
||
bytesRequired = 0;
|
||
|
||
listEntryRoot = &SrvShareHashTable[ HASH_TO_SHARE_INDEX( Srp->Parameters.Get.ResumeHandle >> 16 ) ];
|
||
oldSkipCount = Srp->Parameters.Get.ResumeHandle & 0xff;
|
||
|
||
ACQUIRE_LOCK_SHARED( &SrvShareLock );
|
||
|
||
for( ;
|
||
listEntryRoot < &SrvShareHashTable[ NSHARE_HASH_TABLE ];
|
||
listEntryRoot++, newResumeKey = 0 ) {
|
||
|
||
newResumeKey = (listEntryRoot - SrvShareHashTable) << 16;
|
||
|
||
for( listEntry = listEntryRoot->Flink;
|
||
listEntry != listEntryRoot;
|
||
listEntry = listEntry->Flink, newResumeKey++ ) {
|
||
|
||
if( oldSkipCount ) {
|
||
--oldSkipCount;
|
||
++newResumeKey;
|
||
continue;
|
||
}
|
||
|
||
share = CONTAINING_RECORD( listEntry, SHARE, GlobalShareList );
|
||
|
||
//
|
||
// Call the filter routine to determine whether we should
|
||
// return this share.
|
||
//
|
||
|
||
if ( FilterRoutine( Srp, share ) ) {
|
||
|
||
blockSize = SizeRoutine( Srp, share );
|
||
|
||
totalEntries++;
|
||
bytesRequired += blockSize;
|
||
|
||
//
|
||
// If all the information in the share will fit in the
|
||
// output buffer, write it. Otherwise, indicate that there
|
||
// was an overflow. As soon as an entry doesn't fit, stop
|
||
// putting them in the buffer. This ensures that the resume
|
||
// mechanism will work--retuning partial entries would make
|
||
// it nearly impossible to use the resumability of the APIs,
|
||
// since the caller would have to resume from an imcomplete
|
||
// entry.
|
||
//
|
||
|
||
if ( (ULONG)fixedStructurePointer + blockSize <=
|
||
(ULONG)variableData && !bufferOverflow ) {
|
||
|
||
FillRoutine(
|
||
Srp,
|
||
share,
|
||
(PVOID *)&fixedStructurePointer,
|
||
(LPTSTR *)&variableData
|
||
);
|
||
|
||
entriesRead++;
|
||
newResumeKey++;
|
||
|
||
} else {
|
||
|
||
bufferOverflow = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// Set the information to pass back to the server service.
|
||
//
|
||
|
||
Srp->Parameters.Get.EntriesRead = entriesRead;
|
||
Srp->Parameters.Get.TotalEntries = totalEntries;
|
||
Srp->Parameters.Get.TotalBytesNeeded = bytesRequired;
|
||
|
||
//
|
||
// Return appropriate status.
|
||
//
|
||
|
||
if ( entriesRead == 0 && totalEntries > 0 ) {
|
||
|
||
//
|
||
// Not even a single entry fit.
|
||
//
|
||
|
||
Srp->ErrorCode = NERR_BufTooSmall;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else if ( bufferOverflow ) {
|
||
|
||
//
|
||
// At least one entry fit, but not all of them.
|
||
//
|
||
|
||
Srp->ErrorCode = ERROR_MORE_DATA;
|
||
Srp->Parameters.Get.ResumeHandle = newResumeKey;
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// All entries fit.
|
||
//
|
||
|
||
Srp->ErrorCode = NO_ERROR;
|
||
Srp->Parameters.Get.ResumeHandle = 0;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
} // SrvEnumApiHandler
|