5199 lines
147 KiB
C
5199 lines
147 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
|
||
Module Name:
|
||
|
||
open.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routine called by processing routines for
|
||
the various flavors of Open SMBs (SrvCreateFile) and its
|
||
subroutines.
|
||
|
||
!!! Need to use SrvEnableFcbOpens to determine whether to fold FCB
|
||
opens together.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 23-Nov-1989
|
||
Chuck Lenzmeier (chuckl)
|
||
Manny Weiser (mannyw)
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "open.tmh"
|
||
#pragma hdrstop
|
||
|
||
#define BugCheckFileId SRV_FILE_OPEN
|
||
|
||
//
|
||
// Local functions
|
||
//
|
||
|
||
NTSTATUS
|
||
DoNormalOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList,
|
||
IN OPLOCK_TYPE RequestedOplockType
|
||
);
|
||
|
||
NTSTATUS
|
||
DoCompatibilityOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList,
|
||
IN OPLOCK_TYPE RequestedOplockType
|
||
);
|
||
|
||
|
||
NTSTATUS
|
||
DoFcbOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList
|
||
);
|
||
|
||
NTSTATUS
|
||
DoCommDeviceOpen (
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbOpenFunction,
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList
|
||
);
|
||
|
||
PTABLE_ENTRY
|
||
FindAndClaimFileTableEntry (
|
||
IN PCONNECTION Connection,
|
||
OUT PSHORT FidIndex
|
||
);
|
||
|
||
NTSTATUS
|
||
CompleteOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
IN PLFCB ExistingLfcb OPTIONAL,
|
||
IN HANDLE FileHandle OPTIONAL,
|
||
IN PACCESS_MASK RemoteGrantedAccess OPTIONAL,
|
||
IN ULONG ShareAccess,
|
||
IN ULONG FileMode,
|
||
IN BOOLEAN CompatibilityOpen,
|
||
IN BOOLEAN FcbOpen,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList
|
||
);
|
||
|
||
BOOLEAN SRVFASTCALL
|
||
MapCompatibilityOpen(
|
||
IN PUNICODE_STRING FileName,
|
||
IN OUT PUSHORT SmbDesiredAccess
|
||
);
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapDesiredAccess(
|
||
IN USHORT SmbDesiredAccess,
|
||
OUT PACCESS_MASK NtDesiredAccess
|
||
);
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapOpenFunction(
|
||
IN USHORT SmbOpenFunction,
|
||
OUT PULONG CreateDisposition
|
||
);
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapShareAccess(
|
||
IN USHORT SmbDesiredAccess,
|
||
OUT PULONG NtShareAccess
|
||
);
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapCacheHints(
|
||
IN USHORT SmbDesiredAccess,
|
||
IN OUT PULONG NtCreateFlags
|
||
);
|
||
|
||
BOOLEAN
|
||
SetDefaultPipeMode (
|
||
IN HANDLE FileHandle
|
||
);
|
||
|
||
NTSTATUS
|
||
RemapPipeName(
|
||
IN PANSI_STRING AnsiServerName OPTIONAL,
|
||
IN PUNICODE_STRING ServerName OPTIONAL,
|
||
IN OUT PUNICODE_STRING NewRelativeName,
|
||
OUT PBOOLEAN Remapped
|
||
);
|
||
|
||
BOOLEAN
|
||
SrvFailMdlReadDev (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN ULONG LockKey,
|
||
OUT PMDL *MdlChain,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
);
|
||
|
||
BOOLEAN
|
||
SrvFailPrepareMdlWriteDev(
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN ULONG LockKey,
|
||
OUT PMDL *MdlChain,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, RemapPipeName )
|
||
#pragma alloc_text( PAGE, SrvCreateFile )
|
||
#pragma alloc_text( PAGE, DoNormalOpen )
|
||
#pragma alloc_text( PAGE, DoCompatibilityOpen )
|
||
#pragma alloc_text( PAGE, DoFcbOpen )
|
||
#pragma alloc_text( PAGE, CompleteOpen )
|
||
#pragma alloc_text( PAGE, MapCompatibilityOpen )
|
||
#pragma alloc_text( PAGE, MapDesiredAccess )
|
||
#pragma alloc_text( PAGE, MapOpenFunction )
|
||
#pragma alloc_text( PAGE, MapCacheHints )
|
||
#pragma alloc_text( PAGE, MapShareAccess )
|
||
#pragma alloc_text( PAGE, SrvNtCreateFile )
|
||
#pragma alloc_text( PAGE, SetDefaultPipeMode )
|
||
#pragma alloc_text( PAGE8FIL, FindAndClaimFileTableEntry )
|
||
#pragma alloc_text( PAGE, SrvFailMdlReadDev )
|
||
#pragma alloc_text( PAGE, SrvFailPrepareMdlWriteDev )
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
RemapPipeName(
|
||
IN PANSI_STRING AnsiServerName,
|
||
IN PUNICODE_STRING UnicodeName,
|
||
IN OUT PUNICODE_STRING NewRelativeName,
|
||
OUT PBOOLEAN Remapped
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Remaps a pipe name by prepending "$$\<AnsiServerName>\" to the
|
||
relative pipe name (without the trailing spaces in AnsiServerName).
|
||
|
||
Arguments:
|
||
|
||
AnsiServerName - NetBIOS server name, or
|
||
UnicodeName - UNICODE server name
|
||
|
||
NewRelativeName - pointer to pipe name; on successful return,
|
||
points to newly allocated memory for remapped pipe name.
|
||
This memory must be freed by the caller.
|
||
|
||
Remapped - set to TRUE if the name was remapped
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
UNICODE_STRING OldRelativeName;
|
||
UNICODE_STRING UnicodeServerName;
|
||
ULONG nameLength;
|
||
PWCH nextLocation;
|
||
NTSTATUS status;
|
||
int i;
|
||
|
||
PAGED_CODE();
|
||
|
||
*Remapped = FALSE;
|
||
|
||
//
|
||
// Do not remap the pipe name if it is in our SrvNoRemapPipeNames list
|
||
//
|
||
ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
|
||
|
||
for ( i = 0; SrvNoRemapPipeNames[i] != NULL ; i++ ) {
|
||
|
||
UNICODE_STRING NoRemap;
|
||
|
||
RtlInitUnicodeString( &NoRemap, SrvNoRemapPipeNames[i] );
|
||
|
||
if( RtlCompareUnicodeString( &NoRemap, NewRelativeName, TRUE ) == 0 ) {
|
||
|
||
//
|
||
// This is a pipe name that we are not supposed to remap. We
|
||
// return STATUS_SUCCESS, but indicate to our caller that we did
|
||
// not remap the pipe name
|
||
//
|
||
RELEASE_LOCK( &SrvConfigurationLock );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvConfigurationLock );
|
||
|
||
//
|
||
// Save RelativeName before changing it to point to new memory.
|
||
//
|
||
|
||
OldRelativeName = *NewRelativeName;
|
||
|
||
//
|
||
// Trim the trailing spaces from the server name.
|
||
// We know that the last character is a space,
|
||
// because server name is a netbios name.
|
||
//
|
||
|
||
if( !ARGUMENT_PRESENT( UnicodeName ) ) {
|
||
|
||
USHORT SavedLength;
|
||
|
||
ASSERT(AnsiServerName->Length == 16);
|
||
ASSERT(AnsiServerName->Buffer[AnsiServerName->Length - 1] == ' ');
|
||
|
||
SavedLength = AnsiServerName->Length;
|
||
|
||
while (AnsiServerName->Length > 0 &&
|
||
AnsiServerName->Buffer[AnsiServerName->Length - 1] == ' ') {
|
||
|
||
AnsiServerName->Length--;
|
||
}
|
||
|
||
//
|
||
// Convert the server name from ANSI to Unicode.
|
||
//
|
||
status = RtlAnsiStringToUnicodeString(
|
||
&UnicodeServerName,
|
||
AnsiServerName,
|
||
TRUE);
|
||
|
||
AnsiServerName->Length = SavedLength;
|
||
|
||
if (! NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
} else {
|
||
|
||
UnicodeServerName = *UnicodeName;
|
||
|
||
}
|
||
|
||
//
|
||
// Allocate space for new relative name ("$$\server\oldrelative").
|
||
// Start by calculating the string length, and then add one more WCHAR
|
||
// for zero-termination.
|
||
//
|
||
|
||
nameLength = (sizeof(L'$') +
|
||
sizeof(L'$') +
|
||
sizeof(L'\\') +
|
||
UnicodeServerName.Length +
|
||
sizeof(L'\\') +
|
||
OldRelativeName.Length);
|
||
|
||
NewRelativeName->Length = (USHORT)nameLength;
|
||
|
||
if( NewRelativeName->Length != nameLength ) {
|
||
|
||
//
|
||
// Oh no -- string length overflow!
|
||
//
|
||
|
||
if( !ARGUMENT_PRESENT( UnicodeName ) ) {
|
||
RtlFreeUnicodeString(&UnicodeServerName);
|
||
}
|
||
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
NewRelativeName->MaximumLength =
|
||
NewRelativeName->Length + sizeof(L'\0');
|
||
|
||
NewRelativeName->Buffer =
|
||
ALLOCATE_HEAP_COLD(NewRelativeName->MaximumLength, BlockTypeDataBuffer);
|
||
|
||
if (NewRelativeName->Buffer == NULL) {
|
||
|
||
if( !ARGUMENT_PRESENT( UnicodeName ) ) {
|
||
RtlFreeUnicodeString(&UnicodeServerName);
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory(NewRelativeName->Buffer, NewRelativeName->MaximumLength);
|
||
|
||
nextLocation = NewRelativeName->Buffer;
|
||
|
||
//
|
||
// Copy strings and characters to new relative name.
|
||
//
|
||
*nextLocation++ = L'$';
|
||
*nextLocation++ = L'$';
|
||
*nextLocation++ = L'\\';
|
||
|
||
RtlCopyMemory(
|
||
nextLocation,
|
||
UnicodeServerName.Buffer,
|
||
UnicodeServerName.Length
|
||
);
|
||
|
||
nextLocation += (UnicodeServerName.Length / sizeof(WCHAR));
|
||
|
||
*nextLocation++ = L'\\';
|
||
|
||
RtlCopyMemory(
|
||
nextLocation,
|
||
OldRelativeName.Buffer,
|
||
OldRelativeName.Length
|
||
);
|
||
|
||
if( !ARGUMENT_PRESENT( UnicodeName ) ) {
|
||
//
|
||
// Free UnicodeServerName.
|
||
//
|
||
RtlFreeUnicodeString(&UnicodeServerName);
|
||
}
|
||
|
||
*Remapped = TRUE;
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
NTSTATUS
|
||
SrvCreateFile(
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PCHAR SmbFileName,
|
||
IN PCHAR EndOfSmbFileName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
IN OPLOCK_TYPE RequestedOplockType,
|
||
IN PRESTART_ROUTINE RestartRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Does most of the operations necessary to open or create a file.
|
||
First the UID and TID are verified and the corresponding session and
|
||
tree connect blocks located. The input file name name is
|
||
canonicalized, and a fully qualified name is formed. An appropriate
|
||
subroutine is called to do the open, based on whether this is a
|
||
normal, compatibility mode, or FCB open.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
SmbDesiredAccess - The desired access in SMB protocol format.
|
||
|
||
SmbFileAttributes - File attributes in SMB protocol format.
|
||
|
||
SmbOpenFunction - Open function in SMB protocol format.
|
||
|
||
SmbAllocationSize - Allocation size for new files.
|
||
|
||
SmbFileName - A pointer to the zero-terminated file name in the
|
||
request SMB. NOTE: This pointer should NOT point to the ASCII
|
||
format indicator (\004) present in some SMBs!
|
||
|
||
EndOfSmbFileName - a pointer to the last possible character that
|
||
the file name can be in. If the name extands beyond this location
|
||
without a zero terminator, SrvCanonicalizePathName will fail.
|
||
|
||
EaBuffer - Optional pointer to a full EA list to pass to SrvIoCreateFile.
|
||
|
||
EaLength - Length of the EA buffer.
|
||
|
||
EaErrorOffset - Optional pointer to the location in which to write
|
||
the offset to the EA that caused an error.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PMFCB mfcb;
|
||
PNONPAGED_MFCB nonpagedMfcb;
|
||
PRFCB rfcb;
|
||
|
||
PSESSION session;
|
||
PTREE_CONNECT treeConnect;
|
||
|
||
UNICODE_STRING relativeName;
|
||
UNICODE_STRING pipeRelativeName;
|
||
BOOLEAN pipeRelativeNameAllocated = FALSE;
|
||
UNICODE_STRING fullName;
|
||
SHARE_TYPE shareType;
|
||
|
||
ULONG error;
|
||
ULONG jobId;
|
||
|
||
ULONG hashValue;
|
||
|
||
ULONG attributes;
|
||
ULONG openRetries;
|
||
BOOLEAN isUnicode;
|
||
BOOLEAN caseInsensitive;
|
||
|
||
PSRV_LOCK mfcbLock;
|
||
|
||
//
|
||
// NOTE ON MFCB REFERENCE COUNT HANDLING
|
||
//
|
||
// After finding or creating an MFCB for a file, we increment the
|
||
// MFCB reference count an extra time to simplify our
|
||
// synchronization logic. We hold the MfcbListLock lock while
|
||
// finding/creating the MFCB, but release it after acquiring the the
|
||
// per-MFCB lock. We then call one of the DoXxxOpen routines, which
|
||
// may need to queue an LFCB to the MFCB and thus need to increment
|
||
// the count. But they can't, because the MFCB list lock may not be
|
||
// acquired while the per-MFCB lock is held because of deadlock
|
||
// potential. The boolean LfcbAddedToMfcbList returned from the
|
||
// routines indicates whether they actually queued an LFCB to the
|
||
// MFCB. If they didn't, we need to release the extra reference.
|
||
//
|
||
// Note that it isn't often that we actually have to dereference the
|
||
// MFCB. This only occurs when 1) the open fails, or 2) a
|
||
// compatibility mode or FCB open succeeds when the client already
|
||
// has the file open.
|
||
//
|
||
|
||
BOOLEAN lfcbAddedToMfcbList;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Assume we won't need a temporary open.
|
||
//
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = FALSE;
|
||
|
||
//
|
||
// If a session block has not already been assigned to the current
|
||
// work context, verify the UID. If verified, the address of the
|
||
// session block corresponding to this user is stored in the
|
||
// WorkContext block and the session block is referenced.
|
||
//
|
||
// Find the tree connect corresponding to the given TID if a tree
|
||
// connect pointer has not already been put in the WorkContext block
|
||
// by an AndX command or a previous call to SrvCreateFile.
|
||
//
|
||
|
||
status = SrvVerifyUidAndTid(
|
||
WorkContext,
|
||
&session,
|
||
&treeConnect,
|
||
ShareTypeWild
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Invalid UID or TID\n" ));
|
||
}
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the session has expired, return that info
|
||
//
|
||
if( session->IsSessionExpired )
|
||
{
|
||
return SESSION_EXPIRED_STATUS_CODE;
|
||
}
|
||
|
||
//
|
||
// Decide if we're case sensitive or not
|
||
//
|
||
caseInsensitive = (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE) ||
|
||
session->UsingUppercasePaths;
|
||
|
||
//
|
||
// Here we begin share type specific processing.
|
||
//
|
||
|
||
shareType = treeConnect->Share->ShareType;
|
||
|
||
//
|
||
// If this operation may block, and we are running short of
|
||
// free work items, fail this SMB with an out of resources error.
|
||
// Note that a disk open will block if the file is currently oplocked.
|
||
//
|
||
|
||
if ( shareType == ShareTypeDisk && !WorkContext->BlockingOperation ) {
|
||
|
||
if ( SrvReceiveBufferShortage( ) ) {
|
||
|
||
SrvStatistics.BlockingSmbsRejected++;
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
|
||
} else {
|
||
|
||
//
|
||
// SrvBlockingOpsInProgress has already been incremented.
|
||
// Flag this work item as a blocking operation.
|
||
//
|
||
|
||
WorkContext->BlockingOperation = TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
isUnicode = SMB_IS_UNICODE( WorkContext );
|
||
|
||
switch ( shareType ) {
|
||
|
||
case ShareTypePrint:
|
||
|
||
//
|
||
// Allocate space to hold the file name we're going to open.
|
||
//
|
||
|
||
fullName.MaximumLength = MAXIMUM_FILENAME_LENGTH * sizeof(WCHAR);
|
||
fullName.Buffer = ALLOCATE_HEAP_COLD(
|
||
fullName.MaximumLength,
|
||
BlockTypeDataBuffer
|
||
);
|
||
if ( fullName.Buffer == NULL ) {
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Get a print file name to use for spooling the request.
|
||
// We open this as a disk file, use normal writes to get the
|
||
// data, then call ScheduleJob( ) in XACTSRV to start the
|
||
// actual printing process.
|
||
//
|
||
|
||
status = SrvAddPrintJob(
|
||
WorkContext,
|
||
WorkContext->TreeConnect->Share->Type.hPrinter,
|
||
&fullName,
|
||
&jobId,
|
||
&error
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvCreateFile: SrvAddPrintJob failed: %lx (%ld)\n",
|
||
status, error ));
|
||
}
|
||
FREE_HEAP( fullName.Buffer );
|
||
if ( error != NO_ERROR ) {
|
||
ASSERT( SrvErrorCode(error) == error );
|
||
status = (NTSTATUS)(SRV_WIN32_STATUS | error);
|
||
}
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Scan the Master File Table to see if the named file is already
|
||
// open.
|
||
//
|
||
mfcb = SrvFindMfcb( &fullName, caseInsensitive, &mfcbLock, &hashValue, WorkContext );
|
||
|
||
if ( mfcb == NULL ) {
|
||
|
||
//
|
||
// There is no MFCB for this file. Create one.
|
||
//
|
||
|
||
mfcb = SrvCreateMfcb( &fullName, WorkContext, hashValue );
|
||
|
||
if ( mfcb == NULL ) {
|
||
|
||
//
|
||
// Failure to add open file instance to MFT.
|
||
//
|
||
|
||
if( mfcbLock ) {
|
||
RELEASE_LOCK( mfcbLock );
|
||
}
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Unable to allocate MFCB\n" ));
|
||
}
|
||
|
||
FREE_HEAP( fullName.Buffer );
|
||
|
||
//
|
||
// Free up the Job ID.
|
||
//
|
||
|
||
SrvSchedulePrintJob(
|
||
WorkContext->TreeConnect->Share->Type.hPrinter,
|
||
jobId
|
||
);
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Increment the MFCB reference count. See the note at the beginning of this routine.
|
||
//
|
||
|
||
mfcb->BlockHeader.ReferenceCount++;
|
||
UPDATE_REFERENCE_HISTORY( mfcb, FALSE );
|
||
|
||
//
|
||
// Grab the MFCB-based lock to serialize opens of the same file
|
||
// and release the MFCB list lock.
|
||
//
|
||
|
||
nonpagedMfcb = mfcb->NonpagedMfcb;
|
||
RELEASE_LOCK( mfcbLock );
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// Set up the share access and desired access in SMB terms.
|
||
// We will only write to the file, so just request write
|
||
// as the desired access. As an optimization, the spooler
|
||
// may read from the file before we finish writing to it,
|
||
// so allow other readers.
|
||
//
|
||
|
||
SmbDesiredAccess = SMB_DA_ACCESS_WRITE | SMB_DA_SHARE_DENY_WRITE | SMB_LR_SEQUENTIAL;
|
||
|
||
//
|
||
// Set up the open function to create the file it it doesn't
|
||
// exist and to truncate it if it does exist. There shouldn't
|
||
// be preexisting data in the file, hence the truncation.
|
||
//
|
||
// !!! The spooler may change to create the file for us, in which
|
||
// case this should change to only truncate.
|
||
|
||
SmbOpenFunction = SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_TRUNCATE;
|
||
|
||
//
|
||
// This is a normal sharing mode open. Do the actual open
|
||
// of the disk file.
|
||
//
|
||
|
||
status = DoNormalOpen(
|
||
&rfcb,
|
||
mfcb,
|
||
WorkContext,
|
||
&WorkContext->Irp->IoStatus,
|
||
SmbDesiredAccess,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
&fullName,
|
||
NULL,
|
||
0,
|
||
0,
|
||
&lfcbAddedToMfcbList,
|
||
RequestedOplockType
|
||
);
|
||
|
||
//
|
||
// If the open worked, set up the Job ID in the LFCB.
|
||
//
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
|
||
rfcb->Lfcb->JobId = jobId;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Free up the Job ID if the open failed.
|
||
//
|
||
|
||
SrvSchedulePrintJob(
|
||
WorkContext->TreeConnect->Share->Type.hPrinter,
|
||
jobId
|
||
);
|
||
}
|
||
|
||
//
|
||
// Release the Open serialization lock and dereference the MFCB.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// If DoNormalOpen didn't queue an LFCB to the MFCB, release the
|
||
// extra reference that we added.
|
||
//
|
||
|
||
if ( !lfcbAddedToMfcbList ) {
|
||
SrvDereferenceMfcb( mfcb );
|
||
}
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
//
|
||
// Deallocate the full path name buffer.
|
||
//
|
||
|
||
FREE_HEAP( fullName.Buffer );
|
||
|
||
break;
|
||
|
||
case ShareTypeDisk:
|
||
case ShareTypePipe:
|
||
|
||
//
|
||
// Canonicalize the path name so that it conforms to NT
|
||
// standards.
|
||
//
|
||
// *** Note that this operation allocates space for the name.
|
||
// This space is deallocated after the DoXxxOpen routine
|
||
// returns.
|
||
//
|
||
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
treeConnect->Share,
|
||
NULL,
|
||
SmbFileName,
|
||
EndOfSmbFileName,
|
||
TRUE,
|
||
isUnicode,
|
||
&relativeName
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// The path tried to do ..\ to get beyond the share it has
|
||
// accessed.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Invalid pathname: %s\n",
|
||
SmbFileName ));
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Form the fully qualified name of the file.
|
||
//
|
||
// *** Note that this operation allocates space for the name.
|
||
// This space is deallocated after the DoXxxOpen routine
|
||
// returns.
|
||
//
|
||
|
||
if ( shareType == ShareTypeDisk ) {
|
||
|
||
#ifdef SLMDBG
|
||
if ( SrvIsSlmStatus( &relativeName ) &&
|
||
SrvIsSlmAccessDisallowed(
|
||
&relativeName,
|
||
treeConnect->Share->RootDirectoryHandle
|
||
) ) {
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
#endif
|
||
|
||
SrvAllocateAndBuildPathName(
|
||
&treeConnect->Share->DosPathName,
|
||
&relativeName,
|
||
NULL,
|
||
&fullName
|
||
);
|
||
|
||
} else {
|
||
|
||
UNICODE_STRING pipePrefix;
|
||
|
||
RtlInitUnicodeString( &pipePrefix, StrSlashPipeSlash );
|
||
|
||
//
|
||
// Check for PIPE pathname prefix.
|
||
//
|
||
|
||
if ( !RtlPrefixUnicodeString(
|
||
&SrvCanonicalNamedPipePrefix,
|
||
&relativeName,
|
||
TRUE
|
||
) ) {
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Invalid pipe pathname: %s\n",
|
||
SmbFileName ));
|
||
}
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &relativeName );
|
||
}
|
||
|
||
return STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||
}
|
||
|
||
//
|
||
// Delete PIPE\ prefix from file path
|
||
//
|
||
|
||
pipeRelativeName.Buffer = (PWSTR)( (PCHAR)relativeName.Buffer +
|
||
SrvCanonicalNamedPipePrefix.Length );
|
||
pipeRelativeName.Length = relativeName.Length -
|
||
SrvCanonicalNamedPipePrefix.Length;
|
||
pipeRelativeName.MaximumLength = pipeRelativeName.Length;
|
||
|
||
if( WorkContext->Endpoint->RemapPipeNames || treeConnect->RemapPipeNames ) {
|
||
|
||
//
|
||
// The RemapPipeNames flag is set, so remap the pipe name
|
||
// to "$$\<server>\<pipe name>".
|
||
//
|
||
// Note: this operation allocates space for pipeRelativeName.
|
||
//
|
||
status = RemapPipeName(
|
||
&WorkContext->Endpoint->TransportAddress,
|
||
treeConnect->RemapPipeNames ? &treeConnect->ServerName : NULL ,
|
||
&pipeRelativeName,
|
||
&pipeRelativeNameAllocated
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &relativeName );
|
||
}
|
||
return status;
|
||
}
|
||
}
|
||
|
||
SrvAllocateAndBuildPathName(
|
||
&pipePrefix,
|
||
&pipeRelativeName,
|
||
NULL,
|
||
&fullName
|
||
);
|
||
|
||
//
|
||
// If this is a compatibility mode or FCB mode open, map
|
||
// it to a normal non-shared open.
|
||
//
|
||
|
||
if ( SmbDesiredAccess == SMB_DA_FCB_MASK ||
|
||
(SmbDesiredAccess & SMB_DA_SHARE_MASK) ==
|
||
SMB_DA_SHARE_COMPATIBILITY ) {
|
||
|
||
SmbDesiredAccess = SMB_DA_ACCESS_READ_WRITE |
|
||
SMB_DA_SHARE_EXCLUSIVE;
|
||
}
|
||
|
||
}
|
||
|
||
if ( fullName.Buffer == NULL ) {
|
||
|
||
//
|
||
// Unable to allocate heap for the full name.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Unable to allocate heap for "
|
||
"full path name\n" ));
|
||
}
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &relativeName );
|
||
}
|
||
|
||
if( pipeRelativeNameAllocated ) {
|
||
FREE_HEAP( pipeRelativeName.Buffer );
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
|
||
attributes = caseInsensitive ? OBJ_CASE_INSENSITIVE : 0;
|
||
|
||
if ( WorkContext->ProcessingCount == 2) {
|
||
|
||
HANDLE fileHandle;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
||
//
|
||
// This is the second time through, so we must be in a blocking
|
||
// thread. Do a blocking open of the file to force an oplock
|
||
// break. Then close the handle and fall through to the normal
|
||
// open path.
|
||
//
|
||
// We must do the blocking open without holding the MFCB
|
||
// lock, because this lock can be acquired during oplock
|
||
// break, resulting in deadlock.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&relativeName,
|
||
attributes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ,
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
NULL,
|
||
0,
|
||
FILE_SHARE_VALID_FLAGS,
|
||
FILE_OPEN,
|
||
0,
|
||
NULL,
|
||
0,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
0,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 9, 0 );
|
||
SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 16, 0 );
|
||
SrvNtClose( fileHandle, TRUE );
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Scan the Master File Table to see if the named file is already
|
||
// open. We can do the scan with a shared lock, but we must have an
|
||
// exclusive lock to modify the table. Start out shared, assuming the
|
||
// file is already open.
|
||
//
|
||
|
||
mfcb = SrvFindMfcb( &fullName, caseInsensitive, &mfcbLock, &hashValue, WorkContext );
|
||
|
||
if ( mfcb == NULL ) {
|
||
|
||
//
|
||
// There is no MFCB for this file. Create one.
|
||
//
|
||
|
||
mfcb = SrvCreateMfcb( &fullName, WorkContext, hashValue );
|
||
|
||
if ( mfcb == NULL ) {
|
||
|
||
//
|
||
// Failure to add open file instance to MFT.
|
||
//
|
||
|
||
if( mfcbLock ) {
|
||
RELEASE_LOCK( mfcbLock );
|
||
}
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvCreateFile: Unable to allocate MFCB\n" ));
|
||
}
|
||
|
||
FREE_HEAP( fullName.Buffer );
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &relativeName );
|
||
}
|
||
|
||
if( pipeRelativeNameAllocated ) {
|
||
FREE_HEAP( pipeRelativeName.Buffer );
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Increment the MFCB reference count. See the note at the beginning of this routine.
|
||
//
|
||
mfcb->BlockHeader.ReferenceCount++;
|
||
UPDATE_REFERENCE_HISTORY( mfcb, FALSE );
|
||
|
||
//
|
||
// Grab the MFCB-based lock to serialize opens of the same file
|
||
// and release the MFCB list lock.
|
||
//
|
||
nonpagedMfcb = mfcb->NonpagedMfcb;
|
||
RELEASE_LOCK( mfcbLock );
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// Call an appropriate routine to actually do the open.
|
||
//
|
||
|
||
openRetries = SrvSharingViolationRetryCount;
|
||
|
||
start_retry:
|
||
|
||
if ( SmbDesiredAccess == SMB_DA_FCB_MASK ) {
|
||
|
||
//
|
||
// This is an FCB open.
|
||
//
|
||
|
||
status = DoFcbOpen(
|
||
&rfcb,
|
||
mfcb,
|
||
WorkContext,
|
||
&WorkContext->Irp->IoStatus,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
&relativeName,
|
||
EaBuffer,
|
||
EaLength,
|
||
EaErrorOffset,
|
||
&lfcbAddedToMfcbList
|
||
);
|
||
|
||
} else if ( (SmbDesiredAccess & SMB_DA_SHARE_MASK) ==
|
||
SMB_DA_SHARE_COMPATIBILITY ) {
|
||
|
||
//
|
||
// This is a compatibility mode open.
|
||
//
|
||
|
||
status = DoCompatibilityOpen(
|
||
&rfcb,
|
||
mfcb,
|
||
WorkContext,
|
||
&WorkContext->Irp->IoStatus,
|
||
SmbDesiredAccess,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
&relativeName,
|
||
EaBuffer,
|
||
EaLength,
|
||
EaErrorOffset,
|
||
&lfcbAddedToMfcbList,
|
||
RequestedOplockType
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a normal sharing mode open.
|
||
//
|
||
|
||
status = DoNormalOpen(
|
||
&rfcb,
|
||
mfcb,
|
||
WorkContext,
|
||
&WorkContext->Irp->IoStatus,
|
||
SmbDesiredAccess,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
shareType == ShareTypePipe ?
|
||
&pipeRelativeName : &relativeName,
|
||
EaBuffer,
|
||
EaLength,
|
||
EaErrorOffset,
|
||
&lfcbAddedToMfcbList,
|
||
RequestedOplockType
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Retry if sharing violation and we are in the blocking thread.
|
||
//
|
||
|
||
if ( (WorkContext->ProcessingCount == 2) &&
|
||
(status == STATUS_SHARING_VIOLATION) &&
|
||
(shareType == ShareTypeDisk) &&
|
||
(openRetries-- > 0) ) {
|
||
|
||
//
|
||
// Release the mfcb lock so that a close might slip through.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
(VOID) KeDelayExecutionThread(
|
||
KernelMode,
|
||
FALSE,
|
||
&SrvSharingViolationDelay
|
||
);
|
||
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
goto start_retry;
|
||
}
|
||
|
||
//
|
||
// Release the Open serialization lock and dereference the MFCB.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// If DoXxxOpen didn't queue an LFCB to the MFCB, release the
|
||
// extra reference that we added.
|
||
//
|
||
|
||
if ( !lfcbAddedToMfcbList ) {
|
||
SrvDereferenceMfcb( mfcb );
|
||
}
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
//
|
||
// Deallocate the full path name buffer.
|
||
//
|
||
|
||
FREE_HEAP( fullName.Buffer );
|
||
|
||
if ( !isUnicode ) {
|
||
RtlFreeUnicodeString( &relativeName );
|
||
}
|
||
|
||
break;
|
||
|
||
//
|
||
// Default case, illegal device type. This should never happen.
|
||
//
|
||
|
||
default:
|
||
|
||
// !!! Is this an appropriate error return code? Probably no.
|
||
status = STATUS_INVALID_PARAMETER;
|
||
rfcb = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// Update the statistics database if the open was successful.
|
||
//
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
SrvStatistics.TotalFilesOpened++;
|
||
}
|
||
|
||
//
|
||
// Make a pointer to the RFCB accessible to the caller.
|
||
//
|
||
|
||
WorkContext->Parameters2.Open.Rfcb = rfcb;
|
||
|
||
//
|
||
// If there is an oplock break in progress, wait for the oplock
|
||
// break to complete.
|
||
//
|
||
|
||
if ( status == STATUS_OPLOCK_BREAK_IN_PROGRESS ) {
|
||
|
||
NTSTATUS startStatus;
|
||
|
||
//
|
||
// Save the Information from the open, so it doesn't
|
||
// get lost when we re-use the WorkContext->Irp for the
|
||
// oplock processing.
|
||
//
|
||
WorkContext->Parameters2.Open.IosbInformation = WorkContext->Irp->IoStatus.Information;
|
||
|
||
startStatus = SrvStartWaitForOplockBreak(
|
||
WorkContext,
|
||
RestartRoutine,
|
||
0,
|
||
rfcb->Lfcb->FileObject
|
||
);
|
||
|
||
if (!NT_SUCCESS( startStatus ) ) {
|
||
|
||
//
|
||
// The file is oplocked, and we cannot wait for the oplock
|
||
// break to complete. Just close the file, and return the
|
||
// error.
|
||
//
|
||
|
||
SrvCloseRfcb( rfcb );
|
||
status = startStatus;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
if( pipeRelativeNameAllocated ) {
|
||
FREE_HEAP( pipeRelativeName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Return the open status.
|
||
//
|
||
|
||
return status;
|
||
|
||
} // SrvCreateFile
|
||
|
||
|
||
NTSTATUS
|
||
DoNormalOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList,
|
||
IN OPLOCK_TYPE RequestedOplockType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a normal sharing mode open.
|
||
|
||
*** The MFCB lock must be held on entry to this routine; the lock
|
||
remains held on exit.
|
||
|
||
Arguments:
|
||
|
||
Rfcb - A pointer to a pointer to an RFCB that will point to the
|
||
newly-created RFCB.
|
||
|
||
Mfcb - A pointer to the MFCB for this file.
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
IoStatusBlock - A pointer to an IO status block.
|
||
|
||
SmbDesiredAccess - The desired access in SMB protocol format.
|
||
|
||
SmbFileAttributes - File attributes in SMB protocol format.
|
||
|
||
SmbOpenFunction - Open function in SMB protocol format.
|
||
|
||
SmbAllocationSize - Allocation size for new files.
|
||
|
||
RelativeName - The share-relative name of the file being opened.
|
||
|
||
EaBuffer - Optional pointer to a full EA list to pass to SrvIoCreateFile.
|
||
|
||
EaLength - Length of the EA buffer.
|
||
|
||
EaErrorOffset - Optional pointer to the location in which to write
|
||
the offset to the EA that caused an error.
|
||
|
||
LfcbAddedToMfcbList - Pointer to a boolean that will be set to TRUE if
|
||
an lfcb is added to the mfcb list of lfcbs. FALSE, otherwise.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
NTSTATUS completionStatus;
|
||
|
||
HANDLE fileHandle;
|
||
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
ULONG attributes;
|
||
|
||
LARGE_INTEGER allocationSize;
|
||
ULONG fileAttributes;
|
||
BOOLEAN directory;
|
||
ULONG shareAccess;
|
||
ULONG createDisposition;
|
||
ULONG createOptions;
|
||
ACCESS_MASK desiredAccess;
|
||
PSHARE fileShare = NULL;
|
||
|
||
UCHAR errorClass = SMB_ERR_CLASS_DOS;
|
||
USHORT error = 0;
|
||
|
||
PAGED_CODE( );
|
||
|
||
*LfcbAddedToMfcbList = FALSE;
|
||
|
||
//
|
||
// Map the desired access from SMB terms to NT terms.
|
||
//
|
||
|
||
status = MapDesiredAccess( SmbDesiredAccess, &desiredAccess );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
#ifdef SLMDBG
|
||
if ( SrvIsSlmStatus( RelativeName ) ||
|
||
SrvIsTempSlmStatus( RelativeName ) ) {
|
||
desiredAccess |= GENERIC_READ;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Map the share mode from SMB terms to NT terms.
|
||
//
|
||
|
||
status = MapShareAccess( SmbDesiredAccess, &shareAccess );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// We're going to open this file relative to the root directory
|
||
// of the share. Load up the necessary fields in the object
|
||
// attributes structure.
|
||
//
|
||
|
||
if ( WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else if ( WorkContext->Session->UsingUppercasePaths ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else {
|
||
attributes = 0L;
|
||
}
|
||
|
||
if ( WorkContext->TreeConnect->Share->ShareType == ShareTypePipe ) {
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
RelativeName,
|
||
attributes,
|
||
SrvNamedPipeHandle,
|
||
NULL
|
||
);
|
||
} else {
|
||
|
||
fileShare = WorkContext->TreeConnect->Share;
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
RelativeName,
|
||
attributes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
//
|
||
// Set block size according to the AllocationSize in the request SMB.
|
||
//
|
||
|
||
allocationSize.QuadPart = SmbAllocationSize;
|
||
|
||
//
|
||
// Get the value for fileAttributes.
|
||
//
|
||
|
||
SRV_SMB_ATTRIBUTES_TO_NT(
|
||
SmbFileAttributes,
|
||
&directory,
|
||
&fileAttributes
|
||
);
|
||
|
||
//
|
||
// Set createDisposition parameter from OpenFunction.
|
||
//
|
||
|
||
status = MapOpenFunction( SmbOpenFunction, &createDisposition );
|
||
|
||
//
|
||
// OS/2 expects that if it creates a file with an allocation size,
|
||
// the end of file pointer will be the same as that allocation size.
|
||
// Therefore, the server is expected to set EOF to the allocation
|
||
// size on creating a file. However, this requires write access,
|
||
// so if the client is creating a file with an allocation size, give
|
||
// him write access. Only do this if creating a file; if this is
|
||
// a "create or open" operation, don't do this, as it could cause
|
||
// an extraneuos audit.
|
||
//
|
||
|
||
if ( SmbAllocationSize != 0 && createDisposition == FILE_CREATE ) {
|
||
desiredAccess |= GENERIC_WRITE;
|
||
}
|
||
|
||
//
|
||
// Set createOptions parameter.
|
||
//
|
||
|
||
if ( SmbDesiredAccess & SMB_DA_WRITE_THROUGH ) {
|
||
createOptions = FILE_WRITE_THROUGH | FILE_NON_DIRECTORY_FILE;
|
||
} else {
|
||
createOptions = FILE_NON_DIRECTORY_FILE;
|
||
}
|
||
|
||
if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
|
||
SMB_FLAGS2_KNOWS_EAS) == 0) {
|
||
|
||
//
|
||
// This guy does not know eas
|
||
//
|
||
|
||
createOptions |= FILE_NO_EA_KNOWLEDGE;
|
||
}
|
||
|
||
//
|
||
// Set the caching hints flags.
|
||
//
|
||
|
||
status = MapCacheHints( SmbDesiredAccess, &createOptions );
|
||
|
||
//
|
||
// Check to see if there is a cached handle for the file.
|
||
//
|
||
|
||
if ( (createDisposition == FILE_OPEN) ||
|
||
(createDisposition == FILE_CREATE) ||
|
||
(createDisposition == FILE_OPEN_IF) ) {
|
||
|
||
ASSERT( *LfcbAddedToMfcbList == FALSE );
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvCreateFile: checking for cached rfcb for %wZ\n", RelativeName ));
|
||
}
|
||
if ( SrvFindCachedRfcb(
|
||
WorkContext,
|
||
Mfcb,
|
||
desiredAccess,
|
||
shareAccess,
|
||
createDisposition,
|
||
createOptions,
|
||
RequestedOplockType,
|
||
&status ) ) {
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvCreateFile: FindCachedRfcb = TRUE, status = %x, rfcb = %p\n",
|
||
status, WorkContext->Rfcb ));
|
||
}
|
||
|
||
IoStatusBlock->Information = FILE_OPENED;
|
||
|
||
return status;
|
||
}
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvCreateFile: FindCachedRfcb = FALSE; do it the slow way\n" ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Call SrvIoCreateFile to create or open the file. (We call
|
||
// SrvIoCreateFile, rather than NtOpenFile, in order to get user-mode
|
||
// access checking.)
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoNormalOpen: Opening file %wZ\n", RelativeName ));
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
//
|
||
// Ensure the EaBuffer is correctly formatted. Since we are a kernel mode
|
||
// component, the Io subsystem does not check it for us.
|
||
//
|
||
if( ARGUMENT_PRESENT( EaBuffer ) ) {
|
||
status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION)EaBuffer, EaLength, EaErrorOffset );
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
createOptions |= FILE_COMPLETE_IF_OPLOCKED;
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
desiredAccess,
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
shareAccess,
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
fileShare
|
||
);
|
||
}
|
||
|
||
//
|
||
// If we got sharing violation and this is a disk file, and this is
|
||
// the first open attempt, setup for a blocking open attempt. If the
|
||
// file is batch oplocked, the non-blocking open would fail, and the
|
||
// oplock will not break.
|
||
//
|
||
|
||
if ( status == STATUS_SHARING_VIOLATION &&
|
||
WorkContext->ProcessingCount == 1 &&
|
||
WorkContext->TreeConnect->Share->ShareType == ShareTypeDisk ) {
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the statistics
|
||
// database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// The open failed.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "DoNormalOpen: SrvIoCreateFile failed, file = %wZ, status = %X, Info = 0x%p\n",
|
||
objectAttributes.ObjectName,
|
||
status, (PVOID)IoStatusBlock->Information ));
|
||
}
|
||
|
||
//
|
||
// Set the error offset if needed.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(EaErrorOffset) &&
|
||
status == STATUS_INVALID_EA_NAME ) {
|
||
*EaErrorOffset = (ULONG)IoStatusBlock->Information;
|
||
IoStatusBlock->Information = 0;
|
||
}
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 10, 0 );
|
||
|
||
//
|
||
// The open was successful. Attempt to allocate structures to
|
||
// represent the open. If any errors occur, CompleteOpen does full
|
||
// cleanup, including closing the file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoNormalOpen: Open of %wZ succeeded, file handle: 0x%p\n",
|
||
RelativeName, fileHandle ));
|
||
}
|
||
|
||
completionStatus = CompleteOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
NULL,
|
||
fileHandle,
|
||
NULL,
|
||
shareAccess,
|
||
createOptions,
|
||
FALSE,
|
||
FALSE,
|
||
LfcbAddedToMfcbList
|
||
);
|
||
|
||
//
|
||
// Return the "interesting" status code. If CompleteOpen() succeeds
|
||
// return the open status. If it fails, it will clean up the open
|
||
// file, and we return a failure status.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( completionStatus ) ) {
|
||
return completionStatus;
|
||
} else {
|
||
return status;
|
||
}
|
||
|
||
} // DoNormalOpen
|
||
|
||
|
||
NTSTATUS
|
||
DoCompatibilityOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbDesiredAccess,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList,
|
||
IN OPLOCK_TYPE RequestedOplockType
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes a compatibility mode open.
|
||
|
||
*** The MFCB lock must be held on entry to this routine; the lock
|
||
remains held on exit.
|
||
|
||
Arguments:
|
||
|
||
Rfcb - A pointer to a pointer to an RFCB that will point to the
|
||
newly-created RFCB.
|
||
|
||
Mfcb - A pointer to the MFCB for this file.
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
IoStatusBlock - A pointer to an IO status block.
|
||
|
||
SmbDesiredAccess - The desired access in SMB protocol format.
|
||
|
||
SmbFileAttributes - File attributes in SMB protocol format.
|
||
|
||
SmbOpenFunction - Open function in SMB protocol format.
|
||
|
||
SmbAllocationSize - Allocation size for new files.
|
||
|
||
RelativeName - The share-relative name of the file being opened.
|
||
|
||
EaBuffer - Optional pointer to a full EA list to pass to SrvIoCreateFile.
|
||
|
||
EaLength - Length of the EA buffer.
|
||
|
||
EaErrorOffset - Optional pointer to the location in which to write
|
||
the offset to the EA that caused an error.
|
||
|
||
LfcbAddedToMfcbList - Pointer to a boolean that will be set to TRUE if
|
||
an lfcb is added to the mfcb list of lfcbs.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
NTSTATUS completionStatus;
|
||
|
||
PLFCB lfcb;
|
||
|
||
HANDLE fileHandle;
|
||
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
ULONG attributes;
|
||
|
||
LARGE_INTEGER allocationSize;
|
||
ULONG fileAttributes;
|
||
BOOLEAN directory;
|
||
ULONG createDisposition;
|
||
ULONG createOptions;
|
||
ACCESS_MASK desiredAccess;
|
||
USHORT smbOpenMode;
|
||
|
||
PAGED_CODE( );
|
||
|
||
*LfcbAddedToMfcbList = FALSE;
|
||
|
||
//
|
||
// Map the desired access from SMB terms to NT terms.
|
||
//
|
||
|
||
status = MapDesiredAccess( SmbDesiredAccess, &desiredAccess );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Set createDisposition parameter from OpenFunction.
|
||
//
|
||
|
||
status = MapOpenFunction( SmbOpenFunction, &createDisposition );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Set createOptions parameter.
|
||
//
|
||
|
||
if ( SmbDesiredAccess & SMB_DA_WRITE_THROUGH ) {
|
||
createOptions = FILE_WRITE_THROUGH | FILE_NON_DIRECTORY_FILE;
|
||
} else {
|
||
createOptions = FILE_NON_DIRECTORY_FILE;
|
||
}
|
||
|
||
if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
|
||
SMB_FLAGS2_KNOWS_EAS) == 0) {
|
||
|
||
//
|
||
// This guy does not know eas
|
||
//
|
||
|
||
createOptions |= FILE_NO_EA_KNOWLEDGE;
|
||
}
|
||
|
||
//
|
||
// We're going to open this file relative to the root directory
|
||
// of the share. Load up the necessary fields in the object
|
||
// attributes structure.
|
||
//
|
||
|
||
if ( WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else if ( WorkContext->Session->UsingUppercasePaths ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else {
|
||
attributes = 0L;
|
||
}
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
RelativeName,
|
||
attributes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( Mfcb->ActiveRfcbCount > 0 ) {
|
||
|
||
//
|
||
// The named file is already opened by the server. If the client
|
||
// specified that it didn't want to open an existing file,
|
||
// reject this open.
|
||
//
|
||
|
||
if ( createDisposition == FILE_CREATE ) {
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Compatibility open of %wZ rejected; wants to create\n", RelativeName ));
|
||
}
|
||
|
||
return STATUS_OBJECT_NAME_COLLISION;
|
||
}
|
||
|
||
//
|
||
// If the existing open is not a compatibility mode open, then
|
||
// we try to map the new open into a normal sharing mode. If
|
||
// that works, we attempt a normal open. If it doesn't work, we
|
||
// reject the new open. If the existing open is a compatibility
|
||
// mode open by this session, we just add a new RFCB. If it's a
|
||
// compatibility mode open by a different session, we reject
|
||
// this open.
|
||
//
|
||
|
||
if ( !Mfcb->CompatibilityOpen ) {
|
||
|
||
//
|
||
// The named file is open, but not in compatibility mode.
|
||
// Determine whether this should be mapped from a
|
||
// compatibility mode open to a normal sharing mode open.
|
||
//
|
||
|
||
smbOpenMode = SmbDesiredAccess;
|
||
|
||
if ( MapCompatibilityOpen( RelativeName, &smbOpenMode ) ) {
|
||
|
||
//
|
||
// The open has been mapped to a normal sharing mode.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Mapped compatibility open of %wZ to normal open\n", RelativeName ));
|
||
}
|
||
|
||
return DoNormalOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
IoStatusBlock,
|
||
smbOpenMode,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
RelativeName,
|
||
EaBuffer,
|
||
EaLength,
|
||
EaErrorOffset,
|
||
LfcbAddedToMfcbList,
|
||
RequestedOplockType
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// The open was not mapped away from compatibility mode.
|
||
// Because the file is already open for normal sharing, this
|
||
// open request must be rejected.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Compatibility open of %wZ rejected; already open in normal mode\n",
|
||
RelativeName ));
|
||
}
|
||
|
||
status = STATUS_SHARING_VIOLATION;
|
||
goto sharing_violation;
|
||
|
||
} // if ( !Mfcb->CompatibilityOpen )
|
||
|
||
//
|
||
// The named file is open in compatibility mode. Get a pointer
|
||
// to the LFCB for the open. Determine whether the requesting
|
||
// session is the one that did the original open.
|
||
//
|
||
// Normally there will only be one LFCB linked to a
|
||
// compatibility mode MFCB. However, it is possible for there
|
||
// to briefly be multiple LFCBs. When an LFCB is in the process
|
||
// of closing, the ActiveRfcbCount will be 0, so a new open will
|
||
// be treated as the first open of the MFCB, and there will be
|
||
// two LFCBs linked to the MFCB. There can actually be more
|
||
// than two LFCBs linked if the rundown of the closing LFCBs
|
||
// takes some time. So the find "the" LFCB for the open, we go
|
||
// to the tail of the MFCB's list.
|
||
//
|
||
|
||
lfcb = CONTAINING_RECORD( Mfcb->LfcbList.Blink, LFCB, MfcbListEntry );
|
||
|
||
if ( lfcb->Session != WorkContext->Session ) {
|
||
|
||
//
|
||
// A different session has the file open in compatibility
|
||
// mode. Reject this open request.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Compatibility open of %wZ rejected; already open in compatibility mode\n",
|
||
RelativeName ));
|
||
}
|
||
|
||
status = STATUS_SHARING_VIOLATION;
|
||
goto sharing_violation;
|
||
}
|
||
|
||
//
|
||
// If this request is asking for more access than could be
|
||
// obtained when the file was originally opened, reject this
|
||
// open.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(IoCheckDesiredAccess(
|
||
&desiredAccess,
|
||
lfcb->GrantedAccess )) ) {
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Duplicate compatibility open of %wZ rejected; access denied\n", RelativeName ));
|
||
}
|
||
|
||
return STATUS_ACCESS_DENIED;
|
||
}
|
||
|
||
//
|
||
// The client has access. Allocate a new RFCB and link it into
|
||
// the existing LFCB. If any errors occur, CompleteOpen does
|
||
// full cleanup.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Duplicate compatibility open of %wZ accepted", RelativeName ));
|
||
}
|
||
|
||
IoStatusBlock->Information = FILE_OPENED;
|
||
|
||
status = CompleteOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
lfcb,
|
||
NULL,
|
||
&desiredAccess,
|
||
0, // ShareAccess
|
||
createOptions,
|
||
TRUE,
|
||
FALSE,
|
||
LfcbAddedToMfcbList
|
||
);
|
||
|
||
if( NT_SUCCESS( status ) &&
|
||
( createDisposition == FILE_OVERWRITE ||
|
||
createDisposition == FILE_OVERWRITE_IF)
|
||
) {
|
||
//
|
||
// The file was successfully opened, and the client wants it
|
||
// truncated. We need to do it here by hand since we
|
||
// didn't actually call the filesystem to open the file and it
|
||
// therefore never had a chance to truncate the file if the
|
||
// open modes requested it.
|
||
//
|
||
LARGE_INTEGER zero;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
||
zero.QuadPart = 0;
|
||
NtSetInformationFile( lfcb->FileHandle,
|
||
&ioStatusBlock,
|
||
&zero,
|
||
sizeof( zero ),
|
||
FileEndOfFileInformation
|
||
);
|
||
}
|
||
|
||
return status;
|
||
|
||
} // if ( mfcb->ActiveRfcbCount > 0 )
|
||
|
||
//
|
||
// The file is not already open (by the server, at least).
|
||
// Determine whether this should be mapped from a compatibility mode
|
||
// open to a normal sharing mode open.
|
||
//
|
||
|
||
smbOpenMode = SmbDesiredAccess;
|
||
|
||
if ( MapCompatibilityOpen( RelativeName, &smbOpenMode ) ) {
|
||
|
||
//
|
||
// The open has been mapped to a normal sharing mode.
|
||
//
|
||
|
||
return DoNormalOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
IoStatusBlock,
|
||
smbOpenMode,
|
||
SmbFileAttributes,
|
||
SmbOpenFunction,
|
||
SmbAllocationSize,
|
||
RelativeName,
|
||
EaBuffer,
|
||
EaLength,
|
||
EaErrorOffset,
|
||
LfcbAddedToMfcbList,
|
||
RequestedOplockType
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// The open was not mapped away from compatibility mode. Attempt to
|
||
// open the file for exclusive access.
|
||
//
|
||
// *** We try to open the file for the most access we'll ever need.
|
||
// This is because we fold multiple compatibility opens by the
|
||
// same client into a single local open. The client may open
|
||
// the file first for readonly access, then for read/write
|
||
// access. Because the local open is exclusive, we can't open
|
||
// again on the second remote open. We try to get Delete
|
||
// access, in case the client tries to delete the file while
|
||
// it's open.
|
||
//
|
||
|
||
//
|
||
// Set block size according to the AllocationSize in the request SMB.
|
||
//
|
||
|
||
allocationSize.QuadPart = SmbAllocationSize;
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Opening file %wZ\n", RelativeName ));
|
||
}
|
||
|
||
//
|
||
// Get the value for fileAttributes.
|
||
//
|
||
|
||
SRV_SMB_ATTRIBUTES_TO_NT(
|
||
SmbFileAttributes,
|
||
&directory,
|
||
&fileAttributes
|
||
);
|
||
|
||
//
|
||
// Try to open the file for Read/Write/Delete access.
|
||
//
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
//
|
||
// Ensure the EaBuffer is correctly formatted. Since we are a kernel mode
|
||
// component, the Io subsystem does not check it for us.
|
||
//
|
||
if( ARGUMENT_PRESENT( EaBuffer ) ) {
|
||
status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION)EaBuffer, EaLength, EaErrorOffset );
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
createOptions |= FILE_COMPLETE_IF_OPLOCKED;
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ | GENERIC_WRITE | DELETE, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
}
|
||
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
|
||
//
|
||
// The client doesn't have Read/Write/Delete access to the file.
|
||
// Try for Read/Write access.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: r/w/d access denied.\n" ));
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ | GENERIC_WRITE, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraPipeCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
|
||
//
|
||
// The client doesn't have Read/Write access to the file.
|
||
// Try Read or Write access, as appropriate.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: r/w access denied.\n" ));
|
||
}
|
||
|
||
if ( (SmbDesiredAccess & SMB_DA_ACCESS_MASK) ==
|
||
SMB_DA_ACCESS_READ ) {
|
||
|
||
//
|
||
// !!! Should this be mapped to a normal sharing mode?
|
||
// Note that we already tried to map into normal
|
||
// mode once, but that failed. (With the current
|
||
// mapping algorithm, we can't get here unless soft
|
||
// compatibility is disabled.)
|
||
//
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
} else if ( (SmbDesiredAccess & SMB_DA_ACCESS_MASK) ==
|
||
SMB_DA_ACCESS_WRITE ) {
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_WRITE, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // NamedPipeCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the
|
||
// statistics database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we got sharing violation, just get a handle so that we can wait
|
||
// for an oplock break.
|
||
//
|
||
|
||
sharing_violation:
|
||
//
|
||
// If we got sharing violation and this is a disk file, and this is
|
||
// the first open attempt, setup for a blocking open attempt. If the
|
||
// file is batch oplocked, the non-blocking open would fail, and the
|
||
// oplock will not break.
|
||
//
|
||
|
||
if ( status == STATUS_SHARING_VIOLATION &&
|
||
WorkContext->ProcessingCount == 1 &&
|
||
WorkContext->TreeConnect->Share->ShareType == ShareTypeDisk ) {
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = TRUE;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// All of the open attempts failed.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: all opens failed; status = %X\n",
|
||
status ));
|
||
}
|
||
|
||
//
|
||
// Set the error offset if needed.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(EaErrorOffset) &&
|
||
status == STATUS_INVALID_EA_NAME ) {
|
||
*EaErrorOffset = (ULONG)IoStatusBlock->Information;
|
||
IoStatusBlock->Information = 0;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 11, 0 );
|
||
|
||
//
|
||
// The file has been successfully opened for exclusive access, with
|
||
// at least as much desired access as requested by the client.
|
||
// Attempt to allocate structures to represent the open. If any
|
||
// errors occur, CompleteOpen does full cleanup, including closing
|
||
// the file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoCompatibilityOpen: Open of %wZ succeeded, file handle: 0x%p\n", RelativeName, fileHandle ));
|
||
}
|
||
|
||
completionStatus = CompleteOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
NULL,
|
||
fileHandle,
|
||
&desiredAccess,
|
||
0, // ShareAccess
|
||
createOptions,
|
||
TRUE,
|
||
FALSE,
|
||
LfcbAddedToMfcbList
|
||
);
|
||
|
||
//
|
||
// Return the "interesting" status code. If CompleteOpen() succeeds
|
||
// return the open status. If it fails, it will clean up the open
|
||
// file, and we return a failure status.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( completionStatus ) ) {
|
||
return completionStatus;
|
||
} else {
|
||
return status;
|
||
}
|
||
|
||
} // DoCompatibilityOpen
|
||
|
||
|
||
NTSTATUS
|
||
DoFcbOpen(
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
OUT PIO_STATUS_BLOCK IoStatusBlock,
|
||
IN USHORT SmbFileAttributes,
|
||
IN USHORT SmbOpenFunction,
|
||
IN ULONG SmbAllocationSize,
|
||
IN PUNICODE_STRING RelativeName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes an FCB open.
|
||
|
||
*** The MFCB lock must be held on entry to this routine; the lock
|
||
remains held on exit.
|
||
|
||
Arguments:
|
||
|
||
Rfcb - A pointer to a pointer to an RFCB that will point to the
|
||
newly-created RFCB.
|
||
|
||
Mfcb - A pointer to the MFCB for this file
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
IoStatusBlock - A pointer to an IO status block.
|
||
|
||
SmbFileAttributes - File attributes in SMB protocol format.
|
||
|
||
SmbOpenFunction - Open function in SMB protocol format.
|
||
|
||
SmbAllocationSize - Allocation size for new files.
|
||
|
||
RelativeName - The share-relative name of the file being opened.
|
||
|
||
EaBuffer - Optional pointer to a full EA list to pass to SrvIoCreateFile.
|
||
|
||
EaLength - Length of the EA buffer.
|
||
|
||
EaErrorOffset - Optional pointer to the location in which to write
|
||
the offset to the EA that caused an error.
|
||
|
||
LfcbAddedToMfcbList - Pointer to a boolean that will be set to TRUE if
|
||
an lfcb is added to the mfcb list of lfcbs.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
NTSTATUS completionStatus;
|
||
|
||
PLIST_ENTRY lfcbEntry;
|
||
PLIST_ENTRY rfcbEntry;
|
||
|
||
PRFCB rfcb;
|
||
PPAGED_RFCB pagedRfcb;
|
||
PLFCB lfcb;
|
||
|
||
HANDLE fileHandle;
|
||
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
ULONG attributes;
|
||
|
||
LARGE_INTEGER allocationSize;
|
||
ULONG fileAttributes;
|
||
BOOLEAN directory;
|
||
ULONG createOptions;
|
||
ULONG createDisposition;
|
||
ULONG shareAccess;
|
||
BOOLEAN compatibilityOpen;
|
||
|
||
PAGED_CODE( );
|
||
|
||
*LfcbAddedToMfcbList = FALSE;
|
||
|
||
//
|
||
// Set createDisposition parameter from OpenFunction.
|
||
//
|
||
|
||
status = MapOpenFunction( SmbOpenFunction, &createDisposition );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Set createOptions parameter.
|
||
//
|
||
|
||
if ( (SmbGetAlignedUshort( &WorkContext->RequestHeader->Flags2 ) &
|
||
SMB_FLAGS2_KNOWS_EAS) == 0) {
|
||
|
||
//
|
||
// This guy does not know eas
|
||
//
|
||
|
||
createOptions = FILE_NON_DIRECTORY_FILE |
|
||
FILE_NO_EA_KNOWLEDGE;
|
||
} else {
|
||
|
||
createOptions = FILE_NON_DIRECTORY_FILE;
|
||
}
|
||
|
||
//
|
||
// We're going to open this file relative to the root directory
|
||
// of the share. Load up the necessary fields in the object
|
||
// attributes structure.
|
||
//
|
||
|
||
if ( WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else if ( WorkContext->Session->UsingUppercasePaths ) {
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
} else {
|
||
attributes = 0L;
|
||
}
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
RelativeName,
|
||
attributes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
createOptions |= FILE_COMPLETE_IF_OPLOCKED;
|
||
|
||
if ( Mfcb->ActiveRfcbCount > 0 ) {
|
||
|
||
//
|
||
// The named file is already open by the server. If the client
|
||
// specified that it didn't want to open an existing file,
|
||
// reject this open.
|
||
//
|
||
|
||
if ( createDisposition == FILE_CREATE ) {
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: FCB open of %wZ rejected; wants to create\n", RelativeName ));
|
||
}
|
||
|
||
return STATUS_OBJECT_NAME_COLLISION;
|
||
}
|
||
|
||
//
|
||
// If the requesting session already has the file open in FCB
|
||
// mode, fold this open into the existing open by returning a
|
||
// pointer to the existing RFCB.
|
||
//
|
||
// *** Multiple FCB opens are folded together because the client
|
||
// may send only one close; that single close closes all FCB
|
||
// opens by the client.
|
||
//
|
||
|
||
for ( lfcbEntry = Mfcb->LfcbList.Flink;
|
||
lfcbEntry != &Mfcb->LfcbList;
|
||
lfcbEntry = lfcbEntry->Flink ) {
|
||
|
||
lfcb = CONTAINING_RECORD( lfcbEntry, LFCB, MfcbListEntry );
|
||
|
||
if ( lfcb->Session == WorkContext->Session ) {
|
||
|
||
//
|
||
// This LFCB is owned by the requesting session. Check
|
||
// for RFCBs opened in FCB mode.
|
||
//
|
||
|
||
for ( rfcbEntry = lfcb->RfcbList.Flink;
|
||
rfcbEntry != &lfcb->RfcbList;
|
||
rfcbEntry = rfcbEntry->Flink ) {
|
||
|
||
pagedRfcb = CONTAINING_RECORD(
|
||
rfcbEntry,
|
||
PAGED_RFCB,
|
||
LfcbListEntry
|
||
);
|
||
|
||
rfcb = pagedRfcb->PagedHeader.NonPagedBlock;
|
||
if ( (pagedRfcb->FcbOpenCount != 0) &&
|
||
(GET_BLOCK_STATE(rfcb) == BlockStateActive) ) {
|
||
|
||
//
|
||
// The requesting session already has the file
|
||
// open in FCB mode. Rather than reopening the
|
||
// file, or even linking a new RFCB off the
|
||
// LFCB, we just return a pointer to the
|
||
// existing RFCB.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: FCB open of %wZ accepted; duplicates FCB open\n", RelativeName ));
|
||
}
|
||
|
||
SrvReferenceRfcb( rfcb );
|
||
|
||
pagedRfcb->FcbOpenCount++;
|
||
|
||
IoStatusBlock->Information = FILE_OPENED;
|
||
|
||
WorkContext->Rfcb = rfcb;
|
||
*Rfcb = rfcb;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // if ( rfcb->FcbOpenCount != 0 )
|
||
|
||
} // for ( rfcbEntry = lfcb->RfcbList.Flink; ...
|
||
|
||
} // if ( lfcb->Session == WorkContext->Session )
|
||
|
||
} // for ( lfcbEntry = mfcb->LfcbList.Flink; ...
|
||
|
||
//
|
||
// The server has the file open, but the requesting session
|
||
// doesn't already have an FCB open for the file. If the
|
||
// existing open is a compatibility mode open open by this
|
||
// session, we just add a new RFCB. If it's a compatibility
|
||
// mode open by a different session, we reject this open.
|
||
//
|
||
|
||
if ( Mfcb->CompatibilityOpen ) {
|
||
|
||
//
|
||
// The named file is open in compatibility mode. Get a
|
||
// pointer to the LFCB for the open. Determine whether the
|
||
// requesting session is the one that did the original open.
|
||
//
|
||
// Normally there will only be one LFCB linked to a
|
||
// compatibility mode MFCB. However, it is possible for
|
||
// there to briefly be multiple LFCBs. When an LFCB is in
|
||
// the process of closing, the ActiveRfcbCount will be 0, so
|
||
// a new open will be treated as the first open of the MFCB,
|
||
// and there will be two LFCBs linked to the MFCB. There
|
||
// can actually be more than two LFCBs linked if the rundown
|
||
// of the closing LFCBs takes some time. So the find "the"
|
||
// LFCB for the open, we go to the tail of the MFCB's list.
|
||
//
|
||
|
||
lfcb = CONTAINING_RECORD( Mfcb->LfcbList.Blink, LFCB, MfcbListEntry );
|
||
|
||
if ( lfcb->Session != WorkContext->Session ) {
|
||
|
||
//
|
||
// A different session has the file open in
|
||
// compatibility mode. Reject this open request.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: FCB open of %wZ rejected; already open in compatibility mode\n",
|
||
RelativeName ));
|
||
}
|
||
|
||
return STATUS_SHARING_VIOLATION;
|
||
}
|
||
|
||
//
|
||
// The same client has the file open in compatibility mode.
|
||
// Allocate a new RFCB and link it into the existing LFCB.
|
||
// If any errors occur, CompleteOpen does full cleanup.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: FCB open of %wZ accepted; duplicates compatibility open\n", RelativeName ));
|
||
}
|
||
|
||
IoStatusBlock->Information = FILE_OPENED;
|
||
|
||
status = CompleteOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
lfcb,
|
||
NULL,
|
||
NULL,
|
||
0, // ShareAccess
|
||
0,
|
||
TRUE,
|
||
TRUE,
|
||
LfcbAddedToMfcbList
|
||
);
|
||
|
||
return status;
|
||
|
||
} // if ( mfcb->CompatibilityOpen )
|
||
|
||
} // if ( mfcb->ActiveRfcbCount > 0 )
|
||
|
||
//
|
||
// Either the file is not already open by the server, or it's open
|
||
// for normal sharing, and not in FCB mode by this session. Because
|
||
// we're supposed to give the client maximum access to the file, we
|
||
// do the following:
|
||
//
|
||
// 1) Try to open the file for read/write/delete, exclusive access.
|
||
// Obviously this will fail if the file is already open. But
|
||
// what we're really trying to find out is what access the client
|
||
// has to the file. If this attempt fails with a sharing
|
||
// violation, then we know the client has write/delete access,
|
||
// but someone else has it open. So we can't get compatibility
|
||
// mode. Therefore, we reject the open. On the other hand, if
|
||
// we get an access denied error, then we know the client can't
|
||
// write/delete the file, so we try again with write access. Of
|
||
// course, this first open could succeed, in which case the
|
||
// client has the file open for read/write in compatibility mode.
|
||
//
|
||
// 2) Try to open the file for read/write, exclusive access. As
|
||
// above, if it fails with a sharing violation, then we know the
|
||
// client has write access, but someone else has it open. So we
|
||
// can't get compatibility mode, and we reject the open. If we
|
||
// get an access denied error, then we know the client can't
|
||
// write the file, so we try again with readonly access. If this
|
||
// open succeeds, the client has the file open for read/write in
|
||
// compatibility mode.
|
||
//
|
||
// 3) If we get here, we know the client can't write to the file,
|
||
// so we try to open the file for readonly, shared access. This
|
||
// no longer a compatibility mode open. If we get any kind of a
|
||
// failure here, we're just out of luck.
|
||
//
|
||
|
||
compatibilityOpen = TRUE;
|
||
|
||
//
|
||
// Set block size according to the AllocationSize in the request SMB.
|
||
//
|
||
|
||
allocationSize.QuadPart = SmbAllocationSize;
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: Opening file %wZ\n", RelativeName ));
|
||
}
|
||
|
||
//
|
||
// Get the value for fileAttributes.
|
||
//
|
||
|
||
SRV_SMB_ATTRIBUTES_TO_NT(
|
||
SmbFileAttributes,
|
||
&directory,
|
||
&fileAttributes
|
||
);
|
||
|
||
//
|
||
// Try to open the file for Read/Write/Delete access.
|
||
//
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
//
|
||
// Ensure the EaBuffer is correctly formatted. Since we are a kernel mode
|
||
// component, the Io subsystem does not check it for us.
|
||
//
|
||
if( ARGUMENT_PRESENT( EaBuffer ) ) {
|
||
status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION)EaBuffer, EaLength, EaErrorOffset );
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ | GENERIC_WRITE | DELETE, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
}
|
||
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
|
||
//
|
||
// The client doesn't have Read/Write/Delete access to the file.
|
||
// Try for Read/Write access.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: r/w/d access denied.\n" ));
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ | GENERIC_WRITE, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
0L, // ShareAccess
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
|
||
//
|
||
// The client doesn't have Read/Write access to the file.
|
||
// Try Read access. If soft compatibility mapping is
|
||
// enabled, use SHARE=READ and don't call this a
|
||
// compatibility mode open.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: r/w access denied.\n" ));
|
||
}
|
||
|
||
shareAccess = 0;
|
||
if ( SrvEnableSoftCompatibility ) {
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: FCB open of %wZ mapped to normal open\n", RelativeName ));
|
||
}
|
||
shareAccess = FILE_SHARE_READ;
|
||
compatibilityOpen = FALSE;
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ, // DesiredAccess
|
||
&objectAttributes,
|
||
IoStatusBlock,
|
||
&allocationSize,
|
||
fileAttributes,
|
||
shareAccess,
|
||
createDisposition,
|
||
createOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
|
||
//
|
||
// If the user didn't have this permission, update the
|
||
// statistics database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If we got sharing violation and this is a disk file, and this is
|
||
// the first open attempt, setup for a blocking open attempt. If the
|
||
// file is batch oplocked, the non-blocking open would fail, and the
|
||
// oplock will not break.
|
||
//
|
||
|
||
if ( status == STATUS_SHARING_VIOLATION &&
|
||
WorkContext->ProcessingCount == 1 &&
|
||
WorkContext->TreeConnect->Share->ShareType == ShareTypeDisk ) {
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = TRUE;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// All of the open attempts failed.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: all opens failed; status = %X\n",
|
||
status ));
|
||
}
|
||
|
||
//
|
||
// Set the error offset if needed.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(EaErrorOffset) ) {
|
||
*EaErrorOffset = (ULONG)IoStatusBlock->Information;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 12, 0 );
|
||
|
||
//
|
||
// The file has been successfully opened. Attempt to allocate
|
||
// structures to represent the open. If any errors occur,
|
||
// CompleteOpen does full cleanup, including closing the file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "DoFcbOpen: Open of %wZ succeeded, file handle: 0x%p\n", RelativeName, fileHandle ));
|
||
}
|
||
|
||
completionStatus = CompleteOpen(
|
||
Rfcb,
|
||
Mfcb,
|
||
WorkContext,
|
||
NULL,
|
||
fileHandle,
|
||
NULL,
|
||
0, // ShareAccess
|
||
0,
|
||
compatibilityOpen,
|
||
TRUE,
|
||
LfcbAddedToMfcbList
|
||
);
|
||
|
||
//
|
||
// Return the "interesting" status code. If CompleteOpen() succeeds
|
||
// return the open status. If it fails, it will clean up the open
|
||
// file, and we return a failure status.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( completionStatus ) ) {
|
||
return completionStatus;
|
||
} else {
|
||
return status;
|
||
}
|
||
|
||
} // DoFcbOpen
|
||
|
||
|
||
PTABLE_ENTRY
|
||
FindAndClaimFileTableEntry (
|
||
IN PCONNECTION Connection,
|
||
OUT PSHORT FidIndex
|
||
)
|
||
{
|
||
PTABLE_HEADER tableHeader;
|
||
SHORT fidIndex;
|
||
PTABLE_ENTRY entry;
|
||
KIRQL oldIrql;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
tableHeader = &Connection->FileTable;
|
||
|
||
ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &oldIrql );
|
||
|
||
if ( tableHeader->FirstFreeEntry == -1
|
||
&&
|
||
SrvGrowTable(
|
||
tableHeader,
|
||
SrvInitialFileTableSize,
|
||
SrvMaxFileTableSize,
|
||
NULL ) == FALSE
|
||
) {
|
||
|
||
RELEASE_SPIN_LOCK( &Connection->SpinLock, oldIrql );
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Remove the FID slot from the free list, but don't set its owner
|
||
// and sequence number yet.
|
||
//
|
||
|
||
fidIndex = tableHeader->FirstFreeEntry;
|
||
|
||
entry = &tableHeader->Table[fidIndex];
|
||
|
||
tableHeader->FirstFreeEntry = entry->NextFreeEntry;
|
||
DEBUG entry->NextFreeEntry = -2;
|
||
if ( tableHeader->LastFreeEntry == fidIndex ) {
|
||
tableHeader->LastFreeEntry = -1;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &Connection->SpinLock, oldIrql );
|
||
|
||
*FidIndex = fidIndex;
|
||
return entry;
|
||
|
||
} // FindAndClaimFileTableEntry
|
||
|
||
|
||
NTSTATUS
|
||
CompleteOpen (
|
||
OUT PRFCB *Rfcb,
|
||
IN PMFCB Mfcb,
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
IN PLFCB ExistingLfcb OPTIONAL,
|
||
IN HANDLE FileHandle OPTIONAL,
|
||
IN PACCESS_MASK RemoteGrantedAccess OPTIONAL,
|
||
IN ULONG ShareAccess,
|
||
IN ULONG FileMode,
|
||
IN BOOLEAN CompatibilityOpen,
|
||
IN BOOLEAN FcbOpen,
|
||
OUT PBOOLEAN LfcbAddedToMfcbList
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Completes structure allocation, initialization, and linking after
|
||
a successful open. Updates Master File Table as appropriate.
|
||
Adds entry to connection's file table.
|
||
|
||
*** The MFCB lock must be held on entry to this routine; the lock
|
||
remains held on exit.
|
||
|
||
Arguments:
|
||
|
||
Rfcb - A pointer to a pointer to an RFCB that will point to the
|
||
newly-created RFCB.
|
||
|
||
Mfcb - A pointer to the MFCB for this file.
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
ExistingLfcb - Optional address of an existing Local File Control
|
||
Block. Specified when folding a duplicate compatibility mode
|
||
open into a single local open.
|
||
|
||
FileHandle - Optional file handle obtained from SrvIoCreateFile.
|
||
Ignored when ExistingLfcb is specified.
|
||
|
||
RemoteGrantedAccess - Optional granted access to be stored in new
|
||
RFCB. If not specified, granted access from LFCB (i.e., access
|
||
obtained on local open) is used.
|
||
|
||
FileMode - Same value specified as CreateOptions on SrvIoCreateFile
|
||
call. Indicates whether client wants writethrough mode.
|
||
|
||
CompatibilityOpen - TRUE if this is a compatibility mode open.
|
||
|
||
FcbOpen - TRUE if this is an FCB open.
|
||
|
||
LfcbAddedToMfcbList - Pointer to a boolean that will be set to TRUE if
|
||
an lfcb is added to the mfcb list of lfcbs.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
|
||
PRFCB rfcb;
|
||
PPAGED_RFCB pagedRfcb;
|
||
PLFCB newLfcb;
|
||
PLFCB lfcb;
|
||
BOOLEAN rfcbLinkedToLfcb;
|
||
|
||
PFILE_OBJECT fileObject;
|
||
|
||
OBJECT_HANDLE_INFORMATION handleInformation;
|
||
|
||
PCONNECTION connection;
|
||
PTABLE_ENTRY entry;
|
||
SHORT fidIndex;
|
||
|
||
ULONG pid;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Initialize various fields for the error handler.
|
||
//
|
||
|
||
rfcb = NULL;
|
||
newLfcb = NULL;
|
||
rfcbLinkedToLfcb = FALSE;
|
||
fileObject = NULL;
|
||
*LfcbAddedToMfcbList = FALSE;
|
||
|
||
//
|
||
// Allocate an RFCB.
|
||
//
|
||
|
||
SrvAllocateRfcb( &rfcb, WorkContext );
|
||
|
||
if ( rfcb == NULL ) {
|
||
|
||
ULONG length = sizeof( RFCB );
|
||
|
||
//
|
||
// Unable to allocate RFCB. Return an error to the client.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Unable to allocate RFCB\n" ));
|
||
}
|
||
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
goto error_exit;
|
||
|
||
}
|
||
|
||
pagedRfcb = rfcb->PagedRfcb;
|
||
|
||
//
|
||
// If no existing LFCB address was passed in (i.e., if this is not a
|
||
// duplicate compatibility mode open), allocate and initialize a new
|
||
// LFCB.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( ExistingLfcb ) ) {
|
||
|
||
ASSERT( CompatibilityOpen );
|
||
ASSERT( ExistingLfcb->CompatibilityOpen );
|
||
|
||
lfcb = ExistingLfcb;
|
||
|
||
} else {
|
||
|
||
PFAST_IO_DISPATCH fastIoDispatch;
|
||
|
||
SrvAllocateLfcb( &newLfcb, WorkContext );
|
||
|
||
if ( newLfcb == NULL ) {
|
||
|
||
//
|
||
// Unable to allocate LFCB. Return an error to the client.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Unable to allocate LFCB\n" ));
|
||
}
|
||
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
goto error_exit;
|
||
|
||
}
|
||
|
||
lfcb = newLfcb;
|
||
|
||
//
|
||
// Get a pointer to the file object, so that we can directly
|
||
// build IRPs for asynchronous operations (read and write).
|
||
// Also, get the granted access mask, so that we can prevent the
|
||
// client from doing things that it isn't allowed to do.
|
||
//
|
||
// *** Note that the granted access on the local open may allow
|
||
// more access than was requested on the remote open.
|
||
// That's why the RFCB has its own granted access field.
|
||
//
|
||
|
||
status = ObReferenceObjectByHandle(
|
||
FileHandle,
|
||
0,
|
||
NULL,
|
||
KernelMode,
|
||
(PVOID *)&fileObject,
|
||
&handleInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
SrvLogServiceFailure( SRV_SVC_OB_REF_BY_HANDLE, status );
|
||
|
||
//
|
||
// This internal error bugchecks the system.
|
||
//
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_IMPOSSIBLE,
|
||
"CompleteOpen: unable to reference file handle 0x%lx",
|
||
FileHandle,
|
||
NULL
|
||
);
|
||
|
||
goto error_exit;
|
||
|
||
}
|
||
|
||
//
|
||
// Initialize the new LFCB.
|
||
//
|
||
|
||
lfcb->FileHandle = FileHandle;
|
||
lfcb->FileObject = fileObject;
|
||
|
||
lfcb->GrantedAccess = handleInformation.GrantedAccess;
|
||
lfcb->DeviceObject = IoGetRelatedDeviceObject( fileObject );
|
||
|
||
fastIoDispatch = lfcb->DeviceObject->DriverObject->FastIoDispatch;
|
||
if ( fastIoDispatch != NULL ) {
|
||
lfcb->FastIoRead = fastIoDispatch->FastIoRead;
|
||
lfcb->FastIoWrite = fastIoDispatch->FastIoWrite;
|
||
lfcb->FastIoLock = fastIoDispatch->FastIoLock;
|
||
lfcb->FastIoUnlockSingle = fastIoDispatch->FastIoUnlockSingle;
|
||
|
||
//
|
||
// Fill in Mdl calls. If the file system's vector is large enough,
|
||
// we still need to check if one of the routines is specified. But
|
||
// if one is specified they all must be.
|
||
//
|
||
if ((fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteComplete)) &&
|
||
(fastIoDispatch->MdlRead != NULL)) {
|
||
|
||
lfcb->MdlRead = fastIoDispatch->MdlRead;
|
||
lfcb->MdlReadComplete = fastIoDispatch->MdlReadComplete;
|
||
lfcb->PrepareMdlWrite = fastIoDispatch->PrepareMdlWrite;
|
||
lfcb->MdlWriteComplete = fastIoDispatch->MdlWriteComplete;
|
||
|
||
|
||
} else if( IoGetBaseFileSystemDeviceObject( fileObject ) == lfcb->DeviceObject ) {
|
||
//
|
||
// Otherwise default to the original FsRtl routines if we are right atop
|
||
// a filesystem.
|
||
//
|
||
lfcb->MdlRead = FsRtlMdlReadDev;
|
||
lfcb->MdlReadComplete = FsRtlMdlReadCompleteDev;
|
||
lfcb->PrepareMdlWrite = FsRtlPrepareMdlWriteDev;
|
||
lfcb->MdlWriteComplete = FsRtlMdlWriteCompleteDev;
|
||
} else {
|
||
//
|
||
// Otherwise, make them fail!
|
||
//
|
||
lfcb->MdlRead = SrvFailMdlReadDev;
|
||
lfcb->PrepareMdlWrite = SrvFailPrepareMdlWriteDev;
|
||
}
|
||
|
||
//
|
||
// Fill in Mdl calls, if the file system vector is long enough.
|
||
// For now we will just copy all six compressed routines over,
|
||
// whether they are actually supplied or not (NULL). There are
|
||
// no default routines for these, they are either supported or not.
|
||
//
|
||
|
||
if ((fastIoDispatch->SizeOfFastIoDispatch > FIELD_OFFSET(FAST_IO_DISPATCH, MdlWriteCompleteCompressed))) {
|
||
|
||
lfcb->FastIoReadCompressed = fastIoDispatch->FastIoReadCompressed;
|
||
lfcb->FastIoWriteCompressed = fastIoDispatch->FastIoWriteCompressed;
|
||
lfcb->MdlReadCompleteCompressed = fastIoDispatch->MdlReadCompleteCompressed;
|
||
lfcb->MdlWriteCompleteCompressed = fastIoDispatch->MdlWriteCompleteCompressed;
|
||
}
|
||
}
|
||
|
||
lfcb->FileMode = FileMode & ~FILE_COMPLETE_IF_OPLOCKED;
|
||
lfcb->CompatibilityOpen = CompatibilityOpen;
|
||
}
|
||
|
||
//
|
||
// Initialize the RFCB.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT( RemoteGrantedAccess ) ) {
|
||
rfcb->GrantedAccess = *RemoteGrantedAccess;
|
||
IoCheckDesiredAccess( &rfcb->GrantedAccess, lfcb->GrantedAccess );
|
||
} else {
|
||
rfcb->GrantedAccess = lfcb->GrantedAccess;
|
||
}
|
||
|
||
rfcb->ShareAccess = ShareAccess;
|
||
rfcb->FileMode = lfcb->FileMode;
|
||
rfcb->Mfcb = Mfcb;
|
||
#ifdef SRVCATCH
|
||
rfcb->SrvCatch = Mfcb->SrvCatch;
|
||
#endif
|
||
|
||
//
|
||
// If delete on close was specified, don't attempt to cache this rfcb.
|
||
//
|
||
|
||
if ( (FileMode & FILE_DELETE_ON_CLOSE) != 0 ) {
|
||
rfcb->IsCacheable = FALSE;
|
||
}
|
||
|
||
//
|
||
// Check for granted access
|
||
//
|
||
|
||
//
|
||
// Locks
|
||
//
|
||
|
||
CHECK_FUNCTION_ACCESS(
|
||
rfcb->GrantedAccess,
|
||
IRP_MJ_LOCK_CONTROL,
|
||
IRP_MN_LOCK,
|
||
0,
|
||
&status
|
||
);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
rfcb->LockAccessGranted = TRUE;
|
||
} else {
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Lock IoCheckFunctionAccess failed: "
|
||
"0x%X, GrantedAccess: %lx\n",
|
||
status, rfcb->GrantedAccess ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unlocks
|
||
//
|
||
|
||
CHECK_FUNCTION_ACCESS(
|
||
rfcb->GrantedAccess,
|
||
IRP_MJ_LOCK_CONTROL,
|
||
IRP_MN_UNLOCK_SINGLE,
|
||
0,
|
||
&status
|
||
);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
rfcb->UnlockAccessGranted = TRUE;
|
||
} else {
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Unlock IoCheckFunctionAccess failed: "
|
||
"0x%X, GrantedAccess: %lx\n",
|
||
status, rfcb->GrantedAccess ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reads
|
||
//
|
||
|
||
CHECK_FUNCTION_ACCESS(
|
||
rfcb->GrantedAccess,
|
||
IRP_MJ_READ,
|
||
0, 0, &status );
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
rfcb->ReadAccessGranted = TRUE;
|
||
} else {
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Read IoCheckFunctionAccess failed: "
|
||
"0x%X, GrantedAccess: %lx\n",
|
||
status, rfcb->GrantedAccess ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Writes
|
||
//
|
||
|
||
if( rfcb->GrantedAccess & FILE_WRITE_DATA ) {
|
||
rfcb->WriteAccessGranted = TRUE;
|
||
}
|
||
if( rfcb->GrantedAccess & FILE_APPEND_DATA ) {
|
||
rfcb->AppendAccessGranted = TRUE;
|
||
|
||
//
|
||
// This hack is required for now. The problem is that clients, given an
|
||
// oplock, will write whole pages to the server. The offset of the page
|
||
// will likely cover the last part of the file, and the server will reject
|
||
// the write. Code needs to be added to the server to ignore the
|
||
// first part of the page. Or we could just not give the client an oplock
|
||
// if append access is granted. For now, we revert to prior NT4 behavior.
|
||
//
|
||
rfcb->WriteAccessGranted = TRUE;
|
||
}
|
||
|
||
//
|
||
// Copy the TID from the tree connect into the RFCB. We do this to
|
||
// reduce the number of indirections we have to take. Save the PID
|
||
// of the remote process that's opening the file. We'll need this
|
||
// if we get a Process Exit SMB.
|
||
//
|
||
|
||
rfcb->Tid = WorkContext->TreeConnect->Tid;
|
||
rfcb->Pid = SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
|
||
pid = rfcb->Pid;
|
||
rfcb->Uid = WorkContext->Session->Uid;
|
||
|
||
if ( FcbOpen ) {
|
||
pagedRfcb->FcbOpenCount = 1;
|
||
}
|
||
|
||
if ( WorkContext->Endpoint->IsConnectionless ) {
|
||
rfcb->WriteMpx.FileObject = lfcb->FileObject;
|
||
rfcb->WriteMpx.MpxGlommingAllowed =
|
||
(BOOLEAN)((lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) != 0);
|
||
}
|
||
|
||
//
|
||
// If this is a named pipe, fill in the named pipe specific
|
||
// information. The default mode on open is always byte mode,
|
||
// blocking.
|
||
//
|
||
|
||
rfcb->ShareType = WorkContext->TreeConnect->Share->ShareType;
|
||
|
||
if ( rfcb->ShareType == ShareTypePipe ) {
|
||
rfcb->BlockingModePipe = TRUE;
|
||
rfcb->ByteModePipe = TRUE;
|
||
}
|
||
|
||
//
|
||
// Link the RFCB into the LFCB.
|
||
//
|
||
|
||
SrvInsertTailList( &lfcb->RfcbList, &pagedRfcb->LfcbListEntry );
|
||
rfcb->Lfcb = lfcb;
|
||
lfcb->BlockHeader.ReferenceCount++;
|
||
UPDATE_REFERENCE_HISTORY( lfcb, FALSE );
|
||
lfcb->HandleCount++;
|
||
rfcbLinkedToLfcb = TRUE;
|
||
|
||
//
|
||
// Making a new RFCB visible is a multi-step operation. It must be
|
||
// inserted in the global ordered file list and the containing
|
||
// connection's file table. If the LFCB is not new, it must be
|
||
// inserted in the MFCB's list of LFCBs, and the connection, the
|
||
// session, and the tree connect must all be referenced. We need to
|
||
// make these operations appear atomic, so that the RFCB cannot be
|
||
// accessed elsewhere before we're done setting it up. In order to
|
||
// do this, we hold all necessary locks the entire time we're doing
|
||
// the operations. The locks that are required are:
|
||
//
|
||
// 1) the MFCB lock (which protects the MFCB's LFCB list),
|
||
//
|
||
// 2) the global ordered list lock (which protects the ordered file
|
||
// list),
|
||
//
|
||
// 3) the connection lock (which prevents closing of the
|
||
// connection, the session, and the tree connect), and
|
||
//
|
||
// These locks are taken out in the order listed above, as dictated
|
||
// by lock levels (see lock.h). Note that the MFCB lock is already
|
||
// held on entry to this routine.
|
||
//
|
||
|
||
connection = WorkContext->Connection;
|
||
|
||
ASSERT( ExIsResourceAcquiredExclusiveLite(&RESOURCE_OF(Mfcb->NonpagedMfcb->Lock)) );
|
||
ASSERT( SrvRfcbList.Lock == &SrvOrderedListLock );
|
||
ACQUIRE_LOCK( SrvRfcbList.Lock );
|
||
ACQUIRE_LOCK( &connection->Lock );
|
||
|
||
//
|
||
// We first check all conditions to make sure that we can actually
|
||
// insert this RFCB.
|
||
//
|
||
// Make sure that the tree connect isn't closing.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(WorkContext->TreeConnect) != BlockStateActive ) {
|
||
|
||
//
|
||
// The tree connect is closing. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Tree connect is closing\n" ));
|
||
}
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cant_insert;
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the session isn't closing.
|
||
//
|
||
|
||
if ( GET_BLOCK_STATE(WorkContext->Session) != BlockStateActive ) {
|
||
|
||
//
|
||
// The session is closing. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Session is closing\n" ));
|
||
}
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cant_insert;
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the connection isn't closing.
|
||
//
|
||
|
||
connection = WorkContext->Connection;
|
||
|
||
if ( GET_BLOCK_STATE(connection) != BlockStateActive ) {
|
||
|
||
//
|
||
// The connection is closing. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: Connection closing\n" ));
|
||
}
|
||
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto cant_insert;
|
||
}
|
||
|
||
//
|
||
// Find and claim a FID that can be used for this file.
|
||
//
|
||
|
||
entry = FindAndClaimFileTableEntry( connection, &fidIndex );
|
||
if ( entry == NULL ) {
|
||
|
||
//
|
||
// No free entries in the file table. Reject the request.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "CompleteOpen: No more FIDs available.\n" ));
|
||
}
|
||
|
||
SrvLogTableFullError( SRV_TABLE_FILE );
|
||
|
||
status = STATUS_OS2_TOO_MANY_OPEN_FILES;
|
||
goto cant_insert;
|
||
}
|
||
|
||
//
|
||
// All conditions have been satisfied. We can now do the things
|
||
// necessary to make the file visible.
|
||
//
|
||
// If this isn't a duplicate open, add this open file instance to
|
||
// the Master File Table.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( ExistingLfcb ) ) {
|
||
|
||
//
|
||
// Add the new LFCB to the master file's list of open instances.
|
||
// We set LfcbAddedToMfcbList to tell the calling routine that
|
||
// an LFCB has been queued to the MFCB and that the reference
|
||
// count that was previously incremented should not be
|
||
// decremented.
|
||
//
|
||
|
||
*LfcbAddedToMfcbList = TRUE;
|
||
Mfcb->CompatibilityOpen = lfcb->CompatibilityOpen;
|
||
SrvInsertTailList( &Mfcb->LfcbList, &lfcb->MfcbListEntry );
|
||
lfcb->Mfcb = Mfcb;
|
||
|
||
//
|
||
// Point the LFCB to the connection, session, and tree connect,
|
||
// referencing them to account for the open file and therefore
|
||
// prevent deletion.
|
||
//
|
||
|
||
SrvReferenceConnection( connection );
|
||
lfcb->Connection = connection;
|
||
|
||
SrvReferenceSession( WorkContext->Session );
|
||
lfcb->Session = WorkContext->Session;
|
||
|
||
SrvReferenceTreeConnect( WorkContext->TreeConnect );
|
||
lfcb->TreeConnect = WorkContext->TreeConnect;
|
||
|
||
//
|
||
// Increment the count of open files in the session and tree
|
||
// connect. These counts prevent autodisconnecting a session
|
||
// that has open files and are used by server APIs.
|
||
//
|
||
|
||
WorkContext->Session->CurrentFileOpenCount++;
|
||
WorkContext->TreeConnect->CurrentFileOpenCount++;
|
||
|
||
}
|
||
|
||
//
|
||
// Capture the connection pointer in the nonpaged RFCB so that we
|
||
// can find the connection at DPC level.
|
||
//
|
||
|
||
rfcb->Connection = lfcb->Connection;
|
||
|
||
//
|
||
// Insert the RFCB on the global ordered list.
|
||
//
|
||
|
||
SrvInsertEntryOrderedList( &SrvRfcbList, rfcb );
|
||
|
||
//
|
||
// Set the owner and sequence number of the file table slot. Create
|
||
// a FID for the file.
|
||
//
|
||
|
||
entry->Owner = rfcb;
|
||
|
||
INCREMENT_FID_SEQUENCE( entry->SequenceNumber );
|
||
if ( fidIndex == 0 && entry->SequenceNumber == 0 ) {
|
||
INCREMENT_FID_SEQUENCE( entry->SequenceNumber );
|
||
}
|
||
|
||
rfcb->Fid = MAKE_FID( fidIndex, entry->SequenceNumber );
|
||
rfcb->ShiftedFid = rfcb->Fid << 16;
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "CompleteOpen: Found FID. Index = 0x%lx, sequence = 0x%lx\n",
|
||
FID_INDEX( rfcb->Fid ), FID_SEQUENCE( rfcb->Fid ) ));
|
||
}
|
||
|
||
//
|
||
// Release the locks used to make this operation appear atomic.
|
||
//
|
||
// Note that our caller expects us to keep the MFCB lock held.
|
||
//
|
||
|
||
RELEASE_LOCK( &connection->Lock );
|
||
RELEASE_LOCK( SrvRfcbList.Lock );
|
||
|
||
//
|
||
// File successfully opened. Save in the WorkContext block for
|
||
// the followon SMB, if any. Cache the Rfcb.
|
||
//
|
||
|
||
WorkContext->Rfcb = rfcb;
|
||
*Rfcb = rfcb;
|
||
|
||
//
|
||
// Update the MFCB active count.
|
||
//
|
||
|
||
++Mfcb->ActiveRfcbCount;
|
||
|
||
//
|
||
// If this is a pipe, set the client ID for the pipe. If it is a
|
||
// message type named pipe, set the read mode to message mode.
|
||
//
|
||
|
||
if ( rfcb->ShareType == ShareTypePipe ) {
|
||
|
||
//
|
||
// NT clients put the high part of the PID in a reserved
|
||
// location in the SMB header.
|
||
//
|
||
|
||
if ( IS_NT_DIALECT( WorkContext->Connection->SmbDialect ) ) {
|
||
pid = (SmbGetUshort( &WorkContext->RequestHeader->PidHigh )
|
||
<< 16) | pid;
|
||
}
|
||
|
||
(VOID)SrvIssueSetClientProcessRequest(
|
||
lfcb->FileObject,
|
||
&lfcb->DeviceObject,
|
||
connection,
|
||
WorkContext->Session,
|
||
LongToPtr( pid )
|
||
);
|
||
|
||
//
|
||
// Set the read mode.
|
||
//
|
||
|
||
rfcb->ByteModePipe = !SetDefaultPipeMode( FileHandle );
|
||
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
cant_insert:
|
||
|
||
//
|
||
// Release the locks.
|
||
//
|
||
// Note that our caller expects us to keep the MFCB lock held.
|
||
//
|
||
|
||
RELEASE_LOCK( &connection->Lock );
|
||
RELEASE_LOCK( SrvRfcbList.Lock );
|
||
|
||
error_exit:
|
||
|
||
//
|
||
// Error cleanup. Put things back into their initial state.
|
||
//
|
||
// If the new RFCB was associated with an LFCB, unlink it now.
|
||
//
|
||
|
||
if ( rfcbLinkedToLfcb ) {
|
||
SrvRemoveEntryList(
|
||
&rfcb->Lfcb->RfcbList,
|
||
&pagedRfcb->LfcbListEntry
|
||
);
|
||
|
||
lfcb->BlockHeader.ReferenceCount--;
|
||
UPDATE_REFERENCE_HISTORY( lfcb, TRUE );
|
||
lfcb->HandleCount--;
|
||
}
|
||
|
||
//
|
||
// If the file object was referenced, dereference it.
|
||
//
|
||
|
||
if ( fileObject != NULL ) {
|
||
ObDereferenceObject( fileObject );
|
||
}
|
||
|
||
//
|
||
// If a new LFCB was allocated, free it.
|
||
//
|
||
|
||
if ( newLfcb != NULL ) {
|
||
SrvFreeLfcb( newLfcb, WorkContext->CurrentWorkQueue );
|
||
}
|
||
|
||
//
|
||
// If a new RFCB was allocated, free it.
|
||
//
|
||
|
||
if ( rfcb != NULL ) {
|
||
SrvFreeRfcb( rfcb, WorkContext->CurrentWorkQueue );
|
||
}
|
||
|
||
//
|
||
// If this not a folded compatibility mode open, close the file.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( ExistingLfcb ) ) {
|
||
SRVDBG_RELEASE_HANDLE( FileHandle, "FIL", 17, 0 );
|
||
SrvNtClose( FileHandle, TRUE );
|
||
}
|
||
|
||
//
|
||
// Indicate failure.
|
||
//
|
||
|
||
*Rfcb = NULL;
|
||
|
||
return status;
|
||
|
||
} // CompleteOpen
|
||
|
||
|
||
BOOLEAN SRVFASTCALL
|
||
MapCompatibilityOpen(
|
||
IN PUNICODE_STRING FileName,
|
||
IN OUT PUSHORT SmbDesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines whether a compatibility mode open can be mapped into
|
||
normal sharing mode.
|
||
|
||
Arguments:
|
||
|
||
FileName - The name of the file being accessed
|
||
|
||
SmbDesiredAccess - On input, the desired access specified in the
|
||
received SMB. On output, the share mode portion of this field
|
||
is updated if the open is mapped to normal sharing.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if the open has been mapped to normal sharing.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// If soft compatibility is not enabled then reject the mapping
|
||
//
|
||
if( !SrvEnableSoftCompatibility ) {
|
||
|
||
IF_SMB_DEBUG( OPEN_CLOSE2 ) {
|
||
KdPrint(( "MapCompatibilityOpen: "
|
||
"SrvEnableSoftCompatibility is FALSE\n" ));
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//
|
||
// If the client is opening one of the following reserved suffixes, be lenient
|
||
//
|
||
if( FileName->Length > 4 * sizeof( WCHAR ) ) {
|
||
|
||
LPWSTR periodp;
|
||
|
||
periodp = FileName->Buffer + (FileName->Length / sizeof( WCHAR ) ) - 4;
|
||
|
||
if( (*periodp++ == L'.') &&
|
||
(_wcsicmp( periodp, L"EXE" ) == 0 ||
|
||
_wcsicmp( periodp, L"DLL" ) == 0 ||
|
||
_wcsicmp( periodp, L"SYM" ) == 0 ||
|
||
_wcsicmp( periodp, L"COM" ) == 0 ) ) {
|
||
|
||
//
|
||
// This is a readonly open of one of the above file types.
|
||
// Map to DENY_NONE
|
||
//
|
||
|
||
IF_SMB_DEBUG( OPEN_CLOSE2 ) {
|
||
KdPrint(( "MapCompatibilityOpen: %wZ mapped to DENY_NONE\n", FileName ));
|
||
}
|
||
|
||
*SmbDesiredAccess |= SMB_DA_SHARE_DENY_NONE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// The filename does not end in one of the special suffixes -- map
|
||
// it to DENY_WRITE if the client is asking for just read permissions.
|
||
//
|
||
if( (*SmbDesiredAccess & SMB_DA_ACCESS_MASK) == SMB_DA_ACCESS_READ) {
|
||
IF_SMB_DEBUG( OPEN_CLOSE2 ) {
|
||
KdPrint(( "MapCompatibilityOpen: %wZ mapped to DENY_WRITE\n", FileName ));
|
||
}
|
||
*SmbDesiredAccess |= SMB_DA_SHARE_DENY_WRITE;
|
||
return TRUE;
|
||
}
|
||
|
||
IF_SMB_DEBUG( OPEN_CLOSE2 ) {
|
||
KdPrint(( "MapCompatibilityOpen: %wZ not mapped, DesiredAccess %X\n", FileName, *SmbDesiredAccess ));
|
||
}
|
||
|
||
return FALSE;
|
||
} // MapCompatibilityOpen
|
||
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapDesiredAccess(
|
||
IN USHORT SmbDesiredAccess,
|
||
OUT PACCESS_MASK NtDesiredAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Maps a desired access specification from SMB form to NT form.
|
||
|
||
Arguments:
|
||
|
||
SmbDesiredAccess - The desired access specified in the received SMB.
|
||
(This includes desired access, share access, and other mode
|
||
bits.)
|
||
|
||
NtDesiredAccess - Returns the NT equivalent of the desired access
|
||
part of SmbDesiredAccess.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates whether SmbDesiredAccess is valid.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
switch ( SmbDesiredAccess & SMB_DA_ACCESS_MASK ) {
|
||
|
||
case SMB_DA_ACCESS_READ:
|
||
|
||
*NtDesiredAccess = GENERIC_READ;
|
||
break;
|
||
|
||
case SMB_DA_ACCESS_WRITE:
|
||
|
||
//
|
||
// Having read attributes is implicit in having the file open in
|
||
// the SMB protocol, so request FILE_READ_ATTRIBUTES in addition
|
||
// to GENERIC_WRITE.
|
||
//
|
||
|
||
*NtDesiredAccess = GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||
break;
|
||
|
||
case SMB_DA_ACCESS_READ_WRITE:
|
||
|
||
*NtDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||
break;
|
||
|
||
case SMB_DA_ACCESS_EXECUTE:
|
||
|
||
// !!! is this right?
|
||
*NtDesiredAccess = GENERIC_READ | GENERIC_EXECUTE;
|
||
break;
|
||
|
||
default:
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "MapDesiredAccess: Invalid desired access: 0x%lx\n",
|
||
SmbDesiredAccess ));
|
||
}
|
||
|
||
return STATUS_OS2_INVALID_ACCESS;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // MapDesiredAccess
|
||
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapOpenFunction(
|
||
IN USHORT SmbOpenFunction,
|
||
OUT PULONG NtCreateDisposition
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Maps an open function specification from SMB form to NT form.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
SmbOpenFunction - The open function specified in the received SMB.
|
||
|
||
NtDesiredAccess - Returns the NT equivalent of SmbOpenFunction.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates whether SmbOpenFunction is valid.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
// The OpenFunction bit mapping:
|
||
//
|
||
// rrrr rrrr rrrC rrOO
|
||
//
|
||
// where:
|
||
//
|
||
// C - Create (action to be taken if the file does not exist)
|
||
// 0 -- Fail
|
||
// 1 -- Create file
|
||
//
|
||
// O - Open (action to be taken if the file exists)
|
||
// 0 - Fail
|
||
// 1 - Open file
|
||
// 2 - Truncate file
|
||
//
|
||
|
||
switch ( SmbOpenFunction & (SMB_OFUN_OPEN_MASK | SMB_OFUN_CREATE_MASK) ) {
|
||
|
||
case SMB_OFUN_CREATE_FAIL | SMB_OFUN_OPEN_OPEN:
|
||
|
||
*NtCreateDisposition = FILE_OPEN;
|
||
break;
|
||
|
||
case SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_FAIL:
|
||
|
||
*NtCreateDisposition = FILE_CREATE;
|
||
break;
|
||
|
||
case SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_OPEN:
|
||
|
||
*NtCreateDisposition = FILE_OPEN_IF;
|
||
break;
|
||
|
||
case SMB_OFUN_CREATE_CREATE | SMB_OFUN_OPEN_TRUNCATE:
|
||
|
||
*NtCreateDisposition = FILE_OVERWRITE_IF;
|
||
break;
|
||
|
||
case SMB_OFUN_CREATE_FAIL | SMB_OFUN_OPEN_TRUNCATE:
|
||
|
||
*NtCreateDisposition = FILE_OVERWRITE;
|
||
break;
|
||
|
||
//case 0x00:
|
||
//case 0x03:
|
||
//case 0x13:
|
||
default:
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "MapOpenFunction: Invalid open function: 0x%lx\n",
|
||
SmbOpenFunction ));
|
||
}
|
||
|
||
return STATUS_OS2_INVALID_ACCESS;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // MapOpenFunction
|
||
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapCacheHints(
|
||
IN USHORT SmbDesiredAccess,
|
||
IN OUT PULONG NtCreateFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function maps the SMB cache mode hints to the NT format. The
|
||
NtCreateFlags are updated.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
SmbOpenFunction - The open function specified in the received SMB.
|
||
|
||
NtCreateFlags - The NT file creation flags
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates whether SmbOpenFunction is valid.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
// The DesiredAccess bit mapping:
|
||
//
|
||
// xxxC xLLL xxxx xxxx
|
||
//
|
||
// where:
|
||
//
|
||
// C - Cache mode
|
||
// 0 -- Normal file
|
||
// 1 -- Do not cache the file
|
||
//
|
||
// LLL - Locality of reference
|
||
// 000 - Unknown
|
||
// 001 - Mainly sequential access
|
||
// 010 - Mainly random access
|
||
// 011 - Random with some locality
|
||
// 1xx - Undefined
|
||
//
|
||
|
||
//
|
||
// If the client doesn't want us to use the cache, we can't give that, but we
|
||
// can at least get the data written immediately.
|
||
//
|
||
|
||
if ( SmbDesiredAccess & SMB_DO_NOT_CACHE ) {
|
||
*NtCreateFlags |= FILE_WRITE_THROUGH;
|
||
}
|
||
|
||
switch ( SmbDesiredAccess & SMB_LR_MASK ) {
|
||
|
||
case SMB_LR_UNKNOWN:
|
||
break;
|
||
|
||
case SMB_LR_SEQUENTIAL:
|
||
*NtCreateFlags |= FILE_SEQUENTIAL_ONLY;
|
||
break;
|
||
|
||
case SMB_LR_RANDOM:
|
||
case SMB_LR_RANDOM_WITH_LOCALITY:
|
||
*NtCreateFlags |= FILE_RANDOM_ACCESS;
|
||
break;
|
||
|
||
default:
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "MapCacheHints: Invalid cache hint: 0x%lx\n",
|
||
SmbDesiredAccess ));
|
||
}
|
||
|
||
return STATUS_OS2_INVALID_ACCESS;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // MapCacheHints
|
||
|
||
|
||
NTSTATUS SRVFASTCALL
|
||
MapShareAccess(
|
||
IN USHORT SmbDesiredAccess,
|
||
OUT PULONG NtShareAccess
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Maps a share access specification from SMB form to NT form.
|
||
|
||
Arguments:
|
||
|
||
SmbDesiredAccess - The desired access specified in the received SMB.
|
||
(This includes desired access, share access, and other mode
|
||
bits.)
|
||
|
||
NtShareAccess - Returns the NT equivalent of the share access part
|
||
of SmbDesiredAccess.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates whether SmbDesiredAccess is valid.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
switch ( SmbDesiredAccess & SMB_DA_SHARE_MASK ) {
|
||
|
||
case SMB_DA_SHARE_EXCLUSIVE:
|
||
|
||
//
|
||
// Deny read and write.
|
||
//
|
||
|
||
*NtShareAccess = 0L;
|
||
break;
|
||
|
||
case SMB_DA_SHARE_DENY_WRITE:
|
||
|
||
//
|
||
// Deny write but allow read.
|
||
//
|
||
|
||
*NtShareAccess = FILE_SHARE_READ;
|
||
break;
|
||
|
||
case SMB_DA_SHARE_DENY_READ:
|
||
|
||
//
|
||
// Deny read but allow write.
|
||
//
|
||
|
||
*NtShareAccess = FILE_SHARE_WRITE;
|
||
break;
|
||
|
||
case SMB_DA_SHARE_DENY_NONE:
|
||
|
||
//
|
||
// Deny none -- allow other processes to read or write the file.
|
||
//
|
||
|
||
*NtShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||
break;
|
||
|
||
default:
|
||
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "MapShareAccess: Invalid share access: 0x%lx\n",
|
||
SmbDesiredAccess ));
|
||
}
|
||
|
||
return STATUS_OS2_INVALID_ACCESS;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // MapShareAccess
|
||
|
||
|
||
NTSTATUS
|
||
SrvNtCreateFile(
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
IN ULONG RootDirectoryFid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN LARGE_INTEGER AllocationSize,
|
||
IN ULONG FileAttributes,
|
||
IN ULONG ShareAccess,
|
||
IN ULONG CreateDisposition,
|
||
IN ULONG CreateOptions,
|
||
IN PVOID SecurityDescriptorBuffer OPTIONAL,
|
||
IN PUNICODE_STRING FileName,
|
||
IN PVOID EaBuffer OPTIONAL,
|
||
IN ULONG EaLength,
|
||
OUT PULONG EaErrorOffset OPTIONAL,
|
||
IN ULONG OptionFlags,
|
||
PSECURITY_QUALITY_OF_SERVICE QualityOfService,
|
||
IN OPLOCK_TYPE RequestedOplockType,
|
||
IN PRESTART_ROUTINE RestartRoutine
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Does most of the operations necessary to open or create a file.
|
||
First the UID and TID are verified and the corresponding session and
|
||
tree connect blocks located. The input file name name is
|
||
canonicalized, and a fully qualified name is formed. The file
|
||
is opened and the appropriate data structures are created.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Work context block for the operation.
|
||
|
||
RootDirectoryFid - The FID of an open root directory. The file is opened
|
||
relative to this directory.
|
||
|
||
DesiredAccess - The access type requested by the client.
|
||
|
||
AllocationSize - The initialize allocation size of the file. It is
|
||
only used if the file is created, overwritten, or superseded.
|
||
|
||
FileAttributes - Specified the file attributes.
|
||
|
||
ShareAccess - Specifies the type of share access requested by the
|
||
client.
|
||
|
||
CreateDisposition - Specifies the action to be taken if the file does
|
||
or does not exist.
|
||
|
||
CreateOptions - Specifies the options to use when creating the file.
|
||
|
||
SecurityDescriptorBuffer - The SD to set on the file.
|
||
|
||
FileName - The name of the file to open.
|
||
|
||
EaBuffer - The EAs to set on the file.
|
||
|
||
EaLength - Length, in bytes, of the EA buffer.
|
||
|
||
EaErrorOffset - Returns the offset, in bytes, in the EA buffer of
|
||
the EA error.
|
||
|
||
OptionFlags - The option flags for creating the file
|
||
|
||
QualityOfService - The security quality of service for the file
|
||
|
||
RestartRoutine - The restart routine for the caller.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Indicates what occurred.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
NTSTATUS completionStatus;
|
||
|
||
PMFCB mfcb;
|
||
PNONPAGED_MFCB nonpagedMfcb;
|
||
PRFCB rfcb;
|
||
|
||
PSESSION session;
|
||
PTREE_CONNECT treeConnect;
|
||
|
||
UNICODE_STRING relativeName;
|
||
UNICODE_STRING fullName;
|
||
BOOLEAN nameAllocated;
|
||
BOOLEAN relativeNameAllocated = FALSE;
|
||
SHARE_TYPE shareType;
|
||
PRFCB rootDirRfcb = NULL;
|
||
PLFCB rootDirLfcb;
|
||
BOOLEAN success;
|
||
ULONG attributes;
|
||
ULONG openRetries;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE fileHandle;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
ULONG ioCreateFlags;
|
||
PSHARE fileShare = NULL;
|
||
BOOLEAN caseInsensitive;
|
||
ULONG hashValue;
|
||
|
||
PSRV_LOCK mfcbLock;
|
||
|
||
//
|
||
// NOTE ON MFCB REFERENCE COUNT HANDLING
|
||
//
|
||
// After finding or creating an MFCB for a file, we increment the
|
||
// MFCB reference count an extra time to simplify our
|
||
// synchronization logic. We hold MfcbListLock lock while
|
||
// finding/creating the MFCB, but release it after acquiring the the
|
||
// per-MFCB lock. After opening the file, we call CompleteOpen,
|
||
// which may need to queue an LFCB to the MFCB and thus need to
|
||
// increment the count. But it can't, because the MFCB list lock may
|
||
// not be acquired while the per-MFCB lock is held because of
|
||
// deadlock potential. The boolean LfcbAddedToMfcbList returned
|
||
// from CompleteOpen indicates whether it actually queued an LFCB to
|
||
// the MFCB. If it didn't, we need to release the extra reference.
|
||
//
|
||
// Note that it isn't often that we actually have to dereference the
|
||
// MFCB. This only occurs when the open fails.
|
||
//
|
||
|
||
BOOLEAN lfcbAddedToMfcbList;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Assume we won't need a temporary open.
|
||
//
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = FALSE;
|
||
|
||
//
|
||
// If a session block has not already been assigned to the current
|
||
// work context, verify the UID. If verified, the address of the
|
||
// session block corresponding to this user is stored in the
|
||
// WorkContext block and the session block is referenced.
|
||
//
|
||
// Find the tree connect corresponding to the given TID if a tree
|
||
// connect pointer has not already been put in the WorkContext block
|
||
// by an AndX command or a previous call to SrvCreateFile.
|
||
//
|
||
|
||
status = SrvVerifyUidAndTid(
|
||
WorkContext,
|
||
&session,
|
||
&treeConnect,
|
||
ShareTypeWild
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(SMB_ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: Invalid UID or TID\n" ));
|
||
}
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the session has expired, return that info
|
||
//
|
||
if( session->IsSessionExpired )
|
||
{
|
||
return SESSION_EXPIRED_STATUS_CODE;
|
||
}
|
||
|
||
if ( RootDirectoryFid != 0 ) {
|
||
|
||
rootDirRfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
(USHORT)RootDirectoryFid,
|
||
FALSE,
|
||
NULL, // don't serialize with raw write
|
||
&status
|
||
);
|
||
|
||
if ( rootDirRfcb == SRV_INVALID_RFCB_POINTER ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: Invalid Root Dir FID: 0x%lx\n",
|
||
RootDirectoryFid ));
|
||
}
|
||
|
||
return status;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Remove the redundant copy of the RFCB to prevent a redundant
|
||
// dereference of this RFCB.
|
||
//
|
||
|
||
WorkContext->Rfcb = NULL;
|
||
|
||
}
|
||
|
||
rootDirLfcb = rootDirRfcb->Lfcb;
|
||
|
||
}
|
||
|
||
//
|
||
// Here we begin share type specific processing.
|
||
//
|
||
|
||
shareType = treeConnect->Share->ShareType;
|
||
|
||
//
|
||
// If this operation may block, and we are running short of
|
||
// free work items, fail this SMB with an out of resources error.
|
||
// Note that a disk open will block if the file is currently oplocked.
|
||
//
|
||
|
||
if ( shareType == ShareTypeDisk && !WorkContext->BlockingOperation ) {
|
||
|
||
if ( SrvReceiveBufferShortage( ) ) {
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
SrvStatistics.BlockingSmbsRejected++;
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
|
||
} else {
|
||
|
||
//
|
||
// SrvBlockingOpsInProgress has already been incremented.
|
||
// Flag this work item as a blocking operation.
|
||
//
|
||
|
||
WorkContext->BlockingOperation = TRUE;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Assume we won't need a temporary open.
|
||
//
|
||
|
||
switch ( shareType ) {
|
||
|
||
case ShareTypeDisk:
|
||
case ShareTypePipe:
|
||
|
||
//
|
||
// Canonicalize the path name so that it conforms to NT
|
||
// standards.
|
||
//
|
||
// *** Note that this operation allocates space for the name.
|
||
//
|
||
|
||
status = SrvCanonicalizePathName(
|
||
WorkContext,
|
||
treeConnect->Share,
|
||
RootDirectoryFid != 0 ? &rootDirLfcb->Mfcb->FileName : NULL,
|
||
FileName->Buffer,
|
||
((PCHAR)FileName->Buffer +
|
||
FileName->Length - sizeof(WCHAR)),
|
||
TRUE, // Strip trailing "."s
|
||
TRUE, // Name is always unicode
|
||
&relativeName
|
||
);
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// The path tried to do ..\ to get beyond the share it has
|
||
// accessed.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: Invalid pathname: "
|
||
"%wZ\n", FileName ));
|
||
}
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
return status;
|
||
|
||
}
|
||
|
||
//
|
||
// Form the fully qualified name of the file.
|
||
//
|
||
// *** Note that this operation allocates space for the name.
|
||
// This space is deallocated after the DoXxxOpen routine
|
||
// returns.
|
||
//
|
||
|
||
if ( shareType == ShareTypeDisk ) {
|
||
|
||
if ( RootDirectoryFid != 0 ) {
|
||
SrvAllocateAndBuildPathName(
|
||
&rootDirLfcb->Mfcb->FileName,
|
||
&relativeName,
|
||
NULL,
|
||
&fullName
|
||
);
|
||
} else {
|
||
SrvAllocateAndBuildPathName(
|
||
&treeConnect->Share->DosPathName,
|
||
&relativeName,
|
||
NULL,
|
||
&fullName
|
||
);
|
||
}
|
||
|
||
} else {
|
||
|
||
UNICODE_STRING pipePrefix;
|
||
|
||
if( !WorkContext->Session->IsNullSession &&
|
||
WorkContext->Session->IsLSNotified == FALSE ) {
|
||
|
||
//
|
||
// We have a pipe open request, not a NULL session, and
|
||
// we haven't gotten clearance from the license server yet.
|
||
// If this pipe requires clearance, get a license.
|
||
//
|
||
|
||
ULONG i;
|
||
BOOLEAN matchFound = FALSE;
|
||
|
||
ACQUIRE_LOCK_SHARED( &SrvConfigurationLock );
|
||
|
||
for ( i = 0; SrvPipesNeedLicense[i] != NULL ; i++ ) {
|
||
|
||
if ( _wcsicmp(
|
||
SrvPipesNeedLicense[i],
|
||
relativeName.Buffer
|
||
) == 0 ) {
|
||
matchFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
RELEASE_LOCK( &SrvConfigurationLock );
|
||
|
||
if( matchFound == TRUE ) {
|
||
status = SrvXsLSOperation( WorkContext->Session,
|
||
XACTSRV_MESSAGE_LSREQUEST );
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
if( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
return status;
|
||
}
|
||
}
|
||
}
|
||
|
||
RtlInitUnicodeString( &pipePrefix, StrSlashPipeSlash );
|
||
|
||
if( WorkContext->Endpoint->RemapPipeNames || treeConnect->RemapPipeNames ) {
|
||
|
||
//
|
||
// The RemapPipeNames flag is set, so remap the pipe name
|
||
// to "$$\<server>\<pipe name>".
|
||
//
|
||
// Note: this operation allocates space for pipeRelativeName.
|
||
//
|
||
|
||
status = RemapPipeName(
|
||
&WorkContext->Endpoint->TransportAddress,
|
||
treeConnect->RemapPipeNames ? &treeConnect->ServerName : NULL,
|
||
&relativeName,
|
||
&relativeNameAllocated
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
|
||
if( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
return status;
|
||
}
|
||
}
|
||
SrvAllocateAndBuildPathName(
|
||
&pipePrefix,
|
||
&relativeName,
|
||
NULL,
|
||
&fullName
|
||
);
|
||
|
||
}
|
||
|
||
if ( fullName.Buffer == NULL ) {
|
||
|
||
//
|
||
// Unable to allocate heap for the full name.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: Unable to allocate heap for "
|
||
"full path name\n" ));
|
||
}
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
if( relativeNameAllocated ) {
|
||
FREE_HEAP( relativeName.Buffer );
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Indicate that we must free the file name buffers on exit.
|
||
//
|
||
|
||
nameAllocated = TRUE;
|
||
|
||
break;
|
||
|
||
//
|
||
// Default case, illegal device type. This should never happen.
|
||
//
|
||
|
||
default:
|
||
|
||
// !!! Is this an appropriate error return code? Probably no.
|
||
return STATUS_INVALID_PARAMETER;
|
||
|
||
}
|
||
|
||
//
|
||
// Determine whether or not the path name is case sensitive.
|
||
//
|
||
|
||
if ( (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE) ||
|
||
WorkContext->Session->UsingUppercasePaths )
|
||
{
|
||
|
||
attributes = OBJ_CASE_INSENSITIVE;
|
||
caseInsensitive = TRUE;
|
||
|
||
} else {
|
||
attributes = 0L;
|
||
caseInsensitive = FALSE;
|
||
}
|
||
|
||
if ( RootDirectoryFid != 0 ) {
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&relativeName,
|
||
attributes,
|
||
rootDirLfcb->FileHandle,
|
||
NULL
|
||
);
|
||
|
||
} else if ( WorkContext->TreeConnect->Share->ShareType == ShareTypePipe ) {
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&relativeName,
|
||
attributes,
|
||
SrvNamedPipeHandle,
|
||
NULL
|
||
);
|
||
} else {
|
||
|
||
fileShare = treeConnect->Share;
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&relativeName,
|
||
attributes,
|
||
NULL,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
if ( SecurityDescriptorBuffer != NULL) {
|
||
objectAttributes.SecurityDescriptor = SecurityDescriptorBuffer;
|
||
}
|
||
|
||
//
|
||
// Always add read attributes since we need to query file info after
|
||
// the open.
|
||
//
|
||
|
||
DesiredAccess |= FILE_READ_ATTRIBUTES;
|
||
|
||
//
|
||
// Interpret the io create flags
|
||
//
|
||
|
||
ioCreateFlags = IO_CHECK_CREATE_PARAMETERS | IO_FORCE_ACCESS_CHECK;
|
||
|
||
if ( OptionFlags & NT_CREATE_OPEN_TARGET_DIR ) {
|
||
ioCreateFlags |= IO_OPEN_TARGET_DIRECTORY;
|
||
}
|
||
|
||
//
|
||
// Override the default server quality of service, with the QOS request
|
||
// by the client.
|
||
//
|
||
|
||
objectAttributes.SecurityQualityOfService = QualityOfService;
|
||
|
||
if ( WorkContext->ProcessingCount == 2 ) {
|
||
|
||
HANDLE fileHandle;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
|
||
//
|
||
// This is the second time through, so we must be in a blocking
|
||
// thread. Do a blocking open of the file to force an oplock
|
||
// break. Then close the handle and fall through to the normal
|
||
// open path.
|
||
//
|
||
// We must do the blocking open without holding the MFCB
|
||
// lock, because this lock can be acquired during oplock
|
||
// break, resulting in deadlock.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&objectAttributes,
|
||
&relativeName,
|
||
attributes,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
GENERIC_READ,
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
NULL,
|
||
0,
|
||
FILE_SHARE_VALID_FLAGS,
|
||
FILE_OPEN,
|
||
0,
|
||
NULL,
|
||
0,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
0,
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
if ( NT_SUCCESS( status ) ) {
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 13, 0 );
|
||
SRVDBG_RELEASE_HANDLE( fileHandle, "FIL", 18, 0 );
|
||
SrvNtClose( fileHandle, TRUE );
|
||
}
|
||
}
|
||
|
||
//
|
||
// Scan the Master File Table to see if the named file is already
|
||
// open.
|
||
//
|
||
|
||
mfcb = SrvFindMfcb( &fullName, caseInsensitive, &mfcbLock, &hashValue, WorkContext );
|
||
|
||
if ( mfcb == NULL ) {
|
||
//
|
||
// There is no MFCB for this file. Create one.
|
||
//
|
||
|
||
mfcb = SrvCreateMfcb( &fullName, WorkContext, hashValue );
|
||
|
||
if ( mfcb == NULL ) {
|
||
|
||
//
|
||
// Failure to add open file instance to MFT.
|
||
//
|
||
|
||
if( mfcbLock ) {
|
||
RELEASE_LOCK( mfcbLock );
|
||
}
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: Unable to allocate MFCB\n" ));
|
||
}
|
||
|
||
if ( nameAllocated ) {
|
||
FREE_HEAP( fullName.Buffer );
|
||
}
|
||
|
||
if( relativeNameAllocated ) {
|
||
FREE_HEAP( relativeName.Buffer );
|
||
}
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Increment the MFCB reference count. See the note at the beginning of this routine.
|
||
//
|
||
mfcb->BlockHeader.ReferenceCount++;
|
||
UPDATE_REFERENCE_HISTORY( mfcb, FALSE );
|
||
|
||
//
|
||
// Grab the MFCB-based lock to serialize opens of the same file
|
||
// and release the MFCB list lock.
|
||
//
|
||
|
||
nonpagedMfcb = mfcb->NonpagedMfcb;
|
||
RELEASE_LOCK( mfcbLock );
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
openRetries = SrvSharingViolationRetryCount;
|
||
|
||
start_retry:
|
||
|
||
//
|
||
// If the client asked for FILE_NO_INTERMEDIATE_BUFFERING, turn that
|
||
// flag off and turn FILE_WRITE_THROUGH on instead. We cannot give
|
||
// the client the true meaning of NO_INTERMEDIATE_BUFFERING, but we
|
||
// can at least get the data out to disk right away.
|
||
//
|
||
|
||
if ( (CreateOptions & FILE_NO_INTERMEDIATE_BUFFERING) != 0 ) {
|
||
CreateOptions |= FILE_WRITE_THROUGH;
|
||
CreateOptions &= ~FILE_NO_INTERMEDIATE_BUFFERING;
|
||
}
|
||
|
||
//
|
||
// Check to see if there is a cached handle for the file.
|
||
//
|
||
|
||
if ( (CreateDisposition == FILE_OPEN) ||
|
||
(CreateDisposition == FILE_CREATE) ||
|
||
(CreateDisposition == FILE_OPEN_IF) ) {
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvNtCreateFile: checking for cached rfcb for %wZ\n", &fullName ));
|
||
}
|
||
if ( SrvFindCachedRfcb(
|
||
WorkContext,
|
||
mfcb,
|
||
DesiredAccess,
|
||
ShareAccess,
|
||
CreateDisposition,
|
||
CreateOptions,
|
||
RequestedOplockType,
|
||
&status ) ) {
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvNtCreateFile: FindCachedRfcb = TRUE, status = %x, rfcb = %p\n",
|
||
status, WorkContext->Rfcb ));
|
||
}
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// We incremented the MFCB reference count in anticipation of
|
||
// the possibility that an LFCB might be queued to the MFCB.
|
||
// This path precludes that possibility.
|
||
//
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
//
|
||
// This second dereference is for the reference done by
|
||
// SrvFindMfcb/SrvCreateMfcb.
|
||
//
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
if ( nameAllocated ) {
|
||
FREE_HEAP( fullName.Buffer );
|
||
}
|
||
|
||
if( relativeNameAllocated ) {
|
||
FREE_HEAP( relativeName.Buffer );
|
||
}
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
IF_DEBUG(FILE_CACHE) {
|
||
KdPrint(( "SrvNtCreateFile: FindCachedRfcb = FALSE; do it the slow way\n" ));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Call SrvIoCreateFile to create or open the file. (We call
|
||
// SrvIoCreateFile, rather than NtOpenFile, in order to get user-mode
|
||
// access checking.)
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "SrvCreateFile: Opening file %wZ\n", &fullName ));
|
||
}
|
||
|
||
CreateOptions |= FILE_COMPLETE_IF_OPLOCKED;
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
|
||
//
|
||
// Ensure the EaBuffer is correctly formatted. Since we are a kernel mode
|
||
// component, the Io subsystem does not check it for us.
|
||
//
|
||
if( ARGUMENT_PRESENT( EaBuffer ) ) {
|
||
status = IoCheckEaBufferValidity( (PFILE_FULL_EA_INFORMATION)EaBuffer, EaLength, EaErrorOffset );
|
||
} else {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&fileHandle,
|
||
DesiredAccess,
|
||
&objectAttributes,
|
||
&ioStatusBlock,
|
||
&AllocationSize,
|
||
FileAttributes,
|
||
ShareAccess,
|
||
CreateDisposition,
|
||
CreateOptions,
|
||
EaBuffer,
|
||
EaLength,
|
||
CreateFileTypeNone,
|
||
NULL, // ExtraCreateParameters
|
||
ioCreateFlags,
|
||
fileShare
|
||
);
|
||
}
|
||
|
||
//
|
||
// If we got sharing violation and this is a disk file.
|
||
// If this is the first open attempt, setup for a blocking open attempt.
|
||
// If the file is batch oplocked, the non-blocking open would fail,
|
||
// and the oplock will not break.
|
||
//
|
||
// If this is the second open attempt, we can assume that we are in
|
||
// the blocking thread. Retry the open.
|
||
//
|
||
|
||
if ( status == STATUS_SHARING_VIOLATION &&
|
||
WorkContext->TreeConnect->Share->ShareType == ShareTypeDisk ) {
|
||
|
||
if ( WorkContext->ProcessingCount == 1 ) {
|
||
|
||
WorkContext->Parameters2.Open.TemporaryOpen = TRUE;
|
||
|
||
} else if ( (WorkContext->ProcessingCount == 2) &&
|
||
(openRetries-- > 0) ) {
|
||
|
||
//
|
||
// We are in the blocking thread.
|
||
//
|
||
|
||
//
|
||
// Release the mfcb lock so that a close might slip through.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
(VOID) KeDelayExecutionThread(
|
||
KernelMode,
|
||
FALSE,
|
||
&SrvSharingViolationDelay
|
||
);
|
||
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
goto start_retry;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Save the open information where the SMB processor can find it.
|
||
//
|
||
|
||
WorkContext->Irp->IoStatus.Information = ioStatusBlock.Information;
|
||
|
||
//
|
||
// If the user didn't have this permission, update the statistics
|
||
// database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// The open failed.
|
||
//
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvNtCreateFile: SrvIoCreateFile failed, file = %wZ, "
|
||
"status = %X, Info = 0x%p\n",
|
||
objectAttributes.ObjectName,
|
||
status, (PVOID)ioStatusBlock.Information ));
|
||
}
|
||
|
||
//
|
||
// Set the error offset if needed.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(EaErrorOffset) &&
|
||
status == STATUS_INVALID_EA_NAME ) {
|
||
*EaErrorOffset = (ULONG)ioStatusBlock.Information;
|
||
ioStatusBlock.Information = 0;
|
||
}
|
||
|
||
//
|
||
// Cleanup allocated memory and return with a failure status.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// We incremented the MFCB reference count in anticipation of
|
||
// the possibility that an LFCB might be queued to the MFCB.
|
||
// This error path precludes that possibility.
|
||
//
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
//
|
||
// This second dereference is for the reference done by
|
||
// SrvFindMfcb/SrvCreateMfcb.
|
||
//
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
if ( nameAllocated ) {
|
||
FREE_HEAP( fullName.Buffer );
|
||
}
|
||
|
||
if( relativeNameAllocated ) {
|
||
FREE_HEAP( relativeName.Buffer );
|
||
}
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
return status;
|
||
|
||
}
|
||
|
||
SRVDBG_CLAIM_HANDLE( fileHandle, "FIL", 14, 0 );
|
||
|
||
//
|
||
// The open was successful. Attempt to allocate structures to
|
||
// represent the open. If any errors occur, CompleteOpen does full
|
||
// cleanup, including closing the file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(OPEN_CLOSE2) {
|
||
KdPrint(( "SrvNtCreateFile: Open of %wZ succeeded, file handle: 0x%p\n", &fullName, fileHandle ));
|
||
}
|
||
|
||
completionStatus = CompleteOpen(
|
||
&rfcb,
|
||
mfcb,
|
||
WorkContext,
|
||
NULL,
|
||
fileHandle,
|
||
NULL,
|
||
ShareAccess,
|
||
CreateOptions,
|
||
FALSE,
|
||
FALSE,
|
||
&lfcbAddedToMfcbList
|
||
);
|
||
|
||
//
|
||
// Remember the "interesting" status code. If CompleteOpen() succeeds
|
||
// return the open status. If it fails, it will clean up the open
|
||
// file, and we return a failure status.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( completionStatus ) ) {
|
||
status = completionStatus;
|
||
}
|
||
|
||
//
|
||
// Release the Open serialization lock and dereference the MFCB.
|
||
//
|
||
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
//
|
||
// If CompleteOpen didn't queue an LFCB to the MFCB, release the
|
||
// extra reference that we added.
|
||
//
|
||
|
||
if ( !lfcbAddedToMfcbList ) {
|
||
SrvDereferenceMfcb( mfcb );
|
||
}
|
||
|
||
SrvDereferenceMfcb( mfcb );
|
||
|
||
//
|
||
// Deallocate the full path name buffer.
|
||
//
|
||
|
||
if ( nameAllocated ) {
|
||
FREE_HEAP( fullName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Deallocate the relative path name buffer.
|
||
//
|
||
if( relativeNameAllocated ) {
|
||
FREE_HEAP( relativeName.Buffer );
|
||
}
|
||
|
||
//
|
||
// Release our reference to the root directory RFCB
|
||
//
|
||
|
||
if ( rootDirRfcb != NULL ) {
|
||
SrvDereferenceRfcb( rootDirRfcb );
|
||
}
|
||
|
||
//
|
||
// If this is a temporary file, don't attempt to cache it.
|
||
//
|
||
|
||
if ( rfcb != NULL && (FileAttributes & FILE_ATTRIBUTE_TEMPORARY) != 0 ) {
|
||
|
||
rfcb->IsCacheable = FALSE;
|
||
}
|
||
|
||
//
|
||
// Update the statistics database if the open was successful.
|
||
//
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
SrvStatistics.TotalFilesOpened++;
|
||
}
|
||
|
||
//
|
||
// Make a pointer to the RFCB accessible to the caller.
|
||
//
|
||
|
||
WorkContext->Parameters2.Open.Rfcb = rfcb;
|
||
|
||
//
|
||
// If there is an oplock break in progress, wait for the oplock
|
||
// break to complete.
|
||
//
|
||
|
||
if ( status == STATUS_OPLOCK_BREAK_IN_PROGRESS ) {
|
||
|
||
NTSTATUS startStatus;
|
||
|
||
//
|
||
// Save the Information from the open, so it doesn't
|
||
// get lost when we re-use the WorkContext->Irp for the
|
||
// oplock processing.
|
||
//
|
||
WorkContext->Parameters2.Open.IosbInformation = WorkContext->Irp->IoStatus.Information;
|
||
|
||
startStatus = SrvStartWaitForOplockBreak(
|
||
WorkContext,
|
||
RestartRoutine,
|
||
0,
|
||
rfcb->Lfcb->FileObject
|
||
);
|
||
|
||
if (!NT_SUCCESS( startStatus ) ) {
|
||
|
||
//
|
||
// The file is oplocked, and we cannot wait for the oplock
|
||
// break to complete. Just close the file, and return the
|
||
// error.
|
||
//
|
||
|
||
SrvCloseRfcb( rfcb );
|
||
status = startStatus;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return status;
|
||
|
||
} // SrvNtCreateFile
|
||
|
||
BOOLEAN
|
||
SetDefaultPipeMode (
|
||
IN HANDLE FileHandle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function set the read mode of a newly opened named pipe. If
|
||
the pipe type is message mode, the read mode is set the message
|
||
mode.
|
||
|
||
Arguments:
|
||
|
||
FileHandle - The client side handle to the named pipe.
|
||
|
||
Return Value:
|
||
|
||
FALSE - Pipe mode is byte mode.
|
||
TRUE - Pipe mode has been set to message mode.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
FILE_PIPE_INFORMATION pipeInformation;
|
||
FILE_PIPE_LOCAL_INFORMATION pipeLocalInformation;
|
||
|
||
PAGED_CODE( );
|
||
|
||
status = NtQueryInformationFile(
|
||
FileHandle,
|
||
&ioStatusBlock,
|
||
(PVOID)&pipeLocalInformation,
|
||
sizeof(pipeLocalInformation),
|
||
FilePipeLocalInformation
|
||
);
|
||
|
||
if ( !NT_SUCCESS( status )) {
|
||
return FALSE;
|
||
}
|
||
|
||
if ( pipeLocalInformation.NamedPipeType != FILE_PIPE_MESSAGE_TYPE ) {
|
||
return FALSE;
|
||
}
|
||
|
||
pipeInformation.ReadMode = FILE_PIPE_MESSAGE_MODE;
|
||
pipeInformation.CompletionMode = FILE_PIPE_QUEUE_OPERATION;
|
||
|
||
//
|
||
// ???: is it ok to ignore the return status for this call?
|
||
//
|
||
|
||
NtSetInformationFile(
|
||
FileHandle,
|
||
&ioStatusBlock,
|
||
(PVOID)&pipeInformation,
|
||
sizeof(pipeInformation),
|
||
FilePipeInformation
|
||
);
|
||
|
||
return TRUE;
|
||
|
||
} // SetDefaultPipeMode
|
||
|
||
BOOLEAN
|
||
SrvFailMdlReadDev (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN ULONG LockKey,
|
||
OUT PMDL *MdlChain,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
BOOLEAN
|
||
SrvFailPrepareMdlWriteDev (
|
||
IN PFILE_OBJECT FileObject,
|
||
IN PLARGE_INTEGER FileOffset,
|
||
IN ULONG Length,
|
||
IN ULONG LockKey,
|
||
OUT PMDL *MdlChain,
|
||
OUT PIO_STATUS_BLOCK IoStatus,
|
||
IN PDEVICE_OBJECT DeviceObject
|
||
)
|
||
{
|
||
return FALSE;
|
||
}
|