/* * 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