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

1425 lines
45 KiB
C

/*
* COPYRIGHT: See COPYRIGHT.TXT
* PROJECT: Ext2 File System Driver for WinNT/2K/XP
* FILE: write.c
* PROGRAMMER: Matt Wu <mattwu@163.com>
* HOMEPAGE: http://www.ext2fsd.com
* UPDATE HISTORY:
*/
/* INCLUDES *****************************************************************/
#include "ext2fs.h"
/* GLOBALS ***************************************************************/
extern PEXT2_GLOBAL Ext2Global;
#define DL_FLP DL_DBG
/* DEFINITIONS *************************************************************/
#define EXT2_FLPFLUSH_MAGIC 'FF2E'
typedef struct _EXT2_FLPFLUSH_CONTEXT {
PEXT2_VCB Vcb;
PEXT2_FCB Fcb;
PFILE_OBJECT FileObject;
KDPC Dpc;
KTIMER Timer;
WORK_QUEUE_ITEM Item;
} EXT2_FLPFLUSH_CONTEXT, *PEXT2_FLPFLUSH_CONTEXT;
VOID NTAPI
Ext2FloppyFlush(IN PVOID Parameter);
VOID NTAPI
Ext2FloppyFlushDpc (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2);
NTSTATUS
Ext2WriteComplete (IN PEXT2_IRP_CONTEXT IrpContext);
NTSTATUS
Ext2WriteFile (IN PEXT2_IRP_CONTEXT IrpContext);
NTSTATUS
Ext2WriteVolume (IN PEXT2_IRP_CONTEXT IrpContext);
VOID
Ext2DeferWrite(IN PEXT2_IRP_CONTEXT, PIRP Irp);
/* FUNCTIONS *************************************************************/
VOID NTAPI
Ext2FloppyFlush(IN PVOID Parameter)
{
PEXT2_FLPFLUSH_CONTEXT Context;
PFILE_OBJECT FileObject;
PEXT2_FCB Fcb;
PEXT2_VCB Vcb;
Context = (PEXT2_FLPFLUSH_CONTEXT) Parameter;
FileObject = Context->FileObject;
Fcb = Context->Fcb;
Vcb = Context->Vcb;
DEBUG(DL_FLP, ("Ext2FloppyFlushing ...\n"));
IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
if (FileObject) {
ASSERT(Fcb == (PEXT2_FCB)FileObject->FsContext);
ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE);
ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Fcb->PagingIoResource);
CcFlushCache(&(Fcb->SectionObject), NULL, 0, NULL);
ExReleaseResourceLite(&Fcb->MainResource);
ObDereferenceObject(FileObject);
}
if (Vcb) {
ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE);
ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Vcb->PagingIoResource);
Ext2FlushVcb(Vcb);
ExReleaseResourceLite(&Vcb->MainResource);
}
IoSetTopLevelIrp(NULL);
Ext2FreePool(Parameter, EXT2_FLPFLUSH_MAGIC);
}
VOID NTAPI
Ext2FloppyFlushDpc (
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PEXT2_FLPFLUSH_CONTEXT Context;
Context = (PEXT2_FLPFLUSH_CONTEXT) DeferredContext;
DEBUG(DL_FLP, ("Ext2FloppyFlushDpc is to be started...\n"));
ExQueueWorkItem(&Context->Item, CriticalWorkQueue);
}
VOID
Ext2StartFloppyFlushDpc (
PEXT2_VCB Vcb,
PEXT2_FCB Fcb,
PFILE_OBJECT FileObject )
{
LARGE_INTEGER OneSecond;
PEXT2_FLPFLUSH_CONTEXT Context;
ASSERT(IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK));
Context = Ext2AllocatePool(
NonPagedPool,
sizeof(EXT2_FLPFLUSH_CONTEXT),
EXT2_FLPFLUSH_MAGIC
);
if (!Context) {
DEBUG(DL_ERR, ( "Ex2StartFloppy...: failed to allocate Context\n"));
DbgBreak();
return;
}
KeInitializeTimer(&Context->Timer);
KeInitializeDpc( &Context->Dpc,
Ext2FloppyFlushDpc,
Context );
ExInitializeWorkItem( &Context->Item,
Ext2FloppyFlush,
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
Ext2ZeroData (
IN PEXT2_IRP_CONTEXT IrpContext,
IN PEXT2_VCB Vcb,
IN PFILE_OBJECT FileObject,
IN PLARGE_INTEGER Start,
IN PLARGE_INTEGER End
)
{
PEXT2_FCB Fcb;
PBCB Bcb;
PVOID Ptr;
ULONG Size;
BOOLEAN rc = TRUE;
ASSERT (End && Start && End->QuadPart > Start->QuadPart);
Fcb = (PEXT2_FCB) FileObject->FsContext;
/* skip data zero if we've already tracked unwritten part */
if (0 == ( End->LowPart & (BLOCK_SIZE -1)) &&
0 == (Start->LowPart & (BLOCK_SIZE -1))) {
if (INODE_HAS_EXTENT(Fcb->Inode)) {
return TRUE;
} else {
#if !EXT2_PRE_ALLOCATION_SUPPORT
return TRUE;
#endif
}
}
/* clear data in range [Start, End) */
_SEH2_TRY {
rc = CcZeroData(FileObject, Start, End, Ext2CanIWait());
} _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
DbgBreak();
} _SEH2_END;
return rc;
}
VOID
Ext2DeferWrite(IN PEXT2_IRP_CONTEXT IrpContext, PIRP Irp)
{
ASSERT(IrpContext->Irp == Irp);
Ext2QueueRequest(IrpContext);
}
NTSTATUS
Ext2WriteVolume (IN PEXT2_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PEXT2_VCB Vcb = NULL;
PEXT2_CCB Ccb = NULL;
PEXT2_FCBVCB FcbOrVcb = NULL;
PFILE_OBJECT FileObject = NULL;
PDEVICE_OBJECT DeviceObject = NULL;
PIRP Irp = NULL;
PIO_STACK_LOCATION IoStackLocation = NULL;
ULONG Length;
LARGE_INTEGER ByteOffset;
BOOLEAN PagingIo = FALSE;
BOOLEAN Nocache = FALSE;
BOOLEAN SynchronousIo = FALSE;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN bDeferred = FALSE;
PUCHAR Buffer = NULL;
PEXT2_EXTENT Chain = NULL;
EXT2_EXTENT BlockArray;
_SEH2_TRY {
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
(IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT)));
DeviceObject = IrpContext->DeviceObject;
Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension;
ASSERT(Vcb != NULL);
ASSERT((Vcb->Identifier.Type == EXT2VCB) &&
(Vcb->Identifier.Size == sizeof(EXT2_VCB)));
FileObject = IrpContext->FileObject;
FcbOrVcb = (PEXT2_FCBVCB) FileObject->FsContext;
ASSERT(FcbOrVcb);
if (!(FcbOrVcb->Identifier.Type == EXT2VCB && (PVOID)FcbOrVcb == (PVOID)Vcb)) {
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
Ccb = (PEXT2_CCB) FileObject->FsContext2;
Irp = IrpContext->Irp;
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
Length = IoStackLocation->Parameters.Write.Length;
ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
PagingIo = IsFlagOn(Irp->Flags, IRP_PAGING_IO);
Nocache = IsFlagOn(Irp->Flags, IRP_NOCACHE) || (Ccb != NULL);
SynchronousIo = IsFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
if (PagingIo) {
ASSERT(Nocache);
}
DEBUG(DL_INF, ("Ext2WriteVolume: 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;
}
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 (!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 ) ) {
Status = Ext2LockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (NT_SUCCESS(Status)) {
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
CcDeferWrite( FileObject,
(PCC_POST_DEFERRED_WRITE)Ext2DeferWrite,
IrpContext,
Irp,
Length,
bAgain );
bDeferred = TRUE;
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
}
}
/*
* User direct volume access
*/
if (Ccb != NULL && !PagingIo) {
if (!FlagOn(Ccb->Flags, CCB_VOLUME_DASD_PURGE)) {
if (!FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) {
Status = Ext2PurgeVolume( Vcb, TRUE);
}
SetFlag(Ccb->Flags, CCB_VOLUME_DASD_PURGE);
}
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);
}
}
} else 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 ( (ByteOffset.QuadPart + Length) > Vcb->Header.FileSize.QuadPart) {
Length = (ULONG)(Vcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);
}
if (!Nocache) {
if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
CcPrepareMdlWrite (
Vcb->Volume,
&ByteOffset,
Length,
&Irp->MdlAddress,
&Irp->IoStatus );
Status = Irp->IoStatus.Status;
} else {
Buffer = Ext2GetUserBuffer(Irp);
if (Buffer == NULL) {
DbgBreak();
Status = STATUS_INVALID_USER_BUFFER;
_SEH2_LEAVE;
}
if (!CcCopyWrite( Vcb->Volume,
(PLARGE_INTEGER)(&ByteOffset),
Length,
TRUE,
Buffer )) {
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
Status = Irp->IoStatus.Status;
Ext2AddVcbExtent(Vcb, ByteOffset.QuadPart, (LONGLONG)Length);
}
if (NT_SUCCESS(Status)) {
Irp->IoStatus.Information = Length;
}
} else if (PagingIo) {
LONGLONG DirtyStart;
LONGLONG DirtyLba;
LONGLONG DirtyLength;
LONGLONG RemainLength;
PEXT2_EXTENT Extent = NULL;
PEXT2_EXTENT List = NULL;
Length &= ~((ULONG)SECTOR_SIZE - 1);
Status = Ext2LockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
if (!NT_SUCCESS(Status)) {
_SEH2_LEAVE;
}
DirtyLba = ByteOffset.QuadPart;
RemainLength = (LONGLONG) Length;
ASSERT(Length >= SECTOR_SIZE);
while (RemainLength > 0) {
DirtyStart = DirtyLba;
ASSERT(DirtyStart >= ByteOffset.QuadPart);
ASSERT(DirtyStart <= ByteOffset.QuadPart + Length);
if (Ext2LookupVcbExtent(Vcb, DirtyStart, &DirtyLba, &DirtyLength)) {
if (DirtyLba == -1) {
DirtyLba = DirtyStart + DirtyLength;
if (ByteOffset.QuadPart + Length > DirtyLba) {
RemainLength = ByteOffset.QuadPart + Length - DirtyLba;
ASSERT(DirtyStart >= ByteOffset.QuadPart);
ASSERT(DirtyStart <= ByteOffset.QuadPart + Length);
} else {
RemainLength = 0;
}
continue;
}
ASSERT(DirtyLba <= DirtyStart);
Extent = Ext2AllocateExtent();
if (!Extent) {
DEBUG(DL_ERR, ( "Ex2WriteVolume: failed to allocate Extent\n"));
Status = STATUS_INSUFFICIENT_RESOURCES;
_SEH2_LEAVE;
}
Extent->Irp = NULL;
Extent->Lba = DirtyStart;
Extent->Offset = (ULONG)( DirtyStart + Length -
RemainLength - DirtyLba );
ASSERT(Extent->Offset <= Length);
if (DirtyLba + DirtyLength >= DirtyStart + RemainLength) {
Extent->Length = (ULONG)( DirtyLba +
RemainLength -
DirtyStart );
ASSERT(Extent->Length <= Length);
RemainLength = 0;
} else {
Extent->Length = (ULONG)(DirtyLength + DirtyLba - DirtyStart);
RemainLength = RemainLength - Extent->Length;
/*
RemainLength = (DirtyStart + RemainLength) -
(DirtyLba + DirtyLength);
*/
ASSERT(RemainLength <= (LONGLONG)Length);
ASSERT(Extent->Length <= Length);
}
ASSERT(Extent->Length >= SECTOR_SIZE);
DirtyLba = DirtyStart + Extent->Length;
if (List) {
List->Next = Extent;
List = Extent;
} else {
Chain = List = Extent;
}
} else {
if (RemainLength > SECTOR_SIZE) {
DirtyLba = DirtyStart + SECTOR_SIZE;
RemainLength -= SECTOR_SIZE;
} else {
RemainLength = 0;
}
}
}
if (Chain) {
Status = Ext2ReadWriteBlocks(IrpContext,
Vcb,
Chain,
Length );
Irp = IrpContext->Irp;
if (NT_SUCCESS(Status)) {
for (Extent = Chain; Extent != NULL; Extent = Extent->Next) {
Ext2RemoveVcbExtent(Vcb, Extent->Lba, Extent->Length);
}
}
if (!Irp) {
_SEH2_LEAVE;
}
} else {
Irp->IoStatus.Information = Length;
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
} else {
Length &= ~((ULONG)SECTOR_SIZE - 1);
Status = Ext2LockUserBuffer(
IrpContext->Irp,
Length,
IoWriteAccess );
if (!NT_SUCCESS(Status)) {
_SEH2_LEAVE;
}
BlockArray.Irp = NULL;
BlockArray.Lba = ByteOffset.QuadPart;
BlockArray.Offset = 0;
BlockArray.Length = Length;
BlockArray.Next = NULL;
Status = Ext2ReadWriteBlocks(IrpContext,
Vcb,
&BlockArray,
Length );
if (NT_SUCCESS(Status)) {
Irp->IoStatus.Information = Length;
}
Irp = IrpContext->Irp;
if (!Irp) {
_SEH2_LEAVE;
}
}
} _SEH2_FINALLY {
if (MainResourceAcquired) {
ExReleaseResourceLite(&Vcb->MainResource);
}
if (!IrpContext->ExceptionInProgress) {
if (Irp) {
if (Status == STATUS_PENDING) {
if (!bDeferred) {
Status = Ext2LockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess );
if (NT_SUCCESS(Status)) {
Status = Ext2QueueRequest(IrpContext);
} else {
Ext2CompleteIrpContext(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);
}
}
Ext2CompleteIrpContext(IrpContext, Status);
}
} else {
Ext2FreeIrpContext(IrpContext);
}
}
if (Chain) {
Ext2DestroyExtentChain(Chain);
}
} _SEH2_END;
return Status;
}
NTSTATUS
Ext2WriteInode (
IN PEXT2_IRP_CONTEXT IrpContext,
IN PEXT2_VCB Vcb,
IN PEXT2_MCB Mcb,
IN ULONGLONG Offset,
IN PVOID Buffer,
IN ULONG Size,
IN BOOLEAN bDirectIo,
OUT PULONG BytesWritten
)
{
PEXT2_EXTENT Chain = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
_SEH2_TRY {
if (BytesWritten) {
*BytesWritten = 0;
}
Status = Ext2BuildExtents (
IrpContext,
Vcb,
Mcb,
Offset,
Size,
S_ISDIR(Mcb->Inode.i_mode) ? FALSE : TRUE,
&Chain
);
if (!NT_SUCCESS(Status)) {
_SEH2_LEAVE;
}
if (Chain == NULL) {
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
if (bDirectIo) {
ASSERT(IrpContext != NULL);
//
// We assume the offset is aligned.
//
Status = Ext2ReadWriteBlocks(
IrpContext,
Vcb,
Chain,
Size
);
} else {
PEXT2_EXTENT Extent;
for (Extent = Chain; Extent != NULL; Extent = Extent->Next) {
if ( !Ext2SaveBuffer(
IrpContext,
Vcb,
Extent->Lba,
Extent->Length,
(PVOID)((PUCHAR)Buffer + Extent->Offset)
)) {
_SEH2_LEAVE;
}
}
if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) {
DEBUG(DL_FLP, ("Ext2WriteInode is starting FlushingDpc...\n"));
Ext2StartFloppyFlushDpc(Vcb, NULL, NULL);
}
Status = STATUS_SUCCESS;
}
} _SEH2_FINALLY {
if (Chain) {
Ext2DestroyExtentChain(Chain);
}
if (NT_SUCCESS(Status) && BytesWritten) {
*BytesWritten = Size;
}
} _SEH2_END;
return Status;
}
NTSTATUS
Ext2WriteFile(IN PEXT2_IRP_CONTEXT IrpContext)
{
PEXT2_VCB Vcb = NULL;
PEXT2_FCB Fcb = NULL;
PEXT2_CCB Ccb = NULL;
PFILE_OBJECT FileObject = NULL;
PDEVICE_OBJECT DeviceObject = NULL;
PIRP Irp = NULL;
PIO_STACK_LOCATION IoStackLocation = NULL;
PUCHAR Buffer = NULL;
LARGE_INTEGER ByteOffset;
ULONG ReturnedLength = 0;
ULONG Length;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
BOOLEAN OpPostIrp = FALSE;
BOOLEAN PagingIo = FALSE;
BOOLEAN Nocache = FALSE;
BOOLEAN SynchronousIo = FALSE;
BOOLEAN RecursiveWriteThrough = FALSE;
BOOLEAN MainResourceAcquired = FALSE;
BOOLEAN PagingIoResourceAcquired = FALSE;
BOOLEAN bDeferred = FALSE;
#ifndef __REACTOS__
BOOLEAN UpdateFileValidSize = FALSE;
#endif
BOOLEAN FileSizesChanged = FALSE;
BOOLEAN rc;
_SEH2_TRY {
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
(IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT)));
DeviceObject = IrpContext->DeviceObject;
Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension;
ASSERT(Vcb != NULL);
ASSERT((Vcb->Identifier.Type == EXT2VCB) &&
(Vcb->Identifier.Size == sizeof(EXT2_VCB)));
FileObject = IrpContext->FileObject;
Fcb = (PEXT2_FCB) FileObject->FsContext;
Ccb = (PEXT2_CCB) FileObject->FsContext2;
ASSERT(Fcb);
ASSERT((Fcb->Identifier.Type == EXT2FCB) &&
(Fcb->Identifier.Size == sizeof(EXT2_FCB)));
Irp = IrpContext->Irp;
IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
Length = IoStackLocation->Parameters.Write.Length;
ByteOffset = IoStackLocation->Parameters.Write.ByteOffset;
PagingIo = IsFlagOn(Irp->Flags, IRP_PAGING_IO);
Nocache = IsFlagOn(Irp->Flags, IRP_NOCACHE);
SynchronousIo = IsFlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO);
if (PagingIo) {
ASSERT(Nocache);
}
DEBUG(DL_INF, ("Ext2WriteFile: %wZ Offset=%I64xh Length=%xh Paging=%xh Nocache=%xh\n",
&Fcb->Mcb->ShortName, ByteOffset.QuadPart, Length, PagingIo, Nocache));
if (IsSpecialFile(Fcb) || IsInodeSymLink(Fcb->Inode) ) {
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
if (IsFileDeleted(Fcb->Mcb) ||
(IsSymLink(Fcb) && IsFileDeleted(Fcb->Mcb->Target)) ) {
Status = STATUS_FILE_DELETED;
_SEH2_LEAVE;
}
if (Length == 0) {
Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
_SEH2_LEAVE;
}
if (ByteOffset.LowPart == FILE_USE_FILE_POINTER_POSITION &&
ByteOffset.HighPart == -1) {
ByteOffset = FileObject->CurrentByteOffset;
} else if (IsWritingToEof(ByteOffset)) {
ByteOffset.QuadPart = Fcb->Header.FileSize.QuadPart;
}
if (Nocache && !PagingIo &&
( (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 (!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 ) ) {
Status = Ext2LockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess);
if (NT_SUCCESS(Status)) {
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DEFERRED);
CcDeferWrite( FileObject,
(PCC_POST_DEFERRED_WRITE)Ext2DeferWrite,
IrpContext,
Irp,
Length,
bAgain );
bDeferred = TRUE;
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
}
}
if (IsDirectory(Fcb) && !PagingIo) {
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
if (IsFlagOn(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) && !IrpContext->IsTopLevel) {
PIRP TopIrp;
TopIrp = IoGetTopLevelIrp();
if ( (ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG &&
NodeType(TopIrp) == IO_TYPE_IRP) {
PIO_STACK_LOCATION IrpStack;
IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
if ((IrpStack->MajorFunction == IRP_MJ_WRITE) &&
(IrpStack->FileObject->FsContext == FileObject->FsContext) &&
!FlagOn(TopIrp->Flags, IRP_NOCACHE) ) {
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
RecursiveWriteThrough = TRUE;
}
}
}
if (PagingIo) {
if (!ExAcquireResourceSharedLite(&Fcb->PagingIoResource, TRUE)) {
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
PagingIoResourceAcquired = TRUE;
if ( (ByteOffset.QuadPart + Length) > Fcb->Header.FileSize.QuadPart) {
if (ByteOffset.QuadPart >= Fcb->Header.AllocationSize.QuadPart) {
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
_SEH2_LEAVE;
} else {
ReturnedLength = (ULONG)(Fcb->Header.FileSize.QuadPart - ByteOffset.QuadPart);
if (ByteOffset.QuadPart + Length > Fcb->Header.AllocationSize.QuadPart)
Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart - ByteOffset.QuadPart);
}
} else {
ReturnedLength = Length;
}
} else {
if (!Ext2CheckFileAccess(Vcb, Fcb->Mcb, Ext2FileCanWrite)) {
Status = STATUS_ACCESS_DENIED;
_SEH2_LEAVE;
}
if (IsDirectory(Fcb)) {
_SEH2_LEAVE;
}
if (!ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE)) {
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
MainResourceAcquired = TRUE;
//
// Do flushing for such cases
//
if (Nocache && Ccb != NULL && Fcb->SectionObject.DataSectionObject != NULL) {
ExAcquireSharedStarveExclusive( &Fcb->PagingIoResource, TRUE);
ExReleaseResourceLite(&Fcb->PagingIoResource);
CcFlushCache( &(Fcb->SectionObject),
&ByteOffset,
CEILING_ALIGNED(ULONG, Length, BLOCK_SIZE),
&(Irp->IoStatus));
ClearLongFlag(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),
&(ByteOffset),
CEILING_ALIGNED(ULONG, Length, BLOCK_SIZE),
FALSE );
}
if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLockAnchor, Irp)) {
Status = STATUS_FILE_LOCK_CONFLICT;
_SEH2_LEAVE;
}
if (Ccb != NULL) {
Status = FsRtlCheckOplock( &Fcb->Oplock,
Irp,
IrpContext,
Ext2OplockComplete,
Ext2LockIrp );
if (Status != STATUS_SUCCESS) {
OpPostIrp = TRUE;
_SEH2_LEAVE;
}
//
// Set the flag indicating if Fast I/O is possible
//
Fcb->Header.IsFastIoPossible = Ext2IsFastIoPossible(Fcb);
}
//
// Extend the inode size when the i/o is beyond the file end ?
//
if ((ByteOffset.QuadPart + Length) > Fcb->Header.FileSize.QuadPart) {
LARGE_INTEGER AllocationSize, Last;
if (!ExAcquireResourceExclusiveLite(&Fcb->PagingIoResource, TRUE)) {
Status = STATUS_PENDING;
_SEH2_LEAVE;
}
PagingIoResourceAcquired = TRUE;
/* let this irp wait, since it has to be synchronous */
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
Last.QuadPart = Fcb->Header.AllocationSize.QuadPart;
AllocationSize.QuadPart = (LONGLONG)(ByteOffset.QuadPart + Length);
AllocationSize.QuadPart = CEILING_ALIGNED(ULONGLONG,
(ULONGLONG)AllocationSize.QuadPart,
(ULONGLONG)BLOCK_SIZE);
/* tell Ext2ExpandFile to allocate unwritten extent or NULL blocks
for indirect files, otherwise we might get gabage data in holes */
IrpContext->MajorFunction += IRP_MJ_MAXIMUM_FUNCTION;
Status = Ext2ExpandFile(IrpContext, Vcb, Fcb->Mcb, &AllocationSize);
IrpContext->MajorFunction -= IRP_MJ_MAXIMUM_FUNCTION;
if (AllocationSize.QuadPart > Last.QuadPart) {
Fcb->Header.AllocationSize.QuadPart = AllocationSize.QuadPart;
SetLongFlag(Fcb->Flags, FCB_ALLOC_IN_WRITE);
}
ExReleaseResourceLite(&Fcb->PagingIoResource);
PagingIoResourceAcquired = FALSE;
if (ByteOffset.QuadPart >= Fcb->Header.AllocationSize.QuadPart) {
if (NT_SUCCESS(Status)) {
DbgBreak();
Status = STATUS_UNSUCCESSFUL;
}
_SEH2_LEAVE;
}
if (ByteOffset.QuadPart + Length > Fcb->Header.AllocationSize.QuadPart) {
Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart - ByteOffset.QuadPart);
}
Fcb->Header.FileSize.QuadPart = Fcb->Inode->i_size = ByteOffset.QuadPart + Length;
Ext2SaveInode(IrpContext, Vcb, Fcb->Inode);
if (CcIsFileCached(FileObject)) {
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
}
FileObject->Flags |= FO_FILE_SIZE_CHANGED | FO_FILE_MODIFIED;
FileSizesChanged = TRUE;
if (Fcb->Header.FileSize.QuadPart >= 0x80000000 &&
!IsFlagOn(SUPER_BLOCK->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
SetFlag(SUPER_BLOCK->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_LARGE_FILE);
Ext2SaveSuper(IrpContext, Vcb);
}
DEBUG(DL_IO, ("Ext2WriteFile: expanding %wZ to FS: %I64xh FA: %I64xh\n",
&Fcb->Mcb->ShortName, Fcb->Header.FileSize.QuadPart,
Fcb->Header.AllocationSize.QuadPart));
}
ReturnedLength = Length;
}
if (!Nocache) {
if (FileObject->PrivateCacheMap == NULL) {
CcInitializeCacheMap(
FileObject,
(PCC_FILE_SIZES)(&Fcb->Header.AllocationSize),
FALSE,
&Ext2Global->CacheManagerCallbacks,
Fcb );
CcSetReadAheadGranularity(
FileObject,
READ_AHEAD_GRANULARITY );
}
if (FlagOn(IrpContext->MinorFunction, IRP_MN_MDL)) {
CcPrepareMdlWrite(
FileObject,
&ByteOffset,
Length,
&Irp->MdlAddress,
&Irp->IoStatus );
Status = Irp->IoStatus.Status;
} else {
Buffer = Ext2GetUserBuffer(Irp);
if (Buffer == NULL) {
DbgBreak();
Status = STATUS_INVALID_USER_BUFFER;
_SEH2_LEAVE;
}
if (ByteOffset.QuadPart > Fcb->Header.ValidDataLength.QuadPart) {
/* let this irp wait, since it has to be synchronous */
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
rc = Ext2ZeroData(IrpContext, Vcb, FileObject,
&Fcb->Header.ValidDataLength, &ByteOffset);
if (!rc) {
Status = STATUS_PENDING;
DbgBreak();
_SEH2_LEAVE;
}
}
if (!CcCopyWrite(FileObject, &ByteOffset, Length, Ext2CanIWait(), Buffer)) {
if (Ext2CanIWait() ||
!CcCopyWrite(FileObject, &ByteOffset, Length, TRUE, Buffer)) {
Status = STATUS_PENDING;
DbgBreak();
_SEH2_LEAVE;
}
}
if (ByteOffset.QuadPart + Length > Fcb->Header.ValidDataLength.QuadPart ) {
if (Fcb->Header.FileSize.QuadPart < ByteOffset.QuadPart + Length) {
Fcb->Header.ValidDataLength.QuadPart = Fcb->Header.FileSize.QuadPart;
} else {
if (Fcb->Header.ValidDataLength.QuadPart < ByteOffset.QuadPart + Length)
Fcb->Header.ValidDataLength.QuadPart = ByteOffset.QuadPart + Length;
}
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
FileSizesChanged = TRUE;
}
Status = STATUS_SUCCESS;
}
if (NT_SUCCESS(Status)) {
Irp->IoStatus.Information = Length;
if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) {
DEBUG(DL_FLP, ("Ext2WriteFile is starting FlushingDpc...\n"));
Ext2StartFloppyFlushDpc(Vcb, Fcb, FileObject);
}
}
} else {
if (!PagingIo && !RecursiveWriteThrough && !IsLazyWriter(Fcb)) {
if (ByteOffset.QuadPart > Fcb->Header.ValidDataLength.QuadPart) {
/* let this irp wait, since it has to be synchronous */
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
rc = Ext2ZeroData(IrpContext, Vcb, FileObject,
&Fcb->Header.ValidDataLength,
&ByteOffset);
if (!rc) {
Status = STATUS_PENDING;
DbgBreak();
_SEH2_LEAVE;
}
}
}
Status = Ext2LockUserBuffer(
IrpContext->Irp,
Length,
IoReadAccess );
if (!NT_SUCCESS(Status)) {
_SEH2_LEAVE;
}
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = ReturnedLength;
Status = Ext2WriteInode(
IrpContext,
Vcb,
Fcb->Mcb,
(ULONGLONG)(ByteOffset.QuadPart),
NULL,
ReturnedLength,
TRUE,
&Length
);
Irp = IrpContext->Irp;
if (NT_SUCCESS(Status) && !RecursiveWriteThrough && !IsLazyWriter(Fcb)) {
if (ByteOffset.QuadPart + Length > Fcb->Header.ValidDataLength.QuadPart ) {
FileSizesChanged = TRUE;
if (Fcb->Header.FileSize.QuadPart < ByteOffset.QuadPart + Length) {
if (!PagingIo)
Fcb->Header.FileSize.QuadPart = ByteOffset.QuadPart + Length;
Fcb->Header.ValidDataLength.QuadPart = Fcb->Header.FileSize.QuadPart;
} else {
if (Fcb->Header.ValidDataLength.QuadPart < ByteOffset.QuadPart + Length)
Fcb->Header.ValidDataLength.QuadPart = ByteOffset.QuadPart + Length;
}
if (!PagingIo && CcIsFileCached(FileObject)) {
CcSetFileSizes(FileObject, (PCC_FILE_SIZES)(&(Fcb->Header.AllocationSize)));
}
DEBUG(DL_IO, ("Ext2WriteFile: %wZ written FS: %I64xh FA: %I64xh BO: %I64xh LEN: %u\n",
&Fcb->Mcb->ShortName, Fcb->Header.FileSize.QuadPart,
Fcb->Header.AllocationSize.QuadPart, ByteOffset.QuadPart, Length));
}
}
}
if (FileSizesChanged) {
FileObject->Flags |= FO_FILE_SIZE_CHANGED | FO_FILE_MODIFIED;
Ext2NotifyReportChange( IrpContext, Vcb, Fcb->Mcb,
FILE_NOTIFY_CHANGE_SIZE,
FILE_ACTION_MODIFIED );
}
} _SEH2_FINALLY {
/*
* in case we got excpetions, we need revert MajorFunction
* back to IRP_MJ_WRITE. The reason we do this, is to tell
* Ext2ExpandFile to allocate unwritten extent or don't add
* new blocks for indirect files.
*/
if (IrpContext->MajorFunction > IRP_MJ_MAXIMUM_FUNCTION)
IrpContext->MajorFunction -= IRP_MJ_MAXIMUM_FUNCTION;
if (Irp) {
if (PagingIoResourceAcquired) {
ExReleaseResourceLite(&Fcb->PagingIoResource);
}
if (MainResourceAcquired) {
ExReleaseResourceLite(&Fcb->MainResource);
}
}
if (!OpPostIrp && !IrpContext->ExceptionInProgress) {
if (Irp) {
if (Status == STATUS_PENDING ||
Status == STATUS_CANT_WAIT ) {
if (!bDeferred) {
Status = Ext2QueueRequest(IrpContext);
}
} else {
if (NT_SUCCESS(Status) && !PagingIo) {
if (SynchronousIo) {
FileObject->CurrentByteOffset.QuadPart =
ByteOffset.QuadPart + Irp->IoStatus.Information;
}
SetFlag(FileObject->Flags, FO_FILE_MODIFIED);
SetLongFlag(Fcb->Flags, FCB_FILE_MODIFIED);
}
Ext2CompleteIrpContext(IrpContext, Status);
}
} else {
Ext2FreeIrpContext(IrpContext);
}
}
} _SEH2_END;
DEBUG(DL_IO, ("Ext2WriteFile: %wZ written at Offset=%I64xh Length=%xh PagingIo=%d Nocache=%d "
"RetLen=%xh VDL=%I64xh FileSize=%I64xh i_size=%I64xh Status=%xh\n",
&Fcb->Mcb->ShortName, ByteOffset, Length, PagingIo, Nocache, ReturnedLength,
Fcb->Header.ValidDataLength.QuadPart,Fcb->Header.FileSize.QuadPart,
Fcb->Inode->i_size, Status));
return Status;
}
NTSTATUS
Ext2WriteComplete (IN PEXT2_IRP_CONTEXT IrpContext)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PFILE_OBJECT FileObject;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
_SEH2_TRY {
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
(IrpContext->Identifier.Size == sizeof(EXT2_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) {
Ext2CompleteIrpContext(IrpContext, Status);
}
} _SEH2_END;
return Status;
}
NTSTATUS
Ext2Write (IN PEXT2_IRP_CONTEXT IrpContext)
{
NTSTATUS Status;
PEXT2_FCBVCB FcbOrVcb;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
PEXT2_VCB Vcb;
BOOLEAN bCompleteRequest = TRUE;
ASSERT(IrpContext);
ASSERT((IrpContext->Identifier.Type == EXT2ICX) &&
(IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT)));
_SEH2_TRY {
if (IsFlagOn(IrpContext->MinorFunction, IRP_MN_COMPLETE)) {
Status = Ext2WriteComplete(IrpContext);
bCompleteRequest = FALSE;
} else {
DeviceObject = IrpContext->DeviceObject;
if (IsExt2FsDevice(DeviceObject)) {
Status = STATUS_INVALID_DEVICE_REQUEST;
_SEH2_LEAVE;
}
FileObject = IrpContext->FileObject;
Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension;
if (Vcb->Identifier.Type != EXT2VCB ||
Vcb->Identifier.Size != sizeof(EXT2_VCB) ) {
Status = STATUS_INVALID_PARAMETER;
_SEH2_LEAVE;
}
if (IsVcbReadOnly(Vcb)) {
Status = STATUS_MEDIA_WRITE_PROTECTED;
_SEH2_LEAVE;
}
if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED) &&
Vcb->LockFile != FileObject ) {
Status = STATUS_ACCESS_DENIED;
_SEH2_LEAVE;
}
FcbOrVcb = (PEXT2_FCBVCB) FileObject->FsContext;
if (FcbOrVcb->Identifier.Type == EXT2VCB) {
Status = Ext2WriteVolume(IrpContext);
if (!NT_SUCCESS(Status)) {
DbgBreak();
}
bCompleteRequest = FALSE;
} else if (FcbOrVcb->Identifier.Type == EXT2FCB) {
if (IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) {
Status = STATUS_TOO_LATE;
_SEH2_LEAVE;
}
Status = Ext2WriteFile(IrpContext);
if (!NT_SUCCESS(Status)) {
DbgBreak();
}
bCompleteRequest = FALSE;
} else {
Status = STATUS_INVALID_PARAMETER;
}
}
} _SEH2_FINALLY {
if (bCompleteRequest) {
Ext2CompleteIrpContext(IrpContext, Status);
}
} _SEH2_END;
return Status;
}