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
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;
|
|
}
|
|
|