1379 lines
50 KiB
C
1379 lines
50 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
write.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the DAV miniredir call down routines pertaining to
|
|
"write" of file system objects.
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman [SethuR]
|
|
|
|
Rohan Kumar [RohanK] 02-Nov-1999
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#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
|
|
MRxDAVWriteContinuation(
|
|
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
|
|
);
|
|
|
|
VOID
|
|
MRxDAVCloseTheFileHandle(
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, MRxDAVWrite)
|
|
#pragma alloc_text(PAGE, MRxDAVWriteContinuation)
|
|
#pragma alloc_text(PAGE, MRxDAVExtendForCache)
|
|
#pragma alloc_text(PAGE, MRxDAVExtendForNonCache)
|
|
#pragma alloc_text(PAGE, MRxDAVFastIoWrite)
|
|
#pragma alloc_text(PAGE, MRxDAVCloseTheFileHandle)
|
|
#endif
|
|
|
|
//
|
|
// Implementation of functions begins here.
|
|
//
|
|
|
|
NTSTATUS
|
|
MRxDAVWrite(
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles network write requests.
|
|
|
|
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 MRxDAVWrite!!!!\n", PsGetCurrentThreadId()));
|
|
|
|
DavDbgTrace(DAV_TRACE_CONTEXT,
|
|
("%ld: MRxDAVWrite: RxContext: %08lx\n",
|
|
PsGetCurrentThreadId(), RxContext));
|
|
|
|
NtStatus = UMRxAsyncEngOuterWrapper(RxContext,
|
|
SIZEOF_DAV_SPECIFIC_CONTEXT,
|
|
MRxDAVFormatTheDAVContext,
|
|
DAV_MINIRDR_ENTRY_FROM_WRITE,
|
|
MRxDAVWriteContinuation,
|
|
"MRxDAVWrite");
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: Leaving MRxDAVWrite with NtStatus = %08lx.\n",
|
|
PsGetCurrentThreadId(), NtStatus));
|
|
|
|
return(NtStatus);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MRxDAVWriteContinuation(
|
|
UMRX_ASYNCENGINE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the continuation routine the for write operation. It uses unbuffered
|
|
write doing prereads as necessary. We cannot use buffered write because such
|
|
a write could be arbitrarily deferred (in CcCanIWrite) so that we deadlock.
|
|
|
|
Arguments:
|
|
|
|
AsyncEngineContext - The exchange to be conducted.
|
|
|
|
RxContext - The RDBSS context.
|
|
|
|
Notes.
|
|
|
|
The routine does this in (potentially) 3 phases.
|
|
|
|
1) If the starting offset is not aligned on a page boundary then,
|
|
- Read from the earlier page boundary to the next page boundary of the
|
|
starting offset.
|
|
- Merge the passed in buffer.
|
|
- Write the whole page.
|
|
|
|
2) 0 or more page size writes.
|
|
|
|
3) Residual write of less than page size, similar to what is explained in
|
|
1) above.
|
|
|
|
Non-Cached writes that do not extend the file have the FCB acquired shared.
|
|
We have an additional resource in the WEBDAV_FCB structure to synchronize
|
|
the "read-modify-write" routine we have here. This is because multiple threads
|
|
can (in the non-cached non extending scenario) overwrite each others data.
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
PWEBDAV_CONTEXT DavContext = NULL;
|
|
PLOWIO_CONTEXT LowIoContext = NULL;
|
|
LARGE_INTEGER ByteOffset = {0,0}, AlignedOffset = {0,0}, EndBytePlusOne = {0,0};
|
|
ULONG ByteCount = 0, TotalLengthActuallyWritten = 0;
|
|
ULONG LengthRead = 0, BytesToCopy = 0, BytesToWrite = 0, LengthWritten = 0;
|
|
ULONG ByteOffsetMisAlignment = 0, InMemoryMisAlignment = 0;
|
|
BOOLEAN SynchronousIo = TRUE, PagingIo = TRUE, DavFcbResourceAcquired = FALSE;
|
|
PWEBDAV_SRV_OPEN davSrvOpen = NULL;
|
|
PWEBDAV_FCB DavFcb = NULL;
|
|
FILE_STANDARD_INFORMATION FileStandardInfo;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PBYTE AllocatedSideBuffer = NULL, UserBuffer = NULL;
|
|
HANDLE LocalFileHandle = INVALID_HANDLE_VALUE;
|
|
ULONG SizeInBytes = 0;
|
|
PWCHAR NtFileName = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING UnicodeFileName;
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
PDAV_GLOBAL_FILE_TABLE_ENTRY FileTableEntry = NULL;
|
|
BOOL Exists = FALSE;
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
|
|
PAGED_CODE();
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: Entering MRxDAVWriteContinuation.\n", PsGetCurrentThreadId()));
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: AsyncEngineContext: %08lx, RxContext: %08lx.\n",
|
|
PsGetCurrentThreadId(), AsyncEngineContext, RxContext));
|
|
|
|
ASSERT_ASYNCENG_CONTEXT(AsyncEngineContext);
|
|
|
|
//
|
|
// We want to keep the AsyncEngineContext alive while we are doing this write
|
|
// operation. The reference is taken away when we leave this function.
|
|
//
|
|
InterlockedIncrement( &(AsyncEngineContext->NodeReferenceCount) );
|
|
|
|
DavContext = (PWEBDAV_CONTEXT)AsyncEngineContext;
|
|
|
|
LowIoContext = &(RxContext->LowIoContext);
|
|
ASSERT(LowIoContext != NULL);
|
|
|
|
ByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
|
|
|
|
ByteCount = LowIoContext->ParamsFor.ReadWrite.ByteCount;
|
|
|
|
EndBytePlusOne.QuadPart = (ByteOffset.QuadPart + ByteCount);
|
|
|
|
UserBuffer = RxLowIoGetBufferAddress(RxContext);
|
|
|
|
PagingIo = BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO);
|
|
|
|
SynchronousIo = !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION);
|
|
|
|
davSrvOpen = MRxDAVGetSrvOpenExtension(RxContext->pRelevantSrvOpen);
|
|
ASSERT(davSrvOpen->UnderlyingHandle != NULL);
|
|
ASSERT(davSrvOpen->UnderlyingFileObject != NULL);
|
|
ASSERT(davSrvOpen->UnderlyingDeviceObject != NULL);
|
|
|
|
if ( davSrvOpen->UnderlyingHandle == NULL ||
|
|
davSrvOpen->UnderlyingFileObject == NULL ||
|
|
davSrvOpen->UnderlyingDeviceObject == NULL ) {
|
|
NtStatus = STATUS_INVALID_PARAMETER;
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation. Invalid davSrvOpen\n",
|
|
PsGetCurrentThreadId()));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavFcb = MRxDAVGetFcbExtension(RxContext->pRelevantSrvOpen->pFcb);
|
|
ASSERT(DavFcb != NULL);
|
|
|
|
//
|
|
// 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. We do the Create to query the
|
|
// filesize of the underlying file.
|
|
//
|
|
|
|
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: MRxDAVWriteContinuation/RxAllocatePoolWithTag: Error Val"
|
|
" = %08lx\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RtlZeroMemory(NtFileName, SizeInBytes);
|
|
|
|
wcscpy( NtFileName, L"\\??\\" );
|
|
wcscpy( &(NtFileName[4]), DavFcb->FileName );
|
|
|
|
RtlInitUnicodeString( &(UnicodeFileName), NtFileName );
|
|
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&UnicodeFileName,
|
|
(OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE),
|
|
0,
|
|
NULL);
|
|
|
|
NtStatus = ZwCreateFile(&(LocalFileHandle),
|
|
FILE_READ_ATTRIBUTES,
|
|
&(ObjectAttributes),
|
|
&(IoStatusBlock),
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_VALID_FLAGS,
|
|
FILE_OPEN,
|
|
0,
|
|
NULL,
|
|
0);
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
LocalFileHandle = INVALID_HANDLE_VALUE;
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: ERROR: MRxDAVWriteContinuation/ZwCreateFile: "
|
|
"Error Val = %08lx\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation: FileHandle = %08lx\n",
|
|
PsGetCurrentThreadId(), LocalFileHandle));
|
|
|
|
//
|
|
// See what the current FileStandardInformation is. We don't use the file
|
|
// handle stored in the davSrvOpen structure, because this could have been
|
|
// created in the svchost process and hence will not be valid in this
|
|
// process.
|
|
//
|
|
NtStatus = ZwQueryInformationFile(LocalFileHandle,
|
|
&(IoStatusBlock),
|
|
&(FileStandardInfo),
|
|
sizeof(FILE_STANDARD_INFORMATION),
|
|
FileStandardInformation);
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/ZwQueryInformationFile. "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. FileName = %wZ, PagingIo = %d, SynchronousIo = %d"
|
|
", ByteOffset.HighPart = %d, ByteOffset.LowPart = %d, ByteCount = %d, EndOfFile.HighPart = %d, "
|
|
"EndOfFile.LowPart = %d\n",
|
|
PsGetCurrentThreadId(), RxContext->pRelevantSrvOpen->pAlreadyPrefixedName,
|
|
PagingIo, SynchronousIo, ByteOffset.HighPart, ByteOffset.LowPart,
|
|
ByteCount, FileStandardInfo.EndOfFile.HighPart, FileStandardInfo.EndOfFile.LowPart));
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
Exists = DavDoesTheFileEntryExist(RxContext->pRelevantSrvOpen->pAlreadyPrefixedName,
|
|
&(FileTableEntry));
|
|
if (!Exists) {
|
|
DbgBreakPoint();
|
|
}
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
//
|
|
// Since we are going to do the write we mark this file as being modified.
|
|
// When the Close happens, we check whether the file has been modified and
|
|
// PUT the file on the server.
|
|
//
|
|
DavFcb->FileWasModified = TRUE;
|
|
DavFcb->DoNotTakeTheCurrentTimeAsLMT = FALSE;
|
|
|
|
if (PagingIo) {
|
|
|
|
ASSERT(RxContext->CurrentIrp->MdlAddress != NULL);
|
|
if (RxContext->CurrentIrp->MdlAddress == NULL) {
|
|
DbgPrint("%ld: MRxDAVWriteContinuation: MdlAddress == NULL\n", PsGetCurrentThreadId());
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
BytesToWrite = ( (ByteCount >> PAGE_SHIFT) << PAGE_SHIFT );
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation(0). ByteCount = %d, BytesToWrite = %d\n",
|
|
PsGetCurrentThreadId(), ByteCount, BytesToWrite));
|
|
|
|
if (BytesToWrite > 0) {
|
|
|
|
LengthWritten = DavReadWriteFileEx(DAV_MJ_WRITE,
|
|
FALSE,
|
|
TRUE,
|
|
RxContext->CurrentIrp->MdlAddress,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
ByteOffset.QuadPart,
|
|
MmGetMdlBaseVa(RxContext->CurrentIrp->MdlAddress),
|
|
BytesToWrite,
|
|
&(IoStatusBlock));
|
|
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(0). "
|
|
"NtStatus = %08lx\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
if (LengthWritten != BytesToWrite) {
|
|
DbgPrint("MRxDAVWriteContinuation(1): LengthWritten(%x) != BytesToWrite(%x)\n",
|
|
LengthWritten, BytesToWrite);
|
|
}
|
|
|
|
ASSERT(LengthWritten == BytesToWrite);
|
|
|
|
TotalLengthActuallyWritten += BytesToWrite;
|
|
|
|
}
|
|
|
|
//
|
|
// If we have already written out the required number of bytes (which
|
|
// means BytesToWrite == ByteCount), then we are done and can exit now.
|
|
//
|
|
if (BytesToWrite == ByteCount) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. BytesToCopy == ByteCount(0)\n",
|
|
PsGetCurrentThreadId()));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Decrement the ByteCount by the number of bytes that have been copied.
|
|
//
|
|
ByteCount -= BytesToWrite;
|
|
ASSERT(ByteCount < PAGE_SIZE);
|
|
|
|
//
|
|
// Increment the ByteOffset with the number of bytes that have been copied.
|
|
// If the original ByteCount was > (PAGE_SIZE - ByteOffsetMisAlignment) then
|
|
// the ByteOffset is now page aligned.
|
|
//
|
|
ByteOffset.QuadPart += BytesToWrite;
|
|
|
|
//
|
|
// Increment the UserBuffer pointer which currently points to the beginning
|
|
// of the buffer which the user supplied by the number of bytes which have
|
|
// been copied.
|
|
//
|
|
UserBuffer += BytesToWrite;
|
|
|
|
//
|
|
// We have written all the bytes that are multiple of pages. We now
|
|
// need to write out the remaining bytes from the last page. From here,
|
|
// we go to Case 3 below.
|
|
//
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Remaining ByteCount = %d\n",
|
|
PsGetCurrentThreadId(), ByteCount));
|
|
|
|
}
|
|
|
|
//
|
|
// We allocate a page size buffer to be used for helping read the data
|
|
// which is not aligned at page boundaries.
|
|
//
|
|
AllocatedSideBuffer = RxAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, DAV_READWRITE_POOLTAG);
|
|
if (AllocatedSideBuffer == NULL) {
|
|
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/RxAllocatePoolWithTag\n",
|
|
PsGetCurrentThreadId()));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// When we issue a write down to the underlying file system, we need to make
|
|
// sure that the offset is page aligned and the bytecount is a multiple of
|
|
// PAGE_SIZE. This is because we created the local handle with the
|
|
// NO_INTERMEDIATE_BUFFERING option. Since there is no cache map for this
|
|
// handle, all the data is read from the disk and hence the alignment issue.
|
|
//
|
|
|
|
//
|
|
// Case 1: ByteOffset is not page aligned. In this case we read the page
|
|
// which contains the ByteOffset and copy the data from the ByteOffset to
|
|
// the end of the page into the PAGE_SIZE buffer (which we allocated above)
|
|
// and write the buffer back to the file.
|
|
//
|
|
|
|
//
|
|
// The "and" operation below does the following. If the ByteOffset is 6377
|
|
// and the PAGE_SIZE is 4096, then the MisAlignment is 2281.
|
|
//
|
|
ByteOffsetMisAlignment = ( ByteOffset.LowPart & (PAGE_SIZE - 1) );
|
|
|
|
if (ByteOffsetMisAlignment != 0) {
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 1. ByteOffsetMisAlignment = %d\n",
|
|
PsGetCurrentThreadId(), ByteOffsetMisAlignment));
|
|
|
|
//
|
|
// Acquire the DavFcb resource exclusively before proceeding further with
|
|
// the "read-modify-write" routing.
|
|
//
|
|
ExAcquireResourceExclusiveLite(DavFcb->DavReadModifyWriteLock, TRUE);
|
|
DavFcbResourceAcquired = TRUE;
|
|
|
|
AlignedOffset = ByteOffset;
|
|
|
|
//
|
|
// The byte offset is not aligned. We need to read the page containing
|
|
// the offset now.
|
|
//
|
|
|
|
//
|
|
// If the PAGE_SIZE is 4096 (0x1000) then (PAGE_SIZE - 1) is 0xFFF.
|
|
// ~(PAGE_SIZE - 1) is 0x000. The bit operation below masks the lower 3
|
|
// bytes of the aligned offset to make it page aligned.
|
|
//
|
|
AlignedOffset.LowPart &= ~(PAGE_SIZE - 1);
|
|
|
|
RtlZeroMemory(AllocatedSideBuffer, PAGE_SIZE);
|
|
|
|
//
|
|
// If the AliignedOffset is within the file then we read the whole page
|
|
// containing the offset first before writing it out.
|
|
//
|
|
if ( (FileStandardInfo.EndOfFile.QuadPart != 0) &&
|
|
(AlignedOffset.QuadPart < FileStandardInfo.EndOfFile.QuadPart) ) {
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 1. AlignedOffset.QuadPart"
|
|
" < FileStandardInfo.EndOfFile.QuadPart\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
LengthRead = DavReadWriteFileEx(DAV_MJ_READ,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
AlignedOffset.QuadPart,
|
|
AllocatedSideBuffer,
|
|
PAGE_SIZE,
|
|
&(IoStatusBlock));
|
|
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS && NtStatus != STATUS_END_OF_FILE) {
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(1). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
} else {
|
|
|
|
LengthRead = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Copy the right number of bytes into the buffer.
|
|
//
|
|
BytesToCopy = min( ByteCount, (PAGE_SIZE - ByteOffsetMisAlignment) );
|
|
|
|
//
|
|
// Copy the bytes to be written back from the UserBuffer into the
|
|
// AllocatedSideBuffer.
|
|
//
|
|
RtlCopyMemory((AllocatedSideBuffer + ByteOffsetMisAlignment),
|
|
UserBuffer,
|
|
BytesToCopy);
|
|
|
|
BytesToWrite = (ByteOffsetMisAlignment + BytesToCopy);
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 1: LengthRead = %d, BytesToCopy = %d"
|
|
" BytesToWrite = %d\n", PsGetCurrentThreadId(), LengthRead,
|
|
BytesToCopy, BytesToWrite));
|
|
|
|
//
|
|
// If the BytesToWrite is less that LengthRead (which is one page in this
|
|
// case) then we make BytesToWrite to be the LengthRead. This is possible
|
|
// if the bytes to be written are contained in a Page starting at a
|
|
// mis-aligned offset.
|
|
//
|
|
if (BytesToWrite < LengthRead) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 1: BytesToWrite < LengthRead\n",
|
|
PsGetCurrentThreadId()));
|
|
BytesToWrite = LengthRead;
|
|
}
|
|
|
|
//
|
|
// Now we write out the entire page to the disk.
|
|
//
|
|
LengthWritten = DavReadWriteFileEx(DAV_MJ_WRITE,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
AlignedOffset.QuadPart,
|
|
AllocatedSideBuffer,
|
|
BytesToWrite,
|
|
&(IoStatusBlock));
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(2). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
} else {
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
PDAV_MR_PAGING_WRITE_ENTRY DavPagingWriteEntry = NULL;
|
|
|
|
DavPagingWriteEntry = RxAllocatePool(PagedPool, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
if (DavPagingWriteEntry == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
RtlZeroMemory(DavPagingWriteEntry, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
|
|
DavPagingWriteEntry->ThisThreadId = PsGetCurrentThreadId();
|
|
|
|
DavPagingWriteEntry->LocByteOffset.QuadPart = AlignedOffset.QuadPart;
|
|
|
|
DavPagingWriteEntry->LocByteCount = BytesToWrite;
|
|
|
|
DavPagingWriteEntry->DataBuffer = RxAllocatePool(PagedPool, BytesToWrite);
|
|
|
|
RtlCopyMemory(DavPagingWriteEntry->DataBuffer, AllocatedSideBuffer, BytesToWrite);
|
|
|
|
wcscpy(DavPagingWriteEntry->FileName, DavFcb->FileName);
|
|
|
|
InsertHeadList( &(FileTableEntry->DavMRPagingEntry), &(DavPagingWriteEntry->thisMPagingWriteEntry) );
|
|
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
}
|
|
|
|
if (LengthWritten != BytesToWrite) {
|
|
DbgPrint("MRxDAVWriteContinuation(2): LengthWritten(%x) != BytesToWrite(%x)\n",
|
|
LengthWritten, BytesToWrite);
|
|
}
|
|
|
|
//
|
|
// If we were successful, then we should have ready PAGE_SIZE bytes.
|
|
//
|
|
ASSERT(LengthWritten == BytesToWrite);
|
|
|
|
TotalLengthActuallyWritten += BytesToCopy;
|
|
|
|
//
|
|
// If we have already written out the required number of bytes (which
|
|
// means BytesToCopy == ByteCount), then we are done and can exit now.
|
|
//
|
|
if (BytesToCopy == ByteCount) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. BytesToCopy == ByteCount(1)\n",
|
|
PsGetCurrentThreadId()));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Decrement the ByteCount by the number of bytes that have been copied.
|
|
//
|
|
ByteCount -= BytesToCopy;
|
|
|
|
//
|
|
// Increment the ByteOffset with the number of bytes that have been
|
|
// copied. The ByteOffset is now page aligned.
|
|
//
|
|
ByteOffset.QuadPart += BytesToCopy;
|
|
|
|
//
|
|
// Increment the UserBuffer pointer which currently points to the beginning
|
|
// of the buffer which the user supplied by the number of bytes which have
|
|
// been copied.
|
|
//
|
|
UserBuffer += BytesToCopy;
|
|
|
|
//
|
|
// If we acquired the DavFcb resource, then we need to release it since
|
|
// we are done with this "read-modify-write" sequence.
|
|
//
|
|
if (DavFcbResourceAcquired) {
|
|
ExReleaseResourceLite(DavFcb->DavReadModifyWriteLock);
|
|
DavFcbResourceAcquired = FALSE;
|
|
}
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 1: ByteCount = %d"
|
|
" ByteOffSet.HighPart = %d, ByteOffSet.LowPart = %d\n",
|
|
PsGetCurrentThreadId(), ByteOffset.HighPart, ByteOffset.LowPart));
|
|
|
|
}
|
|
|
|
//
|
|
// Case 2: At this stage we have copied the bytes from the unaligned offset
|
|
// (if it the ByteOffset was unaligned) to the next page bouandary. Now we
|
|
// write as many pages as we can without copying. If the end pointer is
|
|
// aligned OR we cover the end of file, then we write out everything. If not,
|
|
// we write out as many pages as we can.
|
|
//
|
|
|
|
//
|
|
// We also have to be back to just writing full pages, if including the
|
|
// "trailing bytes" would take us onto a new physical page of memory because
|
|
// we are doing this write under the original Mdl lock?? What does this
|
|
// mean?? Copied this comment from Joe Linn's code in csc.nt5\readrite.c.
|
|
//
|
|
|
|
//
|
|
// If 4200 bytes are remaining, the operation below sets BytesToWrite to
|
|
// 4096.
|
|
//
|
|
BytesToWrite = ( (ByteCount >> PAGE_SHIFT) << PAGE_SHIFT );
|
|
|
|
//
|
|
// Get the ByteOffsetMisAlignment of the EndBytePlusOne position.
|
|
//
|
|
ByteOffsetMisAlignment = (EndBytePlusOne.LowPart & (PAGE_SIZE - 1));
|
|
|
|
InMemoryMisAlignment = (ULONG)( ((ULONG_PTR)UserBuffer) & (PAGE_SIZE - 1) );
|
|
|
|
if ( ( InMemoryMisAlignment == 0 ) &&
|
|
( (EndBytePlusOne.QuadPart) >= (FileStandardInfo.EndOfFile.QuadPart) ) ) {
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 2: UserBuff Page Aligned\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
BytesToWrite = ByteCount;
|
|
|
|
}
|
|
|
|
if ( (BytesToWrite != 0) && (BytesToWrite >= PAGE_SIZE) ) {
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Entered Case 2\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
if ( ( (ULONG_PTR)UserBuffer & 0x3 ) == 0 ) {
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. UserBuffer DWORD Aligned\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
LengthWritten = DavReadWriteFileEx(DAV_MJ_WRITE,
|
|
FALSE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
ByteOffset.QuadPart,
|
|
UserBuffer,
|
|
BytesToWrite,
|
|
&(IoStatusBlock));
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(3). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
} else {
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
PDAV_MR_PAGING_WRITE_ENTRY DavPagingWriteEntry = NULL;
|
|
|
|
DavPagingWriteEntry = RxAllocatePool(PagedPool, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
if (DavPagingWriteEntry == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
RtlZeroMemory(DavPagingWriteEntry, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
|
|
DavPagingWriteEntry->ThisThreadId = PsGetCurrentThreadId();
|
|
|
|
DavPagingWriteEntry->LocByteOffset.QuadPart = ByteOffset.QuadPart;
|
|
|
|
DavPagingWriteEntry->LocByteCount = BytesToWrite;
|
|
|
|
DavPagingWriteEntry->DataBuffer = RxAllocatePool(PagedPool, BytesToWrite);
|
|
|
|
RtlCopyMemory(DavPagingWriteEntry->DataBuffer, UserBuffer, BytesToWrite);
|
|
|
|
wcscpy(DavPagingWriteEntry->FileName, DavFcb->FileName);
|
|
|
|
InsertHeadList( &(FileTableEntry->DavMRPagingEntry), &(DavPagingWriteEntry->thisMPagingWriteEntry) );
|
|
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
}
|
|
|
|
if (LengthWritten != BytesToWrite) {
|
|
DbgPrint("MRxDAVWriteContinuation(3): LengthWritten(%x) != BytesToWrite(%x)\n",
|
|
LengthWritten, BytesToWrite);
|
|
}
|
|
|
|
//
|
|
// If we were successful, then we should have ready PAGE_SIZE bytes.
|
|
//
|
|
ASSERT(LengthWritten == BytesToWrite);
|
|
|
|
TotalLengthActuallyWritten += BytesToWrite;
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. BytesToWrite = %d, "
|
|
" LengthWritten = %d\n", PsGetCurrentThreadId(),
|
|
BytesToWrite, LengthWritten));
|
|
|
|
//
|
|
// If we have already written out the required number of bytes (which
|
|
// means BytesToWrite == ByteCount), then we are done and can exit now.
|
|
//
|
|
if (BytesToWrite == ByteCount) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. BytesToCopy == ByteCount(2)\n",
|
|
PsGetCurrentThreadId()));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
//
|
|
// Decrement the ByteCount by the number of bytes that have been copied.
|
|
//
|
|
ByteCount -= BytesToWrite;
|
|
|
|
//
|
|
// Increment the ByteOffset with the number of bytes that have been copied.
|
|
// If the original ByteCount was > (PAGE_SIZE - ByteOffsetMisAlignment) then
|
|
// the ByteOffset is now page aligned.
|
|
//
|
|
ByteOffset.QuadPart += BytesToWrite;
|
|
|
|
//
|
|
// Increment the UserBuffer pointer which currently points to the beginning
|
|
// of the buffer which the user supplied by the number of bytes which have
|
|
// been copied.
|
|
//
|
|
UserBuffer += BytesToWrite;
|
|
|
|
} else {
|
|
|
|
ULONG BytesToWriteThisIteration = 0;
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. UserBuffer NOT DWORD Aligned\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
//
|
|
// This is the case when the offsets are aligned but the user
|
|
// supplied buffer is not aligned. In such cases we have to resort
|
|
// to copying the user supplied buffer onto the local buffer
|
|
// allocated and then spin out the writes.
|
|
//
|
|
while (BytesToWrite > 0) {
|
|
|
|
//
|
|
// If the BytesToWrite is less than the PAGE_SIZE then we copy
|
|
// the bytes left. If not, we write a PAGE.
|
|
//
|
|
BytesToWriteThisIteration = ( (BytesToWrite < PAGE_SIZE) ? BytesToWrite : PAGE_SIZE );
|
|
|
|
//
|
|
// Copy the memory from the UserBuffer to the AllocatedSideBuffer.
|
|
//
|
|
RtlCopyMemory(AllocatedSideBuffer, UserBuffer, BytesToWriteThisIteration);
|
|
|
|
LengthWritten = DavReadWriteFileEx(DAV_MJ_WRITE,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
ByteOffset.QuadPart,
|
|
AllocatedSideBuffer,
|
|
BytesToWriteThisIteration,
|
|
&(IoStatusBlock));
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(4). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
} else {
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
PDAV_MR_PAGING_WRITE_ENTRY DavPagingWriteEntry = NULL;
|
|
|
|
DavPagingWriteEntry = RxAllocatePool(PagedPool, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
if (DavPagingWriteEntry == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
RtlZeroMemory(DavPagingWriteEntry, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
|
|
DavPagingWriteEntry->ThisThreadId = PsGetCurrentThreadId();
|
|
|
|
DavPagingWriteEntry->LocByteOffset.QuadPart = ByteOffset.QuadPart;
|
|
|
|
DavPagingWriteEntry->LocByteCount = BytesToWriteThisIteration;
|
|
|
|
DavPagingWriteEntry->DataBuffer = RxAllocatePool(PagedPool, BytesToWriteThisIteration);
|
|
|
|
RtlCopyMemory(DavPagingWriteEntry->DataBuffer, AllocatedSideBuffer, BytesToWriteThisIteration);
|
|
|
|
wcscpy(DavPagingWriteEntry->FileName, DavFcb->FileName);
|
|
|
|
InsertHeadList( &(FileTableEntry->DavMRPagingEntry), &(DavPagingWriteEntry->thisMPagingWriteEntry) );
|
|
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
}
|
|
|
|
if (LengthWritten != BytesToWriteThisIteration) {
|
|
DbgPrint("MRxDAVWriteContinuation(4): LengthWritten(%x) != BytesToWriteThisIteration(%x)\n",
|
|
LengthWritten, BytesToWriteThisIteration);
|
|
}
|
|
|
|
//
|
|
// If we were successful, then we should have ready PAGE_SIZE bytes.
|
|
//
|
|
ASSERT(LengthWritten == BytesToWriteThisIteration);
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. BytesToWriteThisIteration = %d, "
|
|
" LengthWritten = %d\n", PsGetCurrentThreadId(),
|
|
BytesToWriteThisIteration, LengthWritten));
|
|
|
|
//
|
|
// Decrement the ByteCount by the number of bytes that have been copied.
|
|
//
|
|
ByteCount -= LengthWritten;
|
|
|
|
//
|
|
// Increment the ByteOffset with the number of bytes that have been copied.
|
|
// If the original ByteCount was > (PAGE_SIZE - ByteOffsetMisAlignment) then
|
|
// the ByteOffset is now page aligned.
|
|
//
|
|
ByteOffset.QuadPart += LengthWritten;
|
|
|
|
//
|
|
// Increment the UserBuffer pointer which currently points to the beginning
|
|
// of the buffer which the user supplied by the number of bytes which have
|
|
// been copied.
|
|
//
|
|
UserBuffer += LengthWritten;
|
|
|
|
TotalLengthActuallyWritten += LengthWritten;
|
|
|
|
//
|
|
// Subtract the LengthWritten from the number of bytes to write.
|
|
//
|
|
BytesToWrite -= LengthWritten;
|
|
|
|
}
|
|
|
|
//
|
|
// IMPORTANT!!! Need to find out why if TotalLengthActuallyWritten == ByteCount
|
|
// is TRUE we are done. This was as Joe Linn did for CSC. Ofcourse
|
|
// if ByteCount is 0, it means we are done.
|
|
//
|
|
if ( (TotalLengthActuallyWritten == ByteCount) || (ByteCount == 0) ) {
|
|
if ((TotalLengthActuallyWritten == ByteCount)) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. TotalLengthActuallyWritten == ByteCount\n",
|
|
PsGetCurrentThreadId()));
|
|
} else {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case2. Leaving!!! ByteCount = 0\n",
|
|
PsGetCurrentThreadId()));
|
|
}
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
}
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case 2: ByteCount = %d"
|
|
" ByteOffSet.HighPart = %d, ByteOffSet.LowPart = %d\n",
|
|
PsGetCurrentThreadId(), ByteOffset.HighPart, ByteOffset.LowPart));
|
|
|
|
}
|
|
|
|
//
|
|
// CASE 3: We don't have the whole buffer, ByteCount is non zero and is less
|
|
// than PAGE_SIZE.
|
|
//
|
|
|
|
ASSERT(ByteCount != 0);
|
|
ASSERT(ByteCount < PAGE_SIZE);
|
|
|
|
//
|
|
// Acquire the DavFcb resource exclusively before proceeding further with
|
|
// the "read-modify-write" routing.
|
|
//
|
|
ExAcquireResourceExclusiveLite(DavFcb->DavReadModifyWriteLock, TRUE);
|
|
DavFcbResourceAcquired = TRUE;
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Entered Case 3\n",
|
|
PsGetCurrentThreadId()));
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case3. ByteCount = %d\n",
|
|
PsGetCurrentThreadId(), ByteCount));
|
|
|
|
RtlZeroMemory(AllocatedSideBuffer, PAGE_SIZE);
|
|
|
|
LengthRead = DavReadWriteFileEx(DAV_MJ_READ,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
ByteOffset.QuadPart,
|
|
AllocatedSideBuffer,
|
|
PAGE_SIZE,
|
|
&(IoStatusBlock));
|
|
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS && NtStatus != STATUS_END_OF_FILE) {
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(5). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
goto EXIT_THE_FUNCTION;
|
|
}
|
|
|
|
RtlCopyMemory(AllocatedSideBuffer, UserBuffer, ByteCount);
|
|
|
|
BytesToWrite = ByteCount;
|
|
|
|
//
|
|
// Here, if the ByetsToWrite is not page/sector aligned, it gets so because
|
|
// LengthRead must be page/sector aligned.
|
|
//
|
|
if (BytesToWrite < LengthRead) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. Case3. BytesToWrite < LengthRead\n",
|
|
PsGetCurrentThreadId()));
|
|
BytesToWrite = LengthRead;
|
|
}
|
|
|
|
if (BytesToWrite) {
|
|
|
|
LengthWritten = DavReadWriteFileEx(DAV_MJ_WRITE,
|
|
TRUE,
|
|
FALSE,
|
|
NULL,
|
|
davSrvOpen->UnderlyingDeviceObject,
|
|
davSrvOpen->UnderlyingFileObject,
|
|
ByteOffset.QuadPart,
|
|
AllocatedSideBuffer,
|
|
BytesToWrite,
|
|
&(IoStatusBlock));
|
|
NtStatus = IoStatusBlock.Status;
|
|
|
|
if (NtStatus != STATUS_SUCCESS) {
|
|
|
|
DavDbgTrace(DAV_TRACE_ERROR,
|
|
("%ld: MRxDAVWriteContinuation/DavReadWriteFileEx(6). "
|
|
"NtStatus = %d\n", PsGetCurrentThreadId(), NtStatus));
|
|
|
|
goto EXIT_THE_FUNCTION;
|
|
|
|
} else {
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
PDAV_MR_PAGING_WRITE_ENTRY DavPagingWriteEntry = NULL;
|
|
|
|
DavPagingWriteEntry = RxAllocatePool(PagedPool, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
if (DavPagingWriteEntry == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
RtlZeroMemory(DavPagingWriteEntry, sizeof(DAV_MR_PAGING_WRITE_ENTRY));
|
|
|
|
DavPagingWriteEntry->ThisThreadId = PsGetCurrentThreadId();
|
|
|
|
DavPagingWriteEntry->LocByteOffset.QuadPart = ByteOffset.QuadPart;
|
|
|
|
DavPagingWriteEntry->LocByteCount = BytesToWrite;
|
|
|
|
DavPagingWriteEntry->DataBuffer = RxAllocatePool(PagedPool, BytesToWrite);
|
|
|
|
RtlCopyMemory(DavPagingWriteEntry->DataBuffer, AllocatedSideBuffer, BytesToWrite);
|
|
|
|
wcscpy(DavPagingWriteEntry->FileName, DavFcb->FileName);
|
|
|
|
InsertHeadList( &(FileTableEntry->DavMRPagingEntry), &(DavPagingWriteEntry->thisMPagingWriteEntry) );
|
|
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
}
|
|
|
|
if (LengthWritten != BytesToWrite) {
|
|
DbgPrint("MRxDAVWriteContinuation(5): LengthWritten(%x) != BytesToWrite(%x)\n",
|
|
LengthWritten, BytesToWrite);
|
|
}
|
|
|
|
//
|
|
// If we were successful, then we should have ready PAGE_SIZE bytes.
|
|
//
|
|
ASSERT(LengthWritten == BytesToWrite);
|
|
|
|
//
|
|
// Even though we might have written more than ByteCount, the actual
|
|
// amount of User data written is ByteCount bytes.
|
|
//
|
|
TotalLengthActuallyWritten += ByteCount;
|
|
|
|
}
|
|
|
|
//
|
|
// If we acquired the DavFcb resource, then we need to release it since
|
|
// we are done with this "read-modify-write" sequence.
|
|
//
|
|
if (DavFcbResourceAcquired) {
|
|
ExReleaseResourceLite(DavFcb->DavReadModifyWriteLock);
|
|
DavFcbResourceAcquired = FALSE;
|
|
}
|
|
|
|
EXIT_THE_FUNCTION:
|
|
|
|
//
|
|
// We allocate a page size buffer for the read and the write operations. We
|
|
// need to free it now.
|
|
//
|
|
if (AllocatedSideBuffer) {
|
|
RxFreePool(AllocatedSideBuffer);
|
|
}
|
|
|
|
//
|
|
// If we succeeded, we update the filesize in the namecache just in case we
|
|
// extended the file or reduced the filesize. In case when the filesize
|
|
// does not change, this is a no-op.
|
|
//
|
|
if (NtStatus == STATUS_SUCCESS) {
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: MRxDAVWriteContinuation. NewFileSize.HighPart = %x, NewFileSize.LowPart = %x\n",
|
|
PsGetCurrentThreadId(),
|
|
RxContext->pFcb->Header.FileSize.HighPart,
|
|
RxContext->pFcb->Header.FileSize.LowPart));
|
|
MRxDAVUpdateFileInfoCacheFileSize(RxContext, &(RxContext->pFcb->Header.FileSize));
|
|
}
|
|
|
|
//
|
|
// If we allocated an NtFileName to do the create, we need to free it now.
|
|
//
|
|
if (NtFileName) {
|
|
RxFreePool(NtFileName);
|
|
}
|
|
|
|
//
|
|
// If we acquired the DavFcb resource and came down through some error path,
|
|
// and have not released the resource then we need to release it now.
|
|
//
|
|
if (DavFcbResourceAcquired) {
|
|
ExReleaseResourceLite(DavFcb->DavReadModifyWriteLock);
|
|
DavFcbResourceAcquired = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we created a LocalFileHandle, we need to close it by posting it to
|
|
// a system worker thread. This is because we cannot be calling ZwClose
|
|
// in the write path of a MappedPageWriter thread. Moreover this handle
|
|
// was created in the kernel handle table and hence can be closed in any
|
|
// system thread. Store the LocalFileHandle in the MRxContext[1] pointer
|
|
// of the RxContext. The AsyncEngineContext of this operation is stored
|
|
// in the MRxContext[0] pointer. We need to keep the RxContext alive till
|
|
// the worker thread that picks up this request completes it. Hence we
|
|
// remove the reference we took (on the RxContext) at the beginning of this
|
|
// routine in the MRxDAVCloseTheFileHandle routine. If the handle did not
|
|
// get created (which means the request failed), we remove the reference
|
|
// right here by calling UMRxResumeAsyncEngineContext.
|
|
//
|
|
if (LocalFileHandle != INVALID_HANDLE_VALUE) {
|
|
RxContext->MRxContext[1] = LocalFileHandle;
|
|
RxPostToWorkerThread(RxContext->RxDeviceObject,
|
|
CriticalWorkQueue,
|
|
&(AsyncEngineContext->WorkQueueItem),
|
|
MRxDAVCloseTheFileHandle,
|
|
RxContext);
|
|
} else {
|
|
//
|
|
// We need to remove the reference we took at the beginning of this
|
|
// routine.
|
|
//
|
|
UMRxResumeAsyncEngineContext(RxContext);
|
|
}
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: Leaving MRxDAVWriteContinuation. NtStatus = %08lx, "
|
|
"TotalLengthActuallyWritten = %d\n",
|
|
PsGetCurrentThreadId(), NtStatus, TotalLengthActuallyWritten));
|
|
|
|
AsyncEngineContext->Status = NtStatus;
|
|
|
|
#ifdef DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
if (NtStatus == STATUS_SUCCESS) {
|
|
|
|
PDAV_MR_WRITE_ENTRY DavMRWriteEntry = NULL;
|
|
PBYTE ThisBuffer = NULL;
|
|
|
|
if ( RxContext->pRelevantSrvOpen->pAlreadyPrefixedName != NULL &&
|
|
RxContext->pRelevantSrvOpen->pAlreadyPrefixedName->Length > 0 ) {
|
|
|
|
|
|
DavMRWriteEntry = RxAllocatePool(PagedPool, sizeof(DAV_MR_WRITE_ENTRY));
|
|
if (DavMRWriteEntry == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
RtlZeroMemory(DavMRWriteEntry, sizeof(DAV_RDBSS_WRITE_ENTRY));
|
|
|
|
DavMRWriteEntry->DataBuffer = RxAllocatePool(PagedPool, LowIoContext->ParamsFor.ReadWrite.ByteCount);
|
|
if (DavMRWriteEntry->DataBuffer == NULL) {
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
ThisBuffer = RxLowIoGetBufferAddress(RxContext);
|
|
|
|
RtlCopyMemory((PBYTE)DavMRWriteEntry->DataBuffer,
|
|
ThisBuffer,
|
|
LowIoContext->ParamsFor.ReadWrite.ByteCount);
|
|
|
|
wcscpy(DavMRWriteEntry->FileName, DavFcb->FileName);
|
|
|
|
DavMRWriteEntry->ThisThreadId = PsGetCurrentThreadId();
|
|
|
|
DavMRWriteEntry->LocByteCount = LowIoContext->ParamsFor.ReadWrite.ByteCount;
|
|
|
|
DavMRWriteEntry->LocByteOffset.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
|
|
|
|
InsertHeadList( &(FileTableEntry->DavMREntry), &(DavMRWriteEntry->thisMWriteEntry) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // DAV_DEBUG_READ_WRITE_CLOSE_PATH
|
|
|
|
//
|
|
// We need to set these values in the RxContext. There is code in RDBSS
|
|
// which takes care of putting these values in the IRP.
|
|
//
|
|
RxContext->StoredStatus = NtStatus;
|
|
RxContext->InformationToReturn = TotalLengthActuallyWritten;
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
MRxDAVExtendForCache(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PLARGE_INTEGER NewFileSize,
|
|
OUT PLARGE_INTEGER NewAllocationSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines reserves the necessary space for a file which is being
|
|
extended. This reservation occurs before the actual write takes place. This
|
|
routine handles the case for a cached file.
|
|
|
|
Arguments:
|
|
|
|
RxContext - The RDBSS context.
|
|
|
|
NewFileSize - The new file size after the write.
|
|
|
|
NewAllocationSize - The allocation size reserved.
|
|
|
|
Return Value:
|
|
|
|
The return status for the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
NewAllocationSize->QuadPart = NewFileSize->QuadPart;
|
|
MRxDAVUpdateFileInfoCacheFileSize(RxContext, NewFileSize);
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
MRxDAVExtendForNonCache(
|
|
IN OUT PRX_CONTEXT RxContext,
|
|
IN OUT PLARGE_INTEGER NewFileSize,
|
|
OUT PLARGE_INTEGER NewAllocationSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routines reserves the necessary space for a file which is being
|
|
extended. This reservation occurs before the actual write takes place. This
|
|
routine handles the case for a non-cached file.
|
|
|
|
Arguments:
|
|
|
|
RxContext - The RDBSS context.
|
|
|
|
NewFileSize - The new file size after the write.
|
|
|
|
NewAllocationSize - The allocation size reserved.
|
|
|
|
Return Value:
|
|
|
|
The return status for the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
NewAllocationSize->QuadPart = NewFileSize->QuadPart;
|
|
MRxDAVUpdateFileInfoCacheFileSize(RxContext, NewFileSize);
|
|
|
|
return NtStatus;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
MRxDAVFastIoWrite(
|
|
IN PFILE_OBJECT FileObject,
|
|
IN PLARGE_INTEGER FileOffset,
|
|
IN ULONG Length,
|
|
IN BOOLEAN Wait,
|
|
IN ULONG LockKey,
|
|
OUT PVOID Buffer,
|
|
OUT PIO_STATUS_BLOCK IoStatus,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that handles fast I/O for write operation.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE (succeeded) or FALSE.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN ReturnVal = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
DavDbgTrace(DAV_TRACE_DETAIL,
|
|
("%ld: Entered MRxDAVFastIoWrite.\n", PsGetCurrentThreadId()));
|
|
|
|
IoStatus->Status = STATUS_NOT_IMPLEMENTED;
|
|
IoStatus->Information = 0;
|
|
|
|
return (ReturnVal);
|
|
}
|
|
|
|
|
|
VOID
|
|
MRxDAVCloseTheFileHandle(
|
|
PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the routine that is called in the context of a worker thread to
|
|
close the handle created in the MRxDAVWriteContinuation routine.
|
|
|
|
Arguments:
|
|
|
|
RxContext - The RxContext of this write operation.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
HANDLE LocalFileHandle = INVALID_HANDLE_VALUE;
|
|
|
|
PAGED_CODE();
|
|
|
|
LocalFileHandle = RxContext->MRxContext[1];
|
|
|
|
ZwClose(LocalFileHandle);
|
|
|
|
RxContext->MRxContext[1] = NULL;
|
|
|
|
//
|
|
// We need to remove the reference we took at the beginning of the
|
|
// routine MRxDAVWriteContinuation.
|
|
//
|
|
UMRxResumeAsyncEngineContext(RxContext);
|
|
|
|
return;
|
|
}
|
|
|