NTSTATUS Ext2TruncateBlock( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb, IN ULONG Base, IN ULONG Start, IN ULONG Layer, IN ULONG SizeArray, IN PULONG BlockArray, IN PULONG Extra ) { NTSTATUS Status = STATUS_SUCCESS; ULONG i = 0; ULONG Slot = 0; ULONG Skip = 0; LONGLONG Offset; PBCB Bcb = NULL; PULONG pData = NULL; ASSERT(Mcb != NULL); for (i = 0; i < SizeArray; i++) { if (Layer == 0) { ULONG Number = 1; while (Extra && SizeArray > i + 1 && Number < *Extra) { if (BlockArray[SizeArray - i - 1] == BlockArray[SizeArray - i - 2] + 1) { BlockArray[SizeArray - i - 1] = 0; Number++; SizeArray--; } else { break; } } if (BlockArray[SizeArray - i - 1]) { Status = Ext2FreeBlock(IrpContext, Vcb, BlockArray[SizeArray - i - 1], Number); if (NT_SUCCESS(Status)) { ASSERT(Mcb->Inode.i_blocks >= (Number << (BLOCK_BITS - 9))); if (Mcb->Inode.i_blocks < (Number << (BLOCK_BITS - 9))) { Mcb->Inode.i_blocks = 0; DbgBreak(); } else { Mcb->Inode.i_blocks -= (Number << (BLOCK_BITS - 9)); } BlockArray[SizeArray - i - 1] = 0; } } if (Extra) { /* dec blocks count */ ASSERT(*Extra >= Number); *Extra = *Extra - Number; /* remove block mapping frm Mcb Extents */ if (!Ext2RemoveBlockExtent(Vcb, Mcb, Base + SizeArray - 1 - i, Number)) { DbgBreak(); ClearFlag(Mcb->Flags, MCB_ZONE_INITED); Ext2ClearAllExtents(&Mcb->Extents); } } } else { ASSERT(Layer <= 3); if (BlockArray[SizeArray - i - 1] >= TOTAL_BLOCKS) { DbgBreak(); BlockArray[SizeArray - i - 1] = 0; } if (i == 0) { if (Layer > 1) { Slot = Start / Vcb->max_blocks_per_layer[Layer - 1]; Start = Start % Vcb->max_blocks_per_layer[Layer - 1]; } else { Slot = Start; Start = (BLOCK_SIZE / 4) - 1; } } else { Slot = Start = (BLOCK_SIZE / 4) - 1; } Skip = (SizeArray - i - 1) * Vcb->max_blocks_per_layer[Layer]; if (BlockArray[SizeArray - i - 1]) { Offset = (LONGLONG) (BlockArray[SizeArray - i - 1]); Offset = Offset << BLOCK_BITS; if (!CcPinRead( Vcb->Volume, (PLARGE_INTEGER) (&Offset), BLOCK_SIZE, PIN_WAIT, &Bcb, (void **)&pData )) { DEBUG(DL_ERR, ( "Ext2TruncateBlock: PinLock failed on block %xh ...\n", BlockArray[SizeArray - i - 1])); Status = STATUS_CANT_WAIT; DbgBreak(); goto errorout; } Status = Ext2TruncateBlock( IrpContext, Vcb, Mcb, Base + Skip, Start, Layer - 1, Slot + 1, &pData[0], Extra ); if (!NT_SUCCESS(Status)) { break; } CcSetDirtyPinnedData(Bcb, NULL); Ext2AddVcbExtent(Vcb, Offset, (LONGLONG)BLOCK_SIZE); if (*Extra || Ext2IsBlockEmpty(pData, BLOCK_SIZE/4)) { Ext2TruncateBlock( IrpContext, Vcb, Mcb, Base + Skip, /* base */ 0, /* start */ 0, /* layer */ 1, &BlockArray[SizeArray - i - 1], NULL ); if (!Ext2RemoveMcbMetaExts(Vcb, Mcb, BlockArray[SizeArray - i - 1], 1)) { DbgBreak(); Ext2Sleep(500); Ext2RemoveMcbMetaExts(Vcb, Mcb, BlockArray[SizeArray - i - 1], 1); } } if (pData) { CcUnpinData(Bcb); Bcb = NULL; pData = NULL; } } else { if (Layer > 1) { if (*Extra > Slot * Vcb->max_blocks_per_layer[Layer - 1] + Start + 1) { *Extra -= (Slot * Vcb->max_blocks_per_layer[Layer - 1] + Start + 1); } else { *Extra = 0; } } else { if (*Extra > Slot + 1) { *Extra -= (Slot + 1); } else { *Extra = 0; } } if (!Ext2RemoveBlockExtent(Vcb, Mcb, Base + Skip, (Start + 1))) { DbgBreak(); ClearFlag(Mcb->Flags, MCB_ZONE_INITED); Ext2ClearAllExtents(&Mcb->Extents); } } } if (Extra && *Extra == 0) { break; } } errorout: if (pData) { CcUnpinData(Bcb); } return Status; }
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; __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; __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; __leave; } if (Nocache && (ByteOffset.LowPart & (SECTOR_SIZE - 1) || Length & (SECTOR_SIZE - 1))) { Status = STATUS_INVALID_PARAMETER; __leave; } if (FlagOn(IrpContext->MinorFunction, IRP_MN_DPC)) { ClearFlag(IrpContext->MinorFunction, IRP_MN_DPC); Status = STATUS_PENDING; __leave; } if (ByteOffset.QuadPart >= Vcb->PartitionInformation.PartitionLength.QuadPart ) { Irp->IoStatus.Information = 0; Status = STATUS_END_OF_FILE; __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; __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; __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; __leave; } if (!CcCopyWrite( Vcb->Volume, (PLARGE_INTEGER)(&ByteOffset), Length, TRUE, Buffer )) { Status = STATUS_PENDING; __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)) { __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; __leave; } Extent->Irp = NULL; Extent->Lba = DirtyLba; 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 = (DirtyStart + RemainLength) - (DirtyLba + DirtyLength); ASSERT(RemainLength <= (LONGLONG)Length); ASSERT(Extent->Length <= Length); } ASSERT(Extent->Length >= SECTOR_SIZE); DirtyLba = DirtyStart + DirtyLength; 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) { __leave; } } else { Irp->IoStatus.Information = Length; Status = STATUS_SUCCESS; __leave; } } else { Length &= ~((ULONG)SECTOR_SIZE - 1); Status = Ext2LockUserBuffer( IrpContext->Irp, Length, IoWriteAccess ); if (!NT_SUCCESS(Status)) { __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) { __leave; } } } __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); } } return Status; }
NTSTATUS Ext2GetBlock( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb, IN ULONG Base, IN ULONG Layer, IN ULONG Start, IN ULONG SizeArray, IN PULONG BlockArray, IN BOOLEAN bAlloc, IN OUT PULONG Hint, OUT PULONG Block, OUT PULONG Number ) { NTSTATUS Status = STATUS_SUCCESS; PBCB Bcb = NULL; PULONG pData = NULL; ULONG Slot = 0, i = 0; ULONG Unit = 1; LARGE_INTEGER Offset; if (Layer == 0) { *Number = 1; if (BlockArray[0] == 0 && bAlloc) { /* now allocate new block */ Status = Ext2ExpandLast( IrpContext, Vcb, Mcb, Base, Layer, NULL, Hint, &BlockArray[0], Number ); if (!NT_SUCCESS(Status)) { goto errorout; } } else { /* check the block is valid or not */ if (BlockArray[0] >= TOTAL_BLOCKS) { DbgBreak(); Status = STATUS_DISK_CORRUPT_ERROR; goto errorout; } } *Block = BlockArray[0]; for (i=1; i < SizeArray; i++) { if (BlockArray[i] == BlockArray[i-1] + 1) { *Number = *Number + 1; } else { break; } } *Hint = BlockArray[*Number - 1]; } else if (Layer <= 3) { /* check the block is valid or not */ if (BlockArray[0] == 0 || BlockArray[0] >= TOTAL_BLOCKS) { DbgBreak(); Status = STATUS_DISK_CORRUPT_ERROR; goto errorout; } /* add block to meta extents */ if (!Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[0], 1)) { DbgBreak(); Ext2Sleep(500); Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[0], 1); } /* map memory in cache for the index block */ Offset.QuadPart = ((LONGLONG)BlockArray[0]) << BLOCK_BITS; if ( !CcPinRead( Vcb->Volume, (PLARGE_INTEGER) (&Offset), BLOCK_SIZE, PIN_WAIT, &Bcb, (void **)&pData )) { DEBUG(DL_ERR, ( "Ext2GetBlock: Failed to PinLock block: %xh ...\n", BlockArray[0] )); Status = STATUS_CANT_WAIT; goto errorout; } if (Layer > 1) { Unit = Vcb->max_blocks_per_layer[Layer - 1]; } else { Unit = 1; } Slot = Start / Unit; Start = Start % Unit; if (pData[Slot] == 0) { if (bAlloc) { /* we need allocate new block and zero all data in case it's an in-direct block. Index stores the new block no. */ ULONG Count = 1; Status = Ext2ExpandLast( IrpContext, Vcb, Mcb, Base, Layer, NULL, Hint, &pData[Slot], &Count ); if (!NT_SUCCESS(Status)) { goto errorout; } /* refresh hint block */ *Hint = pData[Slot]; /* set dirty bit to notify system to flush */ CcSetDirtyPinnedData(Bcb, NULL ); SetFlag(Vcb->Volume->Flags, FO_FILE_MODIFIED); if (!Ext2AddVcbExtent(Vcb, Offset.QuadPart, (LONGLONG)BLOCK_SIZE)) { DbgBreak(); Ext2Sleep(100); if (!Ext2AddVcbExtent(Vcb, Offset.QuadPart, (LONGLONG)BLOCK_SIZE)) { Status = STATUS_INSUFFICIENT_RESOURCES; goto errorout; } } /* save inode information here */ Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); } else { *Number = 1; if (Layer == 1) { for (i = Slot + 1; i < BLOCK_SIZE/4; i++) { if (pData[i] == 0) { *Number = *Number + 1; } else { break; } } } else if (Layer == 2) { *Number = BLOCK_SIZE/4 - Start; } else { *Number = BLOCK_SIZE/4; } goto errorout; } } /* transfer to next recursion call */ Status = Ext2GetBlock( IrpContext, Vcb, Mcb, Base, Layer - 1, Start, BLOCK_SIZE/4 - Slot, &pData[Slot], bAlloc, Hint, Block, Number ); if (!NT_SUCCESS(Status)) { goto errorout; } } errorout: /* free the memory of pData */ if (Bcb) { CcUnpinData(Bcb); } if (!NT_SUCCESS(Status)) { *Block = 0; } return Status; }