mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
1228 lines
50 KiB
C++
1228 lines
50 KiB
C++
////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
|
|
// All rights reserved
|
|
// This file was released under the GPLv2 on June 2015.
|
|
////////////////////////////////////////////////////////////////////
|
|
/*************************************************************************
|
|
*
|
|
* File: Write.cpp
|
|
*
|
|
* Module: UDF File System Driver (Kernel mode execution only)
|
|
*
|
|
* Description:
|
|
* Contains code to handle the "Write" dispatch entry point.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#include "udffs.h"
|
|
|
|
// define the file specific bug-check id
|
|
#define UDF_BUG_CHECK_ID UDF_FILE_WRITE
|
|
|
|
#ifndef UDF_READ_ONLY_BUILD
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFWrite()
|
|
*
|
|
* Description:
|
|
* The I/O Manager will invoke this routine to handle a write
|
|
* request
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL (invocation at higher IRQL will cause execution
|
|
* to be deferred to a worker thread context)
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
NTAPI
|
|
UDFWrite(
|
|
PDEVICE_OBJECT DeviceObject, // the logical volume device object
|
|
PIRP Irp // I/O Request Packet
|
|
)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PtrUDFIrpContext PtrIrpContext = NULL;
|
|
BOOLEAN AreWeTopLevel = FALSE;
|
|
|
|
TmPrint(("UDFWrite: , thrd:%8.8x\n",PsGetCurrentThread()));
|
|
|
|
FsRtlEnterFileSystem();
|
|
ASSERT(DeviceObject);
|
|
ASSERT(Irp);
|
|
|
|
// set the top level context
|
|
AreWeTopLevel = UDFIsIrpTopLevel(Irp);
|
|
ASSERT(!UDFIsFSDevObj(DeviceObject));
|
|
|
|
_SEH2_TRY {
|
|
|
|
// get an IRP context structure and issue the request
|
|
PtrIrpContext = UDFAllocateIrpContext(Irp, DeviceObject);
|
|
if(PtrIrpContext) {
|
|
|
|
RC = UDFCommonWrite(PtrIrpContext, Irp);
|
|
|
|
} else {
|
|
RC = STATUS_INSUFFICIENT_RESOURCES;
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = 0;
|
|
// complete the IRP
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
|
|
} _SEH2_EXCEPT (UDFExceptionFilter(PtrIrpContext, _SEH2_GetExceptionInformation())) {
|
|
|
|
RC = UDFExceptionHandler(PtrIrpContext, Irp);
|
|
|
|
UDFLogEvent(UDF_ERROR_INTERNAL_ERROR, RC);
|
|
} _SEH2_END;
|
|
|
|
if (AreWeTopLevel) {
|
|
IoSetTopLevelIrp(NULL);
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return(RC);
|
|
} // end UDFWrite()
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFCommonWrite()
|
|
*
|
|
* Description:
|
|
* The actual work is performed here. This routine may be invoked in one'
|
|
* of the two possible contexts:
|
|
* (a) in the context of a system worker thread
|
|
* (b) in the context of the original caller
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: STATUS_SUCCESS/Error
|
|
*
|
|
*************************************************************************/
|
|
NTSTATUS
|
|
UDFCommonWrite(
|
|
PtrUDFIrpContext PtrIrpContext,
|
|
PIRP Irp)
|
|
{
|
|
NTSTATUS RC = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
LARGE_INTEGER ByteOffset;
|
|
ULONG WriteLength = 0, TruncatedLength = 0;
|
|
SIZE_T NumberBytesWritten = 0;
|
|
PFILE_OBJECT FileObject = NULL;
|
|
PtrUDFFCB Fcb = NULL;
|
|
PtrUDFCCB Ccb = NULL;
|
|
PVCB Vcb = NULL;
|
|
PtrUDFNTRequiredFCB NtReqFcb = NULL;
|
|
PERESOURCE PtrResourceAcquired = NULL;
|
|
PERESOURCE PtrResourceAcquired2 = NULL;
|
|
PVOID SystemBuffer = NULL;
|
|
// PVOID TmpBuffer = NULL;
|
|
// uint32 KeyValue = 0;
|
|
PIRP TopIrp;
|
|
|
|
LONGLONG ASize;
|
|
LONGLONG OldVDL;
|
|
|
|
ULONG Res1Acq = 0;
|
|
ULONG Res2Acq = 0;
|
|
|
|
BOOLEAN CacheLocked = FALSE;
|
|
|
|
BOOLEAN CanWait = FALSE;
|
|
BOOLEAN PagingIo = FALSE;
|
|
BOOLEAN NonBufferedIo = FALSE;
|
|
BOOLEAN SynchronousIo = FALSE;
|
|
BOOLEAN IsThisADeferredWrite = FALSE;
|
|
BOOLEAN WriteToEOF = FALSE;
|
|
BOOLEAN Resized = FALSE;
|
|
BOOLEAN RecursiveWriteThrough = FALSE;
|
|
BOOLEAN WriteFileSizeToDirNdx = FALSE;
|
|
BOOLEAN ZeroBlock = FALSE;
|
|
BOOLEAN VcbAcquired = FALSE;
|
|
BOOLEAN ZeroBlockDone = FALSE;
|
|
|
|
TmPrint(("UDFCommonWrite: irp %x\n", Irp));
|
|
|
|
_SEH2_TRY {
|
|
|
|
|
|
TopIrp = IoGetTopLevelIrp();
|
|
|
|
switch((ULONG_PTR)TopIrp) {
|
|
case FSRTL_FSP_TOP_LEVEL_IRP:
|
|
UDFPrint((" FSRTL_FSP_TOP_LEVEL_IRP\n"));
|
|
break;
|
|
case FSRTL_CACHE_TOP_LEVEL_IRP:
|
|
UDFPrint((" FSRTL_CACHE_TOP_LEVEL_IRP\n"));
|
|
break;
|
|
case FSRTL_MOD_WRITE_TOP_LEVEL_IRP:
|
|
UDFPrint((" FSRTL_MOD_WRITE_TOP_LEVEL_IRP\n"));
|
|
break;
|
|
case FSRTL_FAST_IO_TOP_LEVEL_IRP:
|
|
UDFPrint((" FSRTL_FAST_IO_TOP_LEVEL_IRP\n"));
|
|
BrutePoint();
|
|
break;
|
|
case NULL:
|
|
UDFPrint((" NULL TOP_LEVEL_IRP\n"));
|
|
break;
|
|
default:
|
|
if(TopIrp == Irp) {
|
|
UDFPrint((" TOP_LEVEL_IRP\n"));
|
|
} else {
|
|
UDFPrint((" RECURSIVE_IRP, TOP = %x\n", TopIrp));
|
|
}
|
|
break;
|
|
}
|
|
|
|
// First, get a pointer to the current I/O stack location
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
ASSERT(IrpSp);
|
|
MmPrint((" Enter Irp, MDL=%x\n", Irp->MdlAddress));
|
|
if(Irp->MdlAddress) {
|
|
UDFTouch(Irp->MdlAddress);
|
|
}
|
|
|
|
FileObject = IrpSp->FileObject;
|
|
ASSERT(FileObject);
|
|
|
|
// If this happens to be a MDL write complete request, then
|
|
// allocated MDL can be freed. This may cause a recursive write
|
|
// back into the FSD.
|
|
if (IrpSp->MinorFunction & IRP_MN_COMPLETE) {
|
|
// Caller wants to tell the Cache Manager that a previously
|
|
// allocated MDL can be freed.
|
|
UDFMdlComplete(PtrIrpContext, Irp, IrpSp, FALSE);
|
|
// The IRP has been completed.
|
|
try_return(RC = STATUS_SUCCESS);
|
|
}
|
|
|
|
// If this is a request at IRQL DISPATCH_LEVEL, then post the request
|
|
if (IrpSp->MinorFunction & IRP_MN_DPC) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
|
|
// Get the FCB and CCB pointers
|
|
Ccb = (PtrUDFCCB)(FileObject->FsContext2);
|
|
ASSERT(Ccb);
|
|
Fcb = Ccb->Fcb;
|
|
ASSERT(Fcb);
|
|
Vcb = Fcb->Vcb;
|
|
|
|
if(Fcb->FCBFlags & UDF_FCB_DELETED) {
|
|
ASSERT(FALSE);
|
|
try_return(RC = STATUS_TOO_LATE);
|
|
}
|
|
|
|
// is this operation allowed ?
|
|
if(Vcb->VCBFlags & UDF_VCB_FLAGS_MEDIA_READ_ONLY) {
|
|
try_return(RC = STATUS_ACCESS_DENIED);
|
|
}
|
|
Vcb->VCBFlags |= UDF_VCB_SKIP_EJECT_CHECK;
|
|
|
|
// Disk based file systems might decide to verify the logical volume
|
|
// (if required and only if removable media are supported) at this time
|
|
// As soon as Tray is locked, we needn't call UDFVerifyVcb()
|
|
|
|
ByteOffset = IrpSp->Parameters.Write.ByteOffset;
|
|
|
|
CanWait = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_CAN_BLOCK) ? TRUE : FALSE;
|
|
PagingIo = (Irp->Flags & IRP_PAGING_IO) ? TRUE : FALSE;
|
|
NonBufferedIo = (Irp->Flags & IRP_NOCACHE) ? TRUE : FALSE;
|
|
SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO) ? TRUE : FALSE;
|
|
UDFPrint((" Flags: %s; %s; %s; %s; Irp(W): %8.8x\n",
|
|
CanWait ? "Wt" : "nw", PagingIo ? "Pg" : "np",
|
|
NonBufferedIo ? "NBuf" : "buff", SynchronousIo ? "Snc" : "Asc",
|
|
Irp->Flags));
|
|
|
|
NtReqFcb = Fcb->NTRequiredFCB;
|
|
|
|
Res1Acq = UDFIsResourceAcquired(&(NtReqFcb->MainResource));
|
|
if(!Res1Acq) {
|
|
Res1Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES1_ACQ;
|
|
}
|
|
Res2Acq = UDFIsResourceAcquired(&(NtReqFcb->PagingIoResource));
|
|
if(!Res2Acq) {
|
|
Res2Acq = PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_RES2_ACQ;
|
|
}
|
|
|
|
if(!NonBufferedIo &&
|
|
(Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB)) {
|
|
if((Fcb->NodeIdentifier.NodeType != UDF_NODE_TYPE_VCB) &&
|
|
UDFIsAStream(Fcb->FileInfo)) {
|
|
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
|
|
FILE_NOTIFY_CHANGE_STREAM_WRITE,
|
|
FILE_ACTION_MODIFIED_STREAM);
|
|
} else {
|
|
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
|
|
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
|
FILE_ACTION_MODIFIED);
|
|
}
|
|
}
|
|
|
|
// Get some of the parameters supplied to us
|
|
WriteLength = IrpSp->Parameters.Write.Length;
|
|
if (WriteLength == 0) {
|
|
// a 0 byte write can be immediately succeeded
|
|
if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) {
|
|
// NT expects changing CurrentByteOffset to zero in this case
|
|
FileObject->CurrentByteOffset.QuadPart = 0;
|
|
}
|
|
try_return(RC);
|
|
}
|
|
|
|
// If this is the normal file we have to check for
|
|
// write access according to the current state of the file locks.
|
|
if (!PagingIo &&
|
|
!FsRtlCheckLockForWriteAccess( &(NtReqFcb->FileLock), Irp) ) {
|
|
try_return( RC = STATUS_FILE_LOCK_CONFLICT );
|
|
}
|
|
|
|
// **********
|
|
// Is this a write of the volume itself ?
|
|
// **********
|
|
if (Fcb->NodeIdentifier.NodeType == UDF_NODE_TYPE_VCB) {
|
|
// Yup, we need to send this on to the disk driver after
|
|
// validation of the offset and length.
|
|
Vcb = (PVCB)(Fcb);
|
|
if(!CanWait)
|
|
try_return(RC = STATUS_PENDING);
|
|
// I dislike the idea of writing to not locked media
|
|
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED)) {
|
|
try_return(RC = STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
if(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_FLUSH2_REQUIRED) {
|
|
|
|
UDFPrint((" UDF_IRP_CONTEXT_FLUSH2_REQUIRED\n"));
|
|
PtrIrpContext->IrpContextFlags &= ~UDF_IRP_CONTEXT_FLUSH2_REQUIRED;
|
|
|
|
if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK)) {
|
|
UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
|
|
}
|
|
#ifdef UDF_DELAYED_CLOSE
|
|
UDFCloseAllDelayed(Vcb);
|
|
#endif //UDF_DELAYED_CLOSE
|
|
|
|
}
|
|
|
|
// Acquire the volume resource exclusive
|
|
UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
|
|
PtrResourceAcquired = &(Vcb->VCBResource);
|
|
|
|
// I dislike the idea of writing to mounted media too, but M$ has another point of view...
|
|
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_MOUNTED) {
|
|
// flush system cache
|
|
UDFFlushLogicalVolume(NULL, NULL, Vcb, 0);
|
|
}
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
/* FIXME */
|
|
if(PagingIo) {
|
|
CollectStatistics(Vcb, MetaDataWrites);
|
|
CollectStatisticsEx(Vcb, MetaDataWriteBytes, NumberBytesWritten);
|
|
}
|
|
#endif
|
|
// Forward the request to the lower level driver
|
|
// Lock the callers buffer
|
|
if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, WriteLength))) {
|
|
try_return(RC);
|
|
}
|
|
SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
|
|
if(!SystemBuffer)
|
|
try_return(RC = STATUS_INVALID_USER_BUFFER);
|
|
// Indicate, that volume contents can change after this operation
|
|
// This flag will force VerifyVolume in future
|
|
UDFPrint((" set UnsafeIoctl\n"));
|
|
Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
|
|
// Make sure, that volume will never be quick-remounted
|
|
// It is very important for ChkUdf utility.
|
|
Vcb->SerialNumber--;
|
|
// Perform actual Write
|
|
RC = UDFTWrite(Vcb, SystemBuffer, WriteLength,
|
|
(ULONG)(ByteOffset.QuadPart >> Vcb->BlockSizeBits),
|
|
&NumberBytesWritten);
|
|
UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
|
|
try_return(RC);
|
|
}
|
|
|
|
if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) {
|
|
try_return(RC = STATUS_ACCESS_DENIED);
|
|
}
|
|
|
|
// back pressure for very smart and fast system cache ;)
|
|
if(!NonBufferedIo) {
|
|
// cached IO
|
|
if(Vcb->VerifyCtx.QueuedCount ||
|
|
Vcb->VerifyCtx.ItemCount >= UDF_MAX_VERIFY_CACHE) {
|
|
UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
|
|
}
|
|
} else {
|
|
if(Vcb->VerifyCtx.ItemCount > UDF_SYS_CACHE_STOP_THR) {
|
|
UDFVVerify(Vcb, UFD_VERIFY_FLAG_WAIT);
|
|
}
|
|
}
|
|
|
|
// The FSD (if it is a "nice" FSD) should check whether it is
|
|
// convenient to allow the write to proceed by utilizing the
|
|
// CcCanIWrite() function call. If it is not convenient to perform
|
|
// the write at this time, we should defer the request for a while.
|
|
// The check should not however be performed for non-cached write
|
|
// operations. To determine whether we are retrying the operation
|
|
// or now, use Flags in the IrpContext structure we have created
|
|
|
|
IsThisADeferredWrite = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_DEFERRED_WRITE) ? TRUE : FALSE;
|
|
|
|
if (!NonBufferedIo) {
|
|
MmPrint((" CcCanIWrite()\n"));
|
|
if (!CcCanIWrite(FileObject, WriteLength, CanWait, IsThisADeferredWrite)) {
|
|
// Cache Manager and/or the VMM does not want us to perform
|
|
// the write at this time. Post the request.
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_DEFERRED_WRITE;
|
|
UDFPrint(("UDFCommonWrite: Defer write\n"));
|
|
MmPrint((" CcDeferWrite()\n"));
|
|
CcDeferWrite(FileObject, UDFDeferredWriteCallBack, PtrIrpContext, Irp, WriteLength, IsThisADeferredWrite);
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
}
|
|
|
|
// If the write request is directed to a page file,
|
|
// send the request directly to the disk
|
|
if (Fcb->FCBFlags & UDF_FCB_PAGE_FILE) {
|
|
NonBufferedIo = TRUE;
|
|
}
|
|
|
|
// We can continue. Check whether this write operation is targeted
|
|
// to a directory object in which case the UDF FSD will disallow
|
|
// the write request.
|
|
if (Fcb->FCBFlags & UDF_FCB_DIRECTORY) {
|
|
RC = STATUS_INVALID_DEVICE_REQUEST;
|
|
try_return(RC);
|
|
}
|
|
|
|
// Validate start offset and length supplied.
|
|
// Here is a special check that determines whether the caller wishes to
|
|
// begin the write at current end-of-file (whatever the value of that
|
|
// offset might be)
|
|
if(ByteOffset.HighPart == (LONG)0xFFFFFFFF) {
|
|
if(ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE) {
|
|
WriteToEOF = TRUE;
|
|
ByteOffset = NtReqFcb->CommonFCBHeader.FileSize;
|
|
} else
|
|
if(ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION) {
|
|
ByteOffset = FileObject->CurrentByteOffset;
|
|
}
|
|
}
|
|
|
|
// Check if this volume has already been shut down. If it has, fail
|
|
// this write request.
|
|
if (Vcb->VCBFlags & UDF_VCB_FLAGS_SHUTDOWN) {
|
|
try_return(RC = STATUS_TOO_LATE);
|
|
}
|
|
|
|
// Paging I/O write operations are special. If paging i/o write
|
|
// requests begin beyond end-of-file, the request should be no-oped
|
|
// If paging i/o
|
|
// requests extend beyond current end of file, they should be truncated
|
|
// to current end-of-file.
|
|
if(PagingIo && (WriteToEOF || ((ByteOffset.QuadPart + WriteLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart))) {
|
|
if (ByteOffset.QuadPart > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) {
|
|
TruncatedLength = 0;
|
|
} else {
|
|
TruncatedLength = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
|
|
}
|
|
if(!TruncatedLength) try_return(RC = STATUS_SUCCESS);
|
|
} else {
|
|
TruncatedLength = WriteLength;
|
|
}
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
/* FIXME */
|
|
if(PagingIo) {
|
|
CollectStatistics(Vcb, UserFileWrites);
|
|
CollectStatisticsEx(Vcb, UserFileWriteBytes, NumberBytesWritten);
|
|
}
|
|
#endif
|
|
|
|
// There are certain complications that arise when the same file stream
|
|
// has been opened for cached and non-cached access. The FSD is then
|
|
// responsible for maintaining a consistent view of the data seen by
|
|
// the caller.
|
|
// If this happens to be a non-buffered I/O, we should __try to flush the
|
|
// cached data (if some other file object has already initiated caching
|
|
// on the file stream). We should also __try to purge the cached
|
|
// information though the purge will probably fail if the file has been
|
|
// mapped into some process' virtual address space
|
|
// WARNING !!! we should not flush data beyond valid data length
|
|
if ( NonBufferedIo &&
|
|
!PagingIo &&
|
|
NtReqFcb->SectionObject.DataSectionObject &&
|
|
TruncatedLength &&
|
|
(ByteOffset.QuadPart < NtReqFcb->CommonFCBHeader.FileSize.QuadPart)) {
|
|
|
|
if(!Res1Acq) {
|
|
// Try to acquire the FCB MainResource exclusively
|
|
if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
PtrResourceAcquired = &(NtReqFcb->MainResource);
|
|
}
|
|
|
|
if(!Res2Acq) {
|
|
// We hold PagingIo shared around the flush to fix a
|
|
// cache coherency problem.
|
|
UDFAcquireSharedStarveExclusive(&(NtReqFcb->PagingIoResource), TRUE );
|
|
PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
|
|
}
|
|
|
|
// Flush and then attempt to purge the cache
|
|
if((ByteOffset.QuadPart + TruncatedLength) > NtReqFcb->CommonFCBHeader.FileSize.QuadPart) {
|
|
NumberBytesWritten = TruncatedLength;
|
|
} else {
|
|
NumberBytesWritten = (ULONG)(NtReqFcb->CommonFCBHeader.FileSize.QuadPart - ByteOffset.QuadPart);
|
|
}
|
|
|
|
MmPrint((" CcFlushCache()\n"));
|
|
CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, NumberBytesWritten, &(Irp->IoStatus));
|
|
|
|
if(PtrResourceAcquired2) {
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
PtrResourceAcquired2 = NULL;
|
|
}
|
|
// If the flush failed, return error to the caller
|
|
if (!NT_SUCCESS(RC = Irp->IoStatus.Status)) {
|
|
NumberBytesWritten = 0;
|
|
try_return(RC);
|
|
}
|
|
|
|
if(!Res2Acq) {
|
|
// Acquiring and immediately dropping the resource serializes
|
|
// us behind any other writes taking place (either from the
|
|
// lazy writer or modified page writer).
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE );
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
}
|
|
|
|
// Attempt the purge and ignore the return code
|
|
MmPrint((" CcPurgeCacheSection()\n"));
|
|
CcPurgeCacheSection(&(NtReqFcb->SectionObject), &ByteOffset,
|
|
NumberBytesWritten, FALSE);
|
|
NumberBytesWritten = 0;
|
|
// We are finished with our flushing and purging
|
|
if(PtrResourceAcquired) {
|
|
UDFReleaseResource(PtrResourceAcquired);
|
|
PtrResourceAcquired = NULL;
|
|
}
|
|
}
|
|
|
|
// Determine if we were called by the lazywriter.
|
|
// We reuse 'IsThisADeferredWrite' here to decrease stack usage
|
|
IsThisADeferredWrite = (NtReqFcb->LazyWriterThreadID == HandleToUlong(PsGetCurrentThreadId()));
|
|
|
|
// Acquire the appropriate FCB resource
|
|
if(PagingIo) {
|
|
// PagingIoResource is already acquired exclusive
|
|
// on LazyWrite condition (see UDFAcqLazyWrite())
|
|
ASSERT(NonBufferedIo);
|
|
if(!IsThisADeferredWrite) {
|
|
if(!Res2Acq) {
|
|
// Try to acquire the FCB PagingIoResource exclusive
|
|
if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
// Remember the resource that was acquired
|
|
PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
|
|
}
|
|
}
|
|
} else {
|
|
// Try to acquire the FCB MainResource shared
|
|
if(NonBufferedIo) {
|
|
if(!Res2Acq) {
|
|
if(!UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
|
|
//if(!UDFAcquireSharedWaitForExclusive(&(NtReqFcb->PagingIoResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
|
|
}
|
|
} else {
|
|
if(!Res1Acq) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
|
|
//if(!UDFAcquireResourceShared(&(NtReqFcb->MainResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
PtrResourceAcquired = &(NtReqFcb->MainResource);
|
|
}
|
|
}
|
|
// Remember the resource that was acquired
|
|
}
|
|
|
|
// Set the flag indicating if Fast I/O is possible
|
|
NtReqFcb->CommonFCBHeader.IsFastIoPossible = UDFIsFastIoPossible(Fcb);
|
|
/* if(NtReqFcb->CommonFCBHeader.IsFastIoPossible == FastIoIsPossible) {
|
|
NtReqFcb->CommonFCBHeader.IsFastIoPossible = FastIoIsQuestionable;
|
|
}*/
|
|
|
|
if ( (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) &&
|
|
(PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_NOT_TOP_LEVEL)) {
|
|
|
|
// This clause determines if the top level request was
|
|
// in the FastIo path.
|
|
if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG) {
|
|
|
|
PIO_STACK_LOCATION IrpStack;
|
|
ASSERT( TopIrp->Type == IO_TYPE_IRP );
|
|
IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
|
|
|
|
// Finally this routine detects if the Top irp was a
|
|
// write to this file and thus we are the writethrough.
|
|
if ((IrpStack->MajorFunction == IRP_MJ_WRITE) &&
|
|
(IrpStack->FileObject->FsContext == FileObject->FsContext)) {
|
|
|
|
RecursiveWriteThrough = TRUE;
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_WRITE_THROUGH;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Here is the deal with ValidDataLength and FileSize:
|
|
//
|
|
// Rule 1: PagingIo is never allowed to extend file size.
|
|
//
|
|
// Rule 2: Only the top level requestor may extend Valid
|
|
// Data Length. This may be paging IO, as when a
|
|
// a user maps a file, but will never be as a result
|
|
// of cache lazy writer writes since they are not the
|
|
// top level request.
|
|
//
|
|
// Rule 3: If, using Rules 1 and 2, we decide we must extend
|
|
// file size or valid data, we take the Fcb exclusive.
|
|
|
|
// Check whether the current request will extend the file size,
|
|
// or the valid data length (if the FSD supports the concept of a
|
|
// valid data length associated with the file stream). In either case,
|
|
// inform the Cache Manager at this time using CcSetFileSizes() about
|
|
// the new file length. Note that real FSD implementations will have to
|
|
// first allocate enough on-disk space at this point (before they
|
|
// inform the Cache Manager about the new size) to ensure that the write
|
|
// will subsequently not fail due to lack of disk space.
|
|
|
|
OldVDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
|
|
ZeroBlock = (ByteOffset.QuadPart > OldVDL);
|
|
|
|
if (!PagingIo &&
|
|
!RecursiveWriteThrough &&
|
|
!IsThisADeferredWrite) {
|
|
|
|
BOOLEAN ExtendFS;
|
|
|
|
ExtendFS = (ByteOffset.QuadPart + TruncatedLength > NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
|
|
|
|
if( WriteToEOF || ZeroBlock || ExtendFS) {
|
|
// we are extending the file;
|
|
|
|
if(!CanWait)
|
|
try_return(RC = STATUS_PENDING);
|
|
// CanWait = TRUE;
|
|
// Release any resources acquired above ...
|
|
if (PtrResourceAcquired2) {
|
|
UDFReleaseResource(PtrResourceAcquired2);
|
|
PtrResourceAcquired2 = NULL;
|
|
}
|
|
if (PtrResourceAcquired) {
|
|
UDFReleaseResource(PtrResourceAcquired);
|
|
PtrResourceAcquired = NULL;
|
|
}
|
|
if(!UDFAcquireResourceShared(&(Vcb->VCBResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
VcbAcquired = TRUE;
|
|
if(!Res1Acq) {
|
|
// Try to acquire the FCB MainResource exclusively
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
if(!UDFAcquireResourceExclusive(&(NtReqFcb->MainResource), CanWait)) {
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
// Remember the resource that was acquired
|
|
PtrResourceAcquired = &(NtReqFcb->MainResource);
|
|
}
|
|
|
|
if(!Res2Acq) {
|
|
// allocate space...
|
|
AdPrint((" Try to acquire PagingIoRes\n"));
|
|
UDFAcquireResourceExclusive(&(NtReqFcb->PagingIoResource), TRUE );
|
|
PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
|
|
}
|
|
AdPrint((" PagingIoRes Ok, Resizing...\n"));
|
|
|
|
if(ExtendFS) {
|
|
RC = UDFResizeFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart + TruncatedLength);
|
|
|
|
if(!NT_SUCCESS(RC)) {
|
|
if(PtrResourceAcquired2) {
|
|
UDFReleaseResource(&(NtReqFcb->PagingIoResource));
|
|
PtrResourceAcquired2 = NULL;
|
|
}
|
|
try_return(RC);
|
|
}
|
|
Resized = TRUE;
|
|
// ... and inform the Cache Manager about it
|
|
NtReqFcb->CommonFCBHeader.FileSize.QuadPart = ByteOffset.QuadPart + TruncatedLength;
|
|
NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
|
|
if(!Vcb->LowFreeSpace) {
|
|
NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE*9-1);
|
|
} else {
|
|
NtReqFcb->CommonFCBHeader.AllocationSize.QuadPart += (PAGE_SIZE-1);
|
|
}
|
|
NtReqFcb->CommonFCBHeader.AllocationSize.LowPart &= ~(PAGE_SIZE-1);
|
|
}
|
|
|
|
UDFPrint(("UDFCommonWrite: Set size %x (alloc size %x)\n", ByteOffset.LowPart + TruncatedLength, NtReqFcb->CommonFCBHeader.AllocationSize.LowPart));
|
|
if (CcIsFileCached(FileObject)) {
|
|
if(ExtendFS) {
|
|
MmPrint((" CcSetFileSizes()\n"));
|
|
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&(NtReqFcb->CommonFCBHeader.AllocationSize));
|
|
NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
|
|
}
|
|
// Attempt to Zero newly added fragment
|
|
// and ignore the return code
|
|
// This should be done to inform cache manager
|
|
// that given extent has no cached data
|
|
// (Otherwise, CM sometimes thinks that it has)
|
|
if(ZeroBlock) {
|
|
NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
|
|
ThPrint((" UDFZeroDataEx(1)\n"));
|
|
UDFZeroDataEx(NtReqFcb,
|
|
OldVDL,
|
|
/*ByteOffset.QuadPart*/ NtReqFcb->CommonFCBHeader.FileSize.QuadPart - OldVDL,
|
|
CanWait, Vcb, FileObject);
|
|
#ifdef UDF_DBG
|
|
ZeroBlockDone = TRUE;
|
|
#endif //UDF_DBG
|
|
}
|
|
}
|
|
if (PtrResourceAcquired2) {
|
|
UDFReleaseResource(PtrResourceAcquired2);
|
|
PtrResourceAcquired2 = NULL;
|
|
}
|
|
|
|
// Inform any pending IRPs (notify change directory).
|
|
if(UDFIsAStream(Fcb->FileInfo)) {
|
|
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
|
|
FILE_NOTIFY_CHANGE_STREAM_SIZE,
|
|
FILE_ACTION_MODIFIED_STREAM);
|
|
} else {
|
|
UDFNotifyFullReportChange( Vcb, Fcb->FileInfo,
|
|
FILE_NOTIFY_CHANGE_SIZE,
|
|
FILE_ACTION_MODIFIED);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef UDF_DISABLE_SYSTEM_CACHE_MANAGER
|
|
NonBufferedIo = TRUE;
|
|
#endif
|
|
if(Fcb && Fcb->FileInfo && Fcb->FileInfo->Dloc) {
|
|
AdPrint(("UDFCommonWrite: DataLoc %x, Mapping %x\n", Fcb->FileInfo->Dloc->DataLoc, Fcb->FileInfo->Dloc->DataLoc.Mapping));
|
|
}
|
|
|
|
// Branch here for cached vs non-cached I/O
|
|
if (!NonBufferedIo) {
|
|
|
|
// The caller wishes to perform cached I/O. Initiate caching if
|
|
// this is the first cached I/O operation using this file object
|
|
if (!FileObject->PrivateCacheMap) {
|
|
// This is the first cached I/O operation. You must ensure
|
|
// that the FCB Common FCB Header contains valid sizes at this time
|
|
UDFPrint(("UDFCommonWrite: Init system cache\n"));
|
|
MmPrint((" CcInitializeCacheMap()\n"));
|
|
CcInitializeCacheMap(FileObject, (PCC_FILE_SIZES)(&(NtReqFcb->CommonFCBHeader.AllocationSize)),
|
|
FALSE, // We will not utilize pin access for this file
|
|
&(UDFGlobalData.CacheMgrCallBacks), // callbacks
|
|
NtReqFcb); // The context used in callbacks
|
|
MmPrint((" CcSetReadAheadGranularity()\n"));
|
|
CcSetReadAheadGranularity(FileObject, Vcb->SystemCacheGran);
|
|
|
|
}
|
|
|
|
if(ZeroBlock && !ZeroBlockDone) {
|
|
ThPrint((" UDFZeroDataEx(2)\n"));
|
|
UDFZeroDataEx(NtReqFcb,
|
|
OldVDL,
|
|
/*ByteOffset.QuadPart*/ ByteOffset.QuadPart + TruncatedLength - OldVDL,
|
|
CanWait, Vcb, FileObject);
|
|
if(ByteOffset.LowPart & (PAGE_SIZE-1)) {
|
|
}
|
|
}
|
|
|
|
WriteFileSizeToDirNdx = (PtrIrpContext->IrpContextFlags & UDF_IRP_CONTEXT_WRITE_THROUGH) ?
|
|
TRUE : FALSE;
|
|
// Check and see if this request requires a MDL returned to the caller
|
|
if (IrpSp->MinorFunction & IRP_MN_MDL) {
|
|
// Caller does want a MDL returned. Note that this mode
|
|
// implies that the caller is prepared to block
|
|
MmPrint((" CcPrepareMdlWrite()\n"));
|
|
// CcPrepareMdlWrite(FileObject, &ByteOffset, TruncatedLength, &(Irp->MdlAddress), &(Irp->IoStatus));
|
|
// NumberBytesWritten = Irp->IoStatus.Information;
|
|
// RC = Irp->IoStatus.Status;
|
|
|
|
NumberBytesWritten = 0;
|
|
RC = STATUS_INVALID_PARAMETER;
|
|
|
|
try_return(RC);
|
|
}
|
|
|
|
if(NtReqFcb->SectionObject.DataSectionObject &&
|
|
TruncatedLength >= 0x10000 &&
|
|
ByteOffset.LowPart &&
|
|
!(ByteOffset.LowPart & 0x00ffffff)) {
|
|
|
|
//if(WinVer_Id() < WinVer_2k) {
|
|
//LARGE_INTEGER flush_offs;
|
|
//flush_offs.QuadPart = ByteOffset.QuadPart - 0x100*0x10000;
|
|
MmPrint((" CcFlushCache() 16Mb\n"));
|
|
//CcFlushCache(&(NtReqFcb->SectionObject), &ByteOffset, 0x100*0x10000, &(Irp->IoStatus));
|
|
|
|
// there was a nice idea: flush just previous part. But it doesn't work
|
|
CcFlushCache(&(NtReqFcb->SectionObject), NULL, 0, &(Irp->IoStatus));
|
|
//}
|
|
}
|
|
|
|
// This is a regular run-of-the-mill cached I/O request. Let the
|
|
// Cache Manager worry about it!
|
|
// First though, we need a buffer pointer (address) that is valid
|
|
|
|
// We needn't call CcZeroData 'cause udf_info.cpp will care about it
|
|
SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
|
|
if(!SystemBuffer)
|
|
try_return(RC = STATUS_INVALID_USER_BUFFER);
|
|
ASSERT(SystemBuffer);
|
|
NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
|
|
PerfPrint(("UDFCommonWrite: CcCopyWrite %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
|
|
MmPrint((" CcCopyWrite()\n"));
|
|
if(!CcCopyWrite(FileObject, &(ByteOffset), TruncatedLength, CanWait, SystemBuffer)) {
|
|
// The caller was not prepared to block and data is not immediately
|
|
// available in the system cache
|
|
// Mark Irp Pending ...
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
|
|
UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
|
|
// We have the data
|
|
RC = STATUS_SUCCESS;
|
|
NumberBytesWritten = TruncatedLength;
|
|
|
|
try_return(RC);
|
|
|
|
} else {
|
|
|
|
MmPrint((" Write NonBufferedIo\n"));
|
|
|
|
// We needn't call CcZeroData here (like in Fat driver)
|
|
// 'cause we've already done it above
|
|
// (see call to UDFZeroDataEx() )
|
|
if (!RecursiveWriteThrough &&
|
|
!IsThisADeferredWrite &&
|
|
(OldVDL < ByteOffset.QuadPart)) {
|
|
#ifdef UDF_DBG
|
|
ASSERT(!ZeroBlockDone);
|
|
#endif //UDF_DBG
|
|
UDFZeroDataEx(NtReqFcb,
|
|
OldVDL,
|
|
/*ByteOffset.QuadPart*/ ByteOffset.QuadPart - OldVDL,
|
|
CanWait, Vcb, FileObject);
|
|
}
|
|
if(OldVDL < (ByteOffset.QuadPart + TruncatedLength)) {
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = ByteOffset.QuadPart + TruncatedLength;
|
|
}
|
|
|
|
#if 1
|
|
if((ULONG_PTR)TopIrp == FSRTL_MOD_WRITE_TOP_LEVEL_IRP) {
|
|
UDFPrint(("FSRTL_MOD_WRITE_TOP_LEVEL_IRP => CanWait\n"));
|
|
CanWait = TRUE;
|
|
} else
|
|
if((ULONG_PTR)TopIrp == FSRTL_CACHE_TOP_LEVEL_IRP) {
|
|
UDFPrint(("FSRTL_CACHE_TOP_LEVEL_IRP => CanWait\n"));
|
|
CanWait = TRUE;
|
|
}
|
|
|
|
if(NtReqFcb->AcqSectionCount || NtReqFcb->AcqFlushCount) {
|
|
MmPrint((" AcqCount (%d/%d)=> CanWait ?\n", NtReqFcb->AcqSectionCount, NtReqFcb->AcqFlushCount));
|
|
CanWait = TRUE;
|
|
} else
|
|
{}
|
|
/* if((TopIrp != Irp)) {
|
|
UDFPrint(("(TopIrp != Irp) => CanWait\n"));
|
|
CanWait = TRUE;
|
|
} else*/
|
|
#endif
|
|
if(KeGetCurrentIrql() > PASSIVE_LEVEL) {
|
|
MmPrint((" !PASSIVE_LEVEL\n"));
|
|
CanWait = FALSE;
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_FORCED_POST;
|
|
}
|
|
// Successful check will cause WCache lock
|
|
if(!CanWait && UDFIsFileCached__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength, TRUE)) {
|
|
UDFPrint(("UDFCommonWrite: Cached => CanWait\n"));
|
|
CacheLocked = TRUE;
|
|
CanWait = TRUE;
|
|
}
|
|
// Send the request to lower level drivers
|
|
if(!CanWait) {
|
|
UDFPrint(("UDFCommonWrite: Post physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
|
|
|
|
try_return(RC = STATUS_PENDING);
|
|
}
|
|
|
|
if(!Res2Acq) {
|
|
if(UDFAcquireResourceExclusiveWithCheck(&(NtReqFcb->PagingIoResource))) {
|
|
PtrResourceAcquired2 = &(NtReqFcb->PagingIoResource);
|
|
}
|
|
}
|
|
|
|
PerfPrint(("UDFCommonWrite: Physical write %x bytes at %x\n", TruncatedLength, ByteOffset.LowPart));
|
|
|
|
// Lock the callers buffer
|
|
if (!NT_SUCCESS(RC = UDFLockCallersBuffer(PtrIrpContext, Irp, TRUE, TruncatedLength))) {
|
|
try_return(RC);
|
|
}
|
|
|
|
SystemBuffer = UDFGetCallersBuffer(PtrIrpContext, Irp);
|
|
if(!SystemBuffer) {
|
|
try_return(RC = STATUS_INVALID_USER_BUFFER);
|
|
}
|
|
NtReqFcb->NtReqFCBFlags |= UDF_NTREQ_FCB_MODIFIED;
|
|
RC = UDFWriteFile__(Vcb, Fcb->FileInfo, ByteOffset.QuadPart, TruncatedLength,
|
|
CacheLocked, (PCHAR)SystemBuffer, &NumberBytesWritten);
|
|
|
|
UDFUnlockCallersBuffer(PtrIrpContext, Irp, SystemBuffer);
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
/* FIXME */
|
|
if(PagingIo) {
|
|
CollectStatistics(Vcb, UserDiskWrites);
|
|
} else {
|
|
CollectStatistics2(Vcb, NonCachedDiskWrites);
|
|
}
|
|
#endif
|
|
WriteFileSizeToDirNdx = TRUE;
|
|
|
|
try_return(RC);
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} _SEH2_FINALLY {
|
|
|
|
if(CacheLocked) {
|
|
WCacheEODirect__(&(Vcb->FastCache), Vcb);
|
|
}
|
|
|
|
// Release any resources acquired here ...
|
|
if(PtrResourceAcquired2) {
|
|
UDFReleaseResource(PtrResourceAcquired2);
|
|
}
|
|
if(PtrResourceAcquired) {
|
|
if(NtReqFcb &&
|
|
(PtrResourceAcquired ==
|
|
&(NtReqFcb->MainResource))) {
|
|
UDF_CHECK_PAGING_IO_RESOURCE(NtReqFcb);
|
|
}
|
|
UDFReleaseResource(PtrResourceAcquired);
|
|
}
|
|
if(VcbAcquired) {
|
|
UDFReleaseResource(&(Vcb->VCBResource));
|
|
}
|
|
|
|
// Post IRP if required
|
|
if(RC == STATUS_PENDING) {
|
|
|
|
// Lock the callers buffer here. Then invoke a common routine to
|
|
// perform the post operation.
|
|
if (!(IrpSp->MinorFunction & IRP_MN_MDL)) {
|
|
RC = UDFLockCallersBuffer(PtrIrpContext, Irp, FALSE, WriteLength);
|
|
ASSERT(NT_SUCCESS(RC));
|
|
}
|
|
if(PagingIo) {
|
|
if(Res1Acq) {
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES1_ACQ;
|
|
}
|
|
if(Res2Acq) {
|
|
PtrIrpContext->IrpContextFlags |= UDF_IRP_CONTEXT_RES2_ACQ;
|
|
}
|
|
}
|
|
|
|
// Perform the post operation which will mark the IRP pending
|
|
// and will return STATUS_PENDING back to us
|
|
RC = UDFPostRequest(PtrIrpContext, Irp);
|
|
|
|
} else {
|
|
// For synchronous I/O, the FSD must maintain the current byte offset
|
|
// Do not do this however, if I/O is marked as paging-io
|
|
if (SynchronousIo && !PagingIo && NT_SUCCESS(RC)) {
|
|
FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + NumberBytesWritten;
|
|
}
|
|
// If the write completed successfully and this was not a paging-io
|
|
// operation, set a flag in the CCB that indicates that a write was
|
|
// performed and that the file time should be updated at cleanup
|
|
if (NT_SUCCESS(RC) && !PagingIo) {
|
|
Ccb->CCBFlags |= UDF_CCB_MODIFIED;
|
|
// If the file size was changed, set a flag in the FCB indicating that
|
|
// this occurred.
|
|
FileObject->Flags |= FO_FILE_MODIFIED;
|
|
if(Resized) {
|
|
if(!WriteFileSizeToDirNdx) {
|
|
FileObject->Flags |= FO_FILE_SIZE_CHANGED;
|
|
} else {
|
|
ASize = UDFGetFileAllocationSize(Vcb, Fcb->FileInfo);
|
|
UDFSetFileSizeInDirNdx(Vcb, Fcb->FileInfo, &ASize);
|
|
}
|
|
}
|
|
// Update ValidDataLength
|
|
if(!IsThisADeferredWrite &&
|
|
NtReqFcb) {
|
|
if(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart < (ByteOffset.QuadPart + NumberBytesWritten)) {
|
|
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart =
|
|
min(NtReqFcb->CommonFCBHeader.FileSize.QuadPart,
|
|
ByteOffset.QuadPart + NumberBytesWritten);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the request failed, and we had done some nasty stuff like
|
|
// extending the file size (including informing the Cache Manager
|
|
// about the new file size), and allocating on-disk space etc., undo
|
|
// it at this time.
|
|
|
|
// Can complete the IRP here if no exception was encountered
|
|
if(!_SEH2_AbnormalTermination() &&
|
|
Irp) {
|
|
Irp->IoStatus.Status = RC;
|
|
Irp->IoStatus.Information = NumberBytesWritten;
|
|
// complete the IRP
|
|
MmPrint((" Complete Irp, MDL=%x\n", Irp->MdlAddress));
|
|
if(Irp->MdlAddress) {
|
|
UDFTouch(Irp->MdlAddress);
|
|
}
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
}
|
|
// Free up the Irp Context
|
|
UDFReleaseIrpContext(PtrIrpContext);
|
|
|
|
} // can we complete the IRP ?
|
|
} _SEH2_END; // end of "__finally" processing
|
|
|
|
UDFPrint(("\n"));
|
|
return(RC);
|
|
} // end UDFCommonWrite()
|
|
|
|
/*************************************************************************
|
|
*
|
|
* Function: UDFDeferredWriteCallBack()
|
|
*
|
|
* Description:
|
|
* Invoked by the cache manager in the context of a worker thread.
|
|
* Typically, you can simply post the request at this point (just
|
|
* as you would have if the original request could not block) to
|
|
* perform the write in the context of a system worker thread.
|
|
*
|
|
* Expected Interrupt Level (for execution) :
|
|
*
|
|
* IRQL_PASSIVE_LEVEL
|
|
*
|
|
* Return Value: None
|
|
*
|
|
*************************************************************************/
|
|
VOID
|
|
NTAPI
|
|
UDFDeferredWriteCallBack(
|
|
IN PVOID Context1, // Should be PtrIrpContext
|
|
IN PVOID Context2 // Should be Irp
|
|
)
|
|
{
|
|
UDFPrint(("UDFDeferredWriteCallBack\n"));
|
|
// We should typically simply post the request to our internal
|
|
// queue of posted requests (just as we would if the original write
|
|
// could not be completed because the caller could not block).
|
|
// Once we post the request, return from this routine. The write
|
|
// will then be retried in the context of a system worker thread
|
|
UDFPostRequest((PtrUDFIrpContext)Context1, (PIRP)Context2);
|
|
|
|
} // end UDFDeferredWriteCallBack()
|
|
|
|
/*************************************************************************
|
|
*
|
|
*************************************************************************/
|
|
|
|
#define USE_CcCopyWrite_TO_ZERO
|
|
|
|
VOID
|
|
UDFPurgeCacheEx_(
|
|
PtrUDFNTRequiredFCB NtReqFcb,
|
|
LONGLONG Offset,
|
|
LONGLONG Length,
|
|
//#ifndef ALLOW_SPARSE
|
|
BOOLEAN CanWait,
|
|
//#endif //ALLOW_SPARSE
|
|
PVCB Vcb,
|
|
PFILE_OBJECT FileObject
|
|
)
|
|
{
|
|
ULONG Off_l;
|
|
#ifdef USE_CcCopyWrite_TO_ZERO
|
|
ULONG PgLen;
|
|
#endif //USE_CcCopyWrite_TO_ZERO
|
|
|
|
// We'll just purge cache section here,
|
|
// without call to CcZeroData()
|
|
// 'cause udf_info.cpp will care about it
|
|
|
|
#define PURGE_BLOCK_SZ 0x10000000
|
|
|
|
// NOTE: if FS engine doesn't suport
|
|
// sparse/unrecorded areas, CcZeroData must be called
|
|
// In this case we'll see some recursive WRITE requests
|
|
|
|
_SEH2_TRY {
|
|
MmPrint((" UDFPurgeCacheEx_(): Offs: %I64x, ", Offset));
|
|
MmPrint((" Len: %lx\n", Length));
|
|
SECTION_OBJECT_POINTERS* SectionObject = &(NtReqFcb->SectionObject);
|
|
if(Length) {
|
|
LONGLONG Offset0, OffsetX, VDL;
|
|
|
|
Offset0 = Offset;
|
|
if((Off_l = ((ULONG)Offset0 & (PAGE_SIZE-1)))) {
|
|
// Offset, Offset0
|
|
// v
|
|
// ...|dddddddddddd00000|....
|
|
// |<- Off_l ->|
|
|
#ifndef USE_CcCopyWrite_TO_ZERO
|
|
*((PULONG)&Offset0) &= ~(PAGE_SIZE-1);
|
|
MmPrint((" CcFlushCache(s) Offs %I64x, Len %x\n", Offset0, Off_l));
|
|
CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset0, Off_l, NULL );
|
|
#else //USE_CcCopyWrite_TO_ZERO
|
|
// ...|ddddd000000000000|....
|
|
// |<- PgLen ->|
|
|
PgLen = PAGE_SIZE - Off_l; /*(*((PULONG)&Offset) & (PAGE_SIZE-1))*/
|
|
//
|
|
if(PgLen > Length)
|
|
PgLen = (ULONG)Length;
|
|
|
|
MmPrint((" ZeroCache (CcWrite) Offs %I64x, Len %x\n", Offset, PgLen));
|
|
#ifdef DBG
|
|
if(FileObject && Vcb) {
|
|
|
|
ASSERT(CanWait);
|
|
#endif //DBG
|
|
if (PgLen) {
|
|
if (SectionObject->SharedCacheMap) {
|
|
CcCopyWrite(FileObject, (PLARGE_INTEGER)&Offset, PgLen, TRUE || CanWait, Vcb->ZBuffer);
|
|
}
|
|
Offset += PgLen;
|
|
Length -= PgLen;
|
|
}
|
|
#ifdef DBG
|
|
} else {
|
|
MmPrint((" Can't use CcWrite to zero cache\n"));
|
|
}
|
|
#endif //DBG
|
|
#endif //USE_CcCopyWrite_TO_ZERO
|
|
}
|
|
VDL = NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart;
|
|
OffsetX = Offset+Length;
|
|
if((Off_l = ((ULONG)OffsetX & (PAGE_SIZE-1)))) {
|
|
|
|
if(OffsetX < VDL) {
|
|
#ifndef USE_CcCopyWrite_TO_ZERO
|
|
Off_l = ( (ULONG)(VDL-OffsetX) > PAGE_SIZE ) ?
|
|
(PAGE_SIZE - Off_l) :
|
|
((ULONG)(VDL-OffsetX));
|
|
*((PULONG)&OffsetX) &= ~(PAGE_SIZE-1);
|
|
MmPrint((" CcFlushCache(e) Offs %I64x, Len %x\n", OffsetX, Off_l));
|
|
CcFlushCache( SectionObject, (PLARGE_INTEGER)&OffsetX, Off_l, NULL );
|
|
#else //USE_CcCopyWrite_TO_ZERO
|
|
if(VDL - OffsetX > PAGE_SIZE) {
|
|
PgLen = (ULONG)OffsetX & ~(PAGE_SIZE-1);
|
|
} else {
|
|
PgLen = (ULONG)(VDL - OffsetX) & ~(PAGE_SIZE-1);
|
|
}
|
|
// ...|000000000000ddddd|....
|
|
// |<- PgLen ->|
|
|
MmPrint((" ZeroCache (CcWrite - 2) Offs %I64x, Len %x\n", OffsetX, PgLen));
|
|
#ifdef DBG
|
|
if(FileObject && Vcb) {
|
|
ASSERT(CanWait);
|
|
#endif //DBG
|
|
if (SectionObject->SharedCacheMap) {
|
|
CcCopyWrite(FileObject, (PLARGE_INTEGER)&OffsetX, PgLen, TRUE || CanWait, Vcb->ZBuffer);
|
|
}
|
|
Length -= PgLen;
|
|
#ifdef DBG
|
|
} else {
|
|
MmPrint((" Can't use CcWrite to zero cache (2)\n"));
|
|
}
|
|
#endif //DBG
|
|
#endif //USE_CcCopyWrite_TO_ZERO
|
|
}
|
|
}
|
|
#ifndef USE_CcCopyWrite_TO_ZERO
|
|
do
|
|
#else //USE_CcCopyWrite_TO_ZERO
|
|
while(Length)
|
|
#endif //USE_CcCopyWrite_TO_ZERO
|
|
{
|
|
MmPrint((" CcPurgeCacheSection()\n"));
|
|
if(PURGE_BLOCK_SZ > Length) {
|
|
CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
|
|
(ULONG)Length, FALSE);
|
|
/*
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += Length;
|
|
ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
|
|
NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
|
|
MmPrint((" CcFlushCache()\n"));
|
|
CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
|
|
*/
|
|
#ifndef ALLOW_SPARSE
|
|
// UDFZeroFile__(
|
|
#endif //ALLOW_SPARSE
|
|
break;
|
|
} else {
|
|
CcPurgeCacheSection(SectionObject, (PLARGE_INTEGER)&Offset,
|
|
PURGE_BLOCK_SZ, FALSE);
|
|
/*
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart += PURGE_BLOCK_SZ;
|
|
ASSERT(NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart <=
|
|
NtReqFcb->CommonFCBHeader.FileSize.QuadPart);
|
|
MmPrint((" CcFlushCache()\n"));
|
|
CcFlushCache( SectionObject, (PLARGE_INTEGER)&Offset, (ULONG)Length, NULL );
|
|
*/
|
|
#ifndef ALLOW_SPARSE
|
|
// UDFZeroFile__(
|
|
#endif //ALLOW_SPARSE
|
|
Length -= PURGE_BLOCK_SZ;
|
|
Offset += PURGE_BLOCK_SZ;
|
|
}
|
|
}
|
|
#ifndef USE_CcCopyWrite_TO_ZERO
|
|
while(Length);
|
|
#endif //USE_CcCopyWrite_TO_ZERO
|
|
if(VDL < Offset)
|
|
NtReqFcb->CommonFCBHeader.ValidDataLength.QuadPart = Offset;
|
|
}
|
|
} _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
|
|
BrutePoint();
|
|
} _SEH2_END;
|
|
} // end UDFPurgeCacheEx_()
|
|
|
|
#endif //UDF_READ_ONLY_BUILD
|