/*++ Copyright (c) 1999 Microsoft Corporation Module Name: fcbfobx.c Abstract: This code manages the finalizing of the FCB and FOBX strucutres of the DAV Mini-Redir. Author: Rohan Kumar [RohanK] 26-Sept-1999 --*/ #include "precomp.h" #pragma hdrstop #include "webdav.h" // // Mentioned below are the prototypes of functions tht are used only within // this module (file). These functions should not be exposed outside. // NTSTATUS MRxDAVDeallocateForFobxContinuation( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE ); NTSTATUS MRxDAVFormatUserModeFobxFinalizeRequest( IN UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, IN OUT PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, IN ULONG WorkItemLength, OUT PULONG_PTR ReturnedLength ); BOOL MRxDAVPrecompleteUserModeFobxFinalizeRequest( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, ULONG WorkItemLength, BOOL OperationCancelled ); VOID DavLogDelayedWriteError( PUNICODE_STRING PathName ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, MRxDAVDeallocateForFobx) #pragma alloc_text(PAGE, MRxDAVDeallocateForFobxContinuation) #pragma alloc_text(PAGE, MRxDAVFormatUserModeFobxFinalizeRequest) #pragma alloc_text(PAGE, MRxDAVPrecompleteUserModeFobxFinalizeRequest) #pragma alloc_text(PAGE, MRxDAVCleanupFobx) #pragma alloc_text(PAGE, MRxDAVDeallocateForFcb) #pragma alloc_text(PAGE, DavLogDelayedWriteError) #endif // // Implementation of functions begins here. // NTSTATUS MRxDAVDeallocateForFobx( IN OUT PMRX_FOBX pFobx ) /*++ Routine Description: This routine is called when the wrapper is about to deallocate a FOBX. Arguments: pFobx - the Fobx being deallocated. Return Value: RXSTATUS - STATUS_SUCCESS --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PWEBDAV_FOBX DavFobx = NULL; PRX_CONTEXT RxContext = NULL; PMRX_SRV_CALL SrvCall; PRDBSS_DEVICE_OBJECT RxDeviceObject; PUNICODE_STRING RemainingName = NULL; PAGED_CODE(); SrvCall = (PMRX_SRV_CALL)pFobx->pSrvOpen->pFcb->pNetRoot->pSrvCall; ASSERT(SrvCall); RxDeviceObject = SrvCall->RxDeviceObject; DavFobx = MRxDAVGetFobxExtension(pFobx); ASSERT(DavFobx != NULL); RemainingName = pFobx->pSrvOpen->pAlreadyPrefixedName; DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVDeallocateForFobx. RemainingName = %wZ.\n", PsGetCurrentThreadId(), RemainingName)); // // If this FOBX does not have a list of DavFileAttributes, we are done. // if (DavFobx->DavFileAttributes == NULL) { return NtStatus; } DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVDeallocateForFobx. DavFileAttributes = %08lx.\n", PsGetCurrentThreadId(), DavFobx->DavFileAttributes)); // // We need to finalize the list of DavFileAttributes. // // // Unfortunately, we do not have an RxContext here and hence have to create // one. An RxContext is required for a request to be reflected up. // RxContext = RxCreateRxContext(NULL, RxDeviceObject, 0); if (RxContext == NULL) { NtStatus = STATUS_INSUFFICIENT_RESOURCES; DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVDeallocateForFobx/RxCreateRxContext: " "NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); goto EXIT_THE_FUNCTION; } // // We need to send the Fobx to the format routine and use the // MRxContext[1] pointer of the RxContext structure to store it. // RxContext->MRxContext[1] = (PVOID)pFobx; NtStatus = UMRxAsyncEngOuterWrapper(RxContext, SIZEOF_DAV_SPECIFIC_CONTEXT, MRxDAVFormatTheDAVContext, DAV_MINIRDR_ENTRY_FROM_CLEANUPFOBX, MRxDAVDeallocateForFobxContinuation, "MRxDAVDeallocateForFobx"); if (NtStatus != ERROR_SUCCESS) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVDeallocateForFobx/UMRxAsyncEngOuterWrapper: " "NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); } EXIT_THE_FUNCTION: if (RxContext) { RxDereferenceAndDeleteRxContext(RxContext); } DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Leaving MRxDAVDeallocateForFobx with NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); return(NtStatus); } NTSTATUS MRxDAVDeallocateForFobxContinuation( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE ) /*++ Routine Description: This is the continuation routine which finalizes an Fobx. Arguments: AsyncEngineContext - The Reflectors context. RxContext - The RDBSS context. Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS NtStatus; PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVDeallocateForFobxContinuation.\n", PsGetCurrentThreadId())); DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVDeallocateForFobxContinuation: " "AsyncEngineContext: %08lx, RxContext: %08lx\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext)); // // Try usermode. // NtStatus = UMRxSubmitAsyncEngUserModeRequest( UMRX_ASYNCENGINE_ARGUMENTS, MRxDAVFormatUserModeFobxFinalizeRequest, MRxDAVPrecompleteUserModeFobxFinalizeRequest ); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Leaving MRxDAVFinalizeSrvCallContinuation with NtStatus" " = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); return NtStatus; } NTSTATUS MRxDAVFormatUserModeFobxFinalizeRequest( IN UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, IN OUT PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, IN ULONG WorkItemLength, OUT PULONG_PTR ReturnedLength ) /*++ Routine Description: This routine formats the Fobx finalize request 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 DavWorkItem = (PDAV_USERMODE_WORKITEM)WorkItemHeader; PDAV_USERMODE_FINALIZE_FOBX_REQUEST FinFobxReq = NULL; PMRX_FOBX Fobx = NULL; PWEBDAV_FOBX DavFobx = NULL; PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVFormatUserModeFobxFinalizeRequest.\n", PsGetCurrentThreadId())); DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVFormatUserModeFobxFinalizeRequest: " "AsyncEngineContext: %08lx, RxContext: %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext)); // // We dont impersonate the user before going up to the user mode since // all we do in the user mode is free memory and the users credentials are // not needed to do this. // // // Fobx was set to MRxContext[1] in MRxDAVDeallocateForFobx. We need it to // get the pointer to the DavFileAttributes list. // Fobx = (PMRX_FOBX)RxContext->MRxContext[1]; DavFobx = MRxDAVGetFobxExtension(Fobx); ASSERT(DavFobx != NULL); ASSERT(DavFobx->DavFileAttributes != NULL); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVFormatUserModeFobxFinalizeRequest. DavFileAttributes = %08lx.\n", PsGetCurrentThreadId(), DavFobx->DavFileAttributes)); DavWorkItem->WorkItemType = UserModeFinalizeFobx; FinFobxReq = &(DavWorkItem->FinalizeFobxRequest); FinFobxReq->DavFileAttributes = DavFobx->DavFileAttributes; DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Leaving MRxDAVFormatUserModeFobxFinalizeRequest " "with NtStatus = %08lx.\n", PsGetCurrentThreadId(), NtStatus)); return NtStatus; } BOOL MRxDAVPrecompleteUserModeFobxFinalizeRequest( UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE, PUMRX_USERMODE_WORKITEM_HEADER WorkItemHeader, ULONG WorkItemLength, BOOL OperationCancelled ) /*++ Routine Description: The precompletion routine for the finalize Fobx request. Arguments: RxContext - The RDBSS context. AsyncEngineContext - The reflctor's context. WorkItem - 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; PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVPrecompleteUserModeFobxFinalizeRequest.\n", PsGetCurrentThreadId())); DavDbgTrace(DAV_TRACE_CONTEXT, ("%ld: MRxDAVPrecompleteUserModeFobxFinalizeRequest: " "AsyncEngineContext: %08lx, RxContext: %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext)); // // A FinalizeFobx 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: MRxDAVPrecompleteUserModeFobxFinalizeRequest: Operation Cancelled. " "AsyncEngineContext = %08lx, RxContext = %08lx.\n", PsGetCurrentThreadId(), AsyncEngineContext, RxContext)); } NtStatus = AsyncEngineContext->Status; if (NtStatus != STATUS_SUCCESS) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVPrecompleteUserModeFobxFinalizeRequest: " "Finalize Fobx failed in user mode.\n", PsGetCurrentThreadId())); } DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Leaving MRxDAVPrecompleteUserModeFobxFinalizeRequest.\n", PsGetCurrentThreadId())); return TRUE; } NTSTATUS MRxDAVCleanupFobx( IN PRX_CONTEXT RxContext ) /*++ Routine Description: This routine cleansup a file system object. Normally a no op. Arguments: pRxContext - the RDBSS context Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; RxCaptureFcb; 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); PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVCleanupFobx: RemainingName: %wZ\n", PsGetCurrentThreadId(), RemainingName)); IF_DEBUG { RxCaptureFobx; ASSERT (capFobx != NULL); ASSERT (capFobx->pSrvOpen == RxContext->pRelevantSrvOpen); } ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN ); ASSERT ( NodeTypeIsFcb(capFcb) ); ASSERT (FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT)); // // Because we only have one handle on the file, we do nothing for each // individual handle being closed. In this way we avoid doing paging ios. // We close the handle when the final close for the fcb comes down. // return(Status); } NTSTATUS MRxDAVDeallocateForFcb( IN OUT PMRX_FCB pFcb ) /*++ Routine Description: This routine is called when the wrapper is about to deallocate a FCB. Arguments: pFcb - the Fcb being deallocated. Return Value: RXSTATUS - STATUS_SUCCESS --*/ { NTSTATUS NtStatus = STATUS_SUCCESS; PWEBDAV_FCB DavFcb = (PWEBDAV_FCB)(pFcb->Context); PWCHAR NtFileName = NULL; LONG FileWasModified = 0; PAGED_CODE(); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: Entering MRxDAVDeallocateForFcb: FileName: %ws\n", PsGetCurrentThreadId(), DavFcb->FileName)); // // If we allocated the FCB resource to synchronize the "read-modify-write" // sequence, we need to uninitialize and deallocate it now. // if (DavFcb->DavReadModifyWriteLock) { ExDeleteResourceLite(DavFcb->DavReadModifyWriteLock); RxFreePool(DavFcb->DavReadModifyWriteLock); DavFcb->DavReadModifyWriteLock = NULL; } // // If the value of DavFcb->FileWasModified is TRUE, it means that some write // never made it to the server. This is a delayed write failure in WebDav. // We need to notify this to the user. Hence we log an entry in the EventLog // and call IoRaiseInformationalHardError to inform the user. // FileWasModified = InterlockedCompareExchange(&(DavFcb->FileWasModified), 0, 0); if (FileWasModified != 0) { BOOLEAN RaiseHardError = FALSE; ASSERT(DavFcb->FileNameInfo.Buffer != NULL); // // Log that the write failed in the event log. // DavLogDelayedWriteError( &(DavFcb->FileNameInfo) ); RaiseHardError = IoRaiseInformationalHardError(STATUS_LOST_WRITEBEHIND_DATA, &(DavFcb->FileNameInfo), NULL); if (!RaiseHardError) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVDeallocateForFcb/IoRaiseInformationalHardError", PsGetCurrentThreadId())); } } // // If we allocated any memory for the FileNameInfo, we need to free it now. // if (DavFcb->FileNameInfo.Buffer) { ASSERT(DavFcb->FileNameInfoAllocated == TRUE); RxFreePool(DavFcb->FileNameInfo.Buffer); DavFcb->FileNameInfo.Buffer = NULL; DavFcb->FileNameInfo.Length = 0; DavFcb->FileNameInfo.MaximumLength = 0; DavFcb->FileNameInfoAllocated = FALSE; } // // Delete the EFS file cache at the end of the Fcb lifetime. If the file is // opened again, the EFS file cache will be restored from the WinInet cache // in backup formate. This way, WinInet does not involved in delete the EFS // file cache which will be denied in the context of LocalService. // if (DavFcb->LocalFileIsEncrypted) { NTSTATUS LocalNtStatus = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING UnicodeFileName; ULONG SizeInBytes = 0; SizeInBytes = ( MAX_PATH + wcslen(L"\\??\\") + 1 ) * sizeof(WCHAR); NtFileName = RxAllocatePoolWithTag(PagedPool, SizeInBytes, DAV_FILENAME_POOLTAG); if (NtFileName == NULL) { // // cannot do much, bailout // DavDbgTrace(DAV_TRACE_ERROR, ("%ld: MRxDAVDeallocateForFcb/RxAllocatePoolWithTag failed", PsGetCurrentThreadId())); goto EXIT_THE_FUNCTION; } RtlZeroMemory(NtFileName, SizeInBytes); wcscpy( NtFileName, L"\\??\\" ); wcscpy( &(NtFileName[4]), DavFcb->FileName ); DavDbgTrace(DAV_TRACE_DETAIL, ("%ld: MRxDAVDeallocateForFcb: NtFileName = %ws\n", PsGetCurrentThreadId(), NtFileName)); RtlInitUnicodeString( &(UnicodeFileName), NtFileName ); InitializeObjectAttributes( &ObjectAttributes, &UnicodeFileName, OBJ_CASE_INSENSITIVE, NULL, NULL); LocalNtStatus = ZwDeleteFile( &(ObjectAttributes) ); if (!NT_SUCCESS(LocalNtStatus)) { DavDbgTrace(DAV_TRACE_ERROR, ("%ld: ERROR: MRxDAVDeallocateForFcb/ZwDeleteFile" ". NtStatus = %08lx %ws \n", PsGetCurrentThreadId(), LocalNtStatus,DavFcb->FileName)); } } EXIT_THE_FUNCTION: if (NtFileName) { RxFreePool(NtFileName); } RtlZeroMemory(DavFcb, sizeof(WEBDAV_FCB)); return(NtStatus); } VOID DavLogDelayedWriteError( PUNICODE_STRING PathName ) /*++ Routine Description: This routine logs a delayed write error to the event log. Arguments: PathName - The PathName for which the delayed write failed. Return Value: RXSTATUS - The return status for the operation --*/ { NTSTATUS Status = STATUS_SUCCESS; USHORT RemainingLength = 0; UNICODE_STRING ErrorLog[1]; PAGED_CODE(); RemainingLength = ERROR_LOG_MAXIMUM_SIZE; RemainingLength -= sizeof(IO_ERROR_LOG_PACKET); RemainingLength -= sizeof(UNICODE_NULL); // // If the length of the PathName is less than the RemainingLength, then we // print the entire path, otherwise we print the max amount allowed. This is // because the length of the error log message is limited by the // ERROR_LOG_MAXIMUM_SIZE. // if (PathName->Length > RemainingLength) { ErrorLog[0].Length = RemainingLength; } else { ErrorLog[0].Length = PathName->Length; } ErrorLog[0].MaximumLength = ErrorLog[0].Length; ErrorLog[0].Buffer = PathName->Buffer; RxLogEventWithAnnotation((PRDBSS_DEVICE_OBJECT)MRxDAVDeviceObject, EVENT_DAV_REDIR_DELAYED_WRITE_FAILED, STATUS_LOST_WRITEBEHIND_DATA, NULL, 0, ErrorLog, 1); return; }