Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3595 lines
129 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
openclos.c
Abstract:
This module implements the DAV miniredir call down routines pertaining to
opening/closing of file/directories.
Author:
Balan Sethu Raman [SethuR]
Rohan Kumar [rohank] 15-March-1999
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "webdav.h"
//
// The global list of all the active LOCK tokens (one for every LOCK taken) and
// the resource that is used to synchronize access to it.
//
LIST_ENTRY LockTokenEntryList;
ERESOURCE LockTokenEntryListLock;
//
// The global list of all the LOCK conflict entries and the resource that is
// used to synchronize access to it.
//
LIST_ENTRY LockConflictEntryList;
ERESOURCE LockConflictEntryListLock;
//
// Mentioned below are the prototypes of functions tht are used only within
// this module (file). These functions should not be exposed outside.
//
NTSTATUS
MRxDAVSyncIrpCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP CalldownIrp,
IN PVOID Context
);
NTSTATUS
MRxDAVCreateContinuation(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
);
NTSTATUS
MRxDAVCloseSrvOpenContinuation(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
);
NTSTATUS
MRxDAVFormatUserModeCloseRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
PULONG_PTR ReturnedLength
);
NTSTATUS
MRxDAVFormatUserModeCreateRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
PULONG_PTR ReturnedLength
);
BOOL
MRxDAVPrecompleteUserModeCloseRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
BOOL OperationCancelled
);
BOOL
MRxDAVPrecompleteUserModeCreateRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
BOOL OperationCancelled
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, MRxDAVSyncXxxInformation)
#pragma alloc_text(PAGE, MRxDAVShouldTryToCollapseThisOpen)
#pragma alloc_text(PAGE, MRxDAVSetLoud)
#pragma alloc_text(PAGE, MRxDAVCreate)
#pragma alloc_text(PAGE, MRxDAVFormatUserModeCreateRequest)
#pragma alloc_text(PAGE, MRxDAVPrecompleteUserModeCreateRequest)
#pragma alloc_text(PAGE, MRxDAVCreateContinuation)
#pragma alloc_text(PAGE, MRxDAVCollapseOpen)
#pragma alloc_text(PAGE, MRxDAVComputeNewBufferingState)
#pragma alloc_text(PAGE, MRxDAVTruncate)
#pragma alloc_text(PAGE, MRxDAVForcedClose)
#pragma alloc_text(PAGE, MRxDAVCloseSrvOpen)
#pragma alloc_text(PAGE, MRxDAVFormatUserModeCloseRequest)
#pragma alloc_text(PAGE, MRxDAVPrecompleteUserModeCloseRequest)
#pragma alloc_text(PAGE, MRxDAVCloseSrvOpenContinuation)
#endif
//
// The implementation of functions begins here.
//
#define MRXDAV_ENCRYPTED_DIRECTORY_KEY L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\MRxDAV\\EncryptedDirectories"
NTSTATUS
MRxDAVSyncIrpCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP CalldownIrp,
IN PVOID Context
)
/*++
Routine Description:
This routine is called when the calldownirp is completed.
Arguments:
DeviceObject
CalldownIrp
Context
Return Value:
RXSTATUS - STATUS_MORE_PROCESSING_REQUIRED
--*/
{
PRX_CONTEXT RxContext = (PRX_CONTEXT)Context;
//
// Since this is an IRP completion rotuine, this cannot be paged code.
//
if (CalldownIrp->PendingReturned){
RxSignalSynchronousWaiter(RxContext);
}
return(STATUS_MORE_PROCESSING_REQUIRED);
}
ULONG_PTR DummyReturnedLengthForXxxInfo;
NTSTATUS
MRxDAVSyncXxxInformation(
IN OUT PRX_CONTEXT RxContext,
IN UCHAR MajorFunction,
IN PFILE_OBJECT FileObject,
IN ULONG InformationClass,
IN ULONG Length,
OUT PVOID Information,
OUT PULONG_PTR ReturnedLength OPTIONAL
)
/*++
Routine Description:
This routine returns the requested information about a specified file
or volume. The information returned is determined by the class that
is specified, and it is placed into the caller's output buffer.
Arguments:
FsInformationClass - Specifies the type of information which should be
returned about the file/volume.
Length - Supplies the length of the buffer in bytes.
FsInformation - Supplies a buffer to receive the requested information
returned about the file. This buffer must not be pageable
and must reside in system space.
ReturnedLength - Supplies a variable that is to receive the length of the
information written to the buffer.
FileInformation - Boolean that indicates whether the information requested
is for a file or a volume.
Return Value:
The status returned is the final completion status of the operation.
--*/
{
NTSTATUS Status;
PIRP irp,TopIrp;
PIO_STACK_LOCATION irpSp;
PDEVICE_OBJECT DeviceObject;
PAGED_CODE();
if (ReturnedLength == NULL) {
ReturnedLength = &(DummyReturnedLengthForXxxInfo);
}
ASSERT (FileObject);
DeviceObject = IoGetRelatedDeviceObject(FileObject);
ASSERT (DeviceObject);
//
// Allocate and initialize the I/O Request Packet (IRP) for this operation.
// The allocation is performed with an exception handler in case the
// caller does not have enough quota to allocate the packet.
//
irp = IoAllocateIrp(DeviceObject->StackSize, TRUE);
if (!irp) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irp->Tail.Overlay.OriginalFileObject = FileObject;
irp->Tail.Overlay.Thread = PsGetCurrentThread();
irp->RequestorMode = KernelMode;
//
// Get a pointer to the stack location for the first driver. This will be
// used to pass the original function codes and parameters.
//
irpSp = IoGetNextIrpStackLocation(irp);
irpSp->MajorFunction = MajorFunction;
irpSp->FileObject = FileObject;
IoSetCompletionRoutine(irp,
MRxDAVSyncIrpCompletionRoutine,
RxContext,
TRUE,
TRUE,
TRUE);
irp->AssociatedIrp.SystemBuffer = Information;
//
// Copy the caller's parameters to the service-specific portion of the
// IRP.
//
IF_DEBUG {
ASSERT((irpSp->MajorFunction == IRP_MJ_QUERY_INFORMATION)
|| (irpSp->MajorFunction == IRP_MJ_SET_INFORMATION)
|| (irpSp->MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION));
if (irpSp->MajorFunction == IRP_MJ_SET_INFORMATION) {
//IF_LOUD_DOWNCALLS(MiniFileObject) {
// SetFileInfoInfo = ((PFILE_END_OF_FILE_INFORMATION)Information)->EndOfFile.LowPart;
//}
}
ASSERT(&irpSp->Parameters.QueryFile.Length
== &irpSp->Parameters.SetFile.Length);
ASSERT(&irpSp->Parameters.QueryFile.Length
== &irpSp->Parameters.QueryVolume.Length);
ASSERT(&irpSp->Parameters.QueryFile.FileInformationClass
== &irpSp->Parameters.SetFile.FileInformationClass);
ASSERT(&irpSp->Parameters.QueryFile.FileInformationClass
== &irpSp->Parameters.QueryVolume.FsInformationClass);
}
irpSp->Parameters.QueryFile.Length = Length;
irpSp->Parameters.QueryFile.FileInformationClass = InformationClass;
//
// Now simply invoke the driver at its dispatch entry with the IRP.
//
KeInitializeEvent(&RxContext->SyncEvent,
NotificationEvent,
FALSE);
try {
TopIrp = IoGetTopLevelIrp();
//
// Tell the underlying guy he's all clear.
//
IoSetTopLevelIrp(NULL);
Status = IoCallDriver(DeviceObject,irp);
} finally {
//
// Restore my context for unwind.
//
IoSetTopLevelIrp(TopIrp);
}
if (Status == (STATUS_PENDING)) {
RxWaitSync(RxContext);
Status = irp->IoStatus.Status;
}
if (Status == STATUS_SUCCESS) {
*ReturnedLength = irp->IoStatus.Information;
}
IoFreeIrp(irp);
return(Status);
}
NTSTATUS
MRxDAVShouldTryToCollapseThisOpen(
IN PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine determines if the mini knows of a good reason not
to try collapsing on this open. Presently, the only reason would
be if this were a copychunk open.
Arguments:
RxContext - the RDBSS context
Return Value:
NTSTATUS - The return status for the operation
SUCCESS --> okay to try collapse
other (MORE_PROCESSING_REQUIRED) --> dont collapse
--*/
{
NTSTATUS Status = STATUS_MORE_PROCESSING_REQUIRED;
PAGED_CODE();
//
// We do not collapse any SrvOpen. The idea is to have one SrvOpen per
// create.
//
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVShouldTryToCollapseThisOpen!!!!\n",
PsGetCurrentThreadId()));
return Status;
}
ULONG UMRxLoudStringTableSize = 0;
UNICODE_STRING UMRxLoudStrings[50];
VOID
MRxDAVSetLoud(
IN PBYTE Msg,
IN PRX_CONTEXT RxContext,
IN PUNICODE_STRING s
)
{
ULONG i;
UNICODE_STRING temp;
PAGED_CODE();
for (i=0; i < UMRxLoudStringTableSize; i++) {
PUNICODE_STRING t = &(UMRxLoudStrings[i]);
((PBYTE)temp.Buffer) = ((PBYTE)s->Buffer) + s->Length - t->Length;
temp.Length = t->Length;
if (RtlEqualUnicodeString(&temp, t, TRUE)) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVSetLoud: %s Found %wZ from %wZ.\n",
PsGetCurrentThreadId(), Msg, t, s));
RxContext->LoudCompletionString = t;
break;
}
}
}
NTSTATUS
MRxDAVCreate(
IN PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine handles create request for the DAV mini-redir.
Arguments:
RxContext - The RDBSS context.
Return Value:
RXSTATUS - The return status for the operation.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVCreate!!!!\n", PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVCreate: RxContext: %08lx\n",
PsGetCurrentThreadId(), RxContext));
NtStatus = UMRxAsyncEngOuterWrapper(RxContext,
SIZEOF_DAV_SPECIFIC_CONTEXT,
MRxDAVFormatTheDAVContext,
DAV_MINIRDR_ENTRY_FROM_CREATE,
MRxDAVCreateContinuation,
"MRxDAVCreate");
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVCreate with NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
NTSTATUS
MRxDAVFormatUserModeCreateRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
PULONG_PTR ReturnedLength
)
/*++
Routine Description:
This routine formats the arguments of the create request which is being
sent to the user mode for processing.
Arguments:
RxContext - The RDBSS context.
AsyncEngineContext - The reflctor's context.
WorkItem - The work item buffer.
WorkItemLength - The length of the work item buffer.
ReturnedLength -
Return Value:
STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PDAV_USERMODE_WORKITEM WorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PMRX_FCB Fcb = SrvOpen->pFcb;
PWEBDAV_FCB DavFcb = MRxDAVGetFcbExtension(Fcb);
PWEBDAV_CONTEXT DavContext = (PWEBDAV_CONTEXT)AsyncEngineContext;
PNT_CREATE_PARAMETERS CreateParameters;
PDAV_USERMODE_CREATE_REQUEST CreateRequest = &(WorkItem->CreateRequest);
PDAV_USERMODE_CREATE_RESPONSE CreateResponse = &(WorkItem->CreateResponse);
PWEBDAV_V_NET_ROOT DavVNetRoot = NULL;
PWEBDAV_SRV_CALL DavSrvCall = NULL;
PSECURITY_CLIENT_CONTEXT SecurityClientContext = NULL;
PBYTE SecondaryBuff = NULL;
PWCHAR NetRootName = NULL, AlreadyPrefixedName = NULL;
DWORD PathLen = 0, PathLenInBytes = 0, SdLength = 0;
BOOL didIAllocateFileNameInfo = FALSE;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVFormatUserModeCreateRequest!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVFormatUserModeCreateRequest: AsyncEngineContext = "
"%08lx, RxContext = %08lx.\n", PsGetCurrentThreadId(),
AsyncEngineContext, RxContext));
CreateParameters = &(RxContext->Create.NtCreateParameters);
//
// Set the SecurityClientContext which is used in impersonating.
//
MRxDAVGetSecurityClientContext();
//
// Copy the LogonID in the CreateRequest buffer. The LogonId is in the
// MiniRedir's portion of the V_NET_ROOT.
//
CreateRequest->LogonID.LowPart = DavVNetRoot->LogonID.LowPart;
CreateRequest->LogonID.HighPart = DavVNetRoot->LogonID.HighPart;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCreateRequest: LogonID.LowPart = %08lx\n",
PsGetCurrentThreadId(), DavVNetRoot->LogonID.LowPart));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCreateRequest: LogonID.HighPart = %08lx\n",
PsGetCurrentThreadId(), DavVNetRoot->LogonID.HighPart));
//
// Impersonate the client who initiated the request. If we fail to
// impersonate, tough luck.
//
if (SecurityClientContext != NULL) {
NtStatus = UMRxImpersonateClient(SecurityClientContext, WorkItemHeader);
if (!NT_SUCCESS(NtStatus)) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFormatUserModeCreateRequest/"
"UMRxImpersonateClient. NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
} else {
NtStatus = STATUS_INVALID_PARAMETER;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFormatUserModeCreateRequest: "
"SecurityClientContext is NULL.\n",
PsGetCurrentThreadId()));
goto EXIT_THE_FUNCTION;
}
NetRootName = SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Buffer;
AlreadyPrefixedName = SrvOpen->pAlreadyPrefixedName->Buffer;
//
// Allocate memory for the complete path name and copy it.
// The CompletePathName = NetRootName + AlreadyPrefixedName. The extra two
// bytes are for '\0' at the end.
//
PathLen = SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length;
PathLen += SrvOpen->pAlreadyPrefixedName->Length;
PathLen += sizeof(WCHAR);
PathLenInBytes = PathLen;
SecondaryBuff = UMRxAllocateSecondaryBuffer(AsyncEngineContext,
PathLenInBytes);
if (SecondaryBuff == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: MRxDAVFormatUserModeCreateRequest/"
"UMRxAllocateSecondaryBuffer: ERROR: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
CreateRequest->CompletePathName = (PWCHAR)SecondaryBuff;
RtlZeroMemory(CreateRequest->CompletePathName, PathLenInBytes);
//
// Copy the NetRootName.
//
RtlCopyMemory(SecondaryBuff,
NetRootName,
SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length);
//
// Copy the AlreadyPrefixedName after the NetRootName to make the complete
// path name.
//
RtlCopyMemory(SecondaryBuff + SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length,
AlreadyPrefixedName,
SrvOpen->pAlreadyPrefixedName->Length);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCreateRequest: CPN = %ws.\n",
PsGetCurrentThreadId(), CreateRequest->CompletePathName));
//
// If this is the first create, then we need to allocate the FileNameInfo
// in the FCB. This is used in logging the delayed write failure.
//
if (DavFcb->FileNameInfoAllocated != TRUE) {
DavFcb->FileNameInfo.Buffer = RxAllocatePoolWithTag(PagedPool,
PathLenInBytes,
DAV_FILEINFO_POOLTAG);
if (DavFcb->FileNameInfo.Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVFormatUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(DavFcb->FileNameInfo.Buffer, PathLenInBytes);
RtlCopyMemory(DavFcb->FileNameInfo.Buffer,
CreateRequest->CompletePathName,
PathLenInBytes);
DavFcb->FileNameInfo.Length = (USHORT)PathLenInBytes - sizeof(WCHAR);
DavFcb->FileNameInfo.MaximumLength = (USHORT)PathLenInBytes;
DavFcb->FileNameInfoAllocated = TRUE;
didIAllocateFileNameInfo = TRUE;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCreateRequest: FileNameInfo = %wZ\n",
PsGetCurrentThreadId(), &(DavFcb->FileNameInfo)));
}
WorkItem->WorkItemType = UserModeCreate;
//
// Set the ServerID that was got during the CreateSrvCall operation.
//
ASSERT(RxContext->pRelevantSrvOpen->pVNetRoot->pNetRoot->pSrvCall->Context);
DavSrvCall = (PWEBDAV_SRV_CALL)RxContext->pRelevantSrvOpen->pVNetRoot->
pNetRoot->pSrvCall->Context;
CreateRequest->ServerID = DavSrvCall->ServerID;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCreateRequest: ServerID = %d.\n",
PsGetCurrentThreadId(), CreateRequest->ServerID));
CreateRequest->AllocationSize = CreateParameters->AllocationSize;
CreateRequest->FileAttributes = CreateParameters->FileAttributes;
CreateRequest->ShareAccess = CreateParameters->ShareAccess;
CreateRequest->CreateDisposition = CreateParameters->Disposition;
CreateRequest->EaBuffer = RxContext->Create.EaBuffer;
CreateRequest->EaLength = RxContext->Create.EaLength;
SdLength = CreateRequest->SdLength = RxContext->Create.SdLength;
CreateRequest->ImpersonationLevel = CreateParameters->ImpersonationLevel;
CreateRequest->SecurityFlags = 0;
if (CreateParameters->SecurityContext != NULL) {
if (CreateParameters->SecurityContext->SecurityQos != NULL) {
if (CreateParameters->SecurityContext->
SecurityQos->ContextTrackingMode == SECURITY_DYNAMIC_TRACKING) {
CreateRequest->SecurityFlags |= DAV_SECURITY_DYNAMIC_TRACKING;
}
if (CreateParameters->SecurityContext->SecurityQos->EffectiveOnly) {
CreateRequest->SecurityFlags |= DAV_SECURITY_EFFECTIVE_ONLY;
}
}
}
CreateRequest->DesiredAccess = CreateParameters->DesiredAccess;
CreateRequest->CreateOptions = CreateParameters->CreateOptions;
if (AsyncEngineContext->FileInformationCached) {
CreateRequest->FileInformationCached = TRUE;
CreateResponse->BasicInformation = DavContext->CreateReturnedFileInfo->BasicInformation;
CreateResponse->StandardInformation = DavContext->CreateReturnedFileInfo->StandardInformation;
DavDbgTrace(DAV_TRACE_DETAIL,
("MRxDAVFormatUserModeCreateRequest file info cached %x %x %x %x %ws\n",
CreateResponse->BasicInformation.FileAttributes,
CreateResponse->BasicInformation.CreationTime.HighPart,
CreateResponse->BasicInformation.CreationTime.LowPart,
CreateResponse->StandardInformation.EndOfFile.LowPart,
CreateRequest->CompletePathName));
}
CreateRequest->ParentDirInfomationCached = AsyncEngineContext->ParentDirInfomationCached;
CreateRequest->ParentDirIsEncrypted = AsyncEngineContext->ParentDirIsEncrypted;
if (AsyncEngineContext->FileNotExists) {
CreateRequest->FileNotExists = TRUE;
}
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVFormatUserModeCreateRequest with NtStatus"
" = %08lx.\n", PsGetCurrentThreadId(), NtStatus));
if (NtStatus != STATUS_SUCCESS) {
if (CreateRequest->CompletePathName != NULL) {
UMRxFreeSecondaryBuffer(AsyncEngineContext,
(PBYTE)CreateRequest->CompletePathName);
CreateRequest->CompletePathName = NULL;
}
//
// Free the FileNameInfo buffer only if it was allocated in this call.
//
if (DavFcb->FileNameInfo.Buffer != NULL && didIAllocateFileNameInfo) {
RxFreePool(DavFcb->FileNameInfo.Buffer);
DavFcb->FileNameInfo.Buffer = NULL;
DavFcb->FileNameInfo.Length = 0;
DavFcb->FileNameInfo.MaximumLength = 0;
DavFcb->FileNameInfoAllocated = FALSE;
}
}
return(NtStatus);
}
BOOL
MRxDAVPrecompleteUserModeCreateRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
BOOL OperationCancelled
)
/*++
Routine Description:
Arguments:
RxContext - The RDBSS context.
AsyncEngineContext - The reflctor's context.
WorkItem - The work item buffer.
WorkItemLength - The length of the work item buffer.
OperationCancelled - TRUE if this operation was cancelled by the user.
Return Value:
TRUE - UMRxAsyncEngineCalldownIrpCompletion is called by the function
UMRxCompleteUserModeRequest after we return.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PDAV_USERMODE_WORKITEM WorkItem = NULL;
PWEBDAV_CONTEXT DavContext = NULL;
RxCaptureFcb;
PMRX_SRV_OPEN SrvOpen = NULL;
PWEBDAV_SRV_OPEN davSrvOpen = NULL;
PMRX_FCB Fcb = NULL;
PWEBDAV_FCB DavFcb = NULL;
PDAV_USERMODE_CREATE_RESPONSE CreateResponse = NULL;
PDAV_USERMODE_CREATE_REQUEST CreateRequest = NULL;
HANDLE OpenHandle;
PSECURITY_CLIENT_CONTEXT SecurityClientContext = NULL;
PWEBDAV_V_NET_ROOT DavVNetRoot = NULL;
PMRX_SRV_CALL SrvCall = NULL;
PMRX_NET_ROOT NetRoot = NULL;
PWEBDAV_SRV_CALL DavSrvCall = NULL;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVPrecompleteUserModeCreateRequest!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVPrecompleteUserModeCreateRequest: "
"AsyncEngineContext = %08lx, RxContext = %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
//
// A Create operation can never be Async.
//
ASSERT(AsyncEngineContext->AsyncOperation == FALSE);
WorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader;
DavContext = (PWEBDAV_CONTEXT)AsyncEngineContext;
CreateResponse = &(WorkItem->CreateResponse);
CreateRequest = &(WorkItem->CreateRequest);
//
// If the operation is cancelled, then there is no guarantee that the FCB,
// FOBX etc are still valid. All that we need to do is cleanup and bail.
//
if (!OperationCancelled) {
SrvOpen = RxContext->pRelevantSrvOpen;
davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen);
Fcb = SrvOpen->pFcb;
DavFcb = MRxDAVGetFcbExtension(Fcb);
DavVNetRoot = (PWEBDAV_V_NET_ROOT)RxContext->pRelevantSrvOpen->pVNetRoot->Context;
SrvCall = SrvOpen->pVNetRoot->pNetRoot->pSrvCall;
DavSrvCall = MRxDAVGetSrvCallExtension(SrvCall);
NetRoot = SrvOpen->pFcb->pNetRoot;
}
NtStatus = AsyncEngineContext->Status;
//
// We need to free up the heap we allocated in the format routine.
//
if (CreateRequest->CompletePathName != NULL) {
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest:"
"Open failed for file \"%ws\" with NtStatus = %08lx.\n",
PsGetCurrentThreadId(), CreateRequest->CompletePathName,
NtStatus));
}
NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext,
(PBYTE)CreateRequest->CompletePathName);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
}
}
//
// If the operation has been cancelled and we created a handle in the
// usermode then we need to set callWorkItemCleanup to TRUE which will
// land up closing this handle. In any case, if the operation has been
// cancelled, we can leave right away.
//
if (OperationCancelled) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest: "
"Operation Cancelled.\n", PsGetCurrentThreadId()));
if (CreateResponse->Handle) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest: "
"callWorkItemCleanup\n", PsGetCurrentThreadId()));
WorkItem->callWorkItemCleanup = TRUE;
}
goto EXIT_THE_FUNCTION;
}
//
// Open didn't work. We can bail out now.
//
if (AsyncEngineContext->Status != STATUS_SUCCESS) {
//
// If the file was already locked on the server then we need to create
// a LockConflictEntry and add it to the list. The apps can then use
// the FileName to query who has locked this file.
//
if (CreateResponse->FileWasAlreadyLocked) {
ULONG PathLengthInBytes = 0, OwnerLengthInBytes = 0;
PWEBDAV_LOCK_CONFLICT_ENTRY LockConflictEntry;
PBYTE TempBuffer = NULL;
LockConflictEntry = RxAllocatePoolWithTag(PagedPool,
sizeof(WEBDAV_LOCK_CONFLICT_ENTRY),
DAV_LOCKCONFLICTENTRY_POOLTAG);
if (LockConflictEntry == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(LockConflictEntry, sizeof(WEBDAV_LOCK_CONFLICT_ENTRY));
//
// Set the current system time as the creation time of the entry.
//
KeQueryTickCount( &(LockConflictEntry->CreationTimeInTickCount) );
//
// Allocate memory for the complete path name and copy it.
//
PathLengthInBytes = SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length;
PathLengthInBytes += SrvOpen->pAlreadyPrefixedName->Length;
PathLengthInBytes += sizeof(WCHAR);
TempBuffer = RxAllocatePoolWithTag(PagedPool,
PathLengthInBytes,
DAV_LOCKCONFLICTENTRY_POOLTAG);
if (TempBuffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
LockConflictEntry->CompletePathName = (PWCHAR)TempBuffer;
RtlZeroMemory(TempBuffer, PathLengthInBytes);
//
// Copy the NetRootName. It includes the server name and the share
// name.
//
RtlCopyMemory(TempBuffer,
SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Buffer,
SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length);
//
// Copy the AlreadyPrefixedName after the NetRootName to make the
// complete path name.
//
RtlCopyMemory((TempBuffer + SrvOpen->pVNetRoot->pNetRoot->pNetRootName->Length),
SrvOpen->pAlreadyPrefixedName->Buffer,
SrvOpen->pAlreadyPrefixedName->Length);
//
// Allocate memory for the OwnerName and copy it.
//
OwnerLengthInBytes = (1 + wcslen(CreateResponse->LockOwner)) * sizeof(WCHAR);
LockConflictEntry->LockOwner = RxAllocatePoolWithTag(PagedPool,
OwnerLengthInBytes,
DAV_LOCKCONFLICTENTRY_POOLTAG);
if (LockConflictEntry->LockOwner == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(LockConflictEntry->LockOwner, OwnerLengthInBytes);
RtlCopyMemory(LockConflictEntry->LockOwner,
CreateResponse->LockOwner,
OwnerLengthInBytes);
//
// Add the newly created entry to the global LockConflictEntryList.
//
ExAcquireResourceExclusiveLite(&(LockConflictEntryListLock), TRUE);
InsertHeadList(&(LockConflictEntryList), &(LockConflictEntry->listEntry));
ExReleaseResourceLite(&(LockConflictEntryListLock));
}
goto EXIT_THE_FUNCTION;
}
//
// We need to do the "handle to fileobject" association only if its a file.
// If the create was for a directory, no handle would have been created in
// the user mode.
//
if (!CreateResponse->StandardInformation.Directory) {
OpenHandle = CreateResponse->Handle;
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVPrecompleteUserModeCreateRequest: "
"OpenHandle = %08lx.\n", PsGetCurrentThreadId(),
OpenHandle));
DavFcb->isDirectory = FALSE;
if ( (OpenHandle != NULL) ) {
NtStatus = ObReferenceObjectByHandle(OpenHandle,
0L,
NULL,
KernelMode,
(PVOID *)&(davSrvOpen->UnderlyingFileObject),
NULL);
if (NtStatus == STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVPrecompleteUserModeCreateRequest: UFO(1) = %08lx\n",
PsGetCurrentThreadId(), davSrvOpen->UnderlyingFileObject));
davSrvOpen->UnderlyingHandle = OpenHandle;
davSrvOpen->UserModeKey = CreateResponse->UserModeKey;
davSrvOpen->UnderlyingDeviceObject = IoGetRelatedDeviceObject(davSrvOpen->UnderlyingFileObject);
//
// Copy the local file name into the FCB.
//
wcscpy(DavFcb->FileName, CreateResponse->FileName);
wcscpy(DavFcb->Url, CreateResponse->Url);
DavFcb->LocalFileIsEncrypted = CreateResponse->LocalFileIsEncrypted;
MRxDAVGetSecurityClientContext();
ASSERT(SecurityClientContext != NULL);
RtlCopyMemory(&(DavFcb->SecurityClientContext),
SecurityClientContext,
sizeof(SECURITY_CLIENT_CONTEXT));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVPrecompleteUserModeCreateRequest: "
"LocalFileName = %ws.\n", PsGetCurrentThreadId(),
DavFcb->FileName));
//
// We only get/create the file/dir on the first open. On
// subsequent opens, we do the create in the kernel itself since
// the file exists in the WinInet cache. This caching is used
// till the FCB for the file exists.
//
DavFcb->isFileCached = TRUE;
} else {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"ObReferenceObjectByHandle: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
//
// If we have a valid handle, then why should
// ObReferenceObjectByHandle fail?
//
DbgBreakPoint();
ZwClose(OpenHandle);
goto EXIT_THE_FUNCTION;
}
//
// If "FILE_DELETE_ON_CLOSE" flag was specified as one of
// the CreateOptions, then we need to remember this and
// delete this file on close.
//
if (CreateResponse->DeleteOnClose) {
DavFcb->DeleteOnClose = TRUE;
}
//
// If a new file has been created then we need to set the attributes
// of this new file on close on the server.
//
if (CreateResponse->NewFileCreatedAndSetAttributes) {
DavFcb->fFileAttributesChanged = TRUE;
}
//
// This file exists on the server, but this create operation
// has FILE_OVERWRITE_IF as its CreateDisposition. So, we
// can create this file locally overwrite the one on the
// server on close. We set DoNotTakeTheCurrentTimeAsLMT to
// TRUE since the LMT has been just set on Create and we do
// not need to set it to the current time on close.
//
if (CreateResponse->ExistsAndOverWriteIf) {
InterlockedExchange(&(DavFcb->FileWasModified), 1);
DavFcb->DoNotTakeTheCurrentTimeAsLMT = TRUE;
}
//
// If a new file or directory is created, we need to PROPPATCH the
// time values on close. This is because we use the time values from
// the client when the name cache entry is created for this new
// file. The same time value needs to be on the server.
//
if (CreateResponse->PropPatchTheTimeValues) {
DavFcb->fCreationTimeChanged = TRUE;
DavFcb->fLastAccessTimeChanged = TRUE;
DavFcb->fLastModifiedTimeChanged = TRUE;
}
} else {
//
// We don't have an OpenHandle for a file. This should only happen
// in case where the open is for read/setting sttributes of a file
// or deleting/renaming a file.
//
if (!CreateResponse->fPsuedoOpen) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest: No OpenHandle\n"));
DbgBreakPoint();
}
//
// If "FILE_DELETE_ON_CLOSE" flag was specified as one of
// the CreateOptions, then we need to remember this and
// delete this file on close.
//
if (CreateResponse->DeleteOnClose) {
DavFcb->DeleteOnClose = TRUE;
}
}
//
// If the LOCK was taken on this Create, we need to do the following.
// 1. Add the OpaqueLockToken to the davSrvOpen.
// 2. Create a LockTokenEntry and add it to the list which will be
// used to refresh the LOCKs on the server.
//
if (CreateResponse->LockWasTakenOnThisCreate) {
PWEBDAV_LOCK_TOKEN_ENTRY LockTokenEntry = NULL;
PBYTE TempBuffer = NULL, PathName = NULL;
ULONG LockTokenLengthInBytes = 0, ServerNameLengthInBytes = 0;
ULONG NetRootNameLengthInBytes = 0, PathNameLengthInBytes = 0;
PWCHAR NetRootName = NULL;
//
// We will be sending the OpaqueLockToken in the following format.
// If: (<opaquelocktoken:sdfsdfdsfgsdgdsfgd>)
// So, we store the token in the same format.
//
LockTokenLengthInBytes = (1 + wcslen(CreateResponse->OpaqueLockToken)) * sizeof(WCHAR);
LockTokenLengthInBytes += (wcslen(L"If: (<>)") * sizeof(WCHAR));
TempBuffer = RxAllocatePoolWithTag(PagedPool,
LockTokenLengthInBytes,
DAV_SRVOPEN_POOLTAG);
if (TempBuffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
davSrvOpen->OpaqueLockToken = (PWCHAR)TempBuffer;
RtlZeroMemory(TempBuffer, LockTokenLengthInBytes);
RtlCopyMemory(TempBuffer,
L"If: (<",
wcslen(L"If: (<") * sizeof(WCHAR));
RtlCopyMemory(TempBuffer + (wcslen(L"If: (<") * sizeof(WCHAR)),
CreateResponse->OpaqueLockToken,
wcslen(CreateResponse->OpaqueLockToken) * sizeof(WCHAR));
RtlCopyMemory(TempBuffer + (wcslen(L"If: (<") * sizeof(WCHAR)) + (wcslen(CreateResponse->OpaqueLockToken) * sizeof(WCHAR)),
L">)",
wcslen(L">)") * sizeof(WCHAR));
DavDbgTrace(DAV_TRACE_DETAIL,
("ld: MRxDAVPrecompleteUserModeCreateRequest. "
"OpaqueLockToken: %ws\n",
PsGetCurrentThreadId(), davSrvOpen->OpaqueLockToken));
LockTokenEntry = RxAllocatePoolWithTag(PagedPool,
sizeof(WEBDAV_LOCK_TOKEN_ENTRY),
DAV_LOCKTOKENENTRY_POOLTAG);
if (LockTokenEntry == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(LockTokenEntry, sizeof(WEBDAV_LOCK_TOKEN_ENTRY));
//
// Set the current system time as the creation time of the entry.
//
KeQueryTickCount( &(LockTokenEntry->CreationTimeInTickCount) );
//
// The OpaqueLockToken will be included in the request that is
// sent to the server.
//
LockTokenEntry->OpaqueLockToken = davSrvOpen->OpaqueLockToken;
//
// The SecurityClientContext will be used to impersonate this
// client while refreshing the LOCK request.
//
LockTokenEntry->SecurityClientContext = &(DavVNetRoot->SecurityClientContext);
//
// Set the timeout value of the LOCK. This will be used to determine
// when to send the refresh requests out.
//
LockTokenEntry->LockTimeOutValueInSec = CreateResponse->LockTimeout;
LockTokenEntry->LogonID.LowPart = DavVNetRoot->LogonID.LowPart;
LockTokenEntry->LogonID.HighPart = DavVNetRoot->LogonID.HighPart;
LockTokenEntry->ServerID = DavSrvCall->ServerID;
LockTokenEntry->ShouldThisEntryBeRefreshed = TRUE;
//
// Allocate memory for the ServerName and copy it.
//
ServerNameLengthInBytes = SrvCall->pSrvCallName->Length + sizeof(WCHAR);
LockTokenEntry->ServerName = RxAllocatePoolWithTag(PagedPool,
ServerNameLengthInBytes,
DAV_LOCKTOKENENTRY_POOLTAG);
if (LockTokenEntry->ServerName == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(LockTokenEntry->ServerName, ServerNameLengthInBytes);
RtlCopyBytes(LockTokenEntry->ServerName,
SrvCall->pSrvCallName->Buffer,
SrvCall->pSrvCallName->Length);
//
// Allocate memory for the PathName and copy it.
//
NetRootName = &(NetRoot->pNetRootName->Buffer[1]);
NetRootName = wcschr(NetRootName, L'\\');
//
// The sizeof(WCHAR) is for the final '\0' char.
//
NetRootNameLengthInBytes = (NetRoot->pNetRootName->Length - NetRoot->pSrvCall->pSrvCallName->Length);
PathNameLengthInBytes = (NetRootNameLengthInBytes + sizeof(WCHAR));
if (SrvOpen->pAlreadyPrefixedName->Length) {
//
// The sizeof(WCHAR) is for the backslash after the NetRootName.
//
PathNameLengthInBytes += (SrvOpen->pAlreadyPrefixedName->Length + sizeof(WCHAR));
}
PathName = RxAllocatePoolWithTag(PagedPool,
PathNameLengthInBytes,
DAV_LOCKTOKENENTRY_POOLTAG);
if (PathName == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVPrecompleteUserModeCreateRequest/"
"RxAllocatePoolWithTag: NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
LockTokenEntry->PathName = (PWCHAR)PathName;
RtlZeroMemory(PathName, PathNameLengthInBytes);
//
// Copy the NetRootName.
//
RtlCopyMemory(PathName, NetRootName, NetRootNameLengthInBytes);
//
// We need to copy the backclash and the remaining path name only if
// the remaining path name exists.
//
if (SrvOpen->pAlreadyPrefixedName->Length) {
if (SrvOpen->pAlreadyPrefixedName->Buffer[0] != L'\\') {
//
// Copy the backslash.
//
RtlCopyMemory((PathName + NetRootNameLengthInBytes), L"\\", sizeof(WCHAR));
//
// Copy the remaining path name after the NetRootName.
//
RtlCopyMemory((PathName + NetRootNameLengthInBytes + sizeof(WCHAR)),
SrvOpen->pAlreadyPrefixedName->Buffer,
SrvOpen->pAlreadyPrefixedName->Length);
} else {
//
// Copy the remaining path name after the NetRootName which has the leading
// backslash already.
//
RtlCopyMemory((PathName + NetRootNameLengthInBytes),
SrvOpen->pAlreadyPrefixedName->Buffer,
SrvOpen->pAlreadyPrefixedName->Length);
}
}
//
// Add the newly created entry to the global LockTokenEntryList.
//
ExAcquireResourceExclusiveLite(&(LockTokenEntryListLock), TRUE);
InsertHeadList(&(LockTokenEntryList), &(LockTokenEntry->listEntry));
ExReleaseResourceLite(&(LockTokenEntryListLock));
//
// Keep a link to this LockTokenEntry in the davSrvOpen. On close
// of this SrvOpen, we will delete this entry.
//
davSrvOpen->LockTokenEntry = LockTokenEntry;
//
// We set this to TRUE since the file has been LOCKed on the server
// on this Create. All the requests that modify the file on the
// server from now on should contain the OpaqueLockToken.
//
DavFcb->FileIsLockedOnTheServer = TRUE;
}
} else {
//
// This was a directory open.
//
DavFcb->isDirectory = TRUE;
if (CreateResponse->DeleteOnClose) {
DavFcb->DeleteOnClose = TRUE;
}
//
// If a new directory has been created then we need to set the
// attributes of this new file on close on the server.
//
if (CreateResponse->NewFileCreatedAndSetAttributes) {
DavFcb->fFileAttributesChanged = TRUE;
}
//
// If a new file or directory is created, we need to PROPPATCH the
// time values on close. This is because we use the time values from
// the client when the name cache entry is created for this new
// file. The same time value needs to be on the server.
//
if (CreateResponse->PropPatchTheTimeValues) {
DavFcb->fCreationTimeChanged = TRUE;
DavFcb->fLastAccessTimeChanged = TRUE;
DavFcb->fLastModifiedTimeChanged = TRUE;
}
}
if (NtStatus == STATUS_SUCCESS) {
RxContext->Create.ReturnedCreateInformation = (ULONG)WorkItem->Information;
*(DavContext->CreateReturnedFileInfo) = CreateResponse->CreateReturnedFileInfo;
capFcb->Attributes = CreateResponse->CreateReturnedFileInfo.BasicInformation.FileAttributes;
if ((capFcb->Attributes & FILE_ATTRIBUTE_ENCRYPTED) &&
((RxContext->Create.ReturnedCreateInformation == FILE_CREATED) ||
(RxContext->Create.ReturnedCreateInformation == FILE_OVERWRITTEN))) {
//
// The encryption user information is added to the file. This
// information need to be sent to the server even if the file
// itself is created empty. We set DoNotTakeTheCurrentTimeAsLMT to
// TRUE since the LMT has been just set on Create and we do not
// need to set it to the current time on close.
//
InterlockedExchange(&(DavFcb->FileWasModified), 1);
DavFcb->DoNotTakeTheCurrentTimeAsLMT = TRUE;
DavFcb->fFileAttributesChanged = TRUE;
DavDbgTrace(DAV_TRACE_DETAIL,
("MRxDAVPrecompleteUserModeCreateRequest: Encrypted file/dir was created %x %x %x\n",
DavFcb, Fcb, ((PFCB)Fcb)->Attributes));
}
if (capFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) {
if (capFcb->Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
//
// We update the registry if the directory has been encrypted
// by someone else.
//
NtStatus = MRxDAVCreateEncryptedDirectoryKey(&DavFcb->FileNameInfo);
} else {
//
// Query the registry to see if the directory should be encrypted.
//
NtStatus = MRxDAVQueryEncryptedDirectoryKey(&DavFcb->FileNameInfo);
if (NtStatus == STATUS_SUCCESS) {
capFcb->Attributes |= FILE_ATTRIBUTE_ENCRYPTED;
} else if (NtStatus == STATUS_OBJECT_NAME_NOT_FOUND) {
NtStatus = STATUS_SUCCESS;
}
}
}
DavDbgTrace(DAV_TRACE_DETAIL,
("MRxDAVPrecompleteUserModeCreateRequest file info %x %x %x %x %x %x %ws\n",
capFcb->Attributes,
CreateResponse->BasicInformation.CreationTime.HighPart,
CreateResponse->BasicInformation.CreationTime.LowPart,
CreateResponse->StandardInformation.EndOfFile.LowPart,
DavFcb,
Fcb,
DavFcb->FileName));
}
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVPrecompleteUserModeCreateRequest with "
"NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus));
return TRUE;
}
NTSTATUS
MRxDAVCreateContinuation(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
)
/*++
Routine Description:
This is the continuation routine for the create operation.
Arguments:
AsyncEngineContext - The Reflectors context.
RxContext - The RDBSS context.
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PWEBDAV_CONTEXT DavContext = (PWEBDAV_CONTEXT)AsyncEngineContext;
RxCaptureFcb;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen);
PMRX_FCB Fcb = SrvOpen->pFcb;
PWEBDAV_FCB DavFcb = MRxDAVGetFcbExtension(Fcb);
PDAV_USERMODE_CREATE_RETURNED_FILEINFO CreateReturnedFileInfo = NULL;
PNT_CREATE_PARAMETERS NtCreateParameters = NULL;
HANDLE FileHandle = INVALID_HANDLE_VALUE;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING UnicodeFileName;
PWCHAR NtFileName = NULL;
BOOL isFileCached = FALSE, isVNRInitialized = FALSE, didIAllocateFcbResource = FALSE;
PWEBDAV_V_NET_ROOT DavVNetRoot = NULL;
ULONG Disposition = RxContext->Create.NtCreateParameters.Disposition;
BOOLEAN CacheFound = FALSE;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVCreateContinuation!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVCreateContinuation: "
"AsyncEngineContext: %08lx, RxContext: %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: Attempt to open: %wZ\n",
PsGetCurrentThreadId(),
GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb)));
NtCreateParameters = &(RxContext->Create.NtCreateParameters);
if (MRxDAVIsFileNotFoundCached(RxContext)) {
DavContext->AsyncEngineContext.FileNotExists = TRUE;
if ( !( (Disposition==FILE_CREATE) || (Disposition==FILE_OPEN_IF) ||
(Disposition==FILE_OVERWRITE_IF) || (Disposition==FILE_SUPERSEDE) ) ) {
//
// If file does not exist on the server and we're not going to
// create it, no further operation is necessary.
//
DavDbgTrace(DAV_TRACE_DETAIL,
("MRxDAVCreateContinuation file not found %wZ\n",GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext)));
NtStatus = STATUS_OBJECT_NAME_NOT_FOUND;
goto EXIT_THE_FUNCTION;
}
}
DavVNetRoot = (PWEBDAV_V_NET_ROOT)RxContext->pRelevantSrvOpen->pVNetRoot->Context;
isFileCached = DavFcb->isFileCached;
//
// We need to initialize the resource that is used to synchronize the
// "read-modify-write" sequence if its not been done already.
//
if (DavFcb->DavReadModifyWriteLock == NULL) {
//
// Allocate memory for the resource.
//
DavFcb->DavReadModifyWriteLock = RxAllocatePoolWithTag(NonPagedPool,
sizeof(ERESOURCE),
DAV_READWRITE_POOLTAG);
if (DavFcb->DavReadModifyWriteLock == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation/RxAllocatePoolWithTag\n",
PsGetCurrentThreadId()));
goto EXIT_THE_FUNCTION;
}
didIAllocateFcbResource = TRUE;
//
// Now that we have allocated memory, we need to initialize it.
//
ExInitializeResourceLite(DavFcb->DavReadModifyWriteLock);
}
//
// If we have a V_NET_ROOT whose LogonID has not been initialized, we need
// to go to the user mode to create an entry for the user, in case this
// new V_NET_ROOT has different user credentials than the one that opened
// this file. We need to do this even if the file is cached since the user
// that opened the file could be different from the current user. Its
// possible for multiple V_NET_ROOTS to be associate with the same FCB since
// FCB is associated with a NET_ROOT.
//
isVNRInitialized = DavVNetRoot->LogonIDSet;
//
// Since we set the LogonId during the creation of the V_NET_ROOT, this
// should always be TRUE.
//
ASSERT(isVNRInitialized == TRUE);
//
// We can look at the FCB and figure out if this file was already opened
// and cached in the WinInet cache. If it was, then we already have the
// local name of the cached file in the FCB. All we need to do is open
// a handle to the file with the create options specified by the caller.
//
if ( !isFileCached || !isVNRInitialized ) {
if ((NtCreateParameters->Disposition == FILE_CREATE) &&
(NtCreateParameters->FileAttributes & FILE_ATTRIBUTE_SYSTEM) &&
(NtCreateParameters->FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
//
// Remove the Encryption flag if creating a SYSTEM file.
//
NtCreateParameters->FileAttributes &= ~FILE_ATTRIBUTE_ENCRYPTED;
}
CreateReturnedFileInfo = RxAllocatePoolWithTag(PagedPool,
sizeof(DAV_USERMODE_CREATE_RETURNED_FILEINFO),
DAV_FILEINFO_POOLTAG);
if (CreateReturnedFileInfo == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation/RxAllocatePool: Error Val"
" = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(CreateReturnedFileInfo, sizeof(DAV_USERMODE_CREATE_RETURNED_FILEINFO));
DavContext->CreateReturnedFileInfo = CreateReturnedFileInfo;
CacheFound = MRxDAVIsFileInfoCacheFound(RxContext,
CreateReturnedFileInfo,
&(NtStatus),
NULL);
if (CacheFound) {
//
// If it exists in the cache, we perform a few checks before
// succeeding the create.
//
//
// If the FileAttributes had the READ_ONLY bit set, then these
// cannot be TRUE.
// 1. CreateDisposition cannot be FILE_OVERWRITE_IF or
// FILE_OVERWRITE or FILE_SUPERSEDE.
// 2. CreateDisposition cannot be FILE_DELETE_ON_CLOSE.
// 3. DesiredAccess cannot be GENERIC_ALL or GENERIC_WRITE or
// FILE_WRITE_DATA or FILE_APPEND_DATA.
// This is because these intend to overwrite the existing file.
//
if ( (CreateReturnedFileInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) &&
( (NtCreateParameters->Disposition == FILE_OVERWRITE) ||
(NtCreateParameters->Disposition == FILE_OVERWRITE_IF) ||
(NtCreateParameters->Disposition == FILE_SUPERSEDE) ||
(NtCreateParameters->CreateOptions & (FILE_DELETE_ON_CLOSE) ||
(NtCreateParameters->DesiredAccess & (GENERIC_ALL | GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) ) ) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation: ReadOnly & ObjectMismatch\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_ACCESS_DENIED;
goto EXIT_THE_FUNCTION;
}
//
// We return failure if FILE_CREATE was specified since the file
// already exists.
//
if (NtCreateParameters->Disposition == FILE_CREATE) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation: FILE_CREATE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_OBJECT_NAME_COLLISION;
goto EXIT_THE_FUNCTION;
}
//
// If the file is a directory and the caller supplied
// FILE_NON_DIRECTORY_FILE as one of the CreateOptions or if the
// file as a file and the CreateOptions has FILE_DIRECTORY_FILE
// then we return STATUS_ACCESS_DENIED.
//
if ( (NtCreateParameters->CreateOptions & FILE_DIRECTORY_FILE) &&
!(CreateReturnedFileInfo->StandardInformation.Directory) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation: File & FILE_DIRECTORY_FILE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_NOT_A_DIRECTORY;
goto EXIT_THE_FUNCTION;
}
if ( (NtCreateParameters->CreateOptions & FILE_NON_DIRECTORY_FILE) &&
(CreateReturnedFileInfo->StandardInformation.Directory) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation: Directory & FILE_NON_DIRECTORY_FILE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_FILE_IS_A_DIRECTORY;
goto EXIT_THE_FUNCTION;
}
//
// If the delete is for a directory and the path is of the form
// \\server\share then we return STATUS_ACCESS_DENIED. This is
// because we do not allow a client to delete a share on the server.
// If the path is of the form \\server\share then the value of
// SrvOpen->pAlreadyPrefixedName->Length is 0.
//
if ( (CreateReturnedFileInfo->StandardInformation.Directory) &&
(SrvOpen->pAlreadyPrefixedName->Length == 0) &&
( (NtCreateParameters->CreateOptions & FILE_DELETE_ON_CLOSE) ||
(NtCreateParameters->DesiredAccess & DELETE) ) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation: ServerShareDelete\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_ACCESS_DENIED;
goto EXIT_THE_FUNCTION;
}
if ((NtCreateParameters->DesiredAccess & DELETE ||
NtCreateParameters->CreateOptions & FILE_DELETE_ON_CLOSE) &&
CreateReturnedFileInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//
// If it is a open for directory deletion, we want to make sure
// no files are under the directory before return success.
//
CacheFound = FALSE;
} else {
DavContext->AsyncEngineContext.FileInformationCached = TRUE;
}
}
//
// We short circuit the open in kernel under the following conditions.
// 1. If the file is in the name cache and open is for a directory.
// 2. The file is NOT encrypted. This is because when we short circuit
// the open, we do not create a local file and hence no local file
// handle and no underlyingdeviceobject. This is needed by some of
// the FSCTLs that are issued against the EFS files. Hence we skip
// short circuiting them.
// 3. A file with desire access of delete or read attributes.
//
if (CacheFound &&
((CreateReturnedFileInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
(NtCreateParameters->Disposition == FILE_OPEN) &&
!(NtCreateParameters->DesiredAccess & ~(SYNCHRONIZE | DELETE | FILE_READ_ATTRIBUTES)) &&
!(CreateReturnedFileInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED))) {
if (CreateReturnedFileInfo->BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
DavFcb->isDirectory = TRUE;
}
//
// If this Create was with FILE_DELETE_ON_CLOSE, we need to set this
// information in the FCB since we'll need to DELETE this file on
// Close.
//
if (NtCreateParameters->CreateOptions & FILE_DELETE_ON_CLOSE) {
DavFcb->DeleteOnClose = TRUE;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: Pseudo Open: %wZ\n",
PsGetCurrentThreadId(),
GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb)));
} else {
UNICODE_STRING ParentDirName;
SHORT i;
FILE_BASIC_INFORMATION BasicInformation;
PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME_FROM_CONTEXT(RxContext);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: Usermode Open: %wZ\n",
PsGetCurrentThreadId(),
GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb)));
if (FileName->Length > 0) {
//
// Try to get the parent directory information from cache so that we don't
// have to query the server.
//
for ( i = ( (FileName->Length / sizeof(WCHAR)) - 1 ); i >= 0; i-- ) {
if (FileName->Buffer[i] == L'\\') {
break;
}
}
//
// Only if we found a wack will i be > 0. If we did not find
// a wack (==> i == -1), it means that the parent directory was
// not specified in the path. Hence we do not perform the check
// below.
//
if (i > 0) {
ParentDirName.Length = (i * sizeof(WCHAR));
ParentDirName.MaximumLength = (i * sizeof(WCHAR));
ParentDirName.Buffer = FileName->Buffer;
if (MRxDAVIsBasicFileInfoCacheFound(RxContext,&BasicInformation,&NtStatus,&ParentDirName)) {
DavContext->AsyncEngineContext.ParentDirInfomationCached = TRUE;
DavContext->AsyncEngineContext.ParentDirIsEncrypted = BooleanFlagOn(BasicInformation.FileAttributes,FILE_ATTRIBUTE_ENCRYPTED);
DavDbgTrace(DAV_TRACE_INFOCACHE,
("MRxDAVCreateContinuation parent dir found %d %wZ\n",
DavContext->AsyncEngineContext.ParentDirIsEncrypted,
&ParentDirName));
} else {
NtStatus = MRxDAVGetFullParentDirectoryPath(RxContext, &ParentDirName);
if (NtStatus != STATUS_SUCCESS) {
goto EXIT_THE_FUNCTION;
}
if (ParentDirName.Buffer != NULL) {
NtStatus = MRxDAVQueryEncryptedDirectoryKey(&ParentDirName);
if (NtStatus == STATUS_SUCCESS) {
DavContext->AsyncEngineContext.ParentDirInfomationCached = TRUE;
DavContext->AsyncEngineContext.ParentDirIsEncrypted = TRUE;
}
}
}
}
}
//
// If the file is not in the name cache we have to send the request to the webclient
//
NtStatus = UMRxSubmitAsyncEngUserModeRequest(
UMRX_ASYNCENGINE_ARGUMENTS,
MRxDAVFormatUserModeCreateRequest,
MRxDAVPrecompleteUserModeCreateRequest
);
ASSERT(NtStatus != STATUS_PENDING);
switch (NtStatus) {
case STATUS_SUCCESS:
MRxDAVInvalidateFileNotFoundCache(RxContext);
MRxDAVCreateFileInfoCache(RxContext,CreateReturnedFileInfo,STATUS_SUCCESS);
break;
case STATUS_OBJECT_NAME_NOT_FOUND:
MRxDAVCacheFileNotFound(RxContext);
MRxDAVInvalidateFileInfoCache(RxContext);
break;
default:
//
// Invalid the name based file not found cache if other error
// happens.
//
MRxDAVInvalidateFileInfoCache(RxContext);
MRxDAVInvalidateFileNotFoundCache(RxContext);
}
}
} else {
ULONG SizeInBytes;
ACCESS_MASK DesiredAccess = 0;
//
// If the FileAttributes had the READ_ONLY bit set, then these
// cannot be TRUE.
// 1. CreateDisposition cannot be FILE_OVERWRITE_IF or
// FILE_OVERWRITE or FILE_SUPERSEDE.
// 2. CreateDisposition cannot be FILE_DELETE_ON_CLOSE.
// 3. DesiredAccess cannot be GENERIC_ALL or GENERIC_WRITE or
// FILE_WRITE_DATA or FILE_APPEND_DATA.
// This is because these intend to overwrite the existing file.
//
if ( (Fcb->Attributes & FILE_ATTRIBUTE_READONLY) &&
( (NtCreateParameters->Disposition == FILE_OVERWRITE) ||
(NtCreateParameters->Disposition == FILE_OVERWRITE_IF) ||
(NtCreateParameters->Disposition == FILE_SUPERSEDE) ||
(NtCreateParameters->CreateOptions & (FILE_DELETE_ON_CLOSE) ||
(NtCreateParameters->DesiredAccess & (GENERIC_ALL | GENERIC_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) ) ) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation(1): ReadOnly & ObjectMismatch\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_ACCESS_DENIED;
goto EXIT_THE_FUNCTION;
}
//
// We return failure if FILE_CREATE was specified since the file
// already exists.
//
if (NtCreateParameters->Disposition == FILE_CREATE) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation(1): FILE_CREATE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_OBJECT_NAME_COLLISION;
goto EXIT_THE_FUNCTION;
}
//
// If the file is a directory and the caller supplied
// FILE_NON_DIRECTORY_FILE as one of the CreateOptions or if the
// file as a file and the CreateOptions has FILE_DIRECTORY_FILE
// then we return STATUS_ACCESS_DENIED.
//
if ( (NtCreateParameters->CreateOptions & FILE_DIRECTORY_FILE) &&
!(DavFcb->isDirectory) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation(1): File & FILE_DIRECTORY_FILE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_NOT_A_DIRECTORY;
goto EXIT_THE_FUNCTION;
}
if ( (NtCreateParameters->CreateOptions & FILE_NON_DIRECTORY_FILE) &&
(DavFcb->isDirectory) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation(1): Directory & FILE_NON_DIRECTORY_FILE\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_FILE_IS_A_DIRECTORY;
goto EXIT_THE_FUNCTION;
}
//
// If the delete is for a directory and the path is of the form
// \\server\share then we return STATUS_ACCESS_DENIED. This is
// because we do not allow a client to delete a share on the server.
// If the path is of the form \\server\share then the value of
// SrvOpen->pAlreadyPrefixedName->Length is 0.
//
if ( (DavFcb->isDirectory) &&
(SrvOpen->pAlreadyPrefixedName->Length == 0) &&
( (NtCreateParameters->CreateOptions & FILE_DELETE_ON_CLOSE) ||
(NtCreateParameters->DesiredAccess & DELETE) ) ) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation(1): ServerShareDelete\n",
PsGetCurrentThreadId()));
NtStatus = STATUS_ACCESS_DENIED;
goto EXIT_THE_FUNCTION;
}
//
// We need to do the create only if its a file. If we are talking about
// a directory, then no create is needed.
//
if ( !DavFcb->isDirectory ) {
//
// We have a cached copy of the file which hasn't been closed. All we
// need to do is call ZwCreateFile on it.
//
//
// Create an NT path name for the cached file. This is used in the
// ZwCreateFile call below. If c:\foo\bar is the DOA path name,
// the NT path name is \??\c:\foo\bar.
//
SizeInBytes = ( MAX_PATH + wcslen(L"\\??\\") + 1 ) * sizeof(WCHAR);
NtFileName = RxAllocatePoolWithTag(PagedPool, SizeInBytes, DAV_FILENAME_POOLTAG);
if (NtFileName == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation/RxAllocatePool: Error Val"
" = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(NtFileName, SizeInBytes);
wcscpy( NtFileName, L"\\??\\" );
wcscpy( &(NtFileName[4]), DavFcb->FileName );
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: NtFileName = %ws\n",
PsGetCurrentThreadId(), NtFileName));
RtlInitUnicodeString( &(UnicodeFileName), NtFileName );
//
// IMPROTANT!!!
// We use OBJ_KERNEL_HANDLE below for the following reason. While
// firing up a word file from explorer, I noticed that the create
// below was happening in one process (A) and the close for the handle
// which is stored in the SrvOpen extension came down in another
// process (B). This could happen if the process that is closing the
// handle (B) duplicated the handle created by A. By using OBJ_KERNEL_HANDLE
// the handle can be closed by any process.
//
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeFileName,
(OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE),
0,
NULL);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: DesiredAccess = %08lx,"
" FileAttributes = %08lx, ShareAccess = %d, Disposition"
" = %d, CreateOptions = %08lx\n",
PsGetCurrentThreadId(),
NtCreateParameters->DesiredAccess,
NtCreateParameters->FileAttributes,
NtCreateParameters->ShareAccess,
NtCreateParameters->Disposition,
NtCreateParameters->CreateOptions));
//
// We use FILE_SHARE_VALID_FLAGS for share access because RDBSS
// checks this for us. Moreover, we delay the close after the final
// close happens and this could cause problems. Consider the scenario.
// 1. Open with NO share access.
// 2. We create a local handle with this share access.
// 3. The app closes the handle. We delay the close and keep the local
// handle.
// 4. Another open comes with any share access. This will be
// conflicting share access since the first one was done with no
// share access. This should succeed since the previous open has
// been closed from the app and the I/O systems point of view.
// 5. It will not if we have created the local handle with the share
// access which came with the first open.
// Therefore we need to pass FILE_SHARE_VALID_FLAGS while creating
// the local handle.
//
//
// We have FILE_NO_INTERMEDIATE_BUFFERING ORed with the CreateOptions
// the user specified, becuase we don't want the underlying file
// system to create another cache map. This way all the I/O that comes
// to us will directly go to the disk. BUG 128843 in the Windows RAID
// database explains some deadlock scenarios that could happen with
// PagingIo if we don't do this. Also since we supply the
// FILE_NO_INTERMEDIATE_BUFFERING option we filter out the
// FILE_APPEND_DATA from the DesiredAccess flags since the underlying
// filesystem expects this.
//
//
// We also always create the file with DesiredAccess ORed with
// FILE_WRITE_DATA if either FILE_READ_DATA or FILE_EXECUTE was
// specified because there can be situations where we get write
// IRPs on a FILE_OBJECT which was not opened with Write Access
// and was only opened with FILE_READ_DATA or FILE_EXECUTE. This
// is BUG 284557. To get around the problem, we do this.
//
DesiredAccess = (NtCreateParameters->DesiredAccess & ~(FILE_APPEND_DATA));
if ( DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE) ) {
DesiredAccess |= (FILE_WRITE_DATA);
}
NtStatus = ZwCreateFile(&(FileHandle),
DesiredAccess,
&(ObjectAttributes),
&(IoStatusBlock),
&(NtCreateParameters->AllocationSize),
NtCreateParameters->FileAttributes,
FILE_SHARE_VALID_FLAGS,
NtCreateParameters->Disposition,
(NtCreateParameters->CreateOptions | FILE_NO_INTERMEDIATE_BUFFERING),
RxContext->Create.EaBuffer,
RxContext->Create.EaLength);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVCreateContinuation/ZwCreateFile: "
"Error Val = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: FileHandle = %08lx, "
"Process = %08lx, SrvOpen = %08lx, davSrvOpen = %08lx\n",
PsGetCurrentThreadId(), FileHandle, PsGetCurrentProcess(),
SrvOpen, davSrvOpen));
//
// On the final close we check this to figure out where the close of the
// handle should occur.
//
davSrvOpen->createdInKernel = TRUE;
NtStatus = ObReferenceObjectByHandle(
FileHandle,
0L,
NULL,
KernelMode,
(PVOID *)&(davSrvOpen->UnderlyingFileObject),
NULL
);
if (NtStatus == STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: UFO(2) = %08lx\n",
PsGetCurrentThreadId(), davSrvOpen->UnderlyingFileObject));
davSrvOpen->UnderlyingHandle = FileHandle;
davSrvOpen->UserModeKey = (PVOID)FileHandle;
davSrvOpen->UnderlyingDeviceObject =
IoGetRelatedDeviceObject(davSrvOpen->UnderlyingFileObject);
} else {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVCreateContinuation/"
"ObReferenceObjectByHandle: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
ZwClose(FileHandle);
}
}
}
ASSERT(RxIsFcbAcquiredExclusive(capFcb));
if ( NtStatus == STATUS_SUCCESS && ( !isFileCached || !isVNRInitialized ) ) {
RX_FILE_TYPE StorageType = 0;
PFILE_BASIC_INFORMATION pBasicInformation =
&(CreateReturnedFileInfo->BasicInformation);
PFILE_STANDARD_INFORMATION pStandardInformation =
&(CreateReturnedFileInfo->StandardInformation);
FCB_INIT_PACKET InitPacket;
// StorageType = RxInferFileType(RxContext);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: Storagetype = %08lx\n",
PsGetCurrentThreadId(), StorageType));
//
// If we have never obtained the characteristics, we have to get them.
//
if ((capFcb->OpenCount == 0)
|| !FlagOn(capFcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET)) {
if (StorageType == 0) {
StorageType = pStandardInformation->Directory?
(FileTypeDirectory):(FileTypeFile);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: "
"ChangedStoragetype %08lx\n",
PsGetCurrentThreadId(), StorageType));
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: Name: %wZ, FileType: %d\n",
PsGetCurrentThreadId(),
GET_ALREADY_PREFIXED_NAME(SrvOpen,capFcb), StorageType));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCreateContinuation: FileSize %08lx\n",
PsGetCurrentThreadId(),
pStandardInformation->EndOfFile.LowPart));
RxFinishFcbInitialization(capFcb,
RDBSS_STORAGE_NTC(StorageType),
RxFormInitPacket(
InitPacket,
&pBasicInformation->FileAttributes,
&pStandardInformation->NumberOfLinks,
&pBasicInformation->CreationTime,
&pBasicInformation->LastAccessTime,
&pBasicInformation->LastWriteTime,
&pBasicInformation->ChangeTime,
&pStandardInformation->AllocationSize,
&pStandardInformation->EndOfFile,
&pStandardInformation->EndOfFile));
}
}
if (NtStatus == STATUS_SUCCESS) {
RxContext->pFobx = RxCreateNetFobx(RxContext, SrvOpen);
if ( !RxContext->pFobx ) {
NTSTATUS PostedCloseStatus;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCreateContinuation/RxCreateNetFobx.\n",
PsGetCurrentThreadId()));
if ( !davSrvOpen->createdInKernel ) {
PostedCloseStatus = UMRxSubmitAsyncEngUserModeRequest(
UMRX_ASYNCENGINE_ARGUMENTS,
MRxDAVFormatUserModeCloseRequest,
MRxDAVPrecompleteUserModeCloseRequest
);
} else {
ZwClose(davSrvOpen->UnderlyingHandle);
davSrvOpen->UnderlyingHandle = davSrvOpen->UserModeKey = NULL;
}
ObDereferenceObject(davSrvOpen->UnderlyingFileObject);
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
//
// Note, collapsing is enabled on fcb but not on any srvopen.
//
SrvOpen->BufferingFlags |= (FCB_STATE_WRITECACHING_ENABLED |
FCB_STATE_FILESIZECACHEING_ENABLED |
FCB_STATE_FILETIMECACHEING_ENABLED |
FCB_STATE_WRITEBUFFERING_ENABLED |
FCB_STATE_LOCK_BUFFERING_ENABLED |
FCB_STATE_READBUFFERING_ENABLED |
FCB_STATE_READCACHING_ENABLED);
}
}
EXIT_THE_FUNCTION:
if (CreateReturnedFileInfo != NULL) {
RxFreePool(CreateReturnedFileInfo);
}
if (NtFileName != NULL) {
RxFreePool(NtFileName);
}
//
// If we allocated the FCB resource and the create failed, we need to free
// up the resource.
//
if (NtStatus != STATUS_SUCCESS && didIAllocateFcbResource) {
ASSERT(DavFcb->DavReadModifyWriteLock != NULL);
ExDeleteResourceLite(DavFcb->DavReadModifyWriteLock);
RxFreePool(DavFcb->DavReadModifyWriteLock);
DavFcb->DavReadModifyWriteLock = NULL;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVCreateContinuation with NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
if (NtStatus == STATUS_SUCCESS && (SrvOpen->pAlreadyPrefixedName->Length > 0) ) {
DavAddEntryToGlobalList(SrvOpen->pAlreadyPrefixedName);
}
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
return(NtStatus);
}
NTSTATUS
MRxDAVCollapseOpen(
IN OUT PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine collapses a open locally
Arguments:
RxContext - the RDBSS context
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
RxCaptureFcb;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PMRX_SRV_CALL SrvCall = RxContext->Create.pSrvCall;
PMRX_NET_ROOT NetRoot = capFcb->pNetRoot;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVCollapseOpen!!!!\n",
PsGetCurrentThreadId()));
//
// We should never come here since we never collapse the Open.
//
ASSERT(FALSE);
RxContext->pFobx = (PMRX_FOBX) RxCreateNetFobx(RxContext, SrvOpen);
if (RxContext->pFobx != NULL) {
ASSERT(RxIsFcbAcquiredExclusive(capFcb));
RxContext->pFobx->OffsetOfNextEaToReturn = 1;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
return Status;
}
NTSTATUS
MRxDAVComputeNewBufferingState(
IN OUT PMRX_SRV_OPEN pMRxSrvOpen,
IN PVOID pMRxContext,
OUT PULONG pNewBufferingState
)
/*++
Routine Description:
This routine computes the appropriate RDBSS buffering state flags
Arguments:
pMRxSrvOpen - the MRX SRV_OPEN extension
pMRxContext - the context passed to RDBSS at Oplock indication time
pNewBufferingState - the place holder for the new buffering state
Return Value:
Notes:
--*/
{
ULONG NewBufferingState;
PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(pMRxSrvOpen);
PAGED_CODE();
ASSERT(pNewBufferingState != NULL);
NewBufferingState = 0;
pMRxSrvOpen->BufferingFlags = NewBufferingState;
*pNewBufferingState = pMRxSrvOpen->BufferingFlags;
return STATUS_SUCCESS;
}
NTSTATUS
MRxDAVTruncate(
IN PRX_CONTEXT pRxContext
)
/*++
Routine Description:
This routine truncates the contents of a file system object
Arguments:
pRxContext - the RDBSS context
Return Value:
RXSTATUS - The return status for the operation
--*/
{
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVTruncate.\n", PsGetCurrentThreadId()));
PAGED_CODE();
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
MRxDAVForcedClose(
IN PMRX_SRV_OPEN pSrvOpen
)
/*++
Routine Description:
This routine closes a file system object
Arguments:
pSrvOpen - the instance to be closed
Return Value:
RXSTATUS - The return status for the operation
Notes:
--*/
{
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVForcedClose.\n", PsGetCurrentThreadId()));
PAGED_CODE();
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
MRxDAVCloseSrvOpen(
IN PRX_CONTEXT RxContext
)
/*++
Routine Description:
This routine handles requests to close the srvopen data structure.
Arguments:
RxContext - the RDBSS context
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVCloseSrvOpen!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVCloseSrvOpen: RxContext: %08lx\n",
PsGetCurrentThreadId(), RxContext));
NtStatus = UMRxAsyncEngOuterWrapper(RxContext,
SIZEOF_DAV_SPECIFIC_CONTEXT,
MRxDAVFormatTheDAVContext,
DAV_MINIRDR_ENTRY_FROM_CLOSESRVOPEN,
MRxDAVCloseSrvOpenContinuation,
"MRxDAVCloseSrvOpen");
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVCloseSrvOpen with NtStatus = %08lx\n",
PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
NTSTATUS
MRxDAVFormatUserModeCloseRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
PULONG_PTR ReturnedLength
)
/*++
Routine Description:
This routine formats the arguments of the close request which is being
sent to the user mode for processing.
Arguments:
RxContext - The RDBSS context.
AsyncEngineContext - The reflctor's context.
WorkItem - The work item buffer.
WorkItemLength - The length of the work item buffer.
ReturnedLength -
Return Value:
STATUS_SUCCESS.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PMRX_SRV_CALL SrvCall = NULL;
PWEBDAV_SRV_CALL DavSrvCall = NULL;
PDAV_USERMODE_WORKITEM WorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader;
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen);
PMRX_FCB Fcb = SrvOpen->pFcb;
PWEBDAV_FCB DavFcb = MRxDAVGetFcbExtension(Fcb);
PDAV_USERMODE_CLOSE_REQUEST CloseRequest = &(WorkItem->CloseRequest);
PWCHAR ServerName = NULL, PathName = NULL;
ULONG ServerNameLengthInBytes = 0, PathNameLengthInBytes = 0;
PWEBDAV_V_NET_ROOT DavVNetRoot = NULL;
PSECURITY_CLIENT_CONTEXT SecurityClientContext = NULL;
PMRX_NET_ROOT NetRoot = NULL;
PWCHAR NetRootName = NULL, JustTheNetRootName = NULL;
ULONG NetRootNameLengthInBytes = 0, NetRootNameLengthInWChars = 0;
LONG FileWasModified = 0;
RxCaptureFobx;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVFormatUserModeCloseRequest!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVFormatUserModeCloseRequest: "
"AsyncEngineContext = %08lx, RxContext = %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
IF_DEBUG {
ASSERT (capFobx != NULL);
ASSERT (capFobx->pSrvOpen == RxContext->pRelevantSrvOpen);
}
WorkItem->WorkItemType = UserModeClose;
SrvCall = SrvOpen->pVNetRoot->pNetRoot->pSrvCall;
ASSERT(SrvCall != NULL);
DavSrvCall = MRxDAVGetSrvCallExtension(SrvCall);
ASSERT(DavSrvCall != NULL);
//
// Copy the local file name.
//
wcscpy(CloseRequest->FileName, DavFcb->FileName);
wcscpy(CloseRequest->Url, DavFcb->Url);
//
// See if the underlying local file has been modified.
//
FileWasModified = InterlockedCompareExchange(&(DavFcb->FileWasModified), 0, 0);
//
// If the FileWasModified field in the DavFcb is not equal to zero, we need
// to clear the FileWasModified field in the DavFcb since we are going to
// PUT the data on the server. We also set FileModifiedBitReset in the
// DavFcb structure to TRUE. If the PUT fails in the usermode we reset the
// FileWasModified in the DavFcb to TRUE (in the PreComplete function).
//
if (FileWasModified != 0) {
InterlockedExchange(&(DavFcb->FileWasModified), 0);
DavFcb->FileModifiedBitReset = TRUE;
}
//
// We need to tell the user mode process about the following file
// information.
//
CloseRequest->DeleteOnClose = DavFcb->DeleteOnClose;
CloseRequest->FileWasModified = (BOOL)FileWasModified;
//
// If this file was modified and DeleteOnClose is not set, we need to
// set RaiseHardErrorIfCloseFails to TRUE. On the precomplete close call,
// if the usermode operation failed and RaiseHardErrorIfCloseFails is TRUE,
// we call IoRaiseInformationalHardError to notify the user (the calls pops
// up a box) that the data could have been lost.
//
if ( (FileWasModified != 0) && !(DavFcb->DeleteOnClose) ) {
davSrvOpen->RaiseHardErrorIfCloseFails = TRUE;
} else {
davSrvOpen->RaiseHardErrorIfCloseFails = FALSE;
}
if (!CloseRequest->DeleteOnClose) {
//
// If the file is modified, just propatch again. This is to get around
// the docfile issue where on a PUT, the properties get cleaned up.
//
if (FileWasModified != 0) {
LARGE_INTEGER CurrentTime;
CloseRequest->fCreationTimeChanged = (((PFCB)Fcb)->CreationTime.QuadPart != 0);
CloseRequest->fLastAccessTimeChanged = (((PFCB)Fcb)->LastAccessTime.QuadPart != 0);
CloseRequest->fLastModifiedTimeChanged = (((PFCB)Fcb)->LastWriteTime.QuadPart != 0);
//
// We query the Current system time and make that the LastWrite
// and the LastAccess time of the file. Even though RxCommonCleanup
// modifies these time values, it only modifies them on FileObjects
// which have FO_FILE_MODIFIED set. Consider the case where h1 and
// h2 are two handles created. A write is issued on h2 setting
// FO_FILE_MODIFIED in its FileObject. CloseHandle(h1) leads to the
// file being PUT on the server since DavFcb->FileWasModified is
// TRUE (write on h2 caused this). But since the FileObject of
// h1 doesn't have FO_FILE_MODIFIED set, in the RxCommonCleanup
// code the LastWrite and LastAccess time values of this FCB are
// not modifed causing us to PROPPATCH the old values on the server.
// To avoid this we query the current time value and set it
// ourselves in the FCB. If "DavFcb->DoNotTakeTheCurrentTimeAsLMT"
// is TRUE then we don't do this since the application explicitly
// set the LastModifiedTime after all the changes were done.
//
if (DavFcb->DoNotTakeTheCurrentTimeAsLMT == FALSE) {
KeQuerySystemTime( &(CurrentTime) );
((PFCB)Fcb)->LastAccessTime.QuadPart = CurrentTime.QuadPart;
((PFCB)Fcb)->LastWriteTime.QuadPart = CurrentTime.QuadPart;
}
//
// If FILE_ATTRIBUTE_NORMAL was the only attribute set on the file
// and the file was modified, then we should replace this with the
// FILE_ATTRIBUTE_ARCHIVE attribute.
//
if ( ((PFCB)Fcb)->Attributes == FILE_ATTRIBUTE_NORMAL ) {
DavDbgTrace(DAV_TRACE_DETAIL,
("ld: ERROR: MRxDAVFormatUserModeCloseRequest: "
"FILE_ATTRIBUTE_NORMAL ===> FILE_ATTRIBUTE_ARCHIVE\n",
PsGetCurrentThreadId()));
((PFCB)Fcb)->Attributes = FILE_ATTRIBUTE_ARCHIVE;
DavFcb->fFileAttributesChanged = TRUE;
}
if ((((PFCB)Fcb)->Attributes != 0) || DavFcb->fFileAttributesChanged) {
CloseRequest->fFileAttributesChanged = TRUE;
}
} else {
//
// If any of the following times have changed, then we need to PROPPATCH
// them to the server.
//
CloseRequest->fCreationTimeChanged = DavFcb->fCreationTimeChanged;
CloseRequest->fLastAccessTimeChanged = DavFcb->fLastAccessTimeChanged;
CloseRequest->fLastModifiedTimeChanged = DavFcb->fLastModifiedTimeChanged;
CloseRequest->fFileAttributesChanged = DavFcb->fFileAttributesChanged;
}
}
//
// Copy the various time values.
//
CloseRequest->CreationTime = ((PFCB)Fcb)->CreationTime;
CloseRequest->LastAccessTime = ((PFCB)Fcb)->LastAccessTime;
CloseRequest->LastModifiedTime = ((PFCB)Fcb)->LastWriteTime;
CloseRequest->dwFileAttributes = ((PFCB)Fcb)->Attributes;
CloseRequest->FileSize = Fcb->Header.FileSize.LowPart;
//
// Copy the ServerName.
//
ServerNameLengthInBytes = ( SrvCall->pSrvCallName->Length + sizeof(WCHAR) );
ServerName = (PWCHAR) UMRxAllocateSecondaryBuffer(AsyncEngineContext,
ServerNameLengthInBytes);
if (ServerName == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVFormatUserModeCloseRequest/"
"UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlCopyBytes(ServerName,
SrvCall->pSrvCallName->Buffer,
SrvCall->pSrvCallName->Length);
ServerName[( ( (ServerNameLengthInBytes) / sizeof(WCHAR) ) - 1 )] = L'\0';
CloseRequest->ServerName = ServerName;
//
// Copy the ServerID.
//
CloseRequest->ServerID = DavSrvCall->ServerID;
NetRoot = SrvOpen->pFcb->pNetRoot;
//
// The NetRootName (pNetRootName) includes the ServerName. Hence to get the
// NetRootNameLengthInBytes, we do the following.
//
NetRootNameLengthInBytes = (NetRoot->pNetRootName->Length - NetRoot->pSrvCall->pSrvCallName->Length);
NetRootNameLengthInWChars = ( NetRootNameLengthInBytes / sizeof(WCHAR) );
NetRootName = &(NetRoot->pNetRootName->Buffer[1]);
JustTheNetRootName = wcschr(NetRootName, L'\\');
//
// Copy the PathName of the Directory. If the file was renamed, we need to
// copy the new path name which is stored in the DavFcb and not the
// AlreadyPrefixedName of the SrvOpen.
//
if (DavFcb->FileWasRenamed) {
PathNameLengthInBytes = ( NetRootNameLengthInBytes +
DavFcb->NewFileNameLength +
sizeof(WCHAR) );
} else {
PathNameLengthInBytes = ( NetRootNameLengthInBytes +
SrvOpen->pAlreadyPrefixedName->Length +
sizeof(WCHAR) );
}
PathName = (PWCHAR) UMRxAllocateSecondaryBuffer(AsyncEngineContext,
PathNameLengthInBytes);
if (PathName == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVFormatUserModeCloseRequest/"
"UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(PathName, PathNameLengthInBytes);
RtlCopyBytes(PathName, JustTheNetRootName, NetRootNameLengthInBytes);
if (DavFcb->FileWasRenamed) {
RtlCopyBytes((PathName + NetRootNameLengthInWChars),
DavFcb->NewFileName,
DavFcb->NewFileNameLength);
DavDbgTrace(DAV_TRACE_DETAIL,
("ld: MRxDAVFormatUserModeCloseRequest. ReNamed!!! NewFileName = %ws\n",
PsGetCurrentThreadId(), PathName));
} else {
RtlCopyBytes((PathName + NetRootNameLengthInWChars),
SrvOpen->pAlreadyPrefixedName->Buffer,
SrvOpen->pAlreadyPrefixedName->Length);
}
PathName[( ( (PathNameLengthInBytes) / sizeof(WCHAR) ) - 1 )] = L'\0';
CloseRequest->PathName = PathName;
DavDbgTrace(DAV_TRACE_DETAIL,
("ld: MRxDAVFormatUserModeCloseRequest. PathName = %ws\n",
PsGetCurrentThreadId(), PathName));
//
// Set the LogonID stored in the Dav V_NET_ROOT. This value is used in the
// user mode.
//
DavVNetRoot = (PWEBDAV_V_NET_ROOT)SrvOpen->pVNetRoot->Context;
ASSERT(DavVNetRoot != NULL);
CloseRequest->LogonID.LowPart = DavVNetRoot->LogonID.LowPart;
CloseRequest->LogonID.HighPart = DavVNetRoot->LogonID.HighPart;
if ( !DavFcb->isDirectory ) {
CloseRequest->isDirectory = FALSE;
if ( !davSrvOpen->createdInKernel ) {
CloseRequest->Handle = davSrvOpen->UnderlyingHandle;
CloseRequest->UserModeKey = davSrvOpen->UserModeKey;
} else {
CloseRequest->Handle = NULL;
CloseRequest->UserModeKey = NULL;
CloseRequest->createdInKernel = davSrvOpen->createdInKernel; // TRUE
}
} else {
CloseRequest->isDirectory = TRUE;
}
//
// If an OpaqueLockToken is associated with this SrvOpen (which means that
// the file was LOCKed on the server) then we need to add this token to
// the requests (PUT etc.) we send to the server.
//
if (davSrvOpen->OpaqueLockToken != NULL) {
ULONG LockTokenLengthInBytes = 0;
ASSERT(davSrvOpen->LockTokenEntry != NULL);
LockTokenLengthInBytes = (1 + wcslen(davSrvOpen->OpaqueLockToken)) * sizeof(WCHAR);
CloseRequest->OpaqueLockToken = (PWCHAR) UMRxAllocateSecondaryBuffer(AsyncEngineContext,
LockTokenLengthInBytes);
if (CloseRequest->OpaqueLockToken == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("ld: ERROR: MRxDAVFormatUserModeCloseRequest/"
"UMRxAllocateSecondaryBuffer. NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(CloseRequest->OpaqueLockToken, LockTokenLengthInBytes);
RtlCopyBytes(CloseRequest->OpaqueLockToken,
davSrvOpen->OpaqueLockToken,
(wcslen(davSrvOpen->OpaqueLockToken) * sizeof(WCHAR)));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVFormatUserModeCloseRequest: CloseRequest->OpaqueLockToken = %ws\n",
PsGetCurrentThreadId(), CloseRequest->OpaqueLockToken));
}
SecurityClientContext = &(DavVNetRoot->SecurityClientContext);
//
// Impersonate the client who initiated the request. If we fail to
// impersonate, tough luck.
//
if (SecurityClientContext != NULL) {
NtStatus = UMRxImpersonateClient(SecurityClientContext, WorkItemHeader);
if (!NT_SUCCESS(NtStatus)) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFormatUserModeCloseRequest/"
"UMRxImpersonateClient. NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
}
} else {
NtStatus = STATUS_INVALID_PARAMETER;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVFormatUserModeCloseRequest: "
"SecurityClientContext is NULL.\n",
PsGetCurrentThreadId()));
}
EXIT_THE_FUNCTION:
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
//
// If we created a LocalFileHandle, we need to close it now.
//
if (LocalFileHandle != INVALID_HANDLE_VALUE) {
ZwClose(LocalFileHandle);
}
//
// If we allocated an NtFileName to do the create, we need to free it now.
//
if (NtFileName) {
RxFreePool(NtFileName);
}
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVFormatUserModeCloseRequest with NtStatus"
" = %08lx.\n", PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
BOOL
MRxDAVPrecompleteUserModeCloseRequest(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE,
PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader,
ULONG WorkItemLength,
BOOL OperationCancelled
)
/*++
Routine Description:
The precompletion routine for the CloseSrvOpen request.
Arguments:
RxContext - The RDBSS context.
AsyncEngineContext - The reflctor's context.
WorkItem - The work item buffer.
WorkItemLength - The length of the work item buffer.
OperationCancelled - TRUE if this operation was cancelled by the user.
Return Value:
TRUE - UMRxAsyncEngineCalldownIrpCompletion is called by the function
UMRxCompleteUserModeRequest after we return.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
PDAV_USERMODE_WORKITEM WorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader;
PDAV_USERMODE_CLOSE_REQUEST CloseRequest = &(WorkItem->CloseRequest);
PMRX_SRV_OPEN SrvOpen = NULL;
PWEBDAV_SRV_OPEN davSrvOpen = NULL;
PMRX_FCB Fcb = NULL;
PWEBDAV_FCB DavFcb = NULL;
PAGED_CODE();
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVPrecompleteUserModeCloseRequest\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVPrecompleteUserModeCloseRequest: "
"AsyncEngineContext = %08lx, RxContext = %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
//
// A CloseSrvOpen request can never by Async.
//
ASSERT(AsyncEngineContext->AsyncOperation == FALSE);
//
// If this operation was cancelled, then we don't need to do anything
// special in the CloseSrvOpen case.
//
if (OperationCancelled) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVPrecompleteUserModeCloseRequest: Operation Cancelled. "
"AsyncEngineContext = %08lx, RxContext = %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
} else {
SrvOpen = RxContext->pRelevantSrvOpen;
davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen);
Fcb = SrvOpen->pFcb;
DavFcb = MRxDAVGetFcbExtension(Fcb);
}
if (AsyncEngineContext->Status != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVPrecompleteUserModeCloseRequest "
"Close failed for file \"%ws\"\n",
PsGetCurrentThreadId(), CloseRequest->PathName));
if (!OperationCancelled) {
//
// If we failed and had reset FileWasModified to 0 in the Format
// function, then we need to reset it to 1.
//
if (DavFcb->FileModifiedBitReset) {
InterlockedExchange(&(DavFcb->FileWasModified), 1);
DavFcb->FileModifiedBitReset = FALSE;
}
}
} else {
if (!OperationCancelled) {
//
// If we were successful and FileModifiedBitReset is TRUE then we
// reset it to FALSE now.
//
if (DavFcb->FileModifiedBitReset) {
DavFcb->FileModifiedBitReset = FALSE;
}
}
}
//
// We need to free up the heaps, we allocated in the format routine.
//
if (CloseRequest->ServerName != NULL) {
NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext,
(PBYTE)CloseRequest->ServerName);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCloseRequest/"
"UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
}
if (CloseRequest->PathName != NULL) {
NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext,
(PBYTE)CloseRequest->PathName);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCloseRequest/"
"UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
}
}
if (CloseRequest->OpaqueLockToken != NULL) {
NtStatus = UMRxFreeSecondaryBuffer(AsyncEngineContext,
(PBYTE)CloseRequest->OpaqueLockToken);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVPrecompleteUserModeCloseRequest/"
"UMRxFreeSecondaryBuffer: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
}
}
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVPrecompleteUserModeCloseRequest with "
"NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus));
return TRUE;
}
NTSTATUS
MRxDAVCloseSrvOpenContinuation(
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
)
/*++
Routine Description:
This routine closes a file across the network.
Arguments:
AsyncEngineContext - The Reflectors context.
RxContext - The RDBSS context.
Return Value:
RXSTATUS - The return status for the operation
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
RxCaptureFcb;
RxCaptureFobx;
NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
PUNICODE_STRING RemainingName = GET_ALREADY_PREFIXED_NAME(SrvOpen, capFcb);
PWEBDAV_SRV_OPEN davSrvOpen = MRxDAVGetSrvOpenExtension(SrvOpen);
PMRX_FCB Fcb = SrvOpen->pFcb;
PWEBDAV_FCB DavFcb = MRxDAVGetFcbExtension(Fcb);
BOOL WentToTheUserMode = FALSE;
ULONG FileWasModified = 0;
PAGED_CODE();
//
// Assert that the FCB has been exclusively acquired.
//
ASSERT( RxIsFcbAcquiredExclusive(Fcb) == TRUE );
if (RxIsFcbAcquiredExclusive(Fcb) != TRUE) {
DbgPrint("MRxDAVCloseSrvOpenContinuation: FCB NOT Exclusive\n");
DbgBreakPoint();
}
IF_DEBUG {
ASSERT (capFobx != NULL);
ASSERT (capFobx->pSrvOpen == RxContext->pRelevantSrvOpen);
}
ASSERT(NodeTypeIsFcb(capFcb));
ASSERT(SrvOpen->OpenCount == 0);
ASSERT(NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN);
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Entering MRxDAVCloseSrvOpenContinuation!!!!\n",
PsGetCurrentThreadId()));
DavDbgTrace(DAV_TRACE_CONTEXT,
("%ld: MRxDAVCloseSrvOpenContinuation: "
"AsyncEngineContext: %08lx, RxContext: %08lx.\n",
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCloseSrvOpenContinuation: Attempt to Close: %wZ\n",
PsGetCurrentThreadId(), RemainingName));
//
// If this SrvOpen has an OpaqueLockToken associated with it, we don't need
// to refresh it anymore since we are going to finalize this SrvOpen.
//
if (davSrvOpen->OpaqueLockToken != NULL) {
ASSERT(davSrvOpen->LockTokenEntry != NULL);
ExAcquireResourceExclusiveLite(&(LockTokenEntryListLock), TRUE);
davSrvOpen->LockTokenEntry->ShouldThisEntryBeRefreshed = FALSE;
ExReleaseResourceLite(&(LockTokenEntryListLock));
}
FileWasModified = InterlockedCompareExchange(&(DavFcb->FileWasModified), 0, 0);
//
// We go to the usermode if one of the following is TRUE.
// 1. The File was not LOCked on the server on Create, OR,
// 2. The File was LOCked on create and this SrvOpen has the LockToken
// associated with the LOCK taken on the server.
// We do not want to go to the server if the file is LOCKed and the SrvOpen
// doesn't contain the LockToken since all the requests to modify the file
// are going to fail (423 - File Is Locked) in such a scenario.
//
if ( (DavFcb->FileIsLockedOnTheServer == FALSE) ||
(DavFcb->FileIsLockedOnTheServer == TRUE && davSrvOpen->OpaqueLockToken != NULL) ) {
WentToTheUserMode = TRUE;
NtStatus = UMRxSubmitAsyncEngUserModeRequest(
UMRX_ASYNCENGINE_ARGUMENTS,
MRxDAVFormatUserModeCloseRequest,
MRxDAVPrecompleteUserModeCloseRequest
);
if (NtStatus != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVCloseSrvOpenContinuation/"
"UMRxSubmitAsyncEngUserModeRequest: NtStatus = %08lx.\n",
PsGetCurrentThreadId(), NtStatus));
}
}
if (DavFcb->isDirectory == FALSE) {
//
// If this handle got created in the kernel, we need to close it now.
//
if (davSrvOpen->createdInKernel) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCloseSrvOpenContinuation: FileHandle = %08lx,"
" Process = %08lx, SrvOpen = %08lx, davSrvOpen = %08lx\n",
PsGetCurrentThreadId(), davSrvOpen->UnderlyingHandle,
PsGetCurrentProcess(), SrvOpen, davSrvOpen));
ZwClose(davSrvOpen->UnderlyingHandle);
davSrvOpen->UnderlyingHandle = NULL;
davSrvOpen->UserModeKey = NULL;
}
//
// Remove our reference which we would have taken on the FileObject
// when the Create succeeded.
//
if (davSrvOpen->UnderlyingFileObject) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: MRxDAVCloseSrvOpenContinuation: Attempt to close"
" %wZ.\n", PsGetCurrentThreadId(), RemainingName));
ObDereferenceObject(davSrvOpen->UnderlyingFileObject);
davSrvOpen->UnderlyingFileObject = NULL;
}
}
if (WentToTheUserMode) {
if (DavFcb->DeleteOnClose) {
MRxDAVInvalidateFileInfoCache(RxContext);
MRxDAVCacheFileNotFound(RxContext);
if ((capFcb->Attributes & FILE_ATTRIBUTE_DIRECTORY) &&
(capFcb->Attributes & FILE_ATTRIBUTE_ENCRYPTED)) {
//
// Remove the directory from the registry since it has been deleted.
//
MRxDAVRemoveEncryptedDirectoryKey(&DavFcb->FileNameInfo);
}
}
if (FileWasModified != 0) {
//
// We cannot predict the size of the file on the server.
//
MRxDAVInvalidateFileInfoCache(RxContext);
}
NtStatus = AsyncEngineContext->Status;
}
//
// If we succeeded in the usermode, we need to reset some values in the
// DavFcb so that we don't do this again.
//
if (NtStatus == STATUS_SUCCESS && WentToTheUserMode) {
DavFcb->fCreationTimeChanged = FALSE;
DavFcb->fFileAttributesChanged = FALSE;
DavFcb->fLastAccessTimeChanged = FALSE;
DavFcb->fLastModifiedTimeChanged = FALSE;
}
//
// If we have a non-NULL OpaqueLockToken then we need to free it now. Also,
// we need to remove the LockTokenEntry associated with this token from
// the global list and free it as well.
//
if (davSrvOpen->OpaqueLockToken != NULL) {
ASSERT(WentToTheUserMode == TRUE);
ASSERT(davSrvOpen->LockTokenEntry != NULL);
//
// Remove the LockTokenEntry associated with this OpaqueLockToken from
// the global LockTokenEntryList.
//
ExAcquireResourceExclusiveLite(&(LockTokenEntryListLock), TRUE);
RemoveEntryList( &(davSrvOpen->LockTokenEntry->listEntry) );
ExReleaseResourceLite(&(LockTokenEntryListLock));
//
// Free the PagedPool that was allocated for the ServerName.
//
RxFreePool(davSrvOpen->LockTokenEntry->ServerName);
davSrvOpen->LockTokenEntry->ServerName = NULL;
//
// Free the PagedPool that was allocated for the PathName.
//
RxFreePool(davSrvOpen->LockTokenEntry->PathName);
davSrvOpen->LockTokenEntry->PathName = NULL;
//
// Free the PagedPool that was allocated for this LockTokenEntry.
//
RxFreePool(davSrvOpen->LockTokenEntry);
davSrvOpen->LockTokenEntry = NULL;
//
// Free the PagedPool that was allocated for this OpaqueLockToken.
//
RxFreePool(davSrvOpen->OpaqueLockToken);
davSrvOpen->OpaqueLockToken = NULL;
//
// The file has now been UnLocked on the server.
//
DavFcb->FileIsLockedOnTheServer = FALSE;
}
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: Leaving MRxDAVCloseSrvOpenContinuation with NtStatus = "
"%08lx\n", PsGetCurrentThreadId(), NtStatus));
return(NtStatus);
}
NTSTATUS
MRxDAVGetFullParentDirectoryPath(
PRX_CONTEXT RxContext,
PUNICODE_STRING ParentDirName
)
/*++
Routine Description:
This routine returns the parent directory name of the file on the RxContext including
server and share.
Here is an example of the FileName on a file object:
\;Y:000000000000cdef\www.msnusers.com\dv1@usa.com\files\mydoc.doc
We want to return the middle part of the FileName:
\www.msnusers.com\dv1@usa.com\files
Arguments:
RxContext - The RDBSS context.
ParentDirName - The full path name of the parent directory starting from the server.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
USHORT i, j;
NTSTATUS NtStatus = STATUS_SUCCESS;
PUNICODE_STRING FileName = &RxContext->CurrentIrpSp->FileObject->FileName;
ParentDirName->Buffer = NULL;
for (i = 1; i < (FileName->Length / sizeof(WCHAR)); i++) {
if (FileName->Buffer[i] == L'\\') {
break;
}
}
if ( i < (FileName->Length / sizeof(WCHAR)) ) {
for (j = ( (FileName->Length / sizeof(WCHAR)) - 1 ); j > i; j--) {
if (FileName->Buffer[j] == L'\\') {
break;
}
}
if (i < j) {
ParentDirName->Buffer = &FileName->Buffer[i];
ParentDirName->Length = ParentDirName->MaximumLength = (j - i) * sizeof(WCHAR);
}
}
DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVGetFullParentDirectoryPath: ParentDirName: %wZ\n", ParentDirName));
return NtStatus;
}
NTSTATUS
MRxDAVGetFullDirectoryPath(
PRX_CONTEXT RxContext,
PUNICODE_STRING FileName,
PUNICODE_STRING DirName
)
/*++
Routine Description:
This routine returns the full directory name including the server and share.
Arguments:
RxContext - The RDBSS context.
FileName - If provided, it will be included in the returned path.
If not provided, the file name on the file object will be returned.
DirName - The full path name of the parent directory starting from the server.
Return Value:
NTSTATUS - The return status for the operation
Note:
If FileName is provided, the caller should free up the the UNICODE buffer.
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
DirName->Buffer = NULL;
DirName->Length = DirName->MaximumLength = 0;
if (FileName == NULL) {
USHORT i;
FileName = &RxContext->CurrentIrpSp->FileObject->FileName;
for (i = 1; i < (FileName->Length / sizeof(WCHAR)); i++) {
if (FileName->Buffer[i] == L'\\') {
break;
}
}
if ( i < (FileName->Length / sizeof(WCHAR)) ) {
DirName->Buffer = &FileName->Buffer[i];
DirName->Length = DirName->MaximumLength = FileName->Length - i*sizeof(WCHAR);
}
} else {
RxCaptureFcb;
USHORT NameLength = 0;
if (FileName->Length == 0) {
goto EXIT_THE_FUNCTION;
}
NameLength = capFcb->pNetRoot->pNetRootName->Length + FileName->Length;
DirName->Length = DirName->MaximumLength = NameLength;
DirName->Buffer = RxAllocatePoolWithTag(PagedPool,
NameLength + sizeof(WCHAR),
DAV_FILEINFO_POOLTAG);
if (DirName->Buffer == NULL) {
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: MRxDAVGetParentDirectory/RxAllocatePool: Error Val"
" = %08lx\n", PsGetCurrentThreadId(), NtStatus));
goto EXIT_THE_FUNCTION;
}
RtlZeroMemory(DirName->Buffer,NameLength + sizeof(WCHAR));
RtlCopyMemory(DirName->Buffer,
capFcb->pNetRoot->pNetRootName->Buffer,
capFcb->pNetRoot->pNetRootName->Length);
RtlCopyMemory(&DirName->Buffer[capFcb->pNetRoot->pNetRootName->Length/sizeof(WCHAR)],
FileName->Buffer,
FileName->Length);
}
EXIT_THE_FUNCTION:
DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVGetFullDirectoryPath: DirName: %wZ\n", DirName));
return NtStatus;
}
NTSTATUS
MRxDAVCreateEncryptedDirectoryKey(
PUNICODE_STRING DirName
)
/*++
Routine Description:
This routine creates the registry key for the encrypted directory.
Arguments:
DirName - The full path name of the directory starting from the server.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
ULONG i = 0;
HKEY Key = NULL;
ULONG RequiredLength = 0;
UNICODE_STRING UnicodeRegKeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVCreateEncryptedDirectoryKey: DirName: %wZ\n", DirName));
RtlInitUnicodeString(&(UnicodeRegKeyName), MRXDAV_ENCRYPTED_DIRECTORY_KEY);
InitializeObjectAttributes(&(ObjectAttributes),
&(UnicodeRegKeyName),
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&Key, KEY_ALL_ACCESS, &ObjectAttributes);
if (Status != STATUS_SUCCESS) {
Status = ZwCreateKey(&Key,
KEY_ALL_ACCESS,
&ObjectAttributes,
0,
NULL,
0,
NULL);
if (Status == STATUS_SUCCESS) {
Status = ZwSetValueKey(Key,
DirName,
0,
REG_DWORD,
&i,
sizeof(ULONG));
ZwClose(Key);
}
} else {
Status = ZwQueryValueKey(Key,
DirName,
KeyValuePartialInformation,
NULL,
0,
&(RequiredLength));
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
Status = ZwSetValueKey(Key,
DirName,
0,
REG_DWORD,
&i,
sizeof(ULONG));
} else if (Status == STATUS_BUFFER_TOO_SMALL) {
Status = STATUS_SUCCESS;
}
ZwClose(Key);
}
if (Status != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVCreateEncryptedDirectoryKey. NtStatus = "
"%08lx\n", PsGetCurrentThreadId(), Status));
}
return Status;
}
NTSTATUS
MRxDAVRemoveEncryptedDirectoryKey(
PUNICODE_STRING DirName
)
/*++
Routine Description:
This routine deletes the registry key for the directory.
Arguments:
DirName - The full path name of the directory starting from the server.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
ULONG i = 0;
HKEY Key = NULL;
ULONG RequiredLength = 0;
UNICODE_STRING UnicodeRegKeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVRemoveEncryptedDirectoryKey: DirName: %wZ\n", DirName));
RtlInitUnicodeString(&(UnicodeRegKeyName), MRXDAV_ENCRYPTED_DIRECTORY_KEY);
InitializeObjectAttributes(&(ObjectAttributes),
&(UnicodeRegKeyName),
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&Key, KEY_ALL_ACCESS, &ObjectAttributes);
if (Status == STATUS_SUCCESS) {
Status = ZwDeleteValueKey(Key,DirName);
ZwClose(Key);
}
if (Status != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_ERROR,
("%ld: ERROR: MRxDAVRemoveEncryptedDirectoryKey. NtStatus = "
"%08lx\n", PsGetCurrentThreadId(), Status));
}
return Status;
}
NTSTATUS
MRxDAVQueryEncryptedDirectoryKey(
PUNICODE_STRING DirName
)
/*++
Routine Description:
This routine queries the registry key for the directory.
Arguments:
DirName - The full path name of the directory starting from the server.
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status;
ULONG i = 0;
HKEY Key = NULL;
ULONG RequiredLength = 0;
UNICODE_STRING UnicodeRegKeyName;
OBJECT_ATTRIBUTES ObjectAttributes;
DavDbgTrace(DAV_TRACE_DETAIL, ("MRxDAVQueryEncryptedDirectoryKey: DirName: %wZ\n", DirName));
RtlInitUnicodeString(&(UnicodeRegKeyName), MRXDAV_ENCRYPTED_DIRECTORY_KEY);
InitializeObjectAttributes(&(ObjectAttributes),
&(UnicodeRegKeyName),
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey(&Key, KEY_ALL_ACCESS, &ObjectAttributes);
if (Status == STATUS_SUCCESS) {
Status = ZwQueryValueKey(Key,
DirName,
KeyValuePartialInformation,
NULL,
0,
&(RequiredLength));
if (Status == STATUS_BUFFER_TOO_SMALL) {
Status = STATUS_SUCCESS;
}
ZwClose(Key);
}
if (Status != STATUS_SUCCESS) {
DavDbgTrace(DAV_TRACE_DETAIL,
("%ld: ERROR: MRxDAVQueryEncryptedDirectoryKey. NtStatus = "
"%08lx\n", PsGetCurrentThreadId(), Status));
}
return Status;
}