mirror of https://github.com/tongzx/nt5src
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.
1490 lines
36 KiB
1490 lines
36 KiB
/*++
|
|
|
|
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
|
|
|