1491 lines
36 KiB
C
1491 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
blkshare.c
|
||
|
||
Abstract:
|
||
|
||
This module implements routines for managing share blocks.
|
||
|
||
Author:
|
||
|
||
Chuck Lenzmeier (chuckl) 4-Oct-1989
|
||
David Treadwell (davidtr)
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "blkshare.tmh"
|
||
#pragma hdrstop
|
||
|
||
#define BugCheckFileId SRV_FILE_BLKSHARE
|
||
|
||
VOID
|
||
GetShareQueryNamePrefix (
|
||
PSHARE Share
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvAllocateShare )
|
||
#pragma alloc_text( PAGE, SrvCloseShare )
|
||
#pragma alloc_text( PAGE, SrvDereferenceShare )
|
||
#pragma alloc_text( PAGE, SrvDereferenceShareForTreeConnect )
|
||
#pragma alloc_text( PAGE, SrvFreeShare )
|
||
#pragma alloc_text( PAGE, SrvReferenceShare )
|
||
#pragma alloc_text( PAGE, SrvReferenceShareForTreeConnect )
|
||
#pragma alloc_text( PAGE, SrvFillInFileSystemName )
|
||
#pragma alloc_text( PAGE, SrvGetShareRootHandle )
|
||
#pragma alloc_text( PAGE, SrvRefreshShareRootHandle )
|
||
#pragma alloc_text( PAGE, GetShareQueryNamePrefix )
|
||
#endif
|
||
|
||
|
||
VOID
|
||
SrvAllocateShare (
|
||
OUT PSHARE *Share,
|
||
IN PUNICODE_STRING ShareName,
|
||
IN PUNICODE_STRING NtPathName,
|
||
IN PUNICODE_STRING DosPathName,
|
||
IN PUNICODE_STRING Remark,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSECURITY_DESCRIPTOR FileSecurityDescriptor OPTIONAL,
|
||
IN SHARE_TYPE ShareType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function allocates a Share Block from the FSP heap.
|
||
|
||
Arguments:
|
||
|
||
Share - Returns a pointer to the share block, or NULL if no
|
||
heap space was available.
|
||
|
||
ShareName - Supplies the name of the share.
|
||
|
||
NtPathName - Supplies a fully qualified directory path in NT format
|
||
to the share.
|
||
|
||
DosPathName - Supplies a fully qualified directory path in DOS
|
||
format to the share.
|
||
|
||
Remark - a comment to store with the share.
|
||
|
||
SecurityDescriptor - security descriptor used for determining whether
|
||
a user can connect to this share.
|
||
|
||
FileSecurityDescriptor - security descriptor used for determining the
|
||
permissions of clients on files in this share.
|
||
|
||
ShareType - Enumerated type indicating type of resource.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
CLONG blockSize;
|
||
PSHARE share;
|
||
ULONG securityDescriptorLength;
|
||
ULONG fileSdLength;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Attempt to allocate from the heap. Note that space for the
|
||
// remark (if any) is allocated separately. Allocate extra space
|
||
// for the security descriptor since it must be longword aligned,
|
||
// and there may be padding between the DOS path name and the
|
||
// security descriptor.
|
||
//
|
||
|
||
securityDescriptorLength = RtlLengthSecurityDescriptor( SecurityDescriptor );
|
||
|
||
blockSize = sizeof(SHARE) +
|
||
ShareName->Length + sizeof(WCHAR) +
|
||
NtPathName->Length + sizeof(WCHAR) +
|
||
DosPathName->Length + sizeof(WCHAR) +
|
||
securityDescriptorLength + sizeof(ULONG);
|
||
|
||
share = ALLOCATE_HEAP( blockSize, BlockTypeShare );
|
||
*Share = share;
|
||
|
||
if ( share == NULL ) {
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvAllocateShare: Unable to allocate %d bytes from heap.",
|
||
blockSize,
|
||
NULL
|
||
);
|
||
return;
|
||
}
|
||
|
||
IF_DEBUG(HEAP) {
|
||
SrvPrint1( "SrvAllocateShare: Allocated share at %p\n", share );
|
||
}
|
||
|
||
RtlZeroMemory( share, blockSize );
|
||
|
||
SET_BLOCK_TYPE_STATE_SIZE( share, BlockTypeShare, BlockStateActive, blockSize );
|
||
share->BlockHeader.ReferenceCount = 2; // allow for Active status
|
||
// and caller's pointer
|
||
|
||
//
|
||
// Save the share type.
|
||
//
|
||
|
||
share->ShareType = ShareType;
|
||
|
||
//
|
||
// Indicate that we've haven't determined the share's query name prefix yet.
|
||
//
|
||
|
||
share->QueryNamePrefixLength = -1;
|
||
|
||
//
|
||
// Put the share name after the share block.
|
||
//
|
||
|
||
share->ShareName.Buffer = (PWSTR)(share + 1);
|
||
share->ShareName.Length = ShareName->Length;
|
||
share->ShareName.MaximumLength =
|
||
(SHORT)(ShareName->Length + sizeof(WCHAR));
|
||
|
||
RtlCopyMemory(
|
||
share->ShareName.Buffer,
|
||
ShareName->Buffer,
|
||
ShareName->Length
|
||
);
|
||
|
||
//
|
||
// Put the NT path name after share name. If no NT path name was
|
||
// specified, just set the path name string to NULL.
|
||
//
|
||
|
||
share->NtPathName.Buffer = (PWSTR)((PCHAR)share->ShareName.Buffer +
|
||
share->ShareName.MaximumLength);
|
||
|
||
share->NtPathName.Length = NtPathName->Length;
|
||
share->NtPathName.MaximumLength = (SHORT)(NtPathName->Length +
|
||
sizeof(WCHAR));
|
||
|
||
RtlCopyMemory(
|
||
share->NtPathName.Buffer,
|
||
NtPathName->Buffer,
|
||
NtPathName->Length
|
||
);
|
||
|
||
|
||
//
|
||
// Put the DOS path name after share name. If no DOS path name was
|
||
// specified, just set the path name string to NULL.
|
||
//
|
||
|
||
share->DosPathName.Buffer = (PWSTR)((PCHAR)share->NtPathName.Buffer +
|
||
share->NtPathName.MaximumLength);
|
||
share->DosPathName.Length = DosPathName->Length;
|
||
share->DosPathName.MaximumLength = (SHORT)(DosPathName->Length +
|
||
sizeof(WCHAR));
|
||
|
||
RtlCopyMemory(
|
||
share->DosPathName.Buffer,
|
||
DosPathName->Buffer,
|
||
DosPathName->Length
|
||
);
|
||
|
||
//
|
||
// Initialize the security RESOURCE for the share
|
||
//
|
||
share->SecurityDescriptorLock = ALLOCATE_NONPAGED_POOL( sizeof(ERESOURCE), BlockTypeShare );
|
||
if( !share->SecurityDescriptorLock )
|
||
{
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvAllocateShare: Unable to allocate %d bytes from NP pool.",
|
||
sizeof(ERESOURCE),
|
||
NULL
|
||
);
|
||
SrvFreeShare( share );
|
||
*Share = NULL;
|
||
return;
|
||
}
|
||
INITIALIZE_LOCK( share->SecurityDescriptorLock, 1, "Share Security Descriptor Lock" );
|
||
|
||
share->SnapShotLock = ALLOCATE_NONPAGED_POOL( sizeof(SRV_LOCK), BlockTypeShare );
|
||
if( !share->SnapShotLock )
|
||
{
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvAllocateShare: Unable to allocate %d bytes from NP pool.",
|
||
sizeof(ERESOURCE),
|
||
NULL
|
||
);
|
||
SrvFreeShare( share );
|
||
*Share = NULL;
|
||
return;
|
||
}
|
||
INITIALIZE_LOCK( share->SnapShotLock, 1, "Share SnapShot Lock" );
|
||
|
||
|
||
|
||
//
|
||
// Allocate space for the remark and copy over the remark. We
|
||
// cannot put the remark after the share block because the remark is
|
||
// settable by NetShareSetInfo. It is possible for the storage
|
||
// required for the remark to increase.
|
||
//
|
||
// If no remark was passed in, do not allocate space. Just set up
|
||
// a null string to describe it.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( Remark ) ) {
|
||
|
||
share->Remark.Buffer = ALLOCATE_HEAP(
|
||
Remark->Length + sizeof(*Remark->Buffer),
|
||
BlockTypeShareRemark
|
||
);
|
||
|
||
if ( share->Remark.Buffer == NULL ) {
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvAllocateShare: Unable to allocate %d bytes from heap.",
|
||
blockSize,
|
||
NULL
|
||
);
|
||
SrvFreeShare( share );
|
||
*Share = NULL;
|
||
return;
|
||
}
|
||
|
||
share->Remark.Length = Remark->Length;
|
||
share->Remark.MaximumLength =
|
||
(SHORT)(Remark->Length + sizeof(*Remark->Buffer));
|
||
|
||
RtlCopyMemory(
|
||
share->Remark.Buffer,
|
||
Remark->Buffer,
|
||
Remark->Length
|
||
);
|
||
|
||
*(PWCH)((PCHAR)share->Remark.Buffer + share->Remark.Length) = 0;
|
||
|
||
} else {
|
||
|
||
RtlInitUnicodeString( &share->Remark, NULL );
|
||
|
||
}
|
||
|
||
//
|
||
// Set up the security descriptor for the share. It must be longword-
|
||
// aligned to be used in various calls.
|
||
//
|
||
|
||
share->SecurityDescriptor =
|
||
(PSECURITY_DESCRIPTOR)( ((ULONG_PTR)share->DosPathName.Buffer +
|
||
share->DosPathName.MaximumLength + 3) & ~3);
|
||
|
||
RtlCopyMemory(
|
||
share->SecurityDescriptor,
|
||
SecurityDescriptor,
|
||
securityDescriptorLength
|
||
);
|
||
|
||
//
|
||
// Set up the file security descriptor for the share. We did not allocate
|
||
// space for the file SD because this is settable and thus cannot have
|
||
// preallocated space.
|
||
//
|
||
|
||
ASSERT( share->FileSecurityDescriptor == NULL );
|
||
|
||
if ( ARGUMENT_PRESENT( FileSecurityDescriptor) ) {
|
||
|
||
fileSdLength = RtlLengthSecurityDescriptor( FileSecurityDescriptor );
|
||
|
||
share->FileSecurityDescriptor = ALLOCATE_HEAP(
|
||
fileSdLength,
|
||
BlockTypeShareSecurityDescriptor
|
||
);
|
||
|
||
if ( share->FileSecurityDescriptor == NULL ) {
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvAllocateShare: Unable to allocate %d bytes from heap.",
|
||
fileSdLength,
|
||
NULL
|
||
);
|
||
|
||
SrvFreeShare( share );
|
||
*Share = NULL;
|
||
return;
|
||
}
|
||
|
||
RtlCopyMemory(
|
||
share->FileSecurityDescriptor,
|
||
FileSecurityDescriptor,
|
||
fileSdLength
|
||
);
|
||
}
|
||
|
||
//
|
||
// Indicate whether or not this share potentially contains the system directory.
|
||
//
|
||
if( DosPathName->Length != 0 && SrvSystemRoot.Length != 0 ) {
|
||
|
||
UNICODE_STRING tmpString;
|
||
|
||
if( DosPathName->Length == SrvSystemRoot.Length ) {
|
||
//
|
||
// If the two names are the same, then the share is exactly at the system
|
||
// directory. All files within this share are system files!
|
||
//
|
||
if( RtlCompareUnicodeString( DosPathName, &SrvSystemRoot, TRUE ) == 0 ) {
|
||
share->PotentialSystemFile = TRUE;
|
||
}
|
||
|
||
} else if( DosPathName->Length < SrvSystemRoot.Length ) {
|
||
//
|
||
// If the share path is a substring of the system root path...
|
||
//
|
||
if( DosPathName->Buffer[ DosPathName->Length/sizeof(WCHAR) - 1 ] ==
|
||
OBJ_NAME_PATH_SEPARATOR ||
|
||
SrvSystemRoot.Buffer[ DosPathName->Length/sizeof(WCHAR) ] ==
|
||
OBJ_NAME_PATH_SEPARATOR ) {
|
||
|
||
//
|
||
// .. and if the share path is for the root of the drive...
|
||
//
|
||
tmpString = SrvSystemRoot;
|
||
tmpString.Length = DosPathName->Length;
|
||
//
|
||
// ... and if the system root is on the same drive...
|
||
//
|
||
if( RtlCompareUnicodeString( DosPathName, &tmpString, TRUE ) == 0 ) {
|
||
//
|
||
// ... then we potentially are accessing system files
|
||
//
|
||
share->PotentialSystemFile = TRUE;
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// If the system root path is a substring of the share path, then every file
|
||
// within the share is a system file.
|
||
//
|
||
if( DosPathName->Buffer[ SrvSystemRoot.Length / sizeof( WCHAR ) ] ==
|
||
OBJ_NAME_PATH_SEPARATOR ) {
|
||
|
||
tmpString = *DosPathName;
|
||
tmpString.Length = SrvSystemRoot.Length;
|
||
|
||
if( RtlCompareUnicodeString( DosPathName, &tmpString, TRUE ) == 0 ) {
|
||
//
|
||
// Every file in the share is a system file
|
||
//
|
||
share->PotentialSystemFile = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Initialize the share's tree connect list.
|
||
//
|
||
|
||
InitializeListHead( &share->TreeConnectList );
|
||
|
||
//
|
||
// Initialize the SnapShot list
|
||
//
|
||
InitializeListHead( &share->SnapShots );
|
||
|
||
|
||
INITIALIZE_REFERENCE_HISTORY( share );
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.ShareInfo.Allocations );
|
||
|
||
#ifdef SRVCATCH
|
||
SrvIsMonitoredShare( share );
|
||
#endif
|
||
|
||
return;
|
||
|
||
} // SrvAllocateShare
|
||
|
||
|
||
VOID
|
||
SrvCloseShare (
|
||
IN PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function closes a share.
|
||
|
||
Arguments:
|
||
|
||
Share - Supplies a pointer to a share Block
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// If the share hasn't already been closed, do so now.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(Share) == BlockStateActive ) {
|
||
|
||
IF_DEBUG(BLOCK1) SrvPrint1( "Closing share at %p\n", Share );
|
||
|
||
SET_BLOCK_STATE( Share, BlockStateClosing );
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// Close all the tree connects on this share.
|
||
//
|
||
|
||
SrvCloseTreeConnectsOnShare( Share );
|
||
|
||
//
|
||
// Dereference the share--this will cause it to be freed when
|
||
// all other references are closed.
|
||
//
|
||
|
||
SrvDereferenceShare( Share );
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.ShareInfo.Closes );
|
||
|
||
} else {
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvCloseShare
|
||
|
||
|
||
VOID
|
||
SrvDereferenceShare (
|
||
IN PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function decrements the reference count on a share. If the
|
||
reference count goes to zero, the share block is deleted.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Enter a critical section and decrement the reference count on the
|
||
// block.
|
||
//
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
IF_DEBUG(REFCNT) {
|
||
SrvPrint2( "Dereferencing share %p; old refcnt %lx\n",
|
||
Share, Share->BlockHeader.ReferenceCount );
|
||
}
|
||
|
||
ASSERT( GET_BLOCK_TYPE(Share) == BlockTypeShare );
|
||
ASSERT( (LONG)Share->BlockHeader.ReferenceCount > 0 );
|
||
UPDATE_REFERENCE_HISTORY( Share, TRUE );
|
||
|
||
if ( --Share->BlockHeader.ReferenceCount == 0 ) {
|
||
|
||
//
|
||
// The new reference count is 0, meaning that it's time to
|
||
// delete this block.
|
||
//
|
||
|
||
ASSERT( Share->CurrentUses == 0 );
|
||
ASSERT( GET_BLOCK_STATE( Share ) != BlockStateActive );
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// Remove the block from the global list.
|
||
//
|
||
|
||
SrvRemoveShare( Share );
|
||
|
||
//
|
||
// Free the share block.
|
||
//
|
||
|
||
SrvFreeShare( Share );
|
||
|
||
} else {
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvDereferenceShare
|
||
|
||
|
||
VOID
|
||
SrvDereferenceShareForTreeConnect (
|
||
PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function decrements the reference count on a share block for
|
||
the referenced pointer in a tree connect block. If this is the last
|
||
reference by a tree connect to the share, the share root directory
|
||
is closed.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// Update the count of tree connects on the share.
|
||
//
|
||
|
||
ASSERT( Share->CurrentUses > 0 );
|
||
|
||
Share->CurrentUses--;
|
||
|
||
//
|
||
// If this is the last reference by a tree connect to the share and
|
||
// this is a disk share, close the share root directory handle.
|
||
//
|
||
|
||
if ( Share->CurrentUses == 0 && Share->ShareType == ShareTypeDisk ) {
|
||
if ( !Share->Removable ) {
|
||
SRVDBG_RELEASE_HANDLE( Share->RootDirectoryHandle, "RTD", 5, Share );
|
||
SrvNtClose( Share->RootDirectoryHandle, FALSE );
|
||
}
|
||
Share->RootDirectoryHandle = NULL;
|
||
}
|
||
|
||
//
|
||
// Dereference the share and return.
|
||
//
|
||
|
||
SrvDereferenceShare( Share );
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
return;
|
||
|
||
} // SrvDereferenceShareForTreeConnect
|
||
|
||
VOID
|
||
SrvFreeShare (
|
||
IN PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a Share Block to the FSP heap.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY shareList;
|
||
|
||
PAGED_CODE( );
|
||
|
||
DEBUG SET_BLOCK_TYPE_STATE_SIZE( Share, BlockTypeGarbage, BlockStateDead, -1 );
|
||
DEBUG Share->BlockHeader.ReferenceCount = (ULONG)-1;
|
||
TERMINATE_REFERENCE_HISTORY( Share );
|
||
|
||
// Delete all the SnapShot shares
|
||
shareList = Share->SnapShots.Flink;
|
||
while( shareList != &Share->SnapShots )
|
||
{
|
||
PSHARE_SNAPSHOT snapShare = CONTAINING_RECORD( shareList, SHARE_SNAPSHOT, SnapShotList );
|
||
shareList = shareList->Flink;
|
||
SrvSnapRemoveShare( snapShare );
|
||
}
|
||
|
||
//
|
||
// Remove storage for the remark, if any.
|
||
//
|
||
|
||
if ( Share->Remark.Buffer != NULL ) {
|
||
FREE_HEAP( Share->Remark.Buffer );
|
||
}
|
||
|
||
//
|
||
// Remove storage for the file security descriptor, if any.
|
||
//
|
||
|
||
if ( Share->FileSecurityDescriptor != NULL ) {
|
||
FREE_HEAP( Share->FileSecurityDescriptor );
|
||
}
|
||
|
||
//
|
||
// Cleanup the file security descriptor lock
|
||
//
|
||
if( Share->SecurityDescriptorLock )
|
||
{
|
||
DELETE_LOCK( Share->SecurityDescriptorLock );
|
||
DEALLOCATE_NONPAGED_POOL( Share->SecurityDescriptorLock );
|
||
}
|
||
|
||
//
|
||
// Cleanup the SnapShot lock
|
||
//
|
||
if( Share->SnapShotLock )
|
||
{
|
||
DELETE_LOCK( Share->SnapShotLock );
|
||
DEALLOCATE_NONPAGED_POOL( Share->SnapShotLock );
|
||
}
|
||
|
||
//
|
||
// Remove storage for the filesystem name
|
||
//
|
||
|
||
if ( Share->Type.FileSystem.Name.Buffer != NULL ) {
|
||
FREE_HEAP( Share->Type.FileSystem.Name.Buffer );
|
||
}
|
||
|
||
FREE_HEAP( Share );
|
||
IF_DEBUG(HEAP) {
|
||
SrvPrint1( "SrvFreeShare: Freed share block at %p\n", Share );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.ShareInfo.Frees );
|
||
|
||
return;
|
||
|
||
} // SrvFreeShare
|
||
|
||
|
||
VOID
|
||
SrvReferenceShare (
|
||
PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function increments the reference count on a share block.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Enter a critical section and increment the reference count on the
|
||
// share.
|
||
//
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
ASSERT( (LONG)Share->BlockHeader.ReferenceCount > 0 );
|
||
ASSERT( GET_BLOCK_TYPE(Share) == BlockTypeShare );
|
||
// ASSERT( GET_BLOCK_STATE(Share) == BlockStateActive );
|
||
UPDATE_REFERENCE_HISTORY( Share, FALSE );
|
||
|
||
Share->BlockHeader.ReferenceCount++;
|
||
|
||
IF_DEBUG(REFCNT) {
|
||
SrvPrint2( "Referencing share %p; new refcnt %lx\n",
|
||
Share, Share->BlockHeader.ReferenceCount );
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
|
||
return;
|
||
|
||
} // SrvReferenceShare
|
||
|
||
|
||
NTSTATUS
|
||
SrvReferenceShareForTreeConnect (
|
||
PSHARE Share
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function increments the reference count on a share block for
|
||
the referenced pointer in a tree connect block. If this is the
|
||
first tree connect to reference the share, the share root directory
|
||
is opened.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK iosb;
|
||
PFILE_FS_ATTRIBUTE_INFORMATION attributeInfo;
|
||
CHAR buffer[ FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName ) + 32 ];
|
||
PVOID allocatedBuffer = NULL;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
//
|
||
// Update the count of tree connects on the share.
|
||
//
|
||
|
||
Share->CurrentUses++;
|
||
|
||
//
|
||
// Check if this is the first tree connect to the share.
|
||
//
|
||
|
||
if ( Share->CurrentUses > 1 ) {
|
||
|
||
//
|
||
// There are already open tree connects on the share. Just
|
||
// reference the share and return.
|
||
//
|
||
|
||
SrvReferenceShare( Share );
|
||
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// If this is not a disk share, then we do not need to open the
|
||
// share root directory, so reference the share and return.
|
||
//
|
||
|
||
if ( Share->ShareType != ShareTypeDisk || Share->Removable ) {
|
||
SrvReferenceShare( Share );
|
||
goto done;
|
||
}
|
||
|
||
#ifdef INCLUDE_SMB_IFMODIFIED
|
||
//
|
||
// by default, we'll assume USN capable until we get back an "unsupported"
|
||
// from the filesystem.
|
||
//
|
||
|
||
Share->UsnCapable = TRUE;
|
||
#endif
|
||
|
||
//
|
||
// This is the first tree connect, so we need to open the share root
|
||
// directory. Future opens of files within the share will be relative
|
||
// to the root of the share.
|
||
//
|
||
Share->RootDirectoryHandle = NULL;
|
||
|
||
if( SrvRefreshShareRootHandle( Share, &status ) == FALSE ) {
|
||
Share->CurrentUses--;
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// All is well -- we are now going to return STATUS_SUCCESS no matter what!
|
||
//
|
||
|
||
SrvReferenceShare( Share );
|
||
|
||
if ( Share->QueryNamePrefixLength == -1 ) {
|
||
|
||
//
|
||
// Query the name associated with the share root directory.
|
||
// The prefix is removed whenever the name of a file in the
|
||
// share is queried. (The logical root must be preserved
|
||
// for remote clients.)
|
||
//
|
||
|
||
GetShareQueryNamePrefix( Share );
|
||
}
|
||
|
||
//
|
||
// Now extract the name of the file system, so that it can be returned
|
||
// in the TreeConnectAndX response.
|
||
//
|
||
//
|
||
if ( Share->Type.FileSystem.Name.Buffer == NULL ) {
|
||
|
||
attributeInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)buffer;
|
||
|
||
status = NtQueryVolumeInformationFile(
|
||
Share->RootDirectoryHandle,
|
||
&iosb,
|
||
attributeInfo,
|
||
sizeof( buffer ),
|
||
FileFsAttributeInformation
|
||
);
|
||
|
||
if ( status == STATUS_BUFFER_OVERFLOW ) {
|
||
|
||
//
|
||
// The file system information was too large to fit in our small
|
||
// stack buffer. Allocate an ample buffer and try again.
|
||
//
|
||
|
||
allocatedBuffer = ALLOCATE_HEAP(
|
||
FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION,FileSystemName) +
|
||
attributeInfo->FileSystemNameLength,
|
||
BlockTypeVolumeInformation
|
||
);
|
||
|
||
if ( allocatedBuffer == NULL ) {
|
||
|
||
//
|
||
// Couldn't allocate the buffer. Give up.
|
||
//
|
||
|
||
goto done;
|
||
}
|
||
|
||
status = NtQueryVolumeInformationFile(
|
||
Share->RootDirectoryHandle,
|
||
&iosb,
|
||
allocatedBuffer,
|
||
FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) +
|
||
attributeInfo->FileSystemNameLength,
|
||
FileFsAttributeInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
goto done;
|
||
}
|
||
|
||
attributeInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)allocatedBuffer;
|
||
|
||
} else if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// Some other, unexpected error occured. Give up.
|
||
//
|
||
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Fill in the file system name
|
||
//
|
||
|
||
SrvFillInFileSystemName(
|
||
Share,
|
||
attributeInfo->FileSystemName,
|
||
attributeInfo->FileSystemNameLength
|
||
);
|
||
}
|
||
|
||
done:
|
||
|
||
if ( allocatedBuffer != NULL ) {
|
||
FREE_HEAP( allocatedBuffer );
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
return STATUS_SUCCESS;
|
||
|
||
} // SrvReferenceShareForTreeConnect
|
||
|
||
BOOLEAN
|
||
SrvCheckNtfsForUniqueFiles()
|
||
{
|
||
ULONG lengthNeeded;
|
||
NTSTATUS status;
|
||
HANDLE keyHandle;
|
||
UNICODE_STRING unicodeParamPath;
|
||
UNICODE_STRING unicodeKeyName;
|
||
OBJECT_ATTRIBUTES objAttributes;
|
||
PKEY_VALUE_FULL_INFORMATION infoBuffer = NULL;
|
||
|
||
RtlInitUnicodeString( &unicodeParamPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem" );
|
||
RtlInitUnicodeString( &unicodeKeyName, L"NtfsDisable8dot3NameCreation" );
|
||
|
||
InitializeObjectAttributes(
|
||
&objAttributes,
|
||
&unicodeParamPath,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenKey(
|
||
&keyHandle,
|
||
KEY_QUERY_VALUE,
|
||
&objAttributes
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
status = ZwQueryValueKey(
|
||
keyHandle,
|
||
&unicodeKeyName,
|
||
KeyValueFullInformation,
|
||
NULL,
|
||
0,
|
||
&lengthNeeded
|
||
);
|
||
|
||
if ( status != STATUS_BUFFER_TOO_SMALL ) {
|
||
NtClose( keyHandle );
|
||
return FALSE;
|
||
}
|
||
|
||
infoBuffer = ALLOCATE_NONPAGED_POOL( lengthNeeded, BlockTypeDataBuffer );
|
||
|
||
if ( infoBuffer == NULL ) {
|
||
NtClose( keyHandle );
|
||
return FALSE;
|
||
}
|
||
|
||
status = ZwQueryValueKey(
|
||
keyHandle,
|
||
&unicodeKeyName,
|
||
KeyValueFullInformation,
|
||
infoBuffer,
|
||
lengthNeeded,
|
||
&lengthNeeded
|
||
);
|
||
|
||
NtClose( keyHandle );
|
||
|
||
if( NT_SUCCESS(status) )
|
||
{
|
||
if( infoBuffer->DataLength == sizeof(DWORD) )
|
||
{
|
||
if( *((LPDWORD)(((PBYTE)infoBuffer)+infoBuffer->DataOffset)) )
|
||
{
|
||
DEALLOCATE_NONPAGED_POOL( infoBuffer );
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
DEALLOCATE_NONPAGED_POOL( infoBuffer );
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
VOID
|
||
SrvFillInFileSystemName (
|
||
IN PSHARE Share,
|
||
IN PWSTR FileSystemName,
|
||
IN ULONG FileSystemNameLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function fills in the stores the given file system name into the
|
||
share block.
|
||
|
||
Arguments:
|
||
|
||
Share - Address of share
|
||
|
||
FileSystemName - A string containing the name of the file system
|
||
|
||
FileSystemNameLength - Length of the above string
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// If we have a FATxx filesystem, we need to return FAT back to the clients,
|
||
// else they will not believe they can create long names. I know, I know....
|
||
//
|
||
if( (FileSystemNameLength > 3 * sizeof( WCHAR ) ) &&
|
||
(FileSystemName[0] == L'F' || FileSystemName[0] == L'f') &&
|
||
(FileSystemName[1] == L'A' || FileSystemName[0] == L'a') &&
|
||
(FileSystemName[2] == L'T' || FileSystemName[0] == L't') ) {
|
||
|
||
FileSystemNameLength = 3 * sizeof( WCHAR );
|
||
FileSystemName[3] = UNICODE_NULL;
|
||
}
|
||
|
||
#ifdef INCLUDE_SMB_PERSISTENT
|
||
if( (FileSystemNameLength >= 3 * sizeof( WCHAR ) ) &&
|
||
(FileSystemName[0] == L'F' || FileSystemName[0] == L'f') &&
|
||
(FileSystemName[1] == L'A' || FileSystemName[0] == L'a') &&
|
||
(FileSystemName[2] == L'T' || FileSystemName[0] == L't') ) {
|
||
|
||
//
|
||
// persistent handles are not allowed for fat volumes
|
||
//
|
||
|
||
Share->AllowPersistentHandles = FALSE;
|
||
}
|
||
#endif
|
||
|
||
if( (FileSystemNameLength == 4*sizeof(WCHAR)) &&
|
||
!STRNICMP(FileSystemName,L"NTFS",4) )
|
||
{
|
||
if( SrvCheckNtfsForUniqueFiles() )
|
||
{
|
||
Share->UniqueNames = TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Allocate enough storage for the ANSI and Unicode representations.
|
||
//
|
||
|
||
Share->Type.FileSystem.Name.Length = (USHORT)FileSystemNameLength;
|
||
Share->Type.FileSystem.Name.MaximumLength =
|
||
(USHORT)(FileSystemNameLength + sizeof( UNICODE_NULL ));
|
||
|
||
Share->Type.FileSystem.Name.Buffer = FileSystemName;
|
||
Share->Type.FileSystem.OemName.MaximumLength =
|
||
(USHORT)RtlUnicodeStringToOemSize( &Share->Type.FileSystem.Name );
|
||
|
||
Share->Type.FileSystem.Name.Buffer =
|
||
ALLOCATE_HEAP(
|
||
Share->Type.FileSystem.Name.MaximumLength +
|
||
Share->Type.FileSystem.OemName.MaximumLength,
|
||
BlockTypeFSName
|
||
);
|
||
|
||
if ( Share->Type.FileSystem.Name.Buffer == NULL) {
|
||
return;
|
||
}
|
||
|
||
|
||
RtlCopyMemory(
|
||
Share->Type.FileSystem.Name.Buffer,
|
||
FileSystemName,
|
||
FileSystemNameLength
|
||
);
|
||
|
||
//
|
||
// Generate the OEM version of the string to return to non-unicode
|
||
// clients.
|
||
//
|
||
|
||
Share->Type.FileSystem.OemName.Buffer =
|
||
(PCHAR)Share->Type.FileSystem.Name.Buffer +
|
||
Share->Type.FileSystem.Name.MaximumLength;
|
||
|
||
RtlUnicodeStringToOemString(
|
||
&Share->Type.FileSystem.OemName,
|
||
&Share->Type.FileSystem.Name,
|
||
FALSE
|
||
);
|
||
|
||
//
|
||
// Append a NUL character to the strings.
|
||
//
|
||
|
||
{
|
||
PCHAR endOfBuffer;
|
||
|
||
endOfBuffer = (PCHAR)Share->Type.FileSystem.Name.Buffer +
|
||
Share->Type.FileSystem.Name.Length;
|
||
|
||
*(PWCH)endOfBuffer = UNICODE_NULL;
|
||
|
||
Share->Type.FileSystem.Name.Length += sizeof( UNICODE_NULL );
|
||
}
|
||
|
||
Share->Type.FileSystem.OemName.Length++;
|
||
|
||
return;
|
||
|
||
} // SrvFillInFileSystemName
|
||
|
||
|
||
NTSTATUS
|
||
SrvGetShareRootHandle (
|
||
IN PSHARE Share
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the root handle for a given share. If the
|
||
root has been opened, return the existing handle. If not, open
|
||
the share root directory and return the handle obtained.
|
||
|
||
Arguments:
|
||
|
||
Share - The share for which the root directory handle is to be returned.
|
||
|
||
Return Value:
|
||
|
||
Status of request.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE( );
|
||
|
||
if ( Share->ShareType != ShareTypeDisk ) {
|
||
return STATUS_INVALID_DEVICE_REQUEST;
|
||
}
|
||
|
||
if ( Share->Removable ) {
|
||
|
||
ACQUIRE_LOCK( &SrvShareLock );
|
||
|
||
++Share->CurrentRootHandleReferences;
|
||
|
||
//
|
||
// This is the first open
|
||
//
|
||
|
||
if ( Share->CurrentRootHandleReferences == 1 ) {
|
||
|
||
ASSERT( Share->RootDirectoryHandle == NULL );
|
||
|
||
//
|
||
// Make sure we have a good handle to the media
|
||
//
|
||
SrvRefreshShareRootHandle( Share, &status );
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
SrvReferenceShare( Share );
|
||
|
||
if ( Share->QueryNamePrefixLength == -1 ) {
|
||
|
||
//
|
||
// Query the name associated with the share root directory.
|
||
// The prefix is removed whenever the name of a file in the
|
||
// share is queried. (The logical root must be preserved
|
||
// for remote clients.)
|
||
//
|
||
|
||
GetShareQueryNamePrefix( Share );
|
||
}
|
||
|
||
} else {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvGetShareRootHandle: NtOpenFile failed %x.\n",
|
||
status ));
|
||
}
|
||
|
||
Share->CurrentRootHandleReferences--;
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvShareLock );
|
||
}
|
||
|
||
return status;
|
||
|
||
} // SrvGetShareRootHandle
|
||
|
||
BOOLEAN
|
||
SrvRefreshShareRootHandle (
|
||
IN PSHARE Share,
|
||
OUT PNTSTATUS Status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tries to obtain a fresh share root handle, replacing the
|
||
one that was there. The handle will need to be refreshed if, for instance,
|
||
the volume has been dismounted and remounted.
|
||
|
||
Arguments:
|
||
|
||
Share - The share for which the root directory handle is to be refreshed.
|
||
|
||
Returns:
|
||
TRUE - if a new handle was generated
|
||
FALSE - if a new handle was not generated
|
||
|
||
--*/
|
||
{
|
||
HANDLE h;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK iosb;
|
||
PFILE_OBJECT fileObject;
|
||
PDEVICE_OBJECT deviceObject;
|
||
|
||
PAGED_CODE();
|
||
|
||
*Status = STATUS_SUCCESS;
|
||
|
||
if( Share->ShareType != ShareTypeDisk ) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Open the root directory of the share. Future opens of files within
|
||
// the share will be relative to the root of the share.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&Share->NtPathName,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
*Status = NtOpenFile(
|
||
&h,
|
||
FILE_TRAVERSE,
|
||
&objectAttributes,
|
||
&iosb,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_DIRECTORY_FILE
|
||
);
|
||
|
||
if( !NT_SUCCESS( *Status ) ) {
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// Check the irp stack size needed to access this share.
|
||
// If it is bigger than what we have allocated, fail
|
||
// this share.
|
||
//
|
||
|
||
*Status = SrvVerifyDeviceStackSize(
|
||
h,
|
||
FALSE,
|
||
&fileObject,
|
||
&deviceObject,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS( *Status )) {
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"SrvReferenceShareForTreeConnect: Verify Device Stack Size failed: %X\n",
|
||
*Status,
|
||
NULL
|
||
);
|
||
|
||
NtClose( h );
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// This handle looks suitable for use. Set it to be the handle
|
||
// for this share
|
||
//
|
||
h = (PRFCB)InterlockedExchangePointer( &Share->RootDirectoryHandle, h );
|
||
|
||
//
|
||
// If we have picked up a different handle, we need to close it
|
||
//
|
||
if( h != 0 ) {
|
||
NtClose( h );
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID
|
||
GetShareQueryNamePrefix (
|
||
IN PSHARE Share
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queries the name associated with the share root
|
||
directory. The prefix is removed whenever the name of a file in the
|
||
share is queried. (The logical root must be preserved for remote
|
||
clients.) For example, if the root of the share X is c:\shares\x,
|
||
then for a query of \\server\x\y, the file system will return
|
||
\shares\x\y, and we need to remove \shares\x and return just \y.
|
||
|
||
It is not sufficient to just remove the local path (e.g.,
|
||
\shares\x), because the file system may have a different idea of the
|
||
name of the root directory. For example, the Netware client
|
||
redirector prefixes the name with volume information from the
|
||
Netware server. So we have to query the filesystem's idea of the
|
||
name of the root to know what to strip off.
|
||
|
||
Arguments:
|
||
|
||
Share - The share for which the query name prefix length is desired.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK iosb;
|
||
ULONG localBuffer[ (FIELD_OFFSET(FILE_NAME_INFORMATION,FileName) + 20) / sizeof( ULONG ) ];
|
||
PFILE_NAME_INFORMATION nameInfo;
|
||
ULONG nameInfoLength;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Do a short query to get the length of the name. This query will
|
||
// fail with STATUS_BUFFER_OVERFLOW unless the path to the share
|
||
// root is short (10 characters or less).
|
||
//
|
||
|
||
nameInfo = (PFILE_NAME_INFORMATION)localBuffer;
|
||
nameInfoLength = sizeof(localBuffer);
|
||
|
||
status = NtQueryInformationFile(
|
||
Share->RootDirectoryHandle,
|
||
&iosb,
|
||
nameInfo,
|
||
nameInfoLength,
|
||
FileNameInformation
|
||
);
|
||
|
||
if ( status == STATUS_BUFFER_OVERFLOW ) {
|
||
|
||
//
|
||
// We got an expected buffer overflow error. Allocate a buffer
|
||
// to hold the entire file name and redo the query.
|
||
//
|
||
|
||
nameInfoLength = sizeof(FILE_NAME_INFORMATION) + nameInfo->FileNameLength;
|
||
nameInfo = ALLOCATE_HEAP( nameInfoLength, BlockTypeNameInfo );
|
||
|
||
if ( nameInfo == NULL ) {
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
} else {
|
||
status = NtQueryInformationFile(
|
||
Share->RootDirectoryHandle,
|
||
&iosb,
|
||
nameInfo,
|
||
nameInfoLength,
|
||
FileNameInformation
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// We have the name. The length of this name is the length we
|
||
// want to strip from each query, unless the last character of
|
||
// the name is \, in which case we need to strip up to, but not
|
||
// including, the \.
|
||
//
|
||
|
||
Share->QueryNamePrefixLength = nameInfo->FileNameLength;
|
||
if ( nameInfo->FileName[nameInfo->FileNameLength/sizeof(WCHAR) - 1] == L'\\') {
|
||
Share->QueryNamePrefixLength -= sizeof(WCHAR);
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// An unexpected error occurred. Just set the prefix length to 0.
|
||
//
|
||
|
||
Share->QueryNamePrefixLength = 0;
|
||
|
||
}
|
||
|
||
//
|
||
// If we allocated a temporary buffer, free it now.
|
||
//
|
||
|
||
if ( (nameInfo != NULL) && (nameInfo != (PFILE_NAME_INFORMATION)localBuffer) ) {
|
||
FREE_HEAP( nameInfo );
|
||
}
|
||
|
||
return;
|
||
|
||
} // GetShareQueryNamePrefix
|
||
|