3922 lines
134 KiB
C
3922 lines
134 KiB
C
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Create.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Create routine for Rx called by the
|
|
dispatch driver.
|
|
|
|
The implementation of SL_OPEN_TARGET_DIRECTORY is a bit unusual...we don't
|
|
actually do it unless the minirdr specifically requests it.
|
|
Instead, we just get the fcb built and then return it. The nodetype is set so
|
|
that no operations can be done except close/cleanup. In this way, we will not
|
|
take an extra trip to the server or a trip to an incorrect server for rename
|
|
operations. If SL_OPEN... can be used for something other than rename, a
|
|
minirdr that uses this facility is toast.
|
|
|
|
Author:
|
|
|
|
Joe Linn [JoeLinn] 8-aug-94
|
|
|
|
Revision History:
|
|
|
|
Balan Sethu Raman [SethuR] 17-July-95
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <ntddmup.h>
|
|
#include <fsctlbuf.h>
|
|
#include <dfsfsctl.h>
|
|
|
|
#if 0 && defined(REMOTE_BOOT)
|
|
BOOLEAN LogAllFiles = FALSE;
|
|
BOOLEAN WatchAllFiles = FALSE;
|
|
BOOLEAN FirstWatchOnly = FALSE;
|
|
BOOLEAN IsFirstWatch = TRUE;
|
|
#endif
|
|
|
|
//
|
|
// The debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_CREATE)
|
|
|
|
#ifdef LOG_SHARINGCHECKS
|
|
#define RxLogForSharingCheck(x) RxLog(x)
|
|
#else
|
|
#define RxLogForSharingCheck(x)
|
|
#endif
|
|
|
|
LUID RxSecurityPrivilege = { SE_SECURITY_PRIVILEGE, 0 };
|
|
|
|
#define StorageType(co) ((co) & FILE_STORAGE_TYPE_MASK)
|
|
#define StorageFlag(co) ((co) & FILE_STORAGE_TYPE_SPECIFIED)
|
|
#define IsStorageTypeSpecified(co) (StorageFlag(co) == FILE_STORAGE_TYPE_SPECIFIED)
|
|
#define MustBeDirectory(co) ((co) & FILE_DIRECTORY_FILE)
|
|
#define MustBeFile(co) ((co) & FILE_NON_DIRECTORY_FILE)
|
|
|
|
//
|
|
// Where 0 represents a SessionId, the following path formats are used:
|
|
//
|
|
// "\;m:0\Server\Share" for drive based connections
|
|
//
|
|
// "\;:0\Server\Share" for UNC based connections
|
|
//
|
|
// The SessionId is always 0 for NT 5, and a number representing a
|
|
// unique session for Hydra.
|
|
//
|
|
#define DRIVE_BASED_PATH_IDENTIFIER (L';')
|
|
|
|
// The following is used to enable tracing when a specific file name is seen.
|
|
// Tracing continues for a specified number of IRPs.
|
|
// Usage:
|
|
// Break in with debugger and set DbgTriggerNameStr to the ansi string for the
|
|
// file name to trigger on (with trailing null).
|
|
// Set DbgTriggerIrpCount to the number of IRPs to trace after the Create is
|
|
// seen on the name string.
|
|
// Set DbgTriggerState to zero and then continue.
|
|
//
|
|
#ifdef RDBSSTRACE
|
|
|
|
UNICODE_STRING DbgTriggerUStr = {0,0,NULL};
|
|
STRING DbgTriggerNameStr = {0,0,NULL};
|
|
CHAR DbgTriggerName[120] = "\\;F:\\davidor4\\nb4\\clients\\client1\\~DMTMP\\WINWORD\\~WRD0003.tmp";
|
|
#define DBG_TRIGGER_INIT 0
|
|
#define DBG_TRIGGER_LOOKING 1
|
|
#define DBG_TRIGGER_FOUND 2
|
|
ULONG DbgTriggerState = DBG_TRIGGER_FOUND;
|
|
ULONG DbgTriggerIrpCount = 130;
|
|
ULONG RxGlobalTraceIrpCount = 0;
|
|
|
|
#endif
|
|
|
|
extern BOOLEAN DisableByteRangeLockingOnReadOnlyFiles;
|
|
|
|
VOID
|
|
RxSetFullNameInFcb(
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PFCB Fcb,
|
|
IN PUNICODE_STRING FinalName
|
|
);
|
|
|
|
VOID
|
|
RxCopyCreateParameters (
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
VOID
|
|
RxFreeCanonicalNameBuffer(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
RxAllocateCanonicalNameBuffer(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PUNICODE_STRING CanonicalName,
|
|
IN ULONG BufferSizeRequired);
|
|
|
|
NTSTATUS
|
|
RxFirstCanonicalize(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FileName,
|
|
IN OUT PUNICODE_STRING CanonicalName,
|
|
OUT PNET_ROOT_TYPE pNetRootType
|
|
);
|
|
|
|
NTSTATUS
|
|
RxCanonicalizeFileNameByServerSpecs(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PUNICODE_STRING RemainingName
|
|
);
|
|
|
|
NTSTATUS
|
|
RxCanonicalizeNameAndObtainNetRoot(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FileName,
|
|
OUT PUNICODE_STRING RemainingName
|
|
);
|
|
|
|
NTSTATUS
|
|
RxFindOrCreateFcb(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING RemainingName
|
|
);
|
|
|
|
VOID
|
|
RxSetupNetFileObject(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
RxSearchForCollapsibleOpen (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG ShareAccess
|
|
);
|
|
|
|
NTSTATUS
|
|
RxCollapseOrCreateSrvOpen (
|
|
IN OUT PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
RxCreateFromNetRoot(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING RemainingName
|
|
);
|
|
|
|
NTSTATUS
|
|
RxCreateTreeConnect (
|
|
RXCOMMON_SIGNATURE
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxCommonCreate)
|
|
#pragma alloc_text(PAGE, RxAllocateCanonicalNameBuffer)
|
|
#pragma alloc_text(PAGE, RxFreeCanonicalNameBuffer)
|
|
#pragma alloc_text(PAGE, RxFirstCanonicalize)
|
|
#pragma alloc_text(PAGE, RxCanonicalizeFileNameByServerSpecs)
|
|
#pragma alloc_text(PAGE, RxCanonicalizeNameAndObtainNetRoot)
|
|
#pragma alloc_text(PAGE, RxFindOrCreateFcb)
|
|
#pragma alloc_text(PAGE, RxSearchForCollapsibleOpen)
|
|
#pragma alloc_text(PAGE, RxCollapseOrCreateSrvOpen)
|
|
#pragma alloc_text(PAGE, RxSetupNetFileObject)
|
|
#pragma alloc_text(PAGE, RxCreateFromNetRoot)
|
|
#pragma alloc_text(PAGE, RxPrefixClaim)
|
|
#pragma alloc_text(PAGE, RxCreateTreeConnect)
|
|
#pragma alloc_text(PAGE, RxCheckShareAccessPerSrvOpens)
|
|
#pragma alloc_text(PAGE, RxUpdateShareAccessPerSrvOpens)
|
|
#pragma alloc_text(PAGE, RxRemoveShareAccessPerSrvOpens)
|
|
#pragma alloc_text(PAGE, RxGetSessionId)
|
|
#endif
|
|
|
|
#if DBG
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxDumpWantedAccess)
|
|
#pragma alloc_text(PAGE, RxDumpCurrentAccess)
|
|
#pragma alloc_text(PAGE, RxCheckShareAccess)
|
|
#pragma alloc_text(PAGE, RxRemoveShareAccess)
|
|
#pragma alloc_text(PAGE, RxSetShareAccess)
|
|
#pragma alloc_text(PAGE, RxUpdateShareAccess)
|
|
#endif
|
|
#endif
|
|
|
|
INLINE VOID
|
|
RxCopyCreateParameters (
|
|
PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This uses the RxContext as a base to reach out and get the values of the NT
|
|
create parameters. The idea is to centralize this code.
|
|
|
|
It also implements such as ideas as (a) it must be a directory if a backslash
|
|
was stripped and (b) unbuffered is translated to write-through.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the context instance
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
PNT_CREATE_PARAMETERS cp = &RxContext->Create.NtCreateParameters;
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCopyCreateParameters\n"));
|
|
|
|
cp->SecurityContext = capPARAMS->Parameters.Create.SecurityContext;
|
|
|
|
if ((cp->SecurityContext->AccessState != NULL) &&
|
|
(cp->SecurityContext->AccessState->SecurityDescriptor != NULL)) {
|
|
RxContext->Create.SdLength = RtlLengthSecurityDescriptor(
|
|
cp->SecurityContext->AccessState->SecurityDescriptor);
|
|
|
|
RxDbgTrace( 0, Dbg, ("->SecurityCtx/SdLength = %08lx %08lx\n",
|
|
cp->SecurityContext,
|
|
RxContext->Create.SdLength));
|
|
RxLog((" SDss %lx %lx\n", cp->SecurityContext, RxContext->Create.SdLength));
|
|
RxWmiLog(LOG,
|
|
RxCopyCreateParameters_1,
|
|
LOGPTR(cp->SecurityContext)
|
|
LOGULONG(RxContext->Create.SdLength));
|
|
}
|
|
|
|
if (cp->SecurityContext->SecurityQos != NULL) {
|
|
cp->ImpersonationLevel = cp->SecurityContext->SecurityQos->ImpersonationLevel;
|
|
} else {
|
|
cp->ImpersonationLevel = DEFAULT_IMPERSONATION_LEVEL;
|
|
}
|
|
|
|
cp->DesiredAccess = cp->SecurityContext->DesiredAccess;
|
|
cp->AllocationSize = capReqPacket->Overlay.AllocationSize;
|
|
cp->FileAttributes = capPARAMS->Parameters.Create.FileAttributes & FILE_ATTRIBUTE_VALID_FLAGS;
|
|
cp->ShareAccess = capPARAMS->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS;
|
|
cp->Disposition = (((capPARAMS->Parameters.Create.Options)) >> 24) & 0x000000ff;
|
|
cp->CreateOptions = capPARAMS->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
|
|
|
|
|
|
cp->DfsContext = capFileObject->FsContext2;
|
|
cp->DfsNameContext = capFileObject->FsContext;
|
|
|
|
ASSERT(cp->DfsContext == NULL ||
|
|
cp->DfsContext == UIntToPtr(DFS_OPEN_CONTEXT) ||
|
|
cp->DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT) ||
|
|
cp->DfsContext == UIntToPtr(DFS_CSCAGENT_NAME_CONTEXT) ||
|
|
cp->DfsContext == UIntToPtr(DFS_USER_NAME_CONTEXT));
|
|
|
|
ASSERT(cp->DfsNameContext == NULL ||
|
|
((PDFS_NAME_CONTEXT)cp->DfsNameContext)->NameContextType == DFS_OPEN_CONTEXT ||
|
|
((PDFS_NAME_CONTEXT)cp->DfsNameContext)->NameContextType == DFS_DOWNLEVEL_OPEN_CONTEXT ||
|
|
((PDFS_NAME_CONTEXT)cp->DfsNameContext)->NameContextType == DFS_CSCAGENT_NAME_CONTEXT ||
|
|
((PDFS_NAME_CONTEXT)cp->DfsNameContext)->NameContextType == DFS_USER_NAME_CONTEXT);
|
|
|
|
capFileObject->FsContext2 = NULL;
|
|
capFileObject->FsContext = NULL;
|
|
|
|
// The FsContext field was placed as the pFcb in the RX_CONTEXT. Clear it also
|
|
RxContext->pFcb = NULL;
|
|
|
|
if (FlagOn(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH)){
|
|
cp->CreateOptions |= FILE_DIRECTORY_FILE;
|
|
}
|
|
|
|
RxContext->Create.ReturnedCreateInformation = 0;
|
|
|
|
RxContext->Create.EaLength = capPARAMS->Parameters.Create.EaLength;
|
|
if (RxContext->Create.EaLength) {
|
|
RxContext->Create.EaBuffer = capReqPacket->AssociatedIrp.SystemBuffer;
|
|
RxDbgTrace( 0, Dbg, ("->System(Ea)Buffer/EALength = %08lx %08lx\n",
|
|
capReqPacket->AssociatedIrp.SystemBuffer,
|
|
capPARAMS->Parameters.Create.EaLength));
|
|
RxLog((" EAs %lx %lx\n",
|
|
capReqPacket->AssociatedIrp.SystemBuffer,
|
|
capPARAMS->Parameters.Create.EaLength));
|
|
RxWmiLog(LOG,
|
|
RxCopyCreateParameters_2,
|
|
LOGPTR(capReqPacket->AssociatedIrp.SystemBuffer)
|
|
LOGULONG(capPARAMS->Parameters.Create.EaLength));
|
|
} else {
|
|
RxContext->Create.EaBuffer = NULL;
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCopyNtCreateParameters\n"));
|
|
}
|
|
|
|
#if DBG
|
|
#define DEBUG_TAG(___xxx) ,(___xxx)
|
|
#else
|
|
#define DEBUG_TAG(_xxx)
|
|
#endif
|
|
|
|
VOID
|
|
RxFreeCanonicalNameBuffer(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to free the canonical name buffer and reset the state.
|
|
COULD BE INLINED!
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
ASSERT (RxContext->Create.CanonicalNameBuffer == RxContext->AlsoCanonicalNameBuffer);
|
|
if (RxContext->Create.CanonicalNameBuffer) {
|
|
RxFreePool( RxContext->Create.CanonicalNameBuffer );
|
|
RxContext->Create.CanonicalNameBuffer = NULL;
|
|
RxContext->AlsoCanonicalNameBuffer = NULL;
|
|
}
|
|
ASSERT ( RxContext->Create.CanonicalNameBuffer == NULL );
|
|
}
|
|
|
|
NTSTATUS
|
|
RxAllocateCanonicalNameBuffer(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PUNICODE_STRING CanonicalName,
|
|
IN ULONG BufferSizeRequired)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to perform the first level of canonicalization on the name.
|
|
Essentially, this amounts to copying the name and then upcasing the first or the first/second
|
|
components.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
CanonicalName - the canonicalized name
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Fsd status for the Operation.
|
|
SUCCESS means that everything worked and processing should continue
|
|
otherwise....failcomplete the operation.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT (RxContext->Create.CanonicalNameBuffer == NULL);
|
|
|
|
CanonicalName->Buffer = (PWCHAR)RxAllocatePoolWithTag(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
BufferSizeRequired,
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (CanonicalName->Buffer == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("RxAllocateCanonicalNameBuffer allocated %08lx length %08lx\n",
|
|
CanonicalName->Buffer,BufferSizeRequired));
|
|
|
|
RxContext->Create.CanonicalNameBuffer = CanonicalName->Buffer;
|
|
RxContext->AlsoCanonicalNameBuffer = CanonicalName->Buffer;
|
|
CanonicalName->MaximumLength = (USHORT)BufferSizeRequired;
|
|
CanonicalName->Length = 0;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
RxFirstCanonicalize(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FileName,
|
|
IN OUT PUNICODE_STRING CanonicalName,
|
|
OUT PNET_ROOT_TYPE pNetRootType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to perform the first level of canonicalization on the
|
|
name. Essentially, this amounts to copying the name and then upcasing the
|
|
first or the first/second components. In addition for pipe/mailslot UNC names
|
|
the appropriate mapping from pipe,mailslot to IPC$ is done by this routine.
|
|
This routine also adds the appropriate prefix to distinguish deviceless
|
|
connects(UNC names ).
|
|
|
|
In addition to canonicalization this routine also deduces the NET_ROOT_TYPE by
|
|
the information provided in the UNC name.
|
|
|
|
Last, as a side effect of this call, the UNC_NAME flag of the RX_CONTEXT is
|
|
set to record that this name came in as a UNC_NAME. This is finally stored in
|
|
the FOBX and used in conjuring the original filename for QueryInfoFile/NameInfo.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
FileName - the initial filename
|
|
|
|
CanonicalName - the canonicalized name
|
|
|
|
pNetRootType - placeholder for the deduced net root type
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Fsd status for the Operation.
|
|
SUCCESS means that everything worked and processing should continue
|
|
otherwise....failcomplete the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureParamBlock;
|
|
|
|
ULONG CanonicalNameLength;
|
|
BOOLEAN SynthesizeCanonicalName = FALSE;
|
|
BOOLEAN ItIsAUNCName = FALSE;
|
|
BOOLEAN MungeNameForDevicelessTreeConnect = FALSE;
|
|
NET_ROOT_TYPE DeducedNetRootType = NET_ROOT_WILD;
|
|
|
|
UNICODE_STRING ServerName;
|
|
UNICODE_STRING ShareName;
|
|
UNICODE_STRING RemainingName;
|
|
ULONG SessionId;
|
|
WCHAR IdBuffer[16]; // From RtlIntegerToUnicodeString()
|
|
UNICODE_STRING IdString;
|
|
|
|
ULONG RxContextFlags = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxFirstCanonicalize entry, filename=%wZ\n",FileName));
|
|
if (FileName->Length == 0) {
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
// for core servers in particular, it's important to get the service string correct.......
|
|
// so, if this is a devicefull netuse the we will get the netroottype by looking at the string
|
|
|
|
if ((FileName->Length > sizeof(L"\\;m:"))
|
|
&& (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
|
|
&& (FileName->Buffer[1] == DRIVE_BASED_PATH_IDENTIFIER)
|
|
) {
|
|
|
|
// it looks like a deviceful netuse.....look for the "early colon"
|
|
// The following test for the classification of net root types is predicated
|
|
// upon the use of single drive letters for Disk files and multiple letters
|
|
// for print shares. This will have to be reworked when the support for
|
|
// extended drive letters is provided.
|
|
if (FileName->Buffer[3] == L':') {
|
|
DeducedNetRootType = NET_ROOT_DISK;
|
|
} else {
|
|
DeducedNetRootType = NET_ROOT_PRINT;
|
|
}
|
|
}
|
|
|
|
if ((FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) &&
|
|
(FileName->Buffer[1] != DRIVE_BASED_PATH_IDENTIFIER)) {
|
|
PWCHAR pBuffer,pEndOfName;
|
|
|
|
// This is a UNC path name presented by the user.
|
|
RemainingName.Length = RemainingName.MaximumLength = FileName->Length - sizeof(WCHAR);
|
|
RemainingName.Buffer = &FileName->Buffer[1];
|
|
ItIsAUNCName = TRUE;
|
|
SetFlag(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_UNC_NAME);
|
|
|
|
//
|
|
// UNC tree connect path names have the following format:
|
|
// "\;:0\Server\Share where 0 represents the SessionId of the session.
|
|
//
|
|
SessionId = RxGetSessionId (capPARAMS);
|
|
IdString.Length = 0;
|
|
IdString.MaximumLength = sizeof(IdBuffer);
|
|
IdString.Buffer = IdBuffer;
|
|
RtlIntegerToUnicodeString( SessionId, 10, &IdString );
|
|
|
|
// scan till the second separator. This will give us the server name.
|
|
ServerName.Buffer = RemainingName.Buffer;
|
|
pEndOfName = (PWCHAR)((PCHAR)RemainingName.Buffer + RemainingName.Length);
|
|
pBuffer = RemainingName.Buffer;
|
|
while ((pBuffer != pEndOfName) && (*pBuffer != OBJ_NAME_PATH_SEPARATOR)) {
|
|
pBuffer++;
|
|
}
|
|
|
|
ServerName.Length = (USHORT)((PCHAR)pBuffer - (PCHAR)ServerName.Buffer);
|
|
RemainingName.Length = (USHORT)((PCHAR)pEndOfName - (PCHAR)pBuffer);
|
|
RemainingName.Buffer = pBuffer;
|
|
|
|
RxDbgTrace(0, Dbg, ("RxFirstCanonicalize entry, remainingname=%wZ\n",&RemainingName));
|
|
|
|
// Apply the transformation for mapping PIPE shares to IPC$ shares.
|
|
// Note that this needs to be done only if the share name is specified.
|
|
// since the mup always passes in the trailing slash account for it
|
|
if (RemainingName.Length > sizeof(WCHAR)) {
|
|
// The second separator has been located. Compare to see if the name
|
|
// maps needs to be munged from PIPE or MAILSLOT to IPC$. Note that the
|
|
// leading / is accounted for as part of the compare
|
|
|
|
ShareName = RemainingName;
|
|
|
|
// Check to see if it is a named pipe connection
|
|
if (ShareName.Length >= s_PipeShareName.Length) {
|
|
if (ShareName.Length == s_PipeShareName.Length ||
|
|
ShareName.Length > s_PipeShareName.Length &&
|
|
ShareName.Buffer[s_PipeShareName.Length/2] == OBJ_NAME_PATH_SEPARATOR) {
|
|
ShareName.Length = s_PipeShareName.Length;
|
|
SynthesizeCanonicalName = RtlEqualUnicodeString(
|
|
&ShareName,
|
|
&s_PipeShareName,
|
|
TRUE); // case insensitive
|
|
} else {
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
} else {
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
|
|
if (SynthesizeCanonicalName) {
|
|
ShareName = s_IpcShareName;
|
|
DeducedNetRootType = NET_ROOT_PIPE;
|
|
RemainingName.Length -= s_PipeShareName.Length;
|
|
RemainingName.Buffer = (PWCHAR)((PCHAR)RemainingName.Buffer + s_PipeShareName.Length);
|
|
} else {
|
|
BOOLEAN FoundIPCdollar;
|
|
|
|
ShareName = RemainingName;
|
|
|
|
if (ShareName.Length >= s_IpcShareName.Length) {
|
|
ShareName.Length = s_IpcShareName.Length;
|
|
FoundIPCdollar = RtlEqualUnicodeString(
|
|
&ShareName,
|
|
&s_IpcShareName,
|
|
TRUE); // Case insensitive
|
|
} else {
|
|
FoundIPCdollar = FALSE;
|
|
}
|
|
|
|
if (FoundIPCdollar) {
|
|
DeducedNetRootType = NET_ROOT_PIPE;
|
|
ASSERT(SynthesizeCanonicalName == FALSE);
|
|
} else {
|
|
ShareName = RemainingName;
|
|
|
|
if (ShareName.Length >= s_MailSlotShareName.Length) {
|
|
if (ShareName.Length == s_MailSlotShareName.Length ||
|
|
ShareName.Length > s_MailSlotShareName.Length &&
|
|
ShareName.Buffer[s_MailSlotShareName.Length/2] == OBJ_NAME_PATH_SEPARATOR) {
|
|
ShareName.Length = s_MailSlotShareName.Length;
|
|
SynthesizeCanonicalName = RtlEqualUnicodeString(
|
|
&ShareName,
|
|
&s_MailSlotShareName,
|
|
TRUE); // Case insensitive
|
|
} else {
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
} else {
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
|
|
if (SynthesizeCanonicalName) {
|
|
WCHAR LastCharacterInServerName;
|
|
|
|
DeducedNetRootType = NET_ROOT_MAILSLOT;
|
|
RxContext->Flags |= RX_CONTEXT_FLAG_CREATE_MAILSLOT;
|
|
|
|
// It is a mailslot share. Check to see if further reduction to canonical
|
|
// form is required.
|
|
LastCharacterInServerName =
|
|
ServerName.Buffer[(ServerName.Length/sizeof(WCHAR)) - 1];
|
|
|
|
if ((LastCharacterInServerName == L'*') &&
|
|
(ServerName.Length == sizeof(WCHAR))) {
|
|
ShareName = s_MailSlotShareName;
|
|
ServerName = s_PrimaryDomainName;
|
|
RemainingName.Length -= s_MailSlotShareName.Length;
|
|
RemainingName.Buffer = (PWCHAR)(
|
|
(PCHAR)RemainingName.Buffer +
|
|
s_MailSlotShareName.Length);
|
|
} else {
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SynthesizeCanonicalName) {
|
|
CanonicalNameLength = sizeof(WCHAR) + // obj name separator
|
|
ServerName.Length +
|
|
ShareName.Length +
|
|
RemainingName.Length;
|
|
|
|
RxContext->Flags |= RxContextFlags;
|
|
if (RxContext->Flags & RX_CONTEXT_FLAG_CREATE_MAILSLOT) {
|
|
CanonicalNameLength += s_MailSlotServerPrefix.Length;
|
|
}
|
|
} else {
|
|
CanonicalNameLength = FileName->Length;
|
|
}
|
|
} else {
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
CanonicalNameLength = FileName->Length;
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
} else {
|
|
CanonicalNameLength = FileName->Length;
|
|
SynthesizeCanonicalName = FALSE;
|
|
}
|
|
|
|
*pNetRootType = DeducedNetRootType;
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// if this is a UNC name AND this is a tree connect then we have to munge
|
|
// the name so as to avoid a conflict by adding '\;:'
|
|
|
|
if (ItIsAUNCName &&
|
|
!SynthesizeCanonicalName) {
|
|
MungeNameForDevicelessTreeConnect = TRUE;
|
|
CanonicalNameLength += (3 * sizeof(WCHAR));
|
|
|
|
// Hydra adds '\;:0' where 0 represents a SessionId
|
|
CanonicalNameLength += IdString.Length;
|
|
}
|
|
|
|
if (!SynthesizeCanonicalName &&
|
|
!MungeNameForDevicelessTreeConnect) {
|
|
if (FileName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR) {
|
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = RxAllocateCanonicalNameBuffer(
|
|
RxContext,
|
|
CanonicalName,
|
|
CanonicalNameLength);
|
|
}
|
|
|
|
if (Status ==STATUS_SUCCESS) {
|
|
if (!SynthesizeCanonicalName) {
|
|
if (!MungeNameForDevicelessTreeConnect) {
|
|
RtlCopyUnicodeString(CanonicalName,FileName);
|
|
} else {
|
|
CanonicalName->Buffer[0] = OBJ_NAME_PATH_SEPARATOR;
|
|
CanonicalName->Buffer[1] = DRIVE_BASED_PATH_IDENTIFIER;
|
|
CanonicalName->Buffer[2] = L':';
|
|
CanonicalName->Length = 3*sizeof(WCHAR);
|
|
|
|
RtlAppendUnicodeStringToString(
|
|
CanonicalName,
|
|
&IdString);
|
|
|
|
RtlAppendUnicodeStringToString(
|
|
CanonicalName,
|
|
FileName);
|
|
}
|
|
} else {
|
|
PCHAR pCanonicalNameBuffer = (PCHAR)CanonicalName->Buffer;
|
|
|
|
// The name has to be synthesized from the appropriate components.
|
|
// Copy the initial prefix
|
|
ASSERT(CanonicalName->MaximumLength == CanonicalNameLength);
|
|
CanonicalName->Length = (USHORT)CanonicalNameLength;
|
|
CanonicalName->Buffer[0] = OBJ_NAME_PATH_SEPARATOR;
|
|
pCanonicalNameBuffer += sizeof(WCHAR);
|
|
|
|
if (MungeNameForDevicelessTreeConnect) {
|
|
CanonicalName->Buffer[1] = DRIVE_BASED_PATH_IDENTIFIER;
|
|
CanonicalName->Buffer[2] = L':';
|
|
CanonicalName->Buffer[3] = OBJ_NAME_PATH_SEPARATOR;
|
|
pCanonicalNameBuffer += 3*sizeof(WCHAR);
|
|
}
|
|
|
|
if (RxContext->Flags & RX_CONTEXT_FLAG_CREATE_MAILSLOT) {
|
|
RtlCopyMemory(
|
|
pCanonicalNameBuffer,
|
|
s_MailSlotServerPrefix.Buffer,
|
|
s_MailSlotServerPrefix.Length);
|
|
|
|
pCanonicalNameBuffer += s_MailSlotServerPrefix.Length;
|
|
}
|
|
|
|
// Copy the server name
|
|
RtlCopyMemory(
|
|
pCanonicalNameBuffer,
|
|
ServerName.Buffer,
|
|
ServerName.Length);
|
|
pCanonicalNameBuffer += ServerName.Length;
|
|
|
|
// Copy the share name. Ensure that the share name includes the leading
|
|
// OBJ_NAME_PATH_SEPARATOR
|
|
ASSERT(ShareName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR);
|
|
RtlCopyMemory(
|
|
pCanonicalNameBuffer,
|
|
ShareName.Buffer,
|
|
ShareName.Length);
|
|
pCanonicalNameBuffer += ShareName.Length;
|
|
|
|
// Copy the remaining name
|
|
RtlCopyMemory(
|
|
pCanonicalNameBuffer,
|
|
RemainingName.Buffer,
|
|
RemainingName.Length);
|
|
|
|
#ifdef _WIN64
|
|
//
|
|
// (fcf) This should be addressed. I was finding that
|
|
// CanonicalName->Length was ending up too large by 32 bytes
|
|
// (16 chars).
|
|
//
|
|
// In the code above, CanonicalNameLength (and therefore
|
|
// CanonicalName->Length) is padded with (16 * sizeof(WCHAR))
|
|
// to accomodate a 16-character session ID... yet that
|
|
// ID is not copied in some circumstances, such as this
|
|
// code path where SynthesizeCanonicalName == TRUE.
|
|
//
|
|
// Someone more familiar with the code should figure out why
|
|
// this isn't causing a problem on 32-bit builds and what
|
|
// the correct fix is.
|
|
//
|
|
|
|
CanonicalName->Length =
|
|
(USHORT)((pCanonicalNameBuffer + RemainingName.Length) -
|
|
(PCHAR)CanonicalName->Buffer);
|
|
#endif
|
|
|
|
RxDbgTrace(0,Dbg,("Final Munged Name .....%wZ\n", CanonicalName));
|
|
}
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxFirstCanonicalize exit, status=%08lx\n",Status));
|
|
return Status;
|
|
}
|
|
|
|
//#define RX2C_USE_ALTERNATES_FOR_DEBUG 1
|
|
#ifndef RX2C_USE_ALTERNATES_FOR_DEBUG
|
|
#define RX2C_IS_SEPARATOR(__c) ((__c==OBJ_NAME_PATH_SEPARATOR)||(__c==L':'))
|
|
#define RX2C_IS_DOT(__c) ((__c==L'.'))
|
|
#define RX2C_IS_COLON(__c) ((__c==L':'))
|
|
#else
|
|
#define RX2C_IS_SEPARATOR(__c) ((__c==OBJ_NAME_PATH_SEPARATOR)||(__c==L':')||(__c==L'!'))
|
|
#define RX2C_IS_DOT(__c) ((__c==L'.')||(__c==L'q'))
|
|
#define RX2C_IS_COLON(__c) ((__c==L':'))
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RxCanonicalizeFileNameByServerSpecs(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PUNICODE_STRING RemainingName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to canonicalize a filename according to the way that
|
|
the server wants it.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
RemainingName - the filename
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Fsd status for the Operation.
|
|
MORE_PROCESSING_REQUIRED means that everything worked and processing
|
|
should continue otherwise....failcomplete the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
PWCHAR Buffer = RemainingName->Buffer;
|
|
ULONG Length = RemainingName->Length / sizeof(WCHAR);
|
|
ULONG i,o; //input and output pointers
|
|
|
|
PAGED_CODE();
|
|
|
|
if (Length==0) {
|
|
return(Status);
|
|
}
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCanonicalizeFileNameByServerSpecs Rname=%wZ\n", RemainingName));
|
|
|
|
for (i=o=0;i<Length;) {
|
|
ULONG firstchar,lastchar; //first and last char of the component
|
|
//find a component starting at i: [\][^\]* is the format
|
|
|
|
firstchar = i;
|
|
for (lastchar=i+1;;lastchar++) {
|
|
if ( (lastchar>=Length) || RX2C_IS_SEPARATOR(Buffer[lastchar]) ) {
|
|
lastchar--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
IF_DEBUG {
|
|
UNICODE_STRING Component;
|
|
Component.Buffer = &Buffer[firstchar];
|
|
Component.Length = (USHORT)(sizeof(WCHAR)*(lastchar-firstchar+1));
|
|
RxDbgTraceLV(0, Dbg, 1001, ("RxCanonicalizeFileNameByServerSpecs component=%wZ\n", &Component));
|
|
}
|
|
|
|
//firstchar..lastchar describe the component
|
|
//according to darrylh, . and .. are illegal now
|
|
//i believe that consecutive slashes are also illegal
|
|
|
|
switch (lastchar-firstchar) {
|
|
case 0: //length 1
|
|
// the two bad cases are a backslash or a dot. if the backslash is at the end then that's okay
|
|
if ( (RX2C_IS_SEPARATOR(Buffer[firstchar])&&(firstchar!=Length-1))
|
|
|| RX2C_IS_DOT(Buffer[firstchar])
|
|
) {
|
|
if (lastchar!=0) {
|
|
// it is ok if two colons stick together, i.e. \\server\share\foo::stream
|
|
if (!RX2C_IS_COLON(Buffer[lastchar+1])) {
|
|
goto BADRETURN;
|
|
}
|
|
} else {
|
|
// it is fine if the colon follows the share, i.e. \\server\share\:stream
|
|
if (!RX2C_IS_COLON(Buffer[1])) {
|
|
goto BADRETURN;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: //length 2
|
|
//bad cases: \. and ..
|
|
if ( RX2C_IS_DOT(Buffer[firstchar+1])
|
|
&& (RX2C_IS_SEPARATOR(Buffer[firstchar])
|
|
|| RX2C_IS_DOT(Buffer[firstchar]) )
|
|
) {
|
|
goto BADRETURN;
|
|
}
|
|
break;
|
|
|
|
case 2: //length 3
|
|
if ( (RX2C_IS_SEPARATOR(Buffer[firstchar])
|
|
&& RX2C_IS_DOT(Buffer[firstchar+1])
|
|
&& RX2C_IS_DOT(Buffer[firstchar+2]))
|
|
) {
|
|
goto BADRETURN;
|
|
}
|
|
break;
|
|
|
|
}
|
|
//DOWNLEVEL this is where you limit by component length. o will be the back ptr
|
|
//but for no downlevel....do nothing.
|
|
i = lastchar + 1;
|
|
}
|
|
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return(Status);
|
|
|
|
BADRETURN:
|
|
RxDbgTrace(-1, Dbg, ("RxCanonicalizeFileNameByServerSpecs BADRETURN \n"));
|
|
return(STATUS_OBJECT_PATH_SYNTAX_BAD);
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCanonicalizeNameAndObtainNetRoot(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING FileName,
|
|
OUT PUNICODE_STRING RemainingName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to find out the server or netroot associated with a
|
|
name. In addition, the name is canonicalized according to what flags are set
|
|
in the srvcall.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
FileName - the initial filename
|
|
|
|
CanonicalName - the canonicalized name. an initial string is passed in; if it
|
|
is not big enough then a bigger one is allocated and freed
|
|
when the rxcontx is freed.
|
|
|
|
RemainingName - the name of the file after the netroot prefix is removed; it
|
|
has been canonicalized. This points into the same buffer as
|
|
canonical name.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Fsd status for the Operation.
|
|
SUCCESS means that everything worked and processing should continue
|
|
otherwise....failcomplete the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
UNICODE_STRING CanonicalName;
|
|
|
|
PFILE_OBJECT RelatedFileObject = capFileObject->RelatedFileObject;
|
|
LOCK_HOLDING_STATE LockHoldingState = LHS_LockNotHeld;
|
|
NET_ROOT_TYPE NetRootType = NET_ROOT_WILD;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCanonicalizeName -> %08lx\n", 0));
|
|
|
|
RemainingName->Buffer = NULL;
|
|
RemainingName->Length = RemainingName->MaximumLength = 0;
|
|
|
|
CanonicalName.Buffer = NULL;
|
|
CanonicalName.Length = CanonicalName.MaximumLength = 0;
|
|
|
|
if (!RelatedFileObject) {
|
|
Status = RxFirstCanonicalize(
|
|
RxContext,
|
|
FileName,
|
|
&CanonicalName,
|
|
&NetRootType);
|
|
|
|
if (Status!=STATUS_SUCCESS) {
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return(Status);
|
|
}
|
|
} else {
|
|
|
|
PFCB RelatedFcb = (PFCB)(RelatedFileObject->FsContext);
|
|
PFOBX RelatedFobx = (PFOBX)(RelatedFileObject->FsContext2);
|
|
ULONG AllocationNeeded;
|
|
PV_NET_ROOT RelatedVNetRoot;
|
|
PUNICODE_STRING RelatedVNetRootName,RelatedFcbName;
|
|
|
|
if ((RelatedFcb == NULL) ||
|
|
(RelatedFobx == NULL)) {
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RelatedVNetRoot = (PV_NET_ROOT)(RelatedFobx->SrvOpen->pVNetRoot);
|
|
|
|
if (!NodeTypeIsFcb(RelatedFcb) ||
|
|
(RelatedVNetRoot == NULL) ||
|
|
(NodeType(RelatedVNetRoot) != RDBSS_NTC_V_NETROOT)) {
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RelatedVNetRootName = &RelatedVNetRoot->PrefixEntry.Prefix;
|
|
RelatedFcbName = &RelatedFcb->FcbTableEntry.Path;
|
|
|
|
//relative open......
|
|
// we have to ensure that we have a canonical name buffer that is
|
|
// long enough so we add the name of the current file to the sum of
|
|
// the vnetroot length of the relative file and the prefixname (not
|
|
// the alreadyprefixedname) of the relative file. plus some slop for
|
|
// chars. If this is greater than the maximum value for a USHORT we
|
|
// reject the name as being invalid since we cannot represent it in
|
|
// a UNICODE_STRING
|
|
|
|
AllocationNeeded = RelatedVNetRootName->Length
|
|
+ RelatedFcbName->Length
|
|
+ FileName->Length
|
|
+ 3 * sizeof(WCHAR);
|
|
|
|
if (AllocationNeeded <= 0xffff) {
|
|
// you may need some backslashs/colons in the middle
|
|
|
|
Status = RxAllocateCanonicalNameBuffer(
|
|
RxContext,
|
|
&CanonicalName,
|
|
AllocationNeeded);
|
|
} else {
|
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|
}
|
|
|
|
if (Status!=STATUS_SUCCESS) {
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return(Status);
|
|
}
|
|
|
|
RtlMoveMemory( CanonicalName.Buffer,
|
|
RelatedVNetRootName->Buffer,
|
|
RelatedVNetRootName->Length );
|
|
RtlMoveMemory( ((PBYTE)(CanonicalName.Buffer)) + RelatedVNetRootName->Length,
|
|
RelatedFcbName->Buffer,
|
|
RelatedFcbName->Length );
|
|
|
|
CanonicalName.Length = (USHORT)(RelatedVNetRootName->Length
|
|
+ RelatedFcbName->Length);
|
|
|
|
RxDbgTrace(0,Dbg,("Name From Related Fileobj.....%wZ\n", &CanonicalName));
|
|
if (FileName->Length != 0) {
|
|
//add on the rest...there are special cases here! with ':' for streams.........
|
|
ULONG LastWCharIndex = (CanonicalName.Length/sizeof(WCHAR)) - 1;
|
|
if (CanonicalName.Buffer[LastWCharIndex] != OBJ_NAME_PATH_SEPARATOR
|
|
&& (FileName->Buffer[0] != L':' ) ) {
|
|
ASSERT(CanonicalName.Length < CanonicalName.MaximumLength);
|
|
CanonicalName.Length += sizeof(WCHAR);
|
|
CanonicalName.Buffer[LastWCharIndex+1] = OBJ_NAME_PATH_SEPARATOR;
|
|
}
|
|
ASSERT (CanonicalName.MaximumLength >= CanonicalName.Length + FileName->Length);
|
|
RxDbgTrace(0,Dbg,("Name From Related Fileobj w/ trailer.....%wZ\n", &CanonicalName));
|
|
RtlMoveMemory(((PCHAR)CanonicalName.Buffer)+CanonicalName.Length,
|
|
FileName->Buffer,FileName->Length);
|
|
CanonicalName.Length += FileName->Length;
|
|
}
|
|
|
|
if (FlagOn(RelatedFobx->Flags,RX_CONTEXT_CREATE_FLAG_UNC_NAME)){
|
|
//if the related guy was a UNC, we're a UNC
|
|
SetFlag(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_UNC_NAME);
|
|
}
|
|
|
|
RxDbgTrace(0,Dbg,("Final Name From Related Fileobj.....%wZ\n", &CanonicalName));
|
|
}
|
|
|
|
Status = RxFindOrConstructVirtualNetRoot(
|
|
RxContext,
|
|
&CanonicalName,
|
|
NetRootType,
|
|
RemainingName);
|
|
|
|
if ((Status != STATUS_SUCCESS) &&
|
|
(Status != STATUS_PENDING) &&
|
|
(RxContext->Flags & RX_CONTEXT_FLAG_MAILSLOT_REPARSE)) {
|
|
|
|
ASSERT(CanonicalName.Buffer == RxContext->Create.CanonicalNameBuffer);
|
|
|
|
RxFreeCanonicalNameBuffer(RxContext);
|
|
|
|
Status = RxFirstCanonicalize(
|
|
RxContext,
|
|
FileName,
|
|
&CanonicalName,
|
|
&NetRootType);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = RxFindOrConstructVirtualNetRoot(
|
|
RxContext,
|
|
&CanonicalName,
|
|
NetRootType,
|
|
RemainingName);
|
|
}
|
|
}
|
|
|
|
if (FsRtlDoesNameContainWildCards( RemainingName )) {
|
|
Status = STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("RxCanonicalizeName SrvCall-> %08lx\n", RxContext->Create.pSrvCall));
|
|
RxDbgTrace( 0, Dbg, ("RxCanonicalizeName Root-> %08lx\n", RxContext->Create.pNetRoot));
|
|
|
|
Status = RxCanonicalizeFileNameByServerSpecs(RxContext,RemainingName);
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
RxDbgTrace(0, Dbg, ("RxCanonicalizeName Remaining -> %wZ\n", RemainingName));
|
|
}
|
|
}
|
|
|
|
if( (NT_SUCCESS(Status) || (Status == STATUS_MORE_PROCESSING_REQUIRED)) &&
|
|
(RxContext->Create.pNetRoot != NULL) )
|
|
{
|
|
NTSTATUS PreparseStatus;
|
|
|
|
// Allow the Mini-RDR to do any extra "scanning" of the name
|
|
MINIRDR_CALL(PreparseStatus,
|
|
RxContext,
|
|
RxContext->Create.pNetRoot->pSrvCall->RxDeviceObject->Dispatch,
|
|
MRxPreparseName,
|
|
(RxContext, &CanonicalName));
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCanonicalizeName Status -> %08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxFindOrCreateFcb(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING RemainingName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to either find the Fcb associated with the
|
|
name or to create it. If everything succeeds, it returns with a
|
|
reference on the name and with the fcblock held exclusive.
|
|
|
|
so, that's a total of two things for success:
|
|
1) fcb lock held exclusive
|
|
2) reference on the fcb ( be it through lookup or taking an additional
|
|
reference on create)
|
|
|
|
The current strategy is to not delete the Fcb if things don't work out and
|
|
to let it be scavenged. this is a good strategy unless we are being bombarded
|
|
with open requests that fail in which case we should change over to something
|
|
different. for this reason, i record in the irp context if the fcb is built here.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
RemainingName - the name of the file after the netroot prefix is removed; it has been
|
|
canonicalized.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The Fsd status for the Irp
|
|
|
|
Notes:
|
|
|
|
On Exit -- the FCB resource would have been accquired exclusive if successful
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PV_NET_ROOT VNetRoot = (PV_NET_ROOT)RxContext->Create.pVNetRoot;
|
|
PNET_ROOT NetRoot = (PNET_ROOT)RxContext->Create.pNetRoot;
|
|
PFCB Fcb;
|
|
|
|
ULONG TableVersion;
|
|
CLONG RemainingNameLength;
|
|
|
|
BOOLEAN FcbTableLockAcquired;
|
|
BOOLEAN FcbTableLockAcquiredExclusive = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxFindOrCreateFcb -> %08lx\n", 0));
|
|
|
|
ASSERT(NetRoot == (PNET_ROOT)VNetRoot->NetRoot);
|
|
|
|
// Acquire the NET_ROOT's FcbTable lock shared to beginwith. This will
|
|
// ensure maximal concurrency and in those cases when the lookup fails
|
|
// this will be converted to an exclusive lock before proceeding further.
|
|
|
|
RxAcquireFcbTableLockShared(&NetRoot->FcbTable, TRUE);
|
|
FcbTableLockAcquired = TRUE;
|
|
|
|
TableVersion = NetRoot->FcbTable.Version;
|
|
RemainingNameLength = RemainingName->Length;
|
|
|
|
Fcb = RxFcbTableLookupFcb(
|
|
&NetRoot->FcbTable,
|
|
RemainingName);
|
|
|
|
IF_DEBUG {
|
|
if (Fcb) {
|
|
RxLoudFcbMsg("RxFindOrCreateFcb found: ",&(Fcb->FcbTableEntry.Path));
|
|
RxDbgTrace(0, Dbg, (" ----->Found Prefix Name=%wZ\n",
|
|
&Fcb->FcbTableEntry.Path ));
|
|
} else {
|
|
RxDbgTrace(0, Dbg, ("Name not found - %wZ\n", RemainingName));
|
|
RxDbgTrace(0, Dbg, ("Fcb is NULL!!\n"));
|
|
RxLoudFcbMsg("RxFindOrCreateFcb fcbisnull found: ",RemainingName);
|
|
}
|
|
}
|
|
|
|
// If it has been marked for orphaning, lets do it!
|
|
if( Fcb && Fcb->fShouldBeOrphaned )
|
|
{
|
|
// Release our reference from the first lookup
|
|
RxDereferenceNetFcb( Fcb );
|
|
|
|
// Switch to an exclusive table lock so we know we're the only one referencing this FCB
|
|
RxReleaseFcbTableLock( &NetRoot->FcbTable );
|
|
FcbTableLockAcquired = FALSE;
|
|
RxAcquireFcbTableLockExclusive( &NetRoot->FcbTable, TRUE );
|
|
FcbTableLockAcquired = TRUE;
|
|
FcbTableLockAcquiredExclusive = TRUE;
|
|
|
|
// Make sure it is still in the table
|
|
Fcb = RxFcbTableLookupFcb(
|
|
&NetRoot->FcbTable,
|
|
RemainingName);
|
|
|
|
if( Fcb && Fcb->fShouldBeOrphaned )
|
|
{
|
|
RxOrphanThisFcb( Fcb );
|
|
RxDereferenceNetFcb( Fcb );
|
|
Fcb = NULL;
|
|
}
|
|
}
|
|
|
|
if ((Fcb == NULL) ||
|
|
(Fcb->FcbTableEntry.Path.Length != RemainingNameLength)) {
|
|
// Convert the shared lock that is currently held to an exclusive lock.
|
|
// This will necessiate another lookup if the FCB table was updated during
|
|
// this interval
|
|
|
|
if( !FcbTableLockAcquiredExclusive )
|
|
{
|
|
RxReleaseFcbTableLock( &NetRoot->FcbTable );
|
|
FcbTableLockAcquired = FALSE;
|
|
|
|
RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
|
|
FcbTableLockAcquired = TRUE;
|
|
}
|
|
|
|
if (TableVersion != NetRoot->FcbTable.Version) {
|
|
Fcb = RxFcbTableLookupFcb(
|
|
&NetRoot->FcbTable,
|
|
RemainingName);
|
|
}
|
|
|
|
if ((Fcb == NULL) ||
|
|
(Fcb->FcbTableEntry.Path.Length != RemainingNameLength)) {
|
|
//we have to build it
|
|
try {
|
|
Fcb = RxCreateNetFcb( RxContext, VNetRoot, RemainingName );
|
|
|
|
if (Fcb == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = RxAcquireExclusiveFcb( RxContext, Fcb );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxContext->Create.FcbAcquired = TRUE;
|
|
} else {
|
|
RxContext->Create.FcbAcquired = FALSE;
|
|
}
|
|
}
|
|
} finally {
|
|
if (AbnormalTermination()) {
|
|
RxReleaseFcbTableLock( &NetRoot->FcbTable );
|
|
FcbTableLockAcquired = FALSE;
|
|
|
|
if (Fcb)
|
|
{
|
|
RxTransitionNetFcb(Fcb,Condition_Bad);
|
|
|
|
ExAcquireResourceExclusiveLite(Fcb->Header.Resource,TRUE);
|
|
if (!RxDereferenceAndFinalizeNetFcb(Fcb,NULL,FALSE,FALSE)) {
|
|
ExReleaseResourceLite(Fcb->Header.Resource);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FcbTableLockAcquired) {
|
|
RxReleaseFcbTableLock( &NetRoot->FcbTable );
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxContext->pFcb = (PMRX_FCB)Fcb;
|
|
RxLog(("Found or created FCB %lx Condition %lx\n",Fcb,Fcb->Condition));
|
|
RxWmiLog(LOG,
|
|
RxFindOrCreateFcb,
|
|
LOGPTR(Fcb)
|
|
LOGULONG(Fcb->Condition));
|
|
if (!RxContext->Create.FcbAcquired){
|
|
// if the FCB was not newly built then ensure that it is in a stable
|
|
// condition before proceeding further. Note that since a reference
|
|
// to this FCB is held by this routine it cannot be finalized
|
|
// before the control can return to this routine.
|
|
|
|
RxWaitForStableNetFcb(Fcb,RxContext);
|
|
|
|
Status = RxAcquireExclusiveFcb( RxContext, Fcb );
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxContext->Create.FcbAcquired = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxFindOrCreateFcb Fcb=%08lx\n", RxContext->pFcb));
|
|
|
|
if (RxContext->pFcb)
|
|
{
|
|
RxDbgTrace(-1, Dbg, ("RxFindOrCreateFcb name=%wZ\n", &(((PFCB)(RxContext->pFcb))->FcbTableEntry.Path)));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxSearchForCollapsibleOpen (
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG ShareAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to seach the list of available srvopens on
|
|
the fcb to see if we can collapse onto an existing open.
|
|
If we search the whole list w/o finding a collapse, then we return
|
|
STATUS_NOT_FOUND.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -- a SRV_OPEN instance was found.
|
|
STATUS_MORE_PROCESSING_REQUIRED -- no SRV_OPEN instance was found.
|
|
|
|
--*/
|
|
{
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
RxCaptureFcb;
|
|
|
|
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
ULONG Disposition;
|
|
ULONG CurrentCreateOptions;
|
|
BOOLEAN AllowCollapse;
|
|
|
|
PNET_ROOT pNetRoot = (PNET_ROOT)(RxContext->Create.pNetRoot);
|
|
PFCB pFcb = (PFCB)(RxContext->pFcb);
|
|
PSRV_OPEN pSrvOpen = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (FlagOn(
|
|
RxContext->Create.NtCreateParameters.CreateOptions,
|
|
FILE_OPEN_FOR_BACKUP_INTENT)) {
|
|
|
|
ClearFlag(pFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
RxScavengeRelatedFobxs(pFcb);
|
|
|
|
RxPurgeFcbInSystemCache(
|
|
pFcb,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
// if the create specifies a special create disposition then we don't
|
|
// collapse; as well, we give the minirdr the opportunity to defeat
|
|
// collapsing by a calldown
|
|
|
|
CurrentCreateOptions = RxContext->Create.NtCreateParameters.CreateOptions;
|
|
Disposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
AllowCollapse = (Disposition == FILE_OPEN) || (Disposition == FILE_OPEN_IF);
|
|
|
|
if (AllowCollapse && (pFcb->MRxDispatch != NULL)) { //should be an ASSERT??
|
|
NTSTATUS CollapseStatus;
|
|
|
|
ASSERT(RxContext->pRelevantSrvOpen == NULL);
|
|
ASSERT(pFcb->MRxDispatch->MRxShouldTryToCollapseThisOpen!=NULL);
|
|
|
|
CollapseStatus =
|
|
pFcb->MRxDispatch->MRxShouldTryToCollapseThisOpen(RxContext);
|
|
AllowCollapse = (CollapseStatus == STATUS_SUCCESS);
|
|
}
|
|
|
|
if (!AllowCollapse) {
|
|
//it may be that there is an existing open that keeps this open from working....
|
|
//if so, prepurge
|
|
NTSTATUS SharingStatus;
|
|
|
|
SharingStatus = RxCheckShareAccessPerSrvOpens(
|
|
pFcb,
|
|
DesiredAccess,
|
|
ShareAccess);
|
|
|
|
if (SharingStatus != STATUS_SUCCESS) {
|
|
ClearFlag(pFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
RxScavengeRelatedFobxs(pFcb);
|
|
|
|
RxPurgeFcbInSystemCache(
|
|
pFcb,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
TRUE);
|
|
}
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if ((pFcb->NetRoot == (PNET_ROOT)RxContext->Create.pNetRoot) &&
|
|
(pFcb->pNetRoot->Type == NET_ROOT_DISK)) {
|
|
|
|
BOOLEAN FobxsScavengingAttempted = FALSE;
|
|
BOOLEAN FcbPurgingAttempted = FALSE;
|
|
|
|
// Search the list of SRV_OPEN's to determine if this open request can be
|
|
// collapsed with an existing SRV_OPEN.
|
|
|
|
for (;;) {
|
|
PLIST_ENTRY pSrvOpenListEntry;
|
|
|
|
pSrvOpenListEntry = pFcb->SrvOpenList.Flink;
|
|
|
|
for (;;) {
|
|
if (pSrvOpenListEntry == &pFcb->SrvOpenList) {
|
|
// If the end of the list of SRV_OPEN's has been reached then it
|
|
// is time to go to the server, i.e., create a new SRV_OPEN.
|
|
Status = STATUS_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
pSrvOpen = (PSRV_OPEN)CONTAINING_RECORD(pSrvOpenListEntry,SRV_OPEN,SrvOpenQLinks);
|
|
|
|
if ((pSrvOpen->pVNetRoot == RxContext->Create.pVNetRoot) &&
|
|
(pSrvOpen->DesiredAccess == DesiredAccess) &&
|
|
(pSrvOpen->ShareAccess == ShareAccess) &&
|
|
!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_COLLAPSING_DISABLED) &&
|
|
!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_CLOSED) &&
|
|
!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_FILE_RENAMED) &&
|
|
!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_FILE_DELETED)) {
|
|
|
|
if ((pSrvOpen->CreateOptions & FILE_OPEN_REPARSE_POINT) !=
|
|
(CurrentCreateOptions & FILE_OPEN_REPARSE_POINT)) {
|
|
FobxsScavengingAttempted = TRUE;
|
|
FcbPurgingAttempted = TRUE;
|
|
Status = STATUS_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// If a SRV_OPEN with identical DesiredAccess and ShareAccess
|
|
// has been found which has not been renamed/deleted then the
|
|
// new open request can be collapsed onto the existing open.
|
|
if (DisableByteRangeLockingOnReadOnlyFiles ||
|
|
!FlagOn(pSrvOpen->pFcb->Attributes,FILE_ATTRIBUTE_READONLY)) {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
if (pSrvOpen->pVNetRoot != RxContext->Create.pVNetRoot) {
|
|
// the file is accessed by another user. It needs to be purged out
|
|
// if the current user is going to use it.
|
|
|
|
RxContext->Create.TryForScavengingOnSharingViolation = TRUE;
|
|
|
|
// Don't collapse srvopens belonging to different vnetroots
|
|
pSrvOpenListEntry = pSrvOpenListEntry->Flink;
|
|
continue;
|
|
|
|
}
|
|
|
|
// If the existing SRV_OPEN does not match the access required by the
|
|
// new open request ensure that the new open request does not conflict
|
|
// with the existing SRV_OPEN's. If it does scavenging/purging needs
|
|
// to be attempted before forwarding the request to the server.
|
|
|
|
Status = RxCheckShareAccessPerSrvOpens(
|
|
pFcb,
|
|
DesiredAccess,
|
|
ShareAccess);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
pSrvOpenListEntry = pSrvOpenListEntry->Flink;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
// a collapsible open was found. return it.
|
|
RxContext->pRelevantSrvOpen = (PMRX_SRV_OPEN)pSrvOpen;
|
|
ASSERT(pFcb->MRxDispatch->MRxShouldTryToCollapseThisOpen!=NULL);
|
|
|
|
if(pFcb->MRxDispatch->MRxShouldTryToCollapseThisOpen(RxContext) == STATUS_SUCCESS)
|
|
{
|
|
|
|
if (FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_CLOSE_DELAYED)) {
|
|
RxLog(("****** Delayed Close worked reusing SrvOpen(%lx)\n",pSrvOpen));
|
|
RxWmiLog(LOG,
|
|
RxSearchForCollapsibleOpen,
|
|
LOGPTR(pSrvOpen));
|
|
InterlockedDecrement(&pNetRoot->SrvCall->NumberOfCloseDelayedFiles);
|
|
ClearFlag(pSrvOpen->Flags,SRVOPEN_FLAG_CLOSE_DELAYED);
|
|
}
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
if (!FobxsScavengingAttempted) {
|
|
// No SRV_OPEN instance onto which the new request can be collapsed was
|
|
// found. Attempt to scavenge any FOBX's, i.e., ensure that all the
|
|
// delayed close operations on the FOBX are done before checking again.
|
|
|
|
FobxsScavengingAttempted = TRUE;
|
|
ClearFlag(pFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
RxScavengeRelatedFobxs(pFcb);
|
|
continue;
|
|
}
|
|
|
|
if (!FcbPurgingAttempted) {
|
|
// No SRV_OPEN instance was found. Ensure that the potential references
|
|
// held by the memory manager/cache manager can be purged before the
|
|
// open request to the server can be attempted.
|
|
|
|
RxPurgeFcbInSystemCache(
|
|
pFcb,
|
|
NULL,
|
|
0,
|
|
FALSE,
|
|
TRUE);
|
|
|
|
FcbPurgingAttempted = TRUE;
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
} else {
|
|
Status = STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if (Status == STATUS_SHARING_VIOLATION) {
|
|
// A local sharing violation was detected.
|
|
RxContext->Create.TryForScavengingOnSharingViolation = TRUE;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCollapseOrCreateSrvOpen (
|
|
IN OUT PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to either find a SRV_OPEN instance that can be
|
|
collapsed onto or, failing that, to build a fresh one IN
|
|
TRANSITION. The fcblock will already be held exclusive and the
|
|
tablelock will be either exclusive or shared. If everything
|
|
succeeds, it returns with a reference on the srvopen and with the
|
|
fcblock still held excl BUT we always release the tablelock. IF
|
|
IT FAILS, THEN IT COMPLETES THE RXCONTEXT FROM HERE WHICH, IN
|
|
TURN, WILL RELEASE THE FCBLOCK.
|
|
|
|
The minirdr is consulted to determine if a collapse is possible so
|
|
there is no reason to call twice. If the minirdr determines to
|
|
collapse, then it will do so and passback a returnable status.
|
|
Thus, RxStatus(SUCCESS) is an terminating return from here. For this
|
|
reason, we return RxStatus(MORE_PROCESSING_REQUIRED) as the
|
|
nonterminating return and the minirdr routine uses the same.
|
|
|
|
RxContext->SrvOpen contains either the collapsed or built srvopen.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
|
|
Return Value:
|
|
|
|
RxStatus(MORE_PROCESSING_REQUIRED) - further processing of the newly
|
|
constructed SRV_OPEN instance is required.
|
|
|
|
RxStatus(SUCCESS) - the SRV_OPEN instance was found/constructed successfully
|
|
|
|
Notes:
|
|
|
|
On Entry -- the FCB resource must have been acquired exclusive
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_NOT_FOUND;
|
|
|
|
PNET_ROOT NetRoot = (PNET_ROOT)(RxContext->Create.pNetRoot);
|
|
|
|
RxCaptureFcb;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
ACCESS_MASK DesiredAccess = capPARAMS->Parameters.Create.SecurityContext->DesiredAccess
|
|
& FILE_ALL_ACCESS;
|
|
ULONG ShareAccess = capPARAMS->Parameters.Create.ShareAccess & FILE_SHARE_VALID_FLAGS;
|
|
|
|
RX_BLOCK_CONDITION FcbCondition;
|
|
|
|
ULONG CreateOptions;
|
|
BOOLEAN DeleteOnClose;
|
|
BOOLEAN NoIntermediateBuffering;
|
|
BOOLEAN PagingIoResourceTaken = FALSE;
|
|
ULONG Disposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
|
|
PSRV_OPEN SrvOpen;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCollapseOrCreateSrvOpen -> %08lx\n", 0));
|
|
|
|
CreateOptions = capPARAMS->Parameters.Create.Options;
|
|
NoIntermediateBuffering = BooleanFlagOn( CreateOptions, FILE_NO_INTERMEDIATE_BUFFERING );
|
|
DeleteOnClose = BooleanFlagOn( CreateOptions, FILE_DELETE_ON_CLOSE );
|
|
|
|
ASSERT ( RxIsFcbAcquiredExclusive ( capFcb ) );
|
|
|
|
//this ensures that the fcb will not be finalized while we are in the minirdr
|
|
capFcb->UncleanCount++;
|
|
|
|
if (!DeleteOnClose) {
|
|
Status = RxSearchForCollapsibleOpen(
|
|
RxContext,
|
|
DesiredAccess,
|
|
ShareAccess );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxContext->CurrentIrp->IoStatus.Information = FILE_OPENED;
|
|
}
|
|
}
|
|
|
|
if ( Status == STATUS_NOT_FOUND ) {
|
|
RxDbgTrace(0, Dbg, ("No collapsible srvopens found for %wZ\n", &capFcb->FcbTableEntry.Path));
|
|
|
|
try {
|
|
SrvOpen = RxCreateSrvOpen(
|
|
(PV_NET_ROOT)RxContext->Create.pVNetRoot,
|
|
capFcb );
|
|
if (SrvOpen != NULL) {
|
|
SrvOpen->DesiredAccess = DesiredAccess;
|
|
SrvOpen->ShareAccess = ShareAccess;
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} except (CATCH_EXPECTED_EXCEPTIONS) {
|
|
//note: we do not give back the FCB!!!!
|
|
RxDbgTrace(-1, Dbg, ("RxCollapseOrCreateSrvOpen EXCEPTION %08lx\n",
|
|
GetExceptionCode()));
|
|
return RxProcessException( RxContext, GetExceptionCode() );
|
|
} //try
|
|
|
|
RxContext->pRelevantSrvOpen = (PMRX_SRV_OPEN)SrvOpen;
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxInitiateSrvOpenKeyAssociation(SrvOpen);
|
|
|
|
//calldown.....
|
|
IF_DEBUG {RxContext->CurrentIrp->IoStatus.Information = 0xabcdef;}
|
|
|
|
MINIRDR_CALL(Status,
|
|
RxContext,
|
|
capFcb->MRxDispatch,
|
|
MRxCreate,
|
|
(RxContext));
|
|
|
|
//help other minirdr writers find this bug, i.e. they should use the new way
|
|
DbgDoit( ASSERT(RxContext->CurrentIrp->IoStatus.Information == 0xabcdef); )
|
|
|
|
// if this is a successful overwrite, then truncate the file
|
|
if ((Disposition==FILE_OVERWRITE) || (Disposition==FILE_OVERWRITE_IF)) {
|
|
if (Status==STATUS_SUCCESS) {
|
|
RxAcquirePagingIoResource(capFcb,RxContext);
|
|
capFcb->Header.FileSize.QuadPart = 0;
|
|
capFcb->Header.AllocationSize.QuadPart = 0;
|
|
capFcb->Header.ValidDataLength.QuadPart = 0;
|
|
RxContext->CurrentIrpSp->FileObject->SectionObjectPointer
|
|
= &((PFCB)(capFcb))->NonPaged->SectionObjectPointers;
|
|
CcSetFileSizes( RxContext->CurrentIrpSp->FileObject,
|
|
(PCC_FILE_SIZES)&capFcb->Header.AllocationSize
|
|
);
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
}
|
|
} else if( Status == STATUS_SUCCESS ) {
|
|
|
|
RxContext->CurrentIrpSp->FileObject->SectionObjectPointer
|
|
= &((PFCB)(capFcb))->NonPaged->SectionObjectPointers;
|
|
|
|
if( CcIsFileCached(RxContext->CurrentIrpSp->FileObject) ) {
|
|
|
|
//
|
|
// Since the file is cached, we need to update the sizes the cache manager
|
|
// has with the ones we just got back from the server. If the server is
|
|
// behaving properly, this will be a nop. But we have to protect ourselves
|
|
// from a bad server that returns updated file sizes that we do not know about.
|
|
//
|
|
|
|
RxAdjustAllocationSizeforCC( capFcb );
|
|
|
|
try {
|
|
|
|
CcSetFileSizes( RxContext->CurrentIrpSp->FileObject,
|
|
(PCC_FILE_SIZES)&capFcb->Header.AllocationSize );
|
|
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
//
|
|
// We took an exception setting the file sizes. This can happen
|
|
// if the cache manager was not able to allocate resources. We
|
|
// cannot restore the previous sizes, since we do not know what they
|
|
// were. The best we can do is purge the file from the cache.
|
|
//
|
|
|
|
RxPurgeFcbInSystemCache( capFcb,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
RxContext->CurrentIrp->IoStatus.Information
|
|
= RxContext->Create.ReturnedCreateInformation;
|
|
|
|
|
|
SrvOpen->OpenStatus = Status;
|
|
|
|
|
|
RxTransitionSrvOpen(SrvOpen, //SrvOpenAsWrapperSrvOpen(SrvOpen),
|
|
(Status==STATUS_SUCCESS)?Condition_Good
|
|
:Condition_Bad
|
|
);
|
|
|
|
|
|
RxDumpCurrentAccess("shareaccess status after calldown....","","ShrAccPostMini",&capFcb->ShareAccess);
|
|
RxDbgTrace(0, Dbg, ("RxCollapseOrCreateSrvOpen Back from the minirdr, Status=%08lx\n", Status ));
|
|
|
|
ASSERT ( RxIsFcbAcquiredExclusive ( capFcb ) );
|
|
|
|
RxCompleteSrvOpenKeyAssociation(SrvOpen);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
FcbCondition = Condition_Bad;
|
|
RxDereferenceSrvOpen(SrvOpen,LHS_ExclusiveLockHeld);
|
|
RxContext->pRelevantSrvOpen = NULL;
|
|
|
|
if (RxContext->pFobx != NULL) {
|
|
RxDereferenceNetFobx(RxContext->pFobx,LHS_ExclusiveLockHeld);
|
|
RxContext->pFobx = NULL;
|
|
}
|
|
|
|
} else {
|
|
if (DeleteOnClose) {
|
|
ClearFlag(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
}
|
|
|
|
SrvOpen->CreateOptions = RxContext->Create.NtCreateParameters.CreateOptions;
|
|
FcbCondition = Condition_Good;
|
|
}
|
|
} else {
|
|
FcbCondition = Condition_Bad;
|
|
}
|
|
|
|
RxLog(("Transitioning FCB %lx Condition %lx\n",capFcb,FcbCondition));
|
|
RxWmiLog(LOG,
|
|
RxCollapseOrCreateSrvOpen,
|
|
LOGPTR(capFcb)
|
|
LOGULONG(FcbCondition));
|
|
|
|
RxTransitionNetFcb(capFcb,FcbCondition);
|
|
} else if (Status == STATUS_SUCCESS) {
|
|
BOOLEAN fTransitionProcessingRequired = FALSE;
|
|
|
|
// An existing SRV_OPEN instance has been found. This instance can be in
|
|
// one of the following two states -- either it has already transitioned
|
|
// into a stable state or it is in the process of being constructed. In
|
|
// the later case this routine needs to wait for this transition to occur.
|
|
// Note that both the reference count and the OpenCount need to be
|
|
// incremented before releasing the resource. An incremented reference
|
|
// count by itself will not ensure that the Close request on a SRV_OPEN
|
|
// will be delayed till the threads waiting for the transitioning of the
|
|
// SRV_OPEN have had a chance to process it.
|
|
|
|
SrvOpen = (PSRV_OPEN)(RxContext->pRelevantSrvOpen);
|
|
if (!StableCondition(SrvOpen->Condition)) {
|
|
fTransitionProcessingRequired = TRUE;
|
|
RxDbgTrace(0,Dbg,("waiting for stable srv open (%lx)\n",SrvOpen));
|
|
|
|
RxReferenceSrvOpen(SrvOpen);
|
|
SrvOpen->OpenCount++;
|
|
|
|
RxReleaseFcb(RxContext,capFcb);
|
|
RxContext->Create.FcbAcquired = FALSE;
|
|
|
|
RxWaitForStableSrvOpen(SrvOpen,RxContext);
|
|
|
|
Status = RxAcquireExclusiveFcb(RxContext,capFcb);
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxContext->Create.FcbAcquired = TRUE;
|
|
}
|
|
}
|
|
|
|
if (SrvOpen->Condition == Condition_Good) {
|
|
|
|
MINIRDR_CALL(Status,RxContext,capFcb->MRxDispatch,MRxCollapseOpen,(RxContext));
|
|
RxDbgTrace(0, Dbg, ("RxCollapseOrCreateSrvOpen Back from the minirdr, Status=%08lx\n", Status ));
|
|
|
|
ASSERT ( RxIsFcbAcquiredExclusive ( capFcb ) );
|
|
} else {
|
|
Status = SrvOpen->OpenStatus;
|
|
}
|
|
|
|
if (fTransitionProcessingRequired) {
|
|
SrvOpen->OpenCount--;
|
|
RxDereferenceSrvOpen(SrvOpen,LHS_ExclusiveLockHeld);
|
|
}
|
|
}
|
|
|
|
capFcb->UncleanCount--; //now that we're back from the minirdr
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCollapseOrCreateSrvOpen SrvOpen %08lx Status %08lx\n"
|
|
, RxContext->pRelevantSrvOpen, Status));
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
RxSetupNetFileObject(
|
|
IN OUT PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to finish setting up the fileobject based on the
|
|
information in the Irpcontext.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
RxCaptureFcb;
|
|
|
|
PVOID VcbOrFcbOrDcb = RxContext->pFcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT((RxContext->pFobx == NULL) || (NodeType(RxContext->pFobx) == RDBSS_NTC_FOBX));
|
|
|
|
if ( VcbOrFcbOrDcb != NULL ) {
|
|
// Set the Vpb field in the file object, and if we were given an
|
|
// Fcb or Dcb move the field over to point to the nonpaged Fcb/Dcb
|
|
if (NodeType(VcbOrFcbOrDcb) == RDBSS_NTC_VCB) {
|
|
NOTHING;
|
|
} else {
|
|
// If this is a temporary file, note it in the FcbState
|
|
if (FlagOn(((PFCB)VcbOrFcbOrDcb)->FcbState, FCB_STATE_TEMPORARY) &&
|
|
(capFileObject != NULL)) {
|
|
SetFlag(capFileObject->Flags, FO_TEMPORARY_FILE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now set the fscontext fields of the file object
|
|
if (capFileObject != NULL) {
|
|
capFileObject->FsContext = VcbOrFcbOrDcb;
|
|
if (RxContext->pFobx != NULL) {
|
|
ULONG_PTR StackBottom,StackTop;
|
|
|
|
IoGetStackLimits( &StackTop, &StackBottom);
|
|
|
|
// Determine if the FileObject passed in is on the stack. If it is do
|
|
// not squirrel away the file object in the FOBX. Otherwise stash it
|
|
// away.
|
|
|
|
if (((ULONG_PTR)capFileObject <= StackBottom) ||
|
|
((ULONG_PTR)capFileObject >= StackTop)) {
|
|
|
|
RxContext->pFobx->AssociatedFileObject = capFileObject;
|
|
} else {
|
|
|
|
RxContext->pFobx->AssociatedFileObject = NULL;
|
|
}
|
|
|
|
if (RxContext->Create.NtCreateParameters.DfsContext == UIntToPtr(DFS_OPEN_CONTEXT)) {
|
|
SetFlag(RxContext->pFobx->Flags,FOBX_FLAG_DFS_OPEN);
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("RxSetupNetFileObject %lx Dfs aware FOBX\n", RxContext->pFobx));
|
|
} else {
|
|
ClearFlag(RxContext->pFobx->Flags,FOBX_FLAG_DFS_OPEN);
|
|
RxDbgTrace(
|
|
0,
|
|
Dbg,
|
|
("RxSetupNetFileObject %lx Dfs unaware FOBX\n", RxContext->pFobx));
|
|
}
|
|
|
|
}
|
|
|
|
capFileObject->FsContext2 = RxContext->pFobx;
|
|
capFileObject->SectionObjectPointer = &capFcb->NonPaged->SectionObjectPointers;
|
|
// The create is being completed successfully. Turn off the remaining
|
|
// desired access flags in the IRP. This is required by Praerit/Robert
|
|
// to facilitate policy code.
|
|
if (capPARAMS->Parameters.Create.SecurityContext != NULL) {
|
|
capPARAMS->Parameters.Create.SecurityContext->AccessState->PreviouslyGrantedAccess |=
|
|
capPARAMS->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess;
|
|
capPARAMS->Parameters.Create.SecurityContext->AccessState->RemainingDesiredAccess = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxpPrepareCreateContextForReuse(
|
|
PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prepares an instance of RX_CONTEXT for reuse. This centralizes all
|
|
the actions required to be undone, i.e., accquistion of resources etc.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
--*/
|
|
{
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
|
|
|
|
RxDbgTrace(0, Dbg, ("RxpPrepareCreateContextForReuse canonname %08lx\n",
|
|
RxContext->Create.CanonicalNameBuffer));
|
|
//the order is important here...release the fcb first
|
|
if (RxContext->Create.FcbAcquired) {
|
|
RxReleaseFcb( RxContext, RxContext->pFcb );
|
|
RxContext->Create.FcbAcquired = FALSE;
|
|
}
|
|
|
|
RxFreeCanonicalNameBuffer(RxContext);
|
|
|
|
if ((RxContext->Create.pVNetRoot != NULL) ||
|
|
(RxContext->Create.NetNamePrefixEntry != NULL)) {
|
|
|
|
PRX_PREFIX_TABLE pRxNetNameTable
|
|
= RxContext->RxDeviceObject->pRxNetNameTable;
|
|
|
|
RxAcquirePrefixTableLockShared( pRxNetNameTable , TRUE);
|
|
|
|
// Dereference the data structures associated with the create operation
|
|
if (RxContext->Create.pVNetRoot != NULL) {
|
|
RxDereferenceVNetRoot((PV_NET_ROOT)(RxContext->Create.pVNetRoot),LHS_SharedLockHeld);
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
}
|
|
|
|
RxReleasePrefixTableLock( pRxNetNameTable );
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCreateFromNetRoot(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN PUNICODE_STRING RemainingName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called during from CommonCreate once a good netroot has
|
|
been established. This routine builds an Fcb, if necessary, and tries to
|
|
collapse the open onto an existing open if it can. If it cannot, then it
|
|
constructs an InTransition srv_open on this netroot and passes the open down
|
|
to the minirdr. By the time that we get here, there is a reference on the
|
|
netroot but we do not have the netname tablelock. When we complete the context,
|
|
this reference is removed.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
|
|
RemainingName - the name of the file after the netroot prefix is removed;
|
|
it has been canonicalized.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The Fsd status for the Irp
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PV_NET_ROOT VNetRoot;
|
|
PNET_ROOT NetRoot;
|
|
PFCB Fcb;
|
|
PSRV_OPEN SrvOpen;
|
|
PFOBX Fobx;
|
|
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
ACCESS_MASK DesiredAccess;
|
|
ULONG ShareAccess;
|
|
BOOLEAN OpenTargetDirectory;
|
|
|
|
PNT_CREATE_PARAMETERS cp;
|
|
|
|
PAGED_CODE();
|
|
|
|
VNetRoot = (PV_NET_ROOT)RxContext->Create.pVNetRoot;
|
|
NetRoot = (PNET_ROOT)RxContext->Create.pNetRoot;
|
|
Fcb = NULL;
|
|
SrvOpen = NULL;
|
|
Fobx = NULL;
|
|
|
|
DesiredAccess = capPARAMS->Parameters.Create.SecurityContext->DesiredAccess
|
|
& FILE_ALL_ACCESS;
|
|
ShareAccess = capPARAMS->Parameters.Create.ShareAccess
|
|
& FILE_SHARE_VALID_FLAGS;
|
|
|
|
OpenTargetDirectory = capPARAMS->Flags & SL_OPEN_TARGET_DIRECTORY;
|
|
|
|
cp = &RxContext->Create.NtCreateParameters;
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCreateFromNetRoot Name=%wZ\n", RemainingName ));
|
|
|
|
// A Create request cannot succeed without a valid NET_ROOT instance.
|
|
if (RxContext->Create.pNetRoot == NULL){
|
|
RxDbgTrace(-1, Dbg, ("RxCreateFromNetRoot Couldn't create the FCB: No NetRoot!!\n"));
|
|
return STATUS_NO_SUCH_FILE;
|
|
}
|
|
|
|
// we cannot proceed unless this device owns the srvcall.
|
|
if (RxContext->RxDeviceObject != RxContext->Create.pNetRoot->pSrvCall->RxDeviceObject){
|
|
RxDbgTrace(-1, Dbg, ("RxCreateFromNetRoot wrong DeviceObject!!!!!\n"));
|
|
return STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
|
|
// The DFS driver builds up a logical name space from different physical
|
|
// volumes. In order to distinguish processing the DFS driver sets the
|
|
// FsContext2 field to DFS_OPEN_CONTEXT or DFS_DOWWNLEVEL_OPEN_CONTEXT. At
|
|
// this point in the control flow the V_NET_ROOT has been determined. This
|
|
// in turn determines the NET_ROOT and SRV_CALL instance and indirectly
|
|
// also determines the Server type. Uplevel opens can only be permitted to
|
|
// servers that are DFS aware.
|
|
|
|
if ((cp->DfsContext == UIntToPtr(DFS_OPEN_CONTEXT)) &&
|
|
!BooleanFlagOn(NetRoot->pSrvCall->Flags,SRVCALL_FLAG_DFS_AWARE_SERVER)) {
|
|
return STATUS_DFS_UNAVAILABLE;
|
|
}
|
|
|
|
if ((cp->DfsContext == UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT)) &&
|
|
BooleanFlagOn(NetRoot->Flags,NETROOT_FLAG_DFS_AWARE_NETROOT)) {
|
|
return STATUS_OBJECT_TYPE_MISMATCH;
|
|
}
|
|
|
|
if (NetRoot->Type == NET_ROOT_PRINT) {
|
|
// allow share read and write to the printer server
|
|
ShareAccess = FILE_SHARE_VALID_FLAGS;
|
|
}
|
|
|
|
// if the create request is for opening the target directory for a rename
|
|
// a fake FCB needs to be created.
|
|
|
|
if (OpenTargetDirectory) {
|
|
if (cp->DesiredAccess & DELETE) {
|
|
RxPurgeRelatedFobxs(
|
|
VNetRoot->NetRoot,
|
|
RxContext,
|
|
ATTEMPT_FINALIZE_ON_PURGE,
|
|
NULL);
|
|
}
|
|
|
|
Fcb = RxCreateNetFcb( RxContext, VNetRoot, RemainingName );
|
|
|
|
if (Fcb != NULL) {
|
|
Fcb->Header.NodeTypeCode = (USHORT)RDBSS_NTC_OPENTARGETDIR_FCB;
|
|
RxContext->Create.FcbAcquired = FALSE;
|
|
|
|
// Normally the FileObjects reference the associated SRV_OPEN instance
|
|
// via the FileObjectExtension(FOBX). In this case there is no
|
|
// corresponding SRV_OPEN and a reference on the FCB is maintained.
|
|
|
|
RxContext->Create.NetNamePrefixEntry = NULL; //don't let it deref the netroot!!!!
|
|
|
|
capFileObject->FsContext = Fcb;
|
|
|
|
if (RxContext->pFobx != NULL) {
|
|
if (capFileObject->FsContext2 == UIntToPtr(DFS_OPEN_CONTEXT)) {
|
|
SetFlag(RxContext->pFobx->Flags,FOBX_FLAG_DFS_OPEN);
|
|
} else {
|
|
ClearFlag(RxContext->pFobx->Flags,FOBX_FLAG_DFS_OPEN);
|
|
}
|
|
}
|
|
|
|
capFileObject->FsContext2 = NULL;
|
|
|
|
Fcb->UncleanCount++;
|
|
Fcb->OpenCount++;
|
|
|
|
Status = RxAcquireExclusiveFcb( RxContext, Fcb );
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxReferenceNetFcb(Fcb);
|
|
RxReleaseFcb( RxContext, Fcb);
|
|
} else {
|
|
RxDbgTrace(-1, Dbg, ("RxCreateFromNetRoot -- Couldn't acquire FCB:(%lx) %lx!\n",Fcb,Status));
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
Status = RxFindOrCreateFcb(RxContext,RemainingName);
|
|
DbgDoit(
|
|
if (RxContext->pFcb == NULL) {
|
|
ASSERT(Status != STATUS_SUCCESS);
|
|
}
|
|
)
|
|
|
|
if ( (Status != STATUS_SUCCESS) || (RxContext->pFcb == NULL) ) {
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCreateFromNetRoot Couldn't create the FCB%c\n", '!' ));
|
|
return Status;
|
|
}
|
|
|
|
Fcb = (PFCB)(RxContext->pFcb);
|
|
|
|
// If the Create request is for a mailslot no further processing is required.
|
|
|
|
if (RxContext->Flags & RX_CONTEXT_FLAG_CREATE_MAILSLOT) {
|
|
Fcb->Header.NodeTypeCode = (USHORT)RDBSS_NTC_MAILSLOT;
|
|
} else {
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxTransitionNetFcb(Fcb,Condition_Good);
|
|
RxLog(("Transitioning FCB %lx Condition %lx\n",Fcb,Fcb->Condition));
|
|
RxWmiLog(LOG,
|
|
RxCollapseOrCreateSrvOpen,
|
|
LOGPTR(Fcb)
|
|
LOGULONG(Fcb->Condition));
|
|
|
|
Fcb->OpenCount++;
|
|
RxSetupNetFileObject( RxContext );
|
|
return Status;
|
|
}
|
|
|
|
// This Create request is for a file/directory or pipe for which further
|
|
// processing is required. At this point the corresponding FCB resource
|
|
// has been accquired ( even for newly constructed FCB's). If this is not the
|
|
// first open then those requests that do not meet the necessary share access
|
|
// constraints can be rejected quickly. Note that this early check avoids
|
|
// a potential Network trip in some cases.
|
|
|
|
RxDumpCurrentAccess("shareaccess status before anything....","","DumpAcc000",&Fcb->ShareAccess);
|
|
if (Fcb->OpenCount > 0) {
|
|
Status = RxCheckShareAccess(
|
|
DesiredAccess,
|
|
ShareAccess,
|
|
capFileObject,
|
|
&Fcb->ShareAccess,
|
|
FALSE,
|
|
"early check per useropens","EarlyPerUO" );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDereferenceNetFcb(Fcb);
|
|
return (Status);
|
|
}
|
|
}
|
|
|
|
if ((cp->CreateOptions & FILE_DELETE_ON_CLOSE) &&
|
|
(cp->DesiredAccess & ~DELETE) == 0) {
|
|
|
|
// if the file is opened for delete only, we push out possible delayed close on this file
|
|
// so that mini rdr has the opportunity to do the perfomance optimization, i.e. delete the
|
|
// file without even open it.
|
|
RxPurgeFcbInSystemCache(
|
|
Fcb,
|
|
NULL,
|
|
0,
|
|
TRUE,
|
|
FALSE);
|
|
RxScavengeRelatedFobxs(Fcb);
|
|
}
|
|
|
|
// A valid FCB which meets the Share access requirements of the create
|
|
// request is on hand. The associated SRV_OPEN should be either located
|
|
// amongst the existing SRV_OPEN's or a new SRV_OPEN instance needs to
|
|
// be constructed.
|
|
|
|
try {
|
|
ULONG CreateOptions;
|
|
BOOLEAN DeleteOnClose;
|
|
BOOLEAN NoIntermediateBuffering;
|
|
|
|
Status = RxCollapseOrCreateSrvOpen(RxContext);
|
|
|
|
IF_DEBUG {
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxDbgTrace(0, Dbg, ("RxCreateFromNetRoot Collapsed onto %08lx\n",
|
|
RxContext->pRelevantSrvOpen ));
|
|
} else {
|
|
RxDbgTrace(0, Dbg, ("RxCreateFromNetRoot Error in Srvopen Collapse %08lx\n", Status ));
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
try_return( Status );
|
|
}
|
|
|
|
|
|
SrvOpen = (PSRV_OPEN)(RxContext->pRelevantSrvOpen);
|
|
Fobx = (PFOBX)(RxContext->pFobx);
|
|
|
|
CreateOptions = capPARAMS->Parameters.Create.Options;
|
|
NoIntermediateBuffering = BooleanFlagOn( CreateOptions, FILE_NO_INTERMEDIATE_BUFFERING );
|
|
DeleteOnClose = BooleanFlagOn( CreateOptions, FILE_DELETE_ON_CLOSE );
|
|
|
|
// If the FCB has multiple SRV_OPEN instances associated with it then it
|
|
// is possible for the Share access associated with the FCB has changed
|
|
// if the FCB resource was dropped by the mini redirector.
|
|
|
|
if (Fcb->OpenCount > 0) {
|
|
Status = RxCheckShareAccess(
|
|
DesiredAccess,
|
|
ShareAccess,
|
|
capFileObject,
|
|
&Fcb->ShareAccess,
|
|
FALSE,
|
|
"second check per useropens","2ndAccPerUO" );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
//
|
|
// When this Fobx goes away it will remove an open from the SrvOpen.
|
|
// Add a reference to the SrvOpen here to account for this. This
|
|
// will prevent the SrvOpen from getting closed prematurely.
|
|
//
|
|
|
|
SrvOpen->OpenCount++;
|
|
|
|
RxDereferenceNetFobx(RxContext->pFobx,LHS_LockNotHeld);
|
|
RxContext->pFobx = NULL;
|
|
try_return (Status);
|
|
}
|
|
} else {
|
|
if (RxContext->Create.pNetRoot->Type != NET_ROOT_PIPE) {
|
|
RxSetShareAccess(
|
|
DesiredAccess,
|
|
ShareAccess,
|
|
capFileObject,
|
|
&Fcb->ShareAccess,
|
|
"initial shareaccess setup","InitShrAcc");
|
|
}
|
|
}
|
|
|
|
RxSetupNetFileObject( RxContext );
|
|
|
|
RxDumpCurrentAccess(
|
|
"shareaccess status after checkorset....",
|
|
"",
|
|
"CurrentAcc",
|
|
&Fcb->ShareAccess);
|
|
|
|
// At this point the necessary infrastructure to handle the create
|
|
// request successfully has been established. What remains to be done
|
|
// is the appropriate initialization of the FileObject( owned by IO
|
|
// subsystem), the FileObjectExtension(FOBX owned by RDBSS) and updating
|
|
// the fields associated with the SRV_OPEN and the FCB. This largely
|
|
// depends upon whether the FCB/SRV_OPEN was newly constructed or
|
|
// was collapsed.
|
|
//
|
|
// SRV_OPEN changes
|
|
// 1) For a newly constructed SRV_OPEN the buffering state needs to
|
|
// be updated
|
|
//
|
|
// FCB changes
|
|
// 1) for an existing FCB the SHARE ACCESS needs to be updated.
|
|
//
|
|
// In all the cases the corresponing OpenCounts and UncleanCounts needs
|
|
// to be updated.
|
|
//
|
|
|
|
if ((Fcb->OpenCount > 0) &&
|
|
(RxContext->Create.pNetRoot->Type != NET_ROOT_PIPE)) {
|
|
RxUpdateShareAccess(
|
|
capFileObject,
|
|
&Fcb->ShareAccess,
|
|
"update share access",
|
|
"UpdShrAcc" );
|
|
}
|
|
|
|
//incrementing the uncleancount must be done before RxChangeBufferingState
|
|
//because that routine will purge the cache otherwise if unclenacount==0
|
|
|
|
Fcb->UncleanCount++;
|
|
|
|
if (FlagOn(capFileObject->Flags,FO_NO_INTERMEDIATE_BUFFERING)) {
|
|
Fcb->UncachedUncleanCount++;
|
|
} else {
|
|
//maybe we should turn on the FO_CACHE_SUPPORTED flag
|
|
}
|
|
|
|
// For the first open, we want to initialize the Fcb buffering state flags
|
|
// to the default value
|
|
if ( (SrvOpen->OpenCount == 0) &&
|
|
(Fcb->UncleanCount == 1) &&
|
|
(!FlagOn(SrvOpen->Flags,SRVOPEN_FLAG_NO_BUFFERING_STATE_CHANGE)) ) {
|
|
RxChangeBufferingState(SrvOpen,NULL,FALSE);
|
|
}
|
|
|
|
// this might be from a previous usage.
|
|
ClearFlag(Fcb->FcbState, FCB_STATE_DELAY_CLOSE);
|
|
|
|
// Reference the Objects as needed
|
|
Fcb->OpenCount++;
|
|
SrvOpen->UncleanFobxCount++;
|
|
SrvOpen->OpenCount++;
|
|
|
|
SrvOpen->ulFileSizeVersion = Fcb->ulFileSizeVersion;
|
|
|
|
// For NoIntermediateBuffering opens, we need to disable cacheing on
|
|
// this FCB
|
|
if( NoIntermediateBuffering )
|
|
{
|
|
SetFlag( SrvOpen->Flags, SRVOPEN_FLAG_DONTUSE_READ_CACHEING );
|
|
SetFlag( SrvOpen->Flags, SRVOPEN_FLAG_DONTUSE_WRITE_CACHEING );
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_READCACHEING_ENABLED );
|
|
ClearFlag( Fcb->FcbState, FCB_STATE_WRITECACHEING_ENABLED );
|
|
RxPurgeFcbInSystemCache( Fcb, NULL, 0, TRUE, TRUE );
|
|
}
|
|
|
|
RxUpdateShareAccessPerSrvOpens(SrvOpen);
|
|
|
|
|
|
// The file object extensions needs to be updated with configuration
|
|
// information for pipes and spool files. In addition the appropriate
|
|
// flags needs to be set based upon the parameters specfied in the
|
|
// request.
|
|
// For spool files the WriteSerializationQueue is the only field of
|
|
// interest.
|
|
|
|
// Mark the DeleteOnClose bit if the operation was successful.
|
|
if ( DeleteOnClose ) {
|
|
SetFlag( Fobx->Flags, FOBX_FLAG_DELETE_ON_CLOSE );
|
|
}
|
|
|
|
if (Fobx != NULL) {
|
|
|
|
// fill in various type-specific fields of the fobx
|
|
|
|
switch (RxContext->Create.pNetRoot->Type) {
|
|
|
|
case NET_ROOT_PIPE:
|
|
capFileObject->Flags |= FO_NAMED_PIPE;
|
|
//lack of break intentional
|
|
|
|
case NET_ROOT_PRINT:
|
|
Fobx->PipeHandleInformation = &Fobx->Specific.NamedPipe.PipeHandleInformation;
|
|
Fobx->Specific.NamedPipe.CollectDataTime.QuadPart = 0;
|
|
Fobx->Specific.NamedPipe.CollectDataSize
|
|
= RxContext->Create.pNetRoot->NamedPipeParameters.DataCollectionSize;
|
|
Fobx->Specific.NamedPipe.TypeOfPipe = RxContext->Create.PipeType;
|
|
Fobx->Specific.NamedPipe.ReadMode = RxContext->Create.PipeReadMode;
|
|
Fobx->Specific.NamedPipe.CompletionMode = RxContext->Create.PipeCompletionMode;
|
|
InitializeListHead(&Fobx->Specific.NamedPipe.ReadSerializationQueue);
|
|
InitializeListHead(&Fobx->Specific.NamedPipe.WriteSerializationQueue);
|
|
break;
|
|
|
|
default:
|
|
|
|
|
|
NOTHING;
|
|
}
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} finally {
|
|
|
|
RxDbgTrace(0, Dbg, ("--->Fobx=%08lx, Ref=%08lx\n", Fobx, (Fobx)?Fobx->NodeReferenceCount:0 ));
|
|
RxDbgTrace(0, Dbg, ("--->SrvOpen=%08lx, Ref=%08lx\n", SrvOpen, (SrvOpen)?SrvOpen->NodeReferenceCount:0 ));
|
|
RxDbgTrace(0, Dbg, ("--->Fcb=%08lx, Ref=%08lx\n", Fcb, (Fcb)?Fcb->NodeReferenceCount:0 ));
|
|
|
|
//get rid of the reference on the fcb; we also finalize here if we can
|
|
|
|
if (Fcb->OpenCount == 0) {
|
|
|
|
// if we have the lock we can finalize.........
|
|
|
|
if (RxContext->Create.FcbAcquired) {
|
|
|
|
//try to finalize right now
|
|
|
|
RxContext->Create.FcbAcquired = !RxDereferenceAndFinalizeNetFcb(Fcb,RxContext,FALSE,FALSE);
|
|
|
|
//the tracker gets very unhappy if you don't do this!
|
|
if (!RxContext->Create.FcbAcquired) {
|
|
RxTrackerUpdateHistory(RxContext,NULL,'rrCr',__LINE__,__FILE__,0);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//cant finalize now.....just remove our reference.......
|
|
|
|
RxDereferenceNetFcb(Fcb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("Exiting RxCreateFromNetRoot status=%08lx\n", Status));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RxPrefixClaim (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the call down from the MUP to claim a name. We pass the name down
|
|
to the routine for finding/creating connections.
|
|
|
|
Arguments:
|
|
|
|
IN PRX_CONTEXT RxContext - Describes the ioctl and Context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
|
|
PQUERY_PATH_REQUEST qpRequest;
|
|
PQUERY_PATH_RESPONSE qpResponse;
|
|
UNICODE_STRING FilePathName;
|
|
UNICODE_STRING CanonicalName;
|
|
UNICODE_STRING RemainingName;
|
|
|
|
NET_ROOT_TYPE NetRootType = NET_ROOT_WILD;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(0, Dbg, ("RxPrefixClaim -> %08lx\n", 0));
|
|
|
|
//
|
|
// Initialize RemainingName.
|
|
//
|
|
RemainingName.Buffer = NULL;
|
|
RemainingName.Length = 0;
|
|
RemainingName.MaximumLength = 0;
|
|
|
|
if (capReqPacket->RequestorMode == UserMode) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
return Status;
|
|
}
|
|
|
|
qpResponse = METHODNEITHER_OriginalOutputBuffer(capReqPacket);
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
|
qpRequest = METHODNEITHER_OriginalInputBuffer(capPARAMS);
|
|
|
|
RxContext->MajorFunction = IRP_MJ_CREATE;
|
|
|
|
RxContext->PrefixClaim.SuppliedPathName.Buffer =
|
|
(PWCHAR)RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
qpRequest->PathNameLength,
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (RxContext->PrefixClaim.SuppliedPathName.Buffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto FINALLY;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
RxContext->PrefixClaim.SuppliedPathName.Buffer,
|
|
qpRequest->FilePathName,
|
|
qpRequest->PathNameLength);
|
|
|
|
RxContext->PrefixClaim.SuppliedPathName.Length =
|
|
(USHORT)qpRequest->PathNameLength;
|
|
|
|
RxContext->PrefixClaim.SuppliedPathName.Length =
|
|
(USHORT)qpRequest->PathNameLength;
|
|
|
|
RtlZeroMemory(
|
|
&RxContext->Create,
|
|
sizeof(RxContext->Create));
|
|
|
|
RxContext->Create.ThisIsATreeConnectOpen = TRUE;
|
|
RxContext->Create.NtCreateParameters.SecurityContext =
|
|
qpRequest->SecurityContext;
|
|
} else {
|
|
ASSERT(RxContext->MajorFunction == IRP_MJ_CREATE);
|
|
ASSERT(RxContext->PrefixClaim.SuppliedPathName.Buffer != NULL);
|
|
}
|
|
|
|
FilePathName = RxContext->PrefixClaim.SuppliedPathName;
|
|
RemainingName = FilePathName;
|
|
|
|
Status = RxFirstCanonicalize(
|
|
RxContext,
|
|
&FilePathName,
|
|
&CanonicalName,
|
|
&NetRootType);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
Status = RxFindOrConstructVirtualNetRoot(
|
|
RxContext,
|
|
&CanonicalName,
|
|
NetRootType,
|
|
&RemainingName);
|
|
|
|
}
|
|
|
|
FINALLY:
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
if (Status == STATUS_SUCCESS) {
|
|
qpResponse->LengthAccepted =
|
|
RxContext->PrefixClaim.SuppliedPathName.Length -
|
|
RemainingName.Length;
|
|
}
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
if (RxContext->PrefixClaim.SuppliedPathName.Buffer != NULL) {
|
|
RxFreePool(RxContext->PrefixClaim.SuppliedPathName.Buffer);
|
|
}
|
|
|
|
RxpPrepareCreateContextForReuse(RxContext);
|
|
|
|
RxContext->MajorFunction = IRP_MJ_DEVICE_CONTROL;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("RxPrefixClaim -> Status %08lx\n", Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCreateTreeConnect (
|
|
RXCOMMON_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine for creating/opening a TC.
|
|
|
|
Arguments:
|
|
|
|
RXCOMMON_SIGNATURE - the normal common arguments
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
UNICODE_STRING CanonicalName,RemainingName;
|
|
PUNICODE_STRING OriginalName = &capFileObject->FileName;
|
|
LOCK_HOLDING_STATE LockHoldingState = LHS_LockNotHeld;
|
|
|
|
NET_ROOT_TYPE NetRootType = NET_ROOT_WILD;
|
|
|
|
ULONG EaInformationLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCreateTreeConnect entry\n"));
|
|
|
|
CanonicalName.Length = CanonicalName.MaximumLength = 0;
|
|
CanonicalName.Buffer = NULL;
|
|
|
|
Status = RxFirstCanonicalize(
|
|
RxContext,
|
|
OriginalName,
|
|
&CanonicalName,
|
|
&NetRootType);
|
|
|
|
if (Status!=STATUS_SUCCESS) {
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
return(Status);
|
|
}
|
|
|
|
RxContext->Create.ThisIsATreeConnectOpen = TRUE;
|
|
RxContext->Create.TreeConnectOpenDeferred = FALSE;
|
|
|
|
RxContext->Create.TransportName.Length = 0;
|
|
RxContext->Create.TransportName.MaximumLength = 0;
|
|
RxContext->Create.TransportName.Buffer = NULL;
|
|
|
|
RxContext->Create.UserName.Length = 0;
|
|
RxContext->Create.UserName.MaximumLength = 0;
|
|
RxContext->Create.UserName.Buffer = NULL;
|
|
|
|
RxContext->Create.Password.Length = 0;
|
|
RxContext->Create.Password.MaximumLength = 0;
|
|
RxContext->Create.Password.Buffer = NULL;
|
|
|
|
RxContext->Create.UserDomainName.Length = 0;
|
|
RxContext->Create.UserDomainName.MaximumLength = 0;
|
|
RxContext->Create.UserDomainName.Buffer = NULL;
|
|
|
|
EaInformationLength = capPARAMS->Parameters.Create.EaLength;
|
|
if (EaInformationLength > 0) {
|
|
BOOLEAN DeferredConnection = FALSE;
|
|
BOOLEAN CredentialsSupplied = FALSE;
|
|
|
|
PFILE_FULL_EA_INFORMATION pEaEntry;
|
|
|
|
pEaEntry = (PFILE_FULL_EA_INFORMATION)capReqPacket->AssociatedIrp.SystemBuffer;
|
|
ASSERT(pEaEntry != NULL);
|
|
|
|
for(;;) {
|
|
PUNICODE_STRING pTargetStringPtr;
|
|
|
|
if (strcmp(pEaEntry->EaName, EA_NAME_CONNECT) == 0) {
|
|
DeferredConnection = TRUE;
|
|
} else if ((strcmp(pEaEntry->EaName, EA_NAME_USERNAME) == 0) ||
|
|
(strcmp(pEaEntry->EaName, EA_NAME_PASSWORD) == 0) ||
|
|
(strcmp(pEaEntry->EaName, EA_NAME_DOMAIN) == 0)) {
|
|
CredentialsSupplied = TRUE;
|
|
}
|
|
|
|
pTargetStringPtr = NULL;
|
|
|
|
RxDbgTrace(0,Dbg,("RxCreateTreeConnect: Processing EA name %s\n",
|
|
pEaEntry->EaName));
|
|
|
|
if (strcmp(pEaEntry->EaName, EA_NAME_TRANSPORT) == 0) {
|
|
pTargetStringPtr = &RxContext->Create.TransportName;
|
|
} else if (strcmp(pEaEntry->EaName, EA_NAME_USERNAME) == 0) {
|
|
pTargetStringPtr = &RxContext->Create.UserName;
|
|
} else if (strcmp(pEaEntry->EaName, EA_NAME_PASSWORD) == 0) {
|
|
pTargetStringPtr = &RxContext->Create.Password;
|
|
} else if (strcmp(pEaEntry->EaName, EA_NAME_DOMAIN) == 0) {
|
|
pTargetStringPtr = &RxContext->Create.UserDomainName;
|
|
} else {
|
|
RxDbgTrace(0,Dbg,("RxCreateTreeConnect: Invalid EA name/value %s\n",
|
|
pEaEntry->EaName));
|
|
}
|
|
|
|
if (pTargetStringPtr != NULL) {
|
|
pTargetStringPtr->Length = pEaEntry->EaValueLength;
|
|
pTargetStringPtr->MaximumLength = pEaEntry->EaValueLength;
|
|
pTargetStringPtr->Buffer = (PWSTR)(pEaEntry->EaName + pEaEntry->EaNameLength + 1);
|
|
}
|
|
|
|
if (pEaEntry->NextEntryOffset == 0) {
|
|
break;
|
|
} else {
|
|
pEaEntry = (PFILE_FULL_EA_INFORMATION)
|
|
((PCHAR) pEaEntry + pEaEntry->NextEntryOffset);
|
|
}
|
|
}
|
|
|
|
if (!CredentialsSupplied && DeferredConnection) {
|
|
RxContext->Create.TreeConnectOpenDeferred = TRUE;
|
|
}
|
|
}
|
|
|
|
Status = RxFindOrConstructVirtualNetRoot( RxContext,
|
|
&CanonicalName,
|
|
NetRootType,
|
|
&RemainingName );
|
|
|
|
if(Status == STATUS_NETWORK_CREDENTIAL_CONFLICT) {
|
|
// Scavenge the VNetRoots
|
|
RxScavengeVNetRoots(RxContext->RxDeviceObject);
|
|
|
|
Status = RxFindOrConstructVirtualNetRoot(
|
|
RxContext,
|
|
&CanonicalName,
|
|
NetRootType,
|
|
&RemainingName);
|
|
}
|
|
|
|
|
|
// We have to check whether the path is valid if it is provided.
|
|
if ((Status == STATUS_SUCCESS) && (RemainingName.Length > 0)) {
|
|
MINIRDR_CALL(Status,
|
|
RxContext,
|
|
RxContext->Create.pNetRoot->pSrvCall->RxDeviceObject->Dispatch,
|
|
MRxIsValidDirectory,
|
|
(RxContext,&RemainingName)
|
|
);
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
BOOLEAN TakeAdditionalReferenceOnVNetRoot = FALSE;
|
|
PV_NET_ROOT pVNetRoot = (PV_NET_ROOT)RxContext->Create.pVNetRoot;
|
|
|
|
RxReferenceVNetRoot(pVNetRoot);
|
|
|
|
TakeAdditionalReferenceOnVNetRoot =
|
|
(InterlockedCompareExchange(
|
|
&pVNetRoot->AdditionalReferenceForDeleteFsctlTaken,
|
|
1,
|
|
0) == 0);
|
|
|
|
if ( !TakeAdditionalReferenceOnVNetRoot ) {
|
|
// The net use connections have a two phase delete protocol. An FSCTL to
|
|
// delete the connection is used followed by a close of the corresponding file
|
|
// object. The additional reference ensures that the finalization is delayed till
|
|
// the actual close of the correspomnding file object.
|
|
|
|
RxDereferenceVNetRoot(pVNetRoot,LHS_LockNotHeld);
|
|
}
|
|
|
|
capFileObject->FsContext = &RxDeviceFCB;
|
|
capFileObject->FsContext2 = RxContext->Create.pVNetRoot;
|
|
|
|
pVNetRoot->IsExplicitConnection = TRUE;
|
|
RxContext->Create.pVNetRoot->NumberOfOpens++;
|
|
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
RxContext->Create.pNetRoot = NULL;
|
|
RxContext->Create.pSrvCall = NULL;
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCreateTreeConnect exit, status=%08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxPrepareToReparseSymbolicLink(
|
|
PRX_CONTEXT RxContext,
|
|
BOOLEAN SymbolicLinkEmbeddedInOldPath,
|
|
PUNICODE_STRING pNewPath,
|
|
BOOLEAN NewPathIsAbsolute,
|
|
BOOLEAN *pReparseRequired)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets up the file object name to facilitate a reparse. This routine
|
|
is used by the mini redirectors to traverse symbolic links.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
SymbolicLinkEmbeddedInOldPath - if TRUE a symbolic link was encountered as part of the
|
|
traversal of the old path.
|
|
|
|
pNewPath - the new path name to be traversed.
|
|
|
|
NewPathIsAbsolute - if FALSE, \Device\Mup should be prepended to pNewPath. If TRUE,
|
|
pNewPath is the full path to which to reparse. In this case, the buffer
|
|
containing pNewPath is used directly, rather than allocating a new one.
|
|
|
|
pReparseRequired - set to TRUE if Reparse is required.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - the return status for the operation
|
|
|
|
Notes:
|
|
|
|
The second parameter passed to this routine is a very important one. In order
|
|
to preserve the correct semantics it should be carefully used. As an example
|
|
consider the old path \A\B\C\D wherein C happens to be symbolic link. In such
|
|
cases the symbolic link is embedded in the path as opposed to the case when
|
|
D happens to be a symbolic link. In the former case the reparse constitutes
|
|
an intermediate step as opposed to the later case when it constitutes the
|
|
final step of the name resolution.
|
|
|
|
If DELETE access is specified the OPEN is denied for all in which the symbolic
|
|
link is not embedded. It is possible that if DELETE access were the only one
|
|
specified then the OPEN attempt must succeed without reparse. This will be
|
|
conformant with UNIX symbolic link semantics.
|
|
|
|
As part of this routine the RxContext is also tagged appropriately. This ensures
|
|
that the return value can be crosschecked with the invocation of this routine.
|
|
Once this routine is invoked the mini rdr has to return STATUS_REPARSE.
|
|
|
|
The value of *pReparseRequired assumes significance only if STATUS_SUCCESS is
|
|
returned from this routine. FALSE implies that no reparse attempt is required
|
|
and the symbolic link file itself should be manipulated as opposed to the
|
|
target of the link. TRUE implies that a reparse attempt was successfully setup.
|
|
In such cases it is imperative that the mini redirector return STATUS_REPARSE
|
|
for the associated MRxCreate call. The wrapper will initiate a check for this.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
*pReparseRequired = FALSE;
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
ACCESS_MASK DesiredAccess = RxContext->Create.NtCreateParameters.DesiredAccess;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
// Check the Create Parameters to determine the type of ACCESS specified.
|
|
// if only DELETE access was specified then no reparse is required and the
|
|
// operation is to be performed on the link itself.
|
|
if (!SymbolicLinkEmbeddedInOldPath) {
|
|
RxDbgTrace(0, Dbg, ("Desired Access In Reparse %lx\n",DesiredAccess));
|
|
if (DesiredAccess & DELETE) {
|
|
*pReparseRequired = FALSE;
|
|
if (DesiredAccess & ~DELETE) {
|
|
Status = STATUS_ACCESS_DENIED;
|
|
}
|
|
} else {
|
|
// If the appropriate flags were specified in the CREATE parameters then
|
|
// the reparse is to be suppressed since the intention is to open the link
|
|
// itself as opposed to the TARGET.
|
|
// TBD. -- The exact combination of flags will be determined for NT 5.0.
|
|
|
|
// If none of the above conditions were satisfied then the reparse is required
|
|
*pReparseRequired = TRUE;
|
|
}
|
|
} else {
|
|
*pReparseRequired = TRUE;
|
|
}
|
|
|
|
if (*pReparseRequired) {
|
|
PWSTR pFileNameBuffer;
|
|
USHORT DeviceNameLength,ReparsePathLength;
|
|
|
|
if (!NewPathIsAbsolute) {
|
|
|
|
DeviceNameLength = wcslen(DD_MUP_DEVICE_NAME) * sizeof(WCHAR);
|
|
|
|
// On a reparse attempt the I/O subsystem will null out the related file
|
|
// object field.
|
|
|
|
ReparsePathLength = (DeviceNameLength + pNewPath->Length);
|
|
|
|
pFileNameBuffer = ExAllocatePoolWithTag(
|
|
PagedPool | POOL_COLD_ALLOCATION ,
|
|
ReparsePathLength,
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (pFileNameBuffer != NULL) {
|
|
// Copy the device name
|
|
RtlCopyMemory(
|
|
pFileNameBuffer,
|
|
DD_MUP_DEVICE_NAME,
|
|
DeviceNameLength);
|
|
|
|
// Copy the new name
|
|
RtlCopyMemory(
|
|
((PBYTE)pFileNameBuffer + DeviceNameLength),
|
|
pNewPath->Buffer,
|
|
pNewPath->Length);
|
|
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
} else {
|
|
pFileNameBuffer = pNewPath->Buffer;
|
|
ReparsePathLength = pNewPath->Length;
|
|
}
|
|
|
|
// Free up the buffer associated with the old name.
|
|
ExFreePool(capFileObject->FileName.Buffer);
|
|
|
|
// Set up the file object with the new name.
|
|
capFileObject->FileName.Buffer = pFileNameBuffer;
|
|
capFileObject->FileName.Length = ReparsePathLength;
|
|
capFileObject->FileName.MaximumLength = capFileObject->FileName.Length;
|
|
|
|
// Mark the RxContext so that the return code can be verified. A mini
|
|
// redirector has to return STATUS_REPARSE is this routine was invoked
|
|
// as a response to MRxCreate. This will be enforced by marking the
|
|
// RxContext appropriately and comparing the returned status code against
|
|
// the expected value.
|
|
SetFlag(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_REPARSE);
|
|
}
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg, ("RxPrepareToReparseSymbolicLink : ReparseReqd: %lx, Status %lx\n",*pReparseRequired,Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCommonCreate ( RXCOMMON_SIGNATURE )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common routine for creating/opening a file called by
|
|
both the fsd and fsp threads.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - the return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
UNICODE_STRING RemainingName;
|
|
|
|
PAGED_CODE();
|
|
|
|
#if 0 && defined(REMOTE_BOOT)
|
|
{
|
|
PWCH buffer = ExAllocatePoolWithTag( NonPagedPool, capFileObject->FileName.Length + 2,RX_MISC_POOLTAG );
|
|
//PWCH b2;
|
|
BOOLEAN watchFile = FALSE;
|
|
BOOLEAN logFile = FALSE;
|
|
if ( buffer != NULL ) {
|
|
RtlCopyMemory( buffer, capFileObject->FileName.Buffer, capFileObject->FileName.Length );
|
|
buffer[capFileObject->FileName.Length/sizeof(WCHAR)] = 0;
|
|
//b2 = wcsstr(buffer,L"\\IMirror\\");
|
|
//if ( b2 != NULL ) {
|
|
// b2 += wcslen(L"\\IMirror\\");
|
|
// if ( (*b2 != 0) &&
|
|
// (wcsstr(b2,L"Clients") == NULL) &&
|
|
// (wcsstr(b2,L"CLIENTS") == NULL) &&
|
|
// (wcsstr(b2,L"Setup") == NULL) &&
|
|
// (wcsstr(b2,L"SETUP") == NULL) ) {
|
|
// logFile = TRUE;
|
|
// }
|
|
//}
|
|
//if ( (wcsstr(buffer,L"%systemroot%") != NULL) || (wcsstr(buffer,L"%SystemRoot%") != NULL) ) {
|
|
// watchFile = TRUE;
|
|
//}
|
|
//if ( (wcsstr(buffer,L"msvcrt20") != NULL) || (wcsstr(buffer,L"MSVCRT20") != NULL) ) {
|
|
// if (capPARAMS->Parameters.Create.SecurityContext->DesiredAccess & FILE_WRITE_ATTRIBUTES) {
|
|
// watchFile = TRUE;
|
|
// }
|
|
//}
|
|
if ( WatchAllFiles ) watchFile = TRUE;
|
|
if ( watchFile && (!FirstWatchOnly || IsFirstWatch) ) {
|
|
logFile = TRUE;
|
|
IsFirstWatch = FALSE;
|
|
}
|
|
if ( LogAllFiles ) logFile = TRUE;
|
|
if ( logFile ) {
|
|
DbgPrint( "RxCommonCreate: create IRP for %ws %x\n", buffer, capFileObject );
|
|
}
|
|
if ( watchFile || (0 && logFile) ) {
|
|
//DbgBreakPoint();
|
|
}
|
|
ExFreePool(buffer);
|
|
}
|
|
}
|
|
#endif // defined(REMOTE_BOOT)
|
|
|
|
// check for the case of a device open; if so, handle and get out now
|
|
if ( (capFileObject->FileName.Length == 0) &&
|
|
(capFileObject->RelatedFileObject == NULL) ) {
|
|
|
|
//here we are just opening the device; set the FsContext&counts and get out
|
|
|
|
capFileObject->FsContext = &RxDeviceFCB;
|
|
capFileObject->FsContext2 = NULL;
|
|
RxDeviceFCB.OpenCount++;
|
|
RxDeviceFCB.UncleanCount++;
|
|
|
|
capReqPacket->IoStatus.Information = FILE_OPENED;
|
|
|
|
RxDbgTrace(0, Dbg, ("RxDeviceCreate, File = %08lx\n", capFileObject));
|
|
|
|
|
|
RxLog(("DevOpen %lx %lx %lx",RxContext,capFileObject,RxContext->RxDeviceObject));
|
|
RxLog(("DevOpen2 %wZ",&RxContext->RxDeviceObject->DeviceName));
|
|
RxWmiLog(LOG,
|
|
RxCommonCreate_1,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFileObject)
|
|
LOGPTR(RxContext->RxDeviceObject)
|
|
LOGUSTR(RxContext->RxDeviceObject->DeviceName));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if ((capPARAMS->Parameters.Create.Options & FILE_STRUCTURED_STORAGE) ==
|
|
FILE_STRUCTURED_STORAGE) {
|
|
|
|
// FILE_STRUCTURED_STORAGE is used by NSS for structured storage
|
|
// opens. Blocking this disables NSS access across the network.
|
|
//
|
|
// Note that FILE_COPY_STRUCTURED_STORAGE should not be blocked, as
|
|
// it is needed by CopyFile
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
//
|
|
// Init the file name that will trigger the trace to start.
|
|
// To trigger on a different file edit DbgTriggerName with debugger (don't
|
|
// forget the null). Set DbgTriggerIrpCount to number of IRPs to trace and
|
|
// then set DbgTriggerState to 0.
|
|
//
|
|
|
|
RxDbgTraceDoit(
|
|
if (DbgTriggerState == DBG_TRIGGER_INIT) {
|
|
DbgTriggerState = DBG_TRIGGER_LOOKING;
|
|
DbgTriggerNameStr.Length = (USHORT)strlen(DbgTriggerName);
|
|
DbgTriggerNameStr.MaximumLength = (USHORT)strlen(DbgTriggerName);
|
|
DbgTriggerNameStr.Buffer = &DbgTriggerName[0];
|
|
|
|
RtlAnsiStringToUnicodeString(&DbgTriggerUStr, &DbgTriggerNameStr, TRUE);
|
|
}
|
|
);
|
|
|
|
//
|
|
// If we find a match on the open file name the enable tracing for the
|
|
// next DbgTriggerIrpCount's worth of IRPs.
|
|
//
|
|
RxDbgTraceDoit(
|
|
if ((DbgTriggerState == DBG_TRIGGER_LOOKING) &&
|
|
RtlEqualUnicodeString(&DbgTriggerUStr, &capFileObject->FileName, TRUE)) {
|
|
DbgTriggerState = DBG_TRIGGER_FOUND;
|
|
RxGlobalTraceIrpCount = DbgTriggerIrpCount;
|
|
RxGlobalTraceSuppress = FALSE;
|
|
}
|
|
);
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCommonCreate\n", 0 ));
|
|
RxDbgTrace( 0, Dbg, ("Irp = %08lx\n", capReqPacket ));
|
|
RxDbgTrace( 0, Dbg, ("->IrpFlags = %08lx\n", capReqPacket->Flags ));
|
|
RxDbgTrace( 0, Dbg, ("->FileObject(Related) = %08lx %08lx\n", //ok4->FileObj
|
|
capFileObject,
|
|
capFileObject->RelatedFileObject ));
|
|
RxDbgTrace( 0, Dbg, (" ->FileName = (%lx) %wZ\n",
|
|
capFileObject->FileName.Length,
|
|
&capFileObject->FileName ));
|
|
RxDbgTrace( 0, Dbg, ("->AllocationSize(Lo/Hi) = %08lx %08lx\n",
|
|
capReqPacket->Overlay.AllocationSize.LowPart,
|
|
capReqPacket->Overlay.AllocationSize.HighPart ));
|
|
RxDbgTrace( 0, Dbg, ("->DesiredAccess/Options = %08lx %08lx\n",
|
|
capPARAMS->Parameters.Create.SecurityContext->DesiredAccess,
|
|
capPARAMS->Parameters.Create.Options ));
|
|
RxDbgTrace( 0, Dbg, ("->Attribs/ShrAccess/SPFlags= %04x %04lx %08lx\n",
|
|
capPARAMS->Parameters.Create.FileAttributes,
|
|
capPARAMS->Parameters.Create.ShareAccess,
|
|
capPARAMS->Flags ));
|
|
|
|
RxLog(("Open %lx %lx %lx %lx %lx %lx %lx\n",
|
|
RxContext,capFileObject, //1,2
|
|
capPARAMS->Parameters.Create.Options, //3
|
|
capPARAMS->Flags, //4
|
|
capPARAMS->Parameters.Create.FileAttributes, //5
|
|
capPARAMS->Parameters.Create.ShareAccess, //6
|
|
capPARAMS->Parameters.Create.SecurityContext->DesiredAccess //7
|
|
));
|
|
RxWmiLog(LOG,
|
|
RxCommonCreate_2,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFileObject)
|
|
LOGULONG(capPARAMS->Parameters.Create.Options)
|
|
LOGUCHAR(capPARAMS->Flags)
|
|
LOGXSHORT(capPARAMS->Parameters.Create.FileAttributes)
|
|
LOGXSHORT(capPARAMS->Parameters.Create.ShareAccess)
|
|
LOGULONG(capPARAMS->Parameters.Create.SecurityContext->DesiredAccess));
|
|
|
|
RxLog((" fn %wZ\n", &capFileObject->FileName));
|
|
RxWmiLog(LOG,
|
|
RxCommonCreate_3,
|
|
LOGUSTR(capFileObject->FileName));
|
|
|
|
if (capFileObject->RelatedFileObject){
|
|
PFCB RelatedFcb = (PFCB)(capFileObject->RelatedFileObject->FsContext);
|
|
RxDbgTrace( 0, Dbg, (" ->RelatedFileName = %wZ\n",
|
|
&(RelatedFcb->FcbTableEntry.Path) ));
|
|
RxLog((
|
|
" relat %lx %wZ\n",
|
|
capFileObject->RelatedFileObject,
|
|
&(RelatedFcb->FcbTableEntry.Path)));
|
|
RxWmiLog(LOG,
|
|
RxCommonCreate_4,
|
|
LOGPTR(capFileObject->RelatedFileObject)
|
|
LOGUSTR(RelatedFcb->FcbTableEntry.Path));
|
|
}
|
|
|
|
if (capPARAMS->Flags&SL_OPEN_TARGET_DIRECTORY) {
|
|
RxDbgTrace( 0, Dbg, (" ->OpenTargetDirectory\n"));
|
|
RxLog((" OpenTargetDir!\n"));
|
|
RxWmiLog(LOG,
|
|
RxCommonCreate_5,
|
|
LOGULONG(Status));
|
|
}
|
|
|
|
|
|
RxCopyCreateParameters(RxContext);
|
|
|
|
if (capPARAMS->Parameters.Create.Options & FILE_CREATE_TREE_CONNECTION) {
|
|
Status = RxCreateTreeConnect( RxContext );
|
|
} else {
|
|
|
|
//
|
|
//
|
|
//
|
|
// It's here because Mark says he can't avoid sending me double beginning
|
|
// backslashes win the Win32 layer.
|
|
//
|
|
|
|
if ((capFileObject->FileName.Length > sizeof(WCHAR)) &&
|
|
(capFileObject->FileName.Buffer[1] == L'\\') &&
|
|
(capFileObject->FileName.Buffer[0] == L'\\')) {
|
|
|
|
capFileObject->FileName.Length -= sizeof(WCHAR);
|
|
|
|
RtlMoveMemory(
|
|
&capFileObject->FileName.Buffer[0],
|
|
&capFileObject->FileName.Buffer[1],
|
|
capFileObject->FileName.Length );
|
|
|
|
//
|
|
// If there are still two beginning backslashes, the name is bogus.
|
|
//
|
|
|
|
if ((capFileObject->FileName.Length > sizeof(WCHAR)) &&
|
|
(capFileObject->FileName.Buffer[1] == L'\\') &&
|
|
(capFileObject->FileName.Buffer[0] == L'\\')) {
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxCommonCreate -> OBJECT_NAME_INVALID[slashes]\n)", 0));
|
|
|
|
// RxCompleteContextAndReturn( RxStatus(OBJECT_NAME_INVALID) );
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
}
|
|
|
|
|
|
do {
|
|
//
|
|
// If the file name has a trailing \, and the request is to
|
|
// operate on a file (not a directory), then the file name is
|
|
// invalid.
|
|
//
|
|
|
|
|
|
if ((capFileObject->FileName.Length>0)
|
|
&&(capFileObject->FileName.Buffer[(capFileObject->FileName.Length/sizeof(WCHAR))-1] == L'\\')) {
|
|
ULONG co = capPARAMS->Parameters.Create.Options;
|
|
if (MustBeFile (co)) {
|
|
RxDbgTrace(-1, Dbg, ("RxCommonCreate -> OBJECT_NAME_INVALID[trailing+MBFile]\n)", 0));
|
|
return(Status = STATUS_OBJECT_NAME_INVALID);
|
|
}
|
|
capFileObject->FileName.Length -= sizeof(WCHAR);
|
|
SetFlag(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_STRIPPED_TRAILING_BACKSLASH);
|
|
}
|
|
|
|
|
|
//
|
|
// If we have Write Through set in the FileObject, then set the FileObject
|
|
// flag as well, so that the fast write path call to FsRtlCopyWrite
|
|
// knows it is Write Through.
|
|
//
|
|
|
|
if (FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH)) {
|
|
capFileObject->Flags |= FO_WRITE_THROUGH;
|
|
}
|
|
|
|
// Convert the name to its canonical form, i.e. without . and .. and with the luid
|
|
// on the front as appropriate. try to avoid a pool operation by using an stack-based buffer
|
|
|
|
|
|
Status = RxCanonicalizeNameAndObtainNetRoot(
|
|
RxContext,
|
|
&capFileObject->FileName,
|
|
&RemainingName);
|
|
|
|
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
|
|
RxDbgTrace(0, Dbg, ("RxCommonCreate -> Couldn't canonicalize %08lx\n", Status));
|
|
} else {
|
|
RxDbgTrace(0, Dbg, ("RxCommonCreate NetRootGoodWasGood status =%08lx\n", Status));
|
|
|
|
Status = RxCreateFromNetRoot(RxContext,&RemainingName);
|
|
|
|
RxDbgTrace(0, Dbg, ("RxCommonCreate RxCreateFromNetRoot status =%08lx\n", Status));
|
|
|
|
switch (Status) {
|
|
case STATUS_SHARING_VIOLATION:
|
|
{
|
|
ULONG Disposition = RxContext->Create.NtCreateParameters.Disposition;
|
|
ASSERT(!FlagOn(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_REPARSE));
|
|
|
|
if (Disposition != FILE_CREATE) {
|
|
if ( RxContext->Create.TryForScavengingOnSharingViolation &&
|
|
!RxContext->Create.ScavengingAlreadyTried &&
|
|
RxContext->Create.pVNetRoot != NULL) {
|
|
PV_NET_ROOT pVNetRoot;
|
|
|
|
NTSTATUS PurgeStatus;
|
|
NT_CREATE_PARAMETERS NtCreateParameters;
|
|
|
|
NtCreateParameters = RxContext->Create.NtCreateParameters;
|
|
|
|
// Reference the VNetRoot instance. Reinitialize
|
|
// the context ( this will drop the FCB if it has
|
|
// been accquired. purge the FOBX's related to the
|
|
// NET_ROOT instance and resume the create
|
|
// operation if it was sucssesful
|
|
pVNetRoot = (PV_NET_ROOT)(RxContext->Create.pVNetRoot);
|
|
RxReferenceVNetRoot(pVNetRoot);
|
|
|
|
// reinitialize the context
|
|
RxpPrepareCreateContextForReuse(RxContext);
|
|
RxReinitializeContext(RxContext);
|
|
|
|
// Reinitialize the Create parameters.
|
|
RxContext->Create.NtCreateParameters = NtCreateParameters;
|
|
RxCopyCreateParameters(RxContext);
|
|
|
|
PurgeStatus = RxPurgeRelatedFobxs(
|
|
pVNetRoot->NetRoot,
|
|
RxContext,
|
|
DONT_ATTEMPT_FINALIZE_ON_PURGE,
|
|
NULL);
|
|
|
|
// Map the SUCCESS code for continuation
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
RxContext->Create.ScavengingAlreadyTried = TRUE;
|
|
|
|
// Ensure that any buffering state change pending
|
|
// requests have been processed. This will cover the
|
|
// cases when owing to processing delays the oplock
|
|
// response did not make it to the server and it
|
|
// returned SHARING_VIOLATION.
|
|
{
|
|
PSRV_CALL pSrvCall;
|
|
|
|
pSrvCall = (PSRV_CALL)pVNetRoot->pNetRoot->pSrvCall;
|
|
|
|
RxReferenceSrvCall(pSrvCall);
|
|
|
|
RxpProcessChangeBufferingStateRequests(
|
|
pSrvCall,
|
|
FALSE); // do not update handler state
|
|
}
|
|
|
|
|
|
// Drop the reference on the V_NET_ROOT instance
|
|
RxDereferenceVNetRoot(pVNetRoot,LHS_LockNotHeld);
|
|
}
|
|
} else {
|
|
Status = STATUS_OBJECT_NAME_COLLISION;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATUS_REPARSE:
|
|
{
|
|
// Ensure that the IRP is approrpiately marked for reparsing.
|
|
RxContext->CurrentIrp->IoStatus.Information = IO_REPARSE;
|
|
RxDbgTrace(0, Dbg, ("RxCommonCreate(Reparse) IRP %lx New Name %wZ status =%08lx\n",
|
|
capReqPacket,&capFileObject->FileName, Status));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(!FlagOn(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_REPARSE));
|
|
break;
|
|
}
|
|
}
|
|
} while (Status == STATUS_MORE_PROCESSING_REQUIRED);
|
|
}
|
|
|
|
if (Status == STATUS_RETRY) {
|
|
RxpPrepareCreateContextForReuse(RxContext);
|
|
}
|
|
|
|
ASSERT(Status != STATUS_PENDING);
|
|
|
|
RxDbgTraceUnIndent(-1,Dbg);
|
|
|
|
#if 0 && defined(REMOTE_BOOT)
|
|
if ( LogAllFiles ) {
|
|
DbgPrint( "RxCommonCreate: status %x creating %wZ %x %x\n", Status, &capFileObject->FileName, capFileObject, capFileObject->FsContext );
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//these next routines are essentially just copies of the same routines from io\iosubs.c
|
|
//i cannot just use them directly because they make all sorts of assumptions about wanting to
|
|
//update the file object
|
|
|
|
#define RxSetAccessVariables(xxx) {\
|
|
ReadAccess = (BOOLEAN) ((DesiredAccess & (FILE_EXECUTE | FILE_READ_DATA)) != 0); \
|
|
WriteAccess = (BOOLEAN) ((DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0); \
|
|
DeleteAccess = (BOOLEAN) ((DesiredAccess & DELETE) != 0); \
|
|
}
|
|
#define RxSetShareVariables(xxx) {\
|
|
SharedRead = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_READ) != 0); \
|
|
SharedWrite = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_WRITE) != 0); \
|
|
SharedDelete = (BOOLEAN) ((DesiredShareAccess & FILE_SHARE_DELETE) != 0); \
|
|
}
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
RxDumpWantedAccess(
|
|
PSZ where1,
|
|
PSZ where2,
|
|
PSZ wherelogtag,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG DesiredShareAccess
|
|
)
|
|
{
|
|
BOOLEAN ReadAccess,WriteAccess,DeleteAccess;
|
|
BOOLEAN SharedRead,SharedWrite,SharedDelete;
|
|
RxSetAccessVariables(SrvOpen);
|
|
RxSetShareVariables(SrvOpen);
|
|
|
|
PAGED_CODE();
|
|
|
|
//(VOID)(DbgPrint
|
|
RxDbgTrace(0, (DEBUG_TRACE_SHAREACCESS),
|
|
("%s%s wanted = %s%s%s:%s%s%s\n", where1,where2,
|
|
ReadAccess?"R":"",
|
|
WriteAccess?"W":"",
|
|
DeleteAccess?"D":"",
|
|
SharedRead?"SR":"",
|
|
SharedWrite?"SW":"",
|
|
SharedDelete?"SD":""
|
|
));
|
|
RxLogForSharingCheck(
|
|
("%s%s wanted = %s%s%s:%s%s%s\n", wherelogtag, where2,
|
|
ReadAccess?"R":"",
|
|
WriteAccess?"W":"",
|
|
DeleteAccess?"D":"",
|
|
SharedRead?"SR":"",
|
|
SharedWrite?"SW":"",
|
|
SharedDelete?"SD":""
|
|
));
|
|
}
|
|
|
|
VOID
|
|
RxDumpCurrentAccess(
|
|
PSZ where1,
|
|
PSZ where2,
|
|
PSZ wherelogtag,
|
|
PSHARE_ACCESS ShareAccess
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
// (VOID)(DbgPrint
|
|
RxDbgTrace(0, (DEBUG_TRACE_SHAREACCESS),
|
|
("%s%s current = %d[%d][%d][%d]:[%d][%d][%d]\n", where1, where2,
|
|
ShareAccess->OpenCount,
|
|
ShareAccess->Readers,
|
|
ShareAccess->Writers,
|
|
ShareAccess->Deleters,
|
|
ShareAccess->SharedRead,
|
|
ShareAccess->SharedWrite,
|
|
ShareAccess->SharedDelete
|
|
));
|
|
RxLogForSharingCheck(
|
|
("%s%s current = %d[%d][%d][%d]:[%d][%d][%d]\n", wherelogtag, where2,
|
|
ShareAccess->OpenCount,
|
|
ShareAccess->Readers,
|
|
ShareAccess->Writers,
|
|
ShareAccess->Deleters,
|
|
ShareAccess->SharedRead,
|
|
ShareAccess->SharedWrite,
|
|
ShareAccess->SharedDelete
|
|
));
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCheckShareAccess(
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG DesiredShareAccess,
|
|
IN OUT PFILE_OBJECT FileObject,
|
|
IN OUT PSHARE_ACCESS ShareAccess,
|
|
IN BOOLEAN Update,
|
|
IN PSZ where,
|
|
IN PSZ wherelogtag
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
RxDumpWantedAccess(where,"",wherelogtag,
|
|
DesiredAccess,DesiredShareAccess
|
|
);
|
|
RxDumpCurrentAccess(where,"",wherelogtag,ShareAccess);
|
|
Status = IoCheckShareAccess(DesiredAccess,
|
|
DesiredShareAccess,
|
|
FileObject,
|
|
ShareAccess,
|
|
Update);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
VOID
|
|
RxRemoveShareAccess(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT PSHARE_ACCESS ShareAccess,
|
|
IN PSZ where,
|
|
IN PSZ wherelogtag
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
RxDumpCurrentAccess(where,"before",wherelogtag,ShareAccess);
|
|
IoRemoveShareAccess(FileObject, ShareAccess);
|
|
RxDumpCurrentAccess(where,"after",wherelogtag,ShareAccess);
|
|
}
|
|
|
|
VOID
|
|
RxSetShareAccess(
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG DesiredShareAccess,
|
|
IN OUT PFILE_OBJECT FileObject,
|
|
OUT PSHARE_ACCESS ShareAccess,
|
|
IN PSZ where,
|
|
IN PSZ wherelogtag
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
RxDumpCurrentAccess(where,"before",wherelogtag,ShareAccess);
|
|
IoSetShareAccess(DesiredAccess, DesiredShareAccess, FileObject, ShareAccess);
|
|
RxDumpCurrentAccess(where,"after",wherelogtag,ShareAccess);
|
|
}
|
|
|
|
VOID
|
|
RxUpdateShareAccess(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN OUT PSHARE_ACCESS ShareAccess,
|
|
IN PSZ where,
|
|
IN PSZ wherelogtag
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
RxDumpCurrentAccess(where,"before",wherelogtag,ShareAccess);
|
|
IoUpdateShareAccess(FileObject,ShareAccess);
|
|
RxDumpCurrentAccess(where,"after",wherelogtag,ShareAccess);
|
|
}
|
|
#endif
|
|
NTSTATUS
|
|
RxCheckShareAccessPerSrvOpens(
|
|
IN PFCB Fcb,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG DesiredShareAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to determine whether or not a new accessor to
|
|
a file actually has shared access to it. The check is made according
|
|
to:
|
|
|
|
1) How the file is currently opened.
|
|
|
|
2) What types of shared accesses are currently specified.
|
|
|
|
3) The desired and shared accesses that the new open is requesting.
|
|
|
|
This check is made against the sharing state represented by the actual SrvOpens
|
|
on an Fcb so that we know whether we have to initiate close-behind.
|
|
|
|
|
|
Arguments:
|
|
|
|
DesiredAccess - Desired access of current open request.
|
|
|
|
DesiredShareAccess - Shared access requested by current open request.
|
|
|
|
Fcb - Pointer to the file object of the current open request.
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status of the access check is the function value. If the
|
|
accessor has access to the file, STATUS_SUCCESS is returned. Otherwise,
|
|
STATUS_SHARING_VIOLATION is returned.
|
|
|
|
Note:
|
|
|
|
Note that the ShareAccess parameter must be locked against other accesses
|
|
from other threads while this routine is executing. Otherwise the counts
|
|
will be out-of-synch.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG ocount;
|
|
PSHARE_ACCESS ShareAccess = &Fcb->ShareAccessPerSrvOpens;
|
|
BOOLEAN ReadAccess,WriteAccess,DeleteAccess;
|
|
BOOLEAN SharedRead,SharedWrite,SharedDelete;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Set the access type in the file object for the current accessor.
|
|
// Note that reading and writing attributes are not included in the
|
|
// access check.
|
|
//
|
|
|
|
RxSetAccessVariables(SrvOpen);
|
|
|
|
//
|
|
// There is no more work to do unless the user specified one of the
|
|
// sharing modes above.
|
|
//
|
|
|
|
if (ReadAccess || WriteAccess || DeleteAccess) {
|
|
|
|
RxSetShareVariables(SrvOpen);
|
|
RxDumpWantedAccess("RxCheckShareAccessPerSrvOpens","","AccChkPerSO",
|
|
DesiredAccess,DesiredShareAccess
|
|
);
|
|
RxDumpCurrentAccess("RxCheckShareAccessPerSrvOpens","","AccChkPerSO",ShareAccess);
|
|
|
|
//
|
|
// Now check to see whether or not the desired accesses are compatible
|
|
// with the way that the file is currently open.
|
|
//
|
|
|
|
ocount = ShareAccess->OpenCount;
|
|
|
|
if ( (ReadAccess && (ShareAccess->SharedRead < ocount))
|
|
||
|
|
(WriteAccess && (ShareAccess->SharedWrite < ocount))
|
|
||
|
|
(DeleteAccess && (ShareAccess->SharedDelete < ocount))
|
|
||
|
|
((ShareAccess->Readers != 0) && !SharedRead)
|
|
||
|
|
((ShareAccess->Writers != 0) && !SharedWrite)
|
|
||
|
|
((ShareAccess->Deleters != 0) && !SharedDelete)
|
|
) {
|
|
|
|
//
|
|
// The check failed. Simply return to the caller indicating that the
|
|
// current open cannot access the file.
|
|
//
|
|
|
|
//DbgPrint("***** FCB Share Access Check (%lx) Status(%lx)\n",Fcb,STATUS_SHARING_VIOLATION);
|
|
return STATUS_SHARING_VIOLATION;
|
|
|
|
}
|
|
}
|
|
|
|
//DbgPrint("***** FCB Share Access Check (%lx) Status(%lx)\n",Fcb,STATUS_SUCCESS);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
RxUpdateShareAccessPerSrvOpens(
|
|
IN PSRV_OPEN SrvOpen
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to update the access information about how the
|
|
file is currently opened by introducing the contribution of this srvopen. the wrapper
|
|
actually keeps up with two states: (a) the access state according to the files that the user
|
|
can see, and (b) the access state according to the srvopens on the file. this rouinte manipulates
|
|
the latter.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Note:
|
|
|
|
Note that the ShareAccess parameter must be locked against other accesses
|
|
from other threads while this routine is executing. Otherwise the counts
|
|
will be out-of-synch.
|
|
|
|
--*/
|
|
|
|
{
|
|
//ULONG ocount;
|
|
PSHARE_ACCESS ShareAccess = &SrvOpen->Fcb->ShareAccessPerSrvOpens;
|
|
BOOLEAN ReadAccess,WriteAccess,DeleteAccess;
|
|
BOOLEAN SharedRead,SharedWrite,SharedDelete;
|
|
ACCESS_MASK DesiredAccess = SrvOpen->DesiredAccess;
|
|
ULONG DesiredShareAccess = SrvOpen->ShareAccess;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (!FlagOn(SrvOpen->Flags,SRVOPEN_FLAG_SHAREACCESS_UPDATED)) {
|
|
//
|
|
// Set the access type in the file object for the current accessor.
|
|
// Note that reading and writing attributes are not included in the
|
|
// access check.
|
|
//
|
|
|
|
RxSetAccessVariables(SrvOpen);
|
|
|
|
//
|
|
// There is no more work to do unless the user specified one of the
|
|
// sharing modes above.
|
|
//
|
|
|
|
if (ReadAccess || WriteAccess || DeleteAccess) {
|
|
|
|
RxSetShareVariables(SrvOpen);
|
|
RxDumpWantedAccess("RxUpdateShareAccessPerSrvOpens","","AccUpdPerSO",
|
|
DesiredAccess,DesiredShareAccess
|
|
);
|
|
RxDumpCurrentAccess("RxUpdateShareAccessPerSrvOpens","","AccUpdPerSO",ShareAccess);
|
|
|
|
ShareAccess->OpenCount++;
|
|
|
|
ShareAccess->Readers += ReadAccess;
|
|
ShareAccess->Writers += WriteAccess;
|
|
ShareAccess->Deleters += DeleteAccess;
|
|
|
|
ShareAccess->SharedRead += SharedRead;
|
|
ShareAccess->SharedWrite += SharedWrite;
|
|
ShareAccess->SharedDelete += SharedDelete;
|
|
}
|
|
|
|
SetFlag(SrvOpen->Flags,SRVOPEN_FLAG_SHAREACCESS_UPDATED);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxRemoveShareAccessPerSrvOpens(
|
|
IN OUT PSRV_OPEN SrvOpen
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to remove the access and share access information
|
|
in a file system Share Access structure for a given open instance.
|
|
|
|
Arguments:
|
|
|
|
ShareAccess - Pointer to the share access structure that describes
|
|
how the file is currently being accessed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSHARE_ACCESS ShareAccess = &SrvOpen->Fcb->ShareAccessPerSrvOpens;
|
|
BOOLEAN ReadAccess,WriteAccess,DeleteAccess;
|
|
BOOLEAN SharedRead,SharedWrite,SharedDelete;
|
|
ACCESS_MASK DesiredAccess = SrvOpen->DesiredAccess;
|
|
ULONG DesiredShareAccess = SrvOpen->ShareAccess;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If this accessor wanted some type of access other than READ_ or
|
|
// WRITE_ATTRIBUTES, then account for the fact that he has closed the
|
|
// file. Otherwise, he hasn't been accounted for in the first place
|
|
// so don't do anything.
|
|
//
|
|
|
|
RxSetAccessVariables(SrvOpen);
|
|
|
|
if (ReadAccess || WriteAccess || DeleteAccess) {
|
|
|
|
RxSetShareVariables(SrvOpen);
|
|
RxDumpWantedAccess("RxRemoveShareAccessPerSrvOpens","","AccRemPerSO",
|
|
DesiredAccess,DesiredShareAccess
|
|
);
|
|
RxDumpCurrentAccess("RxRemoveShareAccessPerSrvOpens","","AccRemPerSO",ShareAccess);
|
|
|
|
//
|
|
// Decrement the number of opens in the Share Access structure.
|
|
//
|
|
|
|
ShareAccess->OpenCount--;
|
|
|
|
ShareAccess->Readers -= ReadAccess;
|
|
ShareAccess->Writers -= WriteAccess;
|
|
ShareAccess->Deleters -= DeleteAccess;
|
|
|
|
ShareAccess->SharedRead -= SharedRead;
|
|
ShareAccess->SharedWrite -= SharedWrite;
|
|
ShareAccess->SharedDelete -= SharedDelete;
|
|
}
|
|
}
|
|
|
|
ULONG
|
|
RxGetSessionId(
|
|
IN PIO_STACK_LOCATION IrpSp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets the effective SessionId to be used for this create.
|
|
|
|
Arguments:
|
|
|
|
SubjectSecurityContext - Supplies the information from IrpSp.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG SessionId;
|
|
PQUERY_PATH_REQUEST QpReq;
|
|
PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxGetSessionId ... \n", 0));
|
|
|
|
// If QUERY_PATH_REQUEST, must access from Type3InputBuffer
|
|
if( (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
|
|
(IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH) ) {
|
|
|
|
QpReq = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
SubjectSecurityContext = &QpReq->SecurityContext->AccessState->SubjectSecurityContext;
|
|
}
|
|
else if( (IrpSp->MajorFunction == IRP_MJ_CREATE) && (IrpSp->Parameters.Create.SecurityContext != NULL) ) {
|
|
|
|
SubjectSecurityContext = &IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext;
|
|
|
|
}
|
|
else {
|
|
// Return 0 for cases we do not handle
|
|
return( 0 );
|
|
}
|
|
|
|
// Is the thread currently impersonating someone else?
|
|
|
|
if (SubjectSecurityContext->ClientToken != NULL) {
|
|
|
|
//
|
|
// If its impersonating someone that is logged in locally then use
|
|
// the local id.
|
|
//
|
|
|
|
SeQuerySessionIdToken(SubjectSecurityContext->ClientToken, &SessionId);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use the processes LogonId
|
|
//
|
|
|
|
SeQuerySessionIdToken(SubjectSecurityContext->PrimaryToken, &SessionId);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, (" ->SessionId = %08lx\n", SessionId));
|
|
|
|
return SessionId;
|
|
}
|
|
|
|
#if 0
|
|
VOID
|
|
RxUpcaseLeadingComponents(
|
|
IN OUT PUNICODE_STRING CanonicalName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to upcase the leading components of a name. Either 2 or 3 components are
|
|
upcased depending on the name (i.e. whether it's a UNC name or a vnetrootname). the operation is performed
|
|
in place!
|
|
|
|
Arguments:
|
|
|
|
RxContext - the current workitem
|
|
CanonicalName - the name being canonicalized
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
ULONG ComponentsToUpcase,wcLength,i;
|
|
UNICODE_STRING ShortenedCanonicalName;
|
|
|
|
PAGED_CODE();
|
|
|
|
ComponentsToUpcase = (*(CanonicalName->Buffer+1) == L';')?3:2;
|
|
wcLength = CanonicalName->Length/sizeof(WCHAR);
|
|
for (i=1;;i++) { //note: don't start at zero
|
|
if (i>=wcLength) break;
|
|
if (CanonicalName->Buffer[i]!=OBJ_NAME_PATH_SEPARATOR) continue;
|
|
ComponentsToUpcase--;
|
|
if (ComponentsToUpcase==0) break;
|
|
}
|
|
ShortenedCanonicalName.Buffer = CanonicalName->Buffer;
|
|
ShortenedCanonicalName.MaximumLength = CanonicalName->MaximumLength;
|
|
ShortenedCanonicalName.Length = (USHORT)(i*sizeof(WCHAR));
|
|
RtlUpcaseUnicodeString(&ShortenedCanonicalName,&ShortenedCanonicalName,FALSE); //don't allocate
|
|
RxDbgTrace(0, Dbg, ("RxUpcaseLeadingComponents -> %wZ\n", &ShortenedCanonicalName));
|
|
return;
|
|
}
|
|
#endif //if 0
|
|
|
|
|
|
#if 0
|
|
|
|
UNICODE_STRING InterestingNames[] = {
|
|
32, 32, L"CreateParent.txt",
|
|
};
|
|
|
|
DWORD
|
|
IsInterestingFile(
|
|
PUNICODE_STRING pFileName
|
|
)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i< sizeof(InterestingNames)/sizeof(UNICODE_STRING); ++i)
|
|
{
|
|
if (pFileName->Length > InterestingNames[i].Length)
|
|
{
|
|
UNICODE_STRING uTemp;
|
|
uTemp.Length = uTemp.MaximumLength = InterestingNames[i].Length;
|
|
uTemp.Buffer = pFileName->Buffer + (pFileName->Length - InterestingNames[i].Length)/sizeof(WCHAR);
|
|
if(RtlCompareUnicodeString(&uTemp, &InterestingNames[i], TRUE)==0)
|
|
{
|
|
// DbgPrint("Interesting string %wZ \n", pFileName);
|
|
return i+1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|