reactos/drivers/filesystems/ffs/src/write.c

1588 lines
31 KiB
C

/*
* FFS File System Driver for Windows
*
* write.c
*
* 2004.5.6 ~
*
* Lee Jae-Hong, http://www.pyrasis.com
*
* See License.txt
*
*/
#include "ntifs.h"
#include "ffsdrv.h"
#if !FFS_READ_ONLY
/* Globals */
extern PFFS_GLOBAL FFSGlobal;
/* Definitions */
typedef struct _FFS_FLPFLUSH_CONTEXT {
PFFS_VCB Vcb;
PFFS_FCB Fcb;
PFILE_OBJECT FileObject;
KDPC Dpc;
KTIMER Timer;
WORK_QUEUE_ITEM Item;
} FFS_FLPFLUSH_CONTEXT, *PFFS_FLPFLUSH_CONTEXT;
#ifdef _PREFAST_
WORKER_THREAD_ROUTINE __drv_mustHoldCriticalRegion FFSFloppyFlush;
#endif // _PREFAST_
__drv_mustHoldCriticalRegion
VOID
FFSFloppyFlush(
IN PVOID Parameter);
#ifdef _PREFAST_
KDEFERRED_ROUTINE FFSFloppyFlushDpc;
#endif // _PREFAST_
VOID
FFSFloppyFlushDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);
NTSTATUS
FFSWriteComplete(
IN PFFS_IRP_CONTEXT IrpContext);
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWriteFile(
IN PFFS_IRP_CONTEXT IrpContext);
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWriteVolume(
IN PFFS_IRP_CONTEXT IrpContext);
VOID
FFSDeferWrite(
IN PFFS_IRP_CONTEXT,
PIRP Irp);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, FFSFloppyFlush)
#pragma alloc_text(PAGE, FFSStartFloppyFlushDpc)
#pragma alloc_text(PAGE, FFSZeroHoles)
#pragma alloc_text(PAGE, FFSWrite)
#pragma alloc_text(PAGE, FFSWriteVolume)
#pragma alloc_text(PAGE, FFSv1WriteInode)
#pragma alloc_text(PAGE, FFSWriteFile)
#pragma alloc_text(PAGE, FFSWriteComplete)
#endif
__drv_mustHoldCriticalRegion
VOID
FFSFloppyFlush(
IN PVOID Parameter)
{
PFFS_FLPFLUSH_CONTEXT Context;
PFILE_OBJECT FileObject;
PFFS_FCB Fcb;
PFFS_VCB Vcb;
PAGED_CODE();
Context = (PFFS_FLPFLUSH_CONTEXT) Parameter;
FileObject = Context->FileObject;
Fcb = Context->Fcb;
Vcb = Context->Vcb;
FFSPrint((DBG_USER, "FFSFloppyFlushing ...\n"));
IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
if (Vcb)
{
ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Vcb->PagingIoResource);
CcFlushCache(&(Vcb->SectionObject), NULL, 0, NULL);
}
if (FileObject)
{
ASSERT(Fcb == (PFFS_FCB)FileObject->FsContext);
ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Fcb->PagingIoResource);
CcFlushCache(&(Fcb->SectionObject), NULL, 0, NULL);
ObDereferenceObject(FileObject);
}
IoSetTopLevelIrp(NULL);
ExFreePool(Parameter);
}
VOID
FFSFloppyFlushDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2)
{
PFFS_FLPFLUSH_CONTEXT Context;
Context = (PFFS_FLPFLUSH_CONTEXT)DeferredContext;
FFSPrint((DBG_USER, "FFSFloppyFlushDpc is to be started...\n"));
ExInitializeWorkItem(&Context->Item,
FFSFloppyFlush,
Context);
ExQueueWorkItem(&Context->Item, CriticalWorkQueue);
}
VOID
FFSStartFloppyFlushDpc(
PFFS_VCB Vcb,
PFFS_FCB Fcb,
PFILE_OBJECT FileObject)
{
LARGE_INTEGER OneSecond;
PFFS_FLPFLUSH_CONTEXT Context;
PAGED_CODE();
ASSERT(IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK));
Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(PFFS_FLPFLUSH_CONTEXT), FFS_POOL_TAG);
if (!Context)
{
FFSBreakPoint();
return;
}
KeInitializeTimer(&Context->Timer);
KeInitializeDpc(&Context->Dpc,
FFSFloppyFlushDpc,
Context);
Context->Vcb = Vcb;
Context->Fcb = Fcb;
Context->FileObject = FileObject;
if (FileObject)
{
ObReferenceObject(FileObject);
}
OneSecond.QuadPart = (LONGLONG) - 1 * 1000 * 1000 * 10;
KeSetTimer(&Context->Timer,
OneSecond,
&Context->Dpc);
}
BOOLEAN
FFSZeroHoles(
IN PFFS_IRP_CONTEXT IrpContext,
IN PFFS_VCB Vcb,
IN PFILE_OBJECT FileObject,
IN LONGLONG Offset,
IN LONGLONG Count)
{
LARGE_INTEGER StartAddr = {0, 0};
LARGE_INTEGER EndAddr = {0, 0};
PAGED_CODE();
StartAddr.QuadPart = (Offset + (SECTOR_SIZE - 1)) &
~((LONGLONG)SECTOR_SIZE - 1);
EndAddr.QuadPart = (Offset + Count + (SECTOR_SIZE - 1)) &
~((LONGLONG)SECTOR_SIZE - 1);
if (StartAddr.QuadPart < EndAddr.QuadPart)
{
return CcZeroData(FileObject,
&StartAddr,
&EndAddr,
IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
}
return TRUE;
}
VOID
FFSDeferWrite(
IN PFFS_IRP_CONTEXT IrpContext,
PIRP Irp)
{
ASSERT(IrpContext->Irp == Irp);
FFSQueueRequest(IrpContext);
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWriteVolume(
IN PFFS_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PFFS_VCB Vcb;
PFFS_CCB Ccb;
PFFS_FCBVCB FcbOrVcb;
PFILE_OBJECT FileObject;
PDEVICE_OBJECT DeviceObject;
PIRP Irp;
PIO_STACK_LOCATION IoStackLocation;
ULONG Length;
LARGE_INTEGER ByteOffset;
BOOLEAN PagingIo;
BOOLEAN Nocache;
BOOLEAN SynchronousIo;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN PagingIoResourceAcquired = FALSE;
BOOLEAN bDeferred = FALSE;
PUCHAR Buffer;
PAGED_CODE();
_SEH2_TRY
{
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == FFSICX) &&
(IrpContext->Identifier.Size == sizeof(FFS_IRP_CONTEXT)));
DeviceObject = IrpContext->DeviceObject;
Vcb = (PFFS_VCB)DeviceObject->DeviceExtension;
ASSERT(Vcb != NULL);
ASSERT((Vcb->Identifier.Type == FFSVCB) &&
(Vcb->Identifier.Size == sizeof(FFS_VCB)));
FileObject = IrpContext->FileObject;
FcbOrVcb = (PFFS_FCBVCB)FileObject->FsContext;
ASSERT(FcbOrVcb);
if (!(FcbOrVcb->Identifier.Type == FFSVCB && (PVOID)FcbOrVcb == (PVOID)Vcb))
{
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
Ccb = (PFFS_CCB)FileObject->FsContext2;
Irp = IrpContext->Irp;
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
Length = IoStackLocation->Parameters.Write.Length;
ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);
FFSPrint((DBG_INFO, "FFSWriteVolume: Off=%I64xh Len=%xh Paging=%xh Nocache=%xh\n",
ByteOffset.QuadPart, Length, PagingIo, Nocache));
if (Length == 0)
{
Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
// For the case of "Direct Access Storage Device", we
// need flush/purge the cache
if (Ccb != NULL)
{
ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
MainResourceAcquired = TRUE;
Status = FFSPurgeVolume(Vcb, TRUE);
ExReleaseResourceLite(&Vcb->MainResource);
MainResourceAcquired = FALSE;
if(!IsFlagOn(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO))
{
if (ByteOffset.QuadPart + Length > Vcb->Header.FileSize.QuadPart)
{
Length = (ULONG)(Vcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);
}
}
{
FFS_BDL BlockArray;
if ((ByteOffset.LowPart & (SECTOR_SIZE - 1)) ||
(Length & (SECTOR_SIZE - 1)))
{
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
Status = FFSLockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (!NT_SUCCESS(Status))
{
_SEH2_LEAVE;
}
BlockArray.Irp = NULL;
BlockArray.Lba = ByteOffset.QuadPart;;
BlockArray.Offset = 0;
BlockArray.Length = Length;
Status = FFSReadWriteBlocks(IrpContext,
Vcb,
&BlockArray,
Length,
1,
FALSE);
Irp = IrpContext->Irp;
_SEH2_LEAVE;
}
}
if (Nocache &&
(ByteOffset.LowPart & (SECTOR_SIZE - 1) ||
Length & (SECTOR_SIZE - 1)))
{
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC))
{
ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
if (ByteOffset.QuadPart >=
Vcb->PartitionInformation.PartitionLength.QuadPart)
{
Irp->IoStatus.Information = 0;
Status = STATUS_END_OF_FILE;
_SEH2_LEAVE;
}
#if FALSE
if (!Nocache)
{
BOOLEAN bAgain = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
BOOLEAN bWait = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
BOOLEAN bQueue = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_REQUEUED);
if (!CcCanIWrite(
FileObject,
Length,
(bWait && bQueue),
bAgain))
{
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
CcDeferWrite(FileObject,
(PCC_POST_DEFERRED_WRITE)FFSDeferWrite,
IrpContext,
Irp,
Length,
bAgain);
bDeferred = TRUE;
FFSBreakPoint();
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
}
#endif
if (Nocache && !PagingIo && (Vcb->SectionObject.DataSectionObject != NULL))
{
ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
MainResourceAcquired = TRUE;
ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Vcb->PagingIoResource);
CcFlushCache(&(Vcb->SectionObject),
&ByteOffset,
Length,
&(Irp->IoStatus));
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
Status = Irp->IoStatus.Status;
_SEH2_LEAVE;
}
ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Vcb->PagingIoResource);
CcPurgeCacheSection(&(Vcb->SectionObject),
(PLARGE_INTEGER)&(ByteOffset),
Length,
FALSE);
ExReleaseResourceLite(&Vcb->MainResource);
MainResourceAcquired = FALSE;
}
if (!PagingIo)
{
#pragma prefast( suppress: 28137, "by design" )
if (!ExAcquireResourceExclusiveLite(
&Vcb->MainResource,
IrpContext->IsSynchronous))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
MainResourceAcquired = TRUE;
}
else
{
/*
ULONG ResShCnt, ResExCnt;
ResShCnt = ExIsResourceAcquiredSharedLite(&Vcb->PagingIoResource);
ResExCnt = ExIsResourceAcquiredExclusiveLite(&Vcb->PagingIoResource);
FFSPrint((DBG_USER, "PagingIoRes: %xh:%xh Synchronous=%xh\n", ResShCnt, ResExCnt, IrpContext->IsSynchronous));
*/
if (Ccb)
{
if (!ExAcquireResourceSharedLite(
&Vcb->PagingIoResource,
IrpContext->IsSynchronous))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
PagingIoResourceAcquired = TRUE;
}
}
if (!Nocache)
{
if ((ByteOffset.QuadPart + Length) >
Vcb->PartitionInformation.PartitionLength.QuadPart
)
{
Length = (ULONG) (
Vcb->PartitionInformation.PartitionLength.QuadPart -
ByteOffset.QuadPart);
Length &= ~((ULONG)SECTOR_SIZE - 1);
}
if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL))
{
CcPrepareMdlWrite(
Vcb->StreamObj,
&ByteOffset,
Length,
&Irp->MdlAddress,
&Irp->IoStatus);
Status = Irp->IoStatus.Status;
}
else
{
Buffer = FFSGetUserBuffer(Irp);
if (Buffer == NULL)
{
FFSBreakPoint();
Status = STATUS_INVALID_USER_BUFFER;
_SEH2_LEAVE;
}
if (!CcCopyWrite(Vcb->StreamObj,
(PLARGE_INTEGER)(&ByteOffset),
Length,
TRUE,
Buffer))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
Status = Irp->IoStatus.Status;
FFSAddMcbEntry(Vcb, ByteOffset.QuadPart, (LONGLONG)Length);
}
if (NT_SUCCESS(Status))
{
Irp->IoStatus.Information = Length;
}
}
else
{
PFFS_BDL ffs_bdl = NULL;
ULONG Blocks = 0;
LONGLONG DirtyStart;
LONGLONG DirtyLba;
LONGLONG DirtyLength;
LONGLONG RemainLength;
if ((ByteOffset.QuadPart + Length) >
Vcb->PartitionInformation.PartitionLength.QuadPart)
{
Length = (ULONG)(
Vcb->PartitionInformation.PartitionLength.QuadPart -
ByteOffset.QuadPart);
Length &= ~((ULONG)SECTOR_SIZE - 1);
}
Status = FFSLockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (!NT_SUCCESS(Status))
{
_SEH2_LEAVE;
}
ffs_bdl = ExAllocatePoolWithTag(PagedPool,
(Length / Vcb->BlockSize) *
sizeof(FFS_BDL), FFS_POOL_TAG);
if (!ffs_bdl)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
DirtyLba = ByteOffset.QuadPart;
RemainLength = (LONGLONG)Length;
while (RemainLength > 0)
{
DirtyStart = DirtyLba;
if (FFSLookupMcbEntry(Vcb,
DirtyStart,
&DirtyLba,
&DirtyLength,
(PLONGLONG)NULL,
(PLONGLONG)NULL,
(PULONG)NULL))
{
if (DirtyLba == -1)
{
DirtyLba = DirtyStart + DirtyLength;
RemainLength = ByteOffset.QuadPart +
(LONGLONG)Length -
DirtyLba;
continue;
}
ffs_bdl[Blocks].Irp = NULL;
ffs_bdl[Blocks].Lba = DirtyLba;
ffs_bdl[Blocks].Offset = (ULONG)((LONGLONG)Length +
DirtyStart -
RemainLength -
DirtyLba);
if (DirtyLba + DirtyLength > DirtyStart + RemainLength)
{
ffs_bdl[Blocks].Length = (ULONG)(DirtyStart +
RemainLength -
DirtyLba);
RemainLength = 0;
}
else
{
ffs_bdl[Blocks].Length = (ULONG)DirtyLength;
RemainLength = (DirtyStart + RemainLength) -
(DirtyLba + DirtyLength);
}
DirtyLba = DirtyStart + DirtyLength;
Blocks++;
}
else
{
if (Blocks == 0)
{
if (ffs_bdl)
ExFreePool(ffs_bdl);
//
// Lookup fails at the first time, ie.
// no dirty blocks in the run
//
FFSBreakPoint();
if (RemainLength == (LONGLONG)Length)
Status = STATUS_SUCCESS;
else
Status = STATUS_UNSUCCESSFUL;
_SEH2_LEAVE;
}
else
{
break;
}
}
}
if (Blocks > 0)
{
Status = FFSReadWriteBlocks(IrpContext,
Vcb,
ffs_bdl,
Length,
Blocks,
FALSE);
Irp = IrpContext->Irp;
if (NT_SUCCESS(Status))
{
ULONG i;
for (i = 0; i < Blocks; i++)
{
FFSRemoveMcbEntry(Vcb,
ffs_bdl[i].Lba,
ffs_bdl[i].Length);
}
}
if (ffs_bdl)
ExFreePool(ffs_bdl);
if (!Irp)
_SEH2_LEAVE;
}
else
{
if (ffs_bdl)
ExFreePool(ffs_bdl);
Irp->IoStatus.Information = Length;
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
}
}
_SEH2_FINALLY
{
if (PagingIoResourceAcquired)
{
ExReleaseResourceForThreadLite(
&Vcb->PagingIoResource,
ExGetCurrentResourceThread());
}
if (MainResourceAcquired)
{
ExReleaseResourceForThreadLite(
&Vcb->MainResource,
ExGetCurrentResourceThread());
}
if (!IrpContext->ExceptionInProgress)
{
if (Irp)
{
if (Status == STATUS_PENDING)
{
if(!bDeferred)
{
Status = FFSLockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (NT_SUCCESS(Status))
{
Status = FFSQueueRequest(IrpContext);
}
else
{
FFSCompleteIrpContext(IrpContext, Status);
}
}
}
else
{
if (NT_SUCCESS(Status))
{
if (SynchronousIo && !PagingIo)
{
FileObject->CurrentByteOffset.QuadPart =
ByteOffset.QuadPart + Irp->IoStatus.Information;
}
if (!PagingIo)
{
SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
}
}
FFSCompleteIrpContext(IrpContext, Status);
}
}
else
{
FFSFreeIrpContext(IrpContext);
}
}
} _SEH2_END;
return Status;
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSv1WriteInode(
IN PFFS_IRP_CONTEXT IrpContext,
IN PFFS_VCB Vcb,
IN PFFSv1_INODE dinode1,
IN ULONGLONG offset,
IN PVOID Buffer,
IN ULONG size,
IN BOOLEAN bWriteToDisk,
OUT PULONG dwRet)
{
PFFS_BDL ffs_bdl = NULL;
ULONG blocks, i;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG Totalblocks;
LONGLONG AllocSize;
PAGED_CODE();
if (dwRet)
{
*dwRet = 0;
}
Totalblocks = (dinode1->di_blocks);
AllocSize = ((LONGLONG)(FFSDataBlocks(Vcb, Totalblocks)) << BLOCK_BITS);
if ((LONGLONG)offset >= AllocSize)
{
FFSPrint((DBG_ERROR, "FFSv1WriteInode: beyond the file range.\n"));
return STATUS_SUCCESS;
}
if ((LONGLONG)offset + size > AllocSize)
{
size = (ULONG)(AllocSize - offset);
}
blocks = FFSv1BuildBDL(IrpContext, Vcb, dinode1, offset, size, &ffs_bdl);
if (blocks <= 0)
{
return STATUS_SUCCESS;
}
#if DBG
{
ULONG dwTotal = 0;
FFSPrint((DBG_INFO, "FFSv1WriteInode: BDLCount = %xh Size=%xh Off=%xh\n",
blocks, size, offset));
for(i = 0; i < blocks; i++)
{
FFSPrint((DBG_INFO, "FFSv1WriteInode: Lba=%I64xh Len=%xh Off=%xh\n",
ffs_bdl[i].Lba, ffs_bdl[i].Length, ffs_bdl[i].Offset));
dwTotal += ffs_bdl[i].Length;
}
if (dwTotal != size)
{
FFSBreakPoint();
}
FFSPrint((DBG_INFO, "FFSv1WriteInode: Total = %xh (WriteToDisk=%x)\n",
dwTotal, bWriteToDisk));
}
#endif
if (bWriteToDisk)
{
#if 0
for(i = 0; i < blocks; i++)
{
{
CcFlushCache(&(Vcb->SectionObject),
(PLARGE_INTEGER)&(ffs_bdl[i].Lba),
ffs_bdl[i].Length,
NULL);
if (Vcb->SectionObject.DataSectionObject != NULL)
{
ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Vcb->PagingIoResource);
CcPurgeCacheSection(&(Vcb->SectionObject),
(PLARGE_INTEGER)&(ffs_bdl[i].Lba),
ffs_bdl[i].Length,
FALSE);
}
}
}
#endif
// assume offset is aligned.
Status = FFSReadWriteBlocks(IrpContext, Vcb, ffs_bdl, size, blocks, FALSE);
}
else
{
for(i = 0; i < blocks; i++)
{
if(!FFSSaveBuffer(IrpContext, Vcb, ffs_bdl[i].Lba, ffs_bdl[i].Length, (PVOID)((PUCHAR)Buffer + ffs_bdl[i].Offset)))
goto errorout;
}
if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK))
{
FFSPrint((DBG_USER, "FFSv1WriteInode is starting FlushingDpc...\n"));
FFSStartFloppyFlushDpc(Vcb, NULL, NULL);
}
Status = STATUS_SUCCESS;
}
errorout:
if (ffs_bdl)
ExFreePool(ffs_bdl);
if (NT_SUCCESS(Status))
{
if (dwRet) *dwRet = size;
}
return Status;
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSv2WriteInode(
IN PFFS_IRP_CONTEXT IrpContext,
IN PFFS_VCB Vcb,
IN PFFSv2_INODE dinode2,
IN ULONGLONG offset,
IN PVOID Buffer,
IN ULONG size,
IN BOOLEAN bWriteToDisk,
OUT PULONG dwRet)
{
return STATUS_UNSUCCESSFUL;
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWriteFile(
IN PFFS_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PFFS_VCB Vcb;
PFFS_FCB Fcb;
PFFS_CCB Ccb;
PFILE_OBJECT FileObject;
PFILE_OBJECT CacheObject;
PDEVICE_OBJECT DeviceObject;
PIRP Irp;
PIO_STACK_LOCATION IoStackLocation;
ULONG Length;
ULONG ReturnedLength;
LARGE_INTEGER ByteOffset;
BOOLEAN PagingIo;
BOOLEAN Nocache;
BOOLEAN SynchronousIo;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN PagingIoResourceAcquired = FALSE;
BOOLEAN bNeedExtending = FALSE;
BOOLEAN bAppendFile = FALSE;
BOOLEAN bDeferred = FALSE;
PUCHAR Buffer;
PAGED_CODE();
_SEH2_TRY
{
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == FFSICX) &&
(IrpContext->Identifier.Size == sizeof(FFS_IRP_CONTEXT)));
DeviceObject = IrpContext->DeviceObject;
Vcb = (PFFS_VCB)DeviceObject->DeviceExtension;
ASSERT(Vcb != NULL);
ASSERT((Vcb->Identifier.Type == FFSVCB) &&
(Vcb->Identifier.Size == sizeof(FFS_VCB)));
FileObject = IrpContext->FileObject;
Fcb = (PFFS_FCB)FileObject->FsContext;
ASSERT(Fcb);
ASSERT((Fcb->Identifier.Type == FFSFCB) &&
(Fcb->Identifier.Size == sizeof(FFS_FCB)));
Ccb = (PFFS_CCB)FileObject->FsContext2;
Irp = IrpContext->Irp;
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
Length = IoStackLocation->Parameters.Write.Length;
ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
PagingIo = (Irp->Flags & IRP_PAGING_IO ? TRUE : FALSE);
Nocache = (Irp->Flags & IRP_NOCACHE ? TRUE : FALSE);
SynchronousIo = (FileObject->Flags & FO_SYNCHRONOUS_IO ? TRUE : FALSE);
FFSPrint((DBG_INFO, "FFSWriteFile: Off=%I64xh Len=%xh Paging=%xh Nocache=%xh\n",
ByteOffset.QuadPart, Length, PagingIo, Nocache));
/*
if (IsFlagOn(Fcb->Flags, FCB_FILE_DELETED))
{
Status = STATUS_FILE_DELETED;
_SEH2_LEAVE;
}
if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING))
{
Status = STATUS_DELETE_PENDING;
_SEH2_LEAVE;
}
*/
if (Length == 0)
{
Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
if (Nocache &&
(ByteOffset.LowPart & (SECTOR_SIZE - 1) ||
Length & (SECTOR_SIZE - 1)))
{
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC))
{
ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC);
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
#if FALSE
if (!Nocache)
{
BOOLEAN bAgain = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
BOOLEAN bWait = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
BOOLEAN bQueue = IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_REQUEUED);
if (!CcCanIWrite(
FileObject,
Length,
(bWait && bQueue),
bAgain))
{
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
CcDeferWrite(FileObject,
(PCC_POST_DEFERRED_WRITE)FFSDeferWrite,
IrpContext,
Irp,
Length,
bAgain);
bDeferred = TRUE;
FFSBreakPoint();
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
}
#endif
if (IsEndOfFile(ByteOffset))
{
bAppendFile = TRUE;
ByteOffset.QuadPart = Fcb->Header.FileSize.QuadPart;
}
if (FlagOn(Fcb->FFSMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY) && !PagingIo)
{
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
//
// Do flushing for such cases
//
if (Nocache && !PagingIo && (Fcb->SectionObject.DataSectionObject != NULL))
{
#pragma prefast( suppress: 28137, "by design" )
ExAcquireResourceExclusiveLite(&Fcb->MainResource,
IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
MainResourceAcquired = TRUE;
ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Fcb->PagingIoResource);
CcFlushCache(&(Fcb->SectionObject),
&ByteOffset,
Length,
&(Irp->IoStatus));
ClearFlag(Fcb->Flags, FCB_FILE_MODIFIED);
if (!NT_SUCCESS(Irp->IoStatus.Status))
{
Status = Irp->IoStatus.Status;
_SEH2_LEAVE;
}
ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Fcb->PagingIoResource);
CcPurgeCacheSection(&(Fcb->SectionObject),
(PLARGE_INTEGER)&(ByteOffset),
Length,
FALSE);
ExReleaseResourceLite(&Fcb->MainResource);
MainResourceAcquired = FALSE;
}
if (!PagingIo)
{
#pragma prefast( suppress: 28137, "by design" )
if (!ExAcquireResourceExclusiveLite(
&Fcb->MainResource,
IrpContext->IsSynchronous))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
MainResourceAcquired = TRUE;
}
else
{
/*
ULONG ResShCnt, ResExCnt;
ResShCnt = ExIsResourceAcquiredSharedLite(&Fcb->PagingIoResource);
ResExCnt = ExIsResourceAcquiredExclusiveLite(&Fcb->PagingIoResource);
FFSPrint((DBG_USER, "FFSWriteFile: Inode=%xh %S PagingIo: %xh:%xh Synchronous=%xh\n",
Fcb->FFSMcb->Inode, Fcb->FFSMcb->ShortName.Buffer, ResShCnt, ResExCnt, IrpContext->IsSynchronous));
*/
if (!ExAcquireResourceSharedLite(
&Fcb->PagingIoResource,
IrpContext->IsSynchronous))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
PagingIoResourceAcquired = TRUE;
}
if (!PagingIo)
{
if (!FsRtlCheckLockForWriteAccess(
&Fcb->FileLockAnchor,
Irp))
{
Status = STATUS_FILE_LOCK_CONFLICT;
_SEH2_LEAVE;
}
}
if (Nocache)
{
if ((ByteOffset.QuadPart + Length) >
Fcb->Header.AllocationSize.QuadPart)
{
if (ByteOffset.QuadPart >=
Fcb->Header.AllocationSize.QuadPart)
{
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
_SEH2_LEAVE;
}
else
{
if (Length > (ULONG)(Fcb->Header.AllocationSize.QuadPart
- ByteOffset.QuadPart))
{
Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart
- ByteOffset.QuadPart);
}
}
}
}
if (!Nocache)
{
if (FlagOn(Fcb->FFSMcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY))
{
_SEH2_LEAVE;
}
if (FileObject->PrivateCacheMap == NULL)
{
CcInitializeCacheMap(
FileObject,
(PCC_FILE_SIZES)(&Fcb->Header.AllocationSize),
FALSE,
&FFSGlobal->CacheManagerCallbacks,
Fcb);
CcSetReadAheadGranularity(
FileObject,
READ_AHEAD_GRANULARITY);
CcSetFileSizes(
FileObject,
(PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
}
CacheObject = FileObject;
//
// Need extending the size of inode ?
//
if ((bAppendFile) || ((ULONG)(ByteOffset.QuadPart + Length) >
(ULONG)(Fcb->Header.FileSize.QuadPart)))
{
LARGE_INTEGER ExtendSize;
LARGE_INTEGER FileSize;
bNeedExtending = TRUE;
FileSize = Fcb->Header.FileSize;
ExtendSize.QuadPart = (LONGLONG)(ByteOffset.QuadPart + Length);
if (ExtendSize.QuadPart > Fcb->Header.AllocationSize.QuadPart)
{
if (!FFSExpandFile(IrpContext, Vcb, Fcb, &ExtendSize))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
}
{
Fcb->Header.FileSize.QuadPart = ExtendSize.QuadPart;
Fcb->dinode1->di_size = (ULONG)ExtendSize.QuadPart;
}
if (FileObject->PrivateCacheMap)
{
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
if (ByteOffset.QuadPart > FileSize.QuadPart)
{
FFSZeroHoles(IrpContext, Vcb, FileObject, FileSize.QuadPart,
ByteOffset.QuadPart - FileSize.QuadPart);
}
if (Fcb->Header.AllocationSize.QuadPart > ExtendSize.QuadPart)
{
FFSZeroHoles(IrpContext, Vcb, FileObject, ExtendSize.QuadPart,
Fcb->Header.AllocationSize.QuadPart - ExtendSize.QuadPart);
}
}
if (FFSv1SaveInode(IrpContext, Vcb, Fcb->FFSMcb->Inode, Fcb->dinode1))
{
Status = STATUS_SUCCESS;
}
FFSNotifyReportChange(
IrpContext,
Vcb,
Fcb,
FILE_NOTIFY_CHANGE_SIZE,
FILE_ACTION_MODIFIED);
}
if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL))
{
CcPrepareMdlWrite(
CacheObject,
(&ByteOffset),
Length,
&Irp->MdlAddress,
&Irp->IoStatus);
Status = Irp->IoStatus.Status;
}
else
{
Buffer = FFSGetUserBuffer(Irp);
if (Buffer == NULL)
{
FFSBreakPoint();
Status = STATUS_INVALID_USER_BUFFER;
_SEH2_LEAVE;
}
if (!CcCopyWrite(
CacheObject,
(PLARGE_INTEGER)&ByteOffset,
Length,
IrpContext->IsSynchronous,
Buffer))
{
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
Status = Irp->IoStatus.Status;
}
if (NT_SUCCESS(Status))
{
Irp->IoStatus.Information = Length;
if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK))
{
FFSPrint((DBG_USER, "FFSWriteFile is starting FlushingDpc...\n"));
FFSStartFloppyFlushDpc(Vcb, Fcb, FileObject);
}
}
}
else
{
ReturnedLength = Length;
Status = FFSLockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (!NT_SUCCESS(Status))
{
_SEH2_LEAVE;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = Length;
Status =
FFSv1WriteInode(
IrpContext,
Vcb,
Fcb->dinode1,
(ULONGLONG)(ByteOffset.QuadPart),
NULL,
Length,
TRUE,
&ReturnedLength);
Irp = IrpContext->Irp;
}
}
_SEH2_FINALLY
{
if (PagingIoResourceAcquired)
{
ExReleaseResourceForThreadLite(
&Fcb->PagingIoResource,
ExGetCurrentResourceThread());
}
if (MainResourceAcquired)
{
ExReleaseResourceForThreadLite(
&Fcb->MainResource,
ExGetCurrentResourceThread());
}
if (!IrpContext->ExceptionInProgress)
{
if (Irp)
{
if (Status == STATUS_PENDING)
{
if (!bDeferred)
{
Status = FFSLockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (NT_SUCCESS(Status))
{
Status = FFSQueueRequest(IrpContext);
}
else
{
FFSCompleteIrpContext(IrpContext, Status);
}
}
}
else
{
if (NT_SUCCESS(Status))
{
if (SynchronousIo && !PagingIo)
{
FileObject->CurrentByteOffset.QuadPart =
ByteOffset.QuadPart + Irp->IoStatus.Information;
}
if (!PagingIo)
{
SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
SetFlag(Fcb->Flags, FCB_FILE_MODIFIED);
}
}
FFSCompleteIrpContext(IrpContext, Status);
}
}
else
{
FFSFreeIrpContext(IrpContext);
}
}
} _SEH2_END;
return Status;
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWriteComplete(
IN PFFS_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PFILE_OBJECT FileObject;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
PAGED_CODE();
_SEH2_TRY
{
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == FFSICX) &&
(IrpContext->Identifier.Size == sizeof(FFS_IRP_CONTEXT)));
FileObject = IrpContext->FileObject;
Irp = IrpContext->Irp;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
CcMdlWriteComplete(FileObject, &(IrpSp->Parameters.Write.ByteOffset), Irp->MdlAddress);
Irp->MdlAddress = NULL;
Status = STATUS_SUCCESS;
}
_SEH2_FINALLY
{
if (!IrpContext->ExceptionInProgress)
{
FFSCompleteIrpContext(IrpContext, Status);
}
} _SEH2_END;
return Status;
}
__drv_mustHoldCriticalRegion
NTSTATUS
FFSWrite(
IN PFFS_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
PFFS_FCBVCB FcbOrVcb;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PFFS_VCB Vcb;
BOOLEAN bCompleteRequest = TRUE;
PAGED_CODE();
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == FFSICX) &&
(IrpContext->Identifier.Size == sizeof(FFS_IRP_CONTEXT)));
_SEH2_TRY
{
if (FlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE))
{
Status = FFSWriteComplete(IrpContext);
bCompleteRequest = FALSE;
}
else
{
DeviceObject = IrpContext->DeviceObject;
if (DeviceObject == FFSGlobal->DeviceObject)
{
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
Vcb = (PFFS_VCB)DeviceObject->DeviceExtension;
if (Vcb->Identifier.Type != FFSVCB ||
Vcb->Identifier.Size != sizeof(FFS_VCB))
{
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
ASSERT(IsMounted(Vcb));
if (IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING))
{
Status = STATUS_TOO_LATE;
_SEH2_LEAVE;
}
if (IsFlagOn(Vcb->Flags, VCB_READ_ONLY))
{
Status = STATUS_MEDIA_WRITE_PROTECTED;
_SEH2_LEAVE;
}
FileObject = IrpContext->FileObject;
FcbOrVcb = (PFFS_FCBVCB)FileObject->FsContext;
if (FcbOrVcb->Identifier.Type == FFSVCB)
{
Status = FFSWriteVolume(IrpContext);
if (!NT_SUCCESS(Status))
{
FFSBreakPoint();
}
bCompleteRequest = FALSE;
}
else if (FcbOrVcb->Identifier.Type == FFSFCB)
{
Status = FFSWriteFile(IrpContext);
if (!NT_SUCCESS(Status))
{
FFSBreakPoint();
}
bCompleteRequest = FALSE;
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
}
}
_SEH2_FINALLY
{
if (bCompleteRequest)
{
FFSCompleteIrpContext(IrpContext, Status);
}
} _SEH2_END;
return Status;
}
#endif // !FFS_READ_ONLY