Source code of Windows XP (NT5)
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

/*++
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