/*++ 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: () // 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; }