NTSTATUS FAT32FindAndMarkAvailableCluster (PDEVICE_EXTENSION DeviceExt, PULONG Cluster) /* * FUNCTION: Finds the first available cluster in a FAT32 table */ { ULONG FatLength; ULONG StartCluster; ULONG i, j; PVOID BaseAddress; ULONG ChunkSize; PVOID Context; LARGE_INTEGER Offset; PULONG Block; PULONG BlockEnd; ChunkSize = CACHEPAGESIZE(DeviceExt); FatLength = (DeviceExt->FatInfo.NumberOfClusters + 2); *Cluster = 0; StartCluster = DeviceExt->LastAvailableCluster; for (j = 0; j < 2; j++) { for (i = StartCluster; i < FatLength;) { Offset.QuadPart = ROUND_DOWN(i * 4, ChunkSize); if(!CcPinRead(DeviceExt->FATFileObject, &Offset, ChunkSize, 1, &Context, &BaseAddress)) { DPRINT1("CcMapData(Offset %x, Length %d) failed\n", (ULONG)Offset.QuadPart, ChunkSize); return STATUS_UNSUCCESSFUL; } Block = (PULONG)((ULONG_PTR)BaseAddress + (i * 4) % ChunkSize); BlockEnd = (PULONG)((ULONG_PTR)BaseAddress + ChunkSize); /* Now process the whole block */ while (Block < BlockEnd && i < FatLength) { if ((*Block & 0x0fffffff) == 0) { DPRINT("Found available cluster 0x%x\n", i); DeviceExt->LastAvailableCluster = *Cluster = i; *Block = 0x0fffffff; CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); if (DeviceExt->AvailableClustersValid) InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); return(STATUS_SUCCESS); } Block++; i++; } CcUnpinData(Context); } FatLength = StartCluster; StartCluster = 2; } return (STATUS_DISK_FULL); }
/* * FUNCTION: Writes a cluster to the FAT12 physical and in-memory tables */ NTSTATUS FAT12WriteCluster( PDEVICE_EXTENSION DeviceExt, ULONG ClusterToWrite, ULONG NewValue, PULONG OldValue) { ULONG FATOffset; PUCHAR CBlock; PVOID BaseAddress; PVOID Context; LARGE_INTEGER Offset; Offset.QuadPart = 0; if (!CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress)) { return STATUS_UNSUCCESSFUL; } CBlock = (PUCHAR)BaseAddress; FATOffset = (ClusterToWrite * 12) / 8; DPRINT("Writing 0x%x for 0x%x at 0x%x\n", NewValue, ClusterToWrite, FATOffset); if ((ClusterToWrite % 2) == 0) { *OldValue = CBlock[FATOffset] + ((CBlock[FATOffset + 1] & 0x0f) << 8); CBlock[FATOffset] = (UCHAR)NewValue; CBlock[FATOffset + 1] &= 0xf0; CBlock[FATOffset + 1] |= (NewValue & 0xf00) >> 8; }
/* * FUNCTION: Finds the first available cluster in a FAT12 table */ NTSTATUS FAT12FindAndMarkAvailableCluster( PDEVICE_EXTENSION DeviceExt, PULONG Cluster) { ULONG FatLength; ULONG StartCluster; ULONG Entry; PUSHORT CBlock; ULONG i, j; PVOID BaseAddress; PVOID Context; LARGE_INTEGER Offset; FatLength = DeviceExt->FatInfo.NumberOfClusters + 2; *Cluster = 0; StartCluster = DeviceExt->LastAvailableCluster; Offset.QuadPart = 0; if (!CcPinRead(DeviceExt->FATFileObject, &Offset, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector, 1, &Context, &BaseAddress)) { DPRINT1("CcMapData(Offset %x, Length %u) failed\n", (ULONG)Offset.QuadPart, DeviceExt->FatInfo.FATSectors * DeviceExt->FatInfo.BytesPerSector); return STATUS_UNSUCCESSFUL; } for (j = 0; j < 2; j++) { for (i = StartCluster; i < FatLength; i++) { CBlock = (PUSHORT)((char*)BaseAddress + (i * 12) / 8); if ((i % 2) == 0) { Entry = *CBlock & 0xfff; } else { Entry = *CBlock >> 4; } if (Entry == 0) { DPRINT("Found available cluster 0x%x\n", i); DeviceExt->LastAvailableCluster = *Cluster = i; if ((i % 2) == 0) *CBlock = (*CBlock & 0xf000) | 0xfff; else *CBlock = (*CBlock & 0xf) | 0xfff0; CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); if (DeviceExt->AvailableClustersValid) InterlockedDecrement((PLONG)&DeviceExt->AvailableClusters); return STATUS_SUCCESS; } } FatLength = StartCluster; StartCluster = 2; } CcUnpinData(Context); return STATUS_DISK_FULL; }
/* * update an existing FAT entry */ NTSTATUS VfatUpdateEntry( IN PVFATFCB pFcb) { PVOID Context; PDIR_ENTRY PinEntry; LARGE_INTEGER Offset; ULONG SizeDirEntry; ULONG dirIndex; ASSERT(pFcb); if (pFcb->Flags & FCB_IS_FATX_ENTRY) { SizeDirEntry = sizeof(FATX_DIR_ENTRY); dirIndex = pFcb->startIndex; } else { SizeDirEntry = sizeof(FAT_DIR_ENTRY); dirIndex = pFcb->dirIndex; } DPRINT("updEntry dirIndex %u, PathName \'%wZ\'\n", dirIndex, &pFcb->PathNameU); if (vfatFCBIsRoot(pFcb) || (pFcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME))) { return STATUS_SUCCESS; } ASSERT(pFcb->parentFcb); Offset.u.HighPart = 0; Offset.u.LowPart = dirIndex * SizeDirEntry; _SEH2_TRY { CcPinRead(pFcb->parentFcb->FileObject, &Offset, SizeDirEntry, PIN_WAIT, &Context, (PVOID*)&PinEntry); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("Failed write to \'%wZ\'.\n", &pFcb->parentFcb->PathNameU); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; pFcb->Flags &= ~FCB_IS_DIRTY; RtlCopyMemory(PinEntry, &pFcb->entry, SizeDirEntry); CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); return STATUS_SUCCESS; }
static NTSTATUS FsdSetFsLabelInformation( PDEVICE_OBJECT DeviceObject, PFILE_FS_LABEL_INFORMATION FsLabelInfo) { PDEVICE_EXTENSION DeviceExt; PVOID Context = NULL; ULONG DirIndex = 0; PDIR_ENTRY Entry; PVFATFCB pRootFcb; LARGE_INTEGER FileOffset; BOOLEAN LabelFound = FALSE; DIR_ENTRY VolumeLabelDirEntry; ULONG VolumeLabelDirIndex; ULONG LabelLen; NTSTATUS Status = STATUS_UNSUCCESSFUL; OEM_STRING StringO; UNICODE_STRING StringW; CHAR cString[43]; ULONG SizeDirEntry; ULONG EntriesPerPage; DPRINT("FsdSetFsLabelInformation()\n"); DeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension; if (sizeof(DeviceObject->Vpb->VolumeLabel) < FsLabelInfo->VolumeLabelLength) { return STATUS_NAME_TOO_LONG; } if (DeviceExt->Flags & VCB_IS_FATX) { if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 42) return STATUS_NAME_TOO_LONG; SizeDirEntry = sizeof(FATX_DIR_ENTRY); EntriesPerPage = FATX_ENTRIES_PER_PAGE; } else { if (FsLabelInfo->VolumeLabelLength / sizeof(WCHAR) > 11) return STATUS_NAME_TOO_LONG; SizeDirEntry = sizeof(FAT_DIR_ENTRY); EntriesPerPage = FAT_ENTRIES_PER_PAGE; } /* Create Volume label dir entry */ LabelLen = FsLabelInfo->VolumeLabelLength / sizeof(WCHAR); RtlZeroMemory(&VolumeLabelDirEntry, SizeDirEntry); StringW.Buffer = FsLabelInfo->VolumeLabel; StringW.Length = StringW.MaximumLength = (USHORT)FsLabelInfo->VolumeLabelLength; StringO.Buffer = cString; StringO.Length = 0; StringO.MaximumLength = 42; Status = RtlUnicodeStringToOemString(&StringO, &StringW, FALSE); if (!NT_SUCCESS(Status)) return Status; if (DeviceExt->Flags & VCB_IS_FATX) { RtlCopyMemory(VolumeLabelDirEntry.FatX.Filename, cString, LabelLen); memset(&VolumeLabelDirEntry.FatX.Filename[LabelLen], ' ', 42 - LabelLen); VolumeLabelDirEntry.FatX.Attrib = _A_VOLID; } else { RtlCopyMemory(VolumeLabelDirEntry.Fat.Filename, cString, max(sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen)); if (LabelLen > sizeof(VolumeLabelDirEntry.Fat.Filename)) { memset(VolumeLabelDirEntry.Fat.Ext, ' ', sizeof(VolumeLabelDirEntry.Fat.Ext)); RtlCopyMemory(VolumeLabelDirEntry.Fat.Ext, cString + sizeof(VolumeLabelDirEntry.Fat.Filename), LabelLen - sizeof(VolumeLabelDirEntry.Fat.Filename)); } else { memset(&VolumeLabelDirEntry.Fat.Filename[LabelLen], ' ', sizeof(VolumeLabelDirEntry.Fat.Filename) - LabelLen); } VolumeLabelDirEntry.Fat.Attrib = _A_VOLID; } pRootFcb = vfatOpenRootFCB(DeviceExt); /* Search existing volume entry on disk */ FileOffset.QuadPart = 0; if (CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry)) { while (TRUE) { if (ENTRY_VOLUME(DeviceExt, Entry)) { /* Update entry */ LabelFound = TRUE; RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry); CcSetDirtyPinnedData(Context, NULL); Status = STATUS_SUCCESS; break; } if (ENTRY_END(DeviceExt, Entry)) { break; } DirIndex++; Entry = (PDIR_ENTRY)((ULONG_PTR)Entry + SizeDirEntry); if ((DirIndex % EntriesPerPage) == 0) { CcUnpinData(Context); FileOffset.u.LowPart += PAGE_SIZE; if (!CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry)) { Context = NULL; break; } } } if (Context) { CcUnpinData(Context); } } if (!LabelFound) { /* Add new entry for label */ if (!vfatFindDirSpace(DeviceExt, pRootFcb, 1, &VolumeLabelDirIndex)) Status = STATUS_DISK_FULL; else { FileOffset.u.HighPart = 0; FileOffset.u.LowPart = VolumeLabelDirIndex * SizeDirEntry; if (!CcPinRead(pRootFcb->FileObject, &FileOffset, SizeDirEntry, TRUE, &Context, (PVOID*)&Entry)) { Status = STATUS_UNSUCCESSFUL; } else { RtlCopyMemory(Entry, &VolumeLabelDirEntry, SizeDirEntry); CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); Status = STATUS_SUCCESS; } } } vfatReleaseFCB(DeviceExt, pRootFcb); if (!NT_SUCCESS(Status)) { return Status; } /* Update volume label in memory */ DeviceObject->Vpb->VolumeLabelLength = (USHORT)FsLabelInfo->VolumeLabelLength; RtlCopyMemory(DeviceObject->Vpb->VolumeLabel, FsLabelInfo->VolumeLabel, DeviceObject->Vpb->VolumeLabelLength); return Status; }
BOOLEAN FFSCheckSetBlock( PFFS_IRP_CONTEXT IrpContext, PFFS_VCB Vcb, ULONG Block) { #if 0 ULONG Group, dwBlk, Length; RTL_BITMAP BlockBitmap; PVOID BitmapCache; PBCB BitmapBcb; LARGE_INTEGER Offset; BOOLEAN bModified = FALSE; //Group = (Block - FFS_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; dwBlk = (Block - FFS_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; Offset.QuadPart = (LONGLONG) Vcb->BlockSize; Offset.QuadPart = Offset.QuadPart * Vcb->ffs_group_desc[Group].bg_block_bitmap; if (Group == Vcb->ffs_groups - 1) { Length = TOTAL_BLOCKS % BLOCKS_PER_GROUP; /* s_blocks_count is integer multiple of s_blocks_per_group */ if (Length == 0) Length = BLOCKS_PER_GROUP; } else { Length = BLOCKS_PER_GROUP; } if (dwBlk >= Length) return FALSE; if (!CcPinRead(Vcb->StreamObj, &Offset, Vcb->BlockSize, PIN_WAIT, &BitmapBcb, &BitmapCache)) { FFSPrint((DBG_ERROR, "FFSDeleteBlock: PinReading error ...\n")); return FALSE; } RtlInitializeBitMap(&BlockBitmap, BitmapCache, Length); if (RtlCheckBit(&BlockBitmap, dwBlk) == 0) { FFSBreakPoint(); RtlSetBits(&BlockBitmap, dwBlk, 1); bModified = TRUE; } if (bModified) { CcSetDirtyPinnedData(BitmapBcb, NULL); FFSRepinBcb(IrpContext, BitmapBcb); FFSAddMcbEntry(Vcb, Offset.QuadPart, (LONGLONG)Vcb->BlockSize); } { CcUnpinData(BitmapBcb); BitmapBcb = NULL; BitmapCache = NULL; RtlZeroMemory(&BlockBitmap, sizeof(RTL_BITMAP)); } return (!bModified); #endif return FALSE; }
VOID FatPrepareWriteDirectoryFile ( IN PIRP_CONTEXT IrpContext, IN PDCB Dcb, IN VBO StartingVbo, IN ULONG ByteCount, OUT PBCB *Bcb, OUT PVOID *Buffer, IN BOOLEAN Zero, IN BOOLEAN Reversible, OUT PNTSTATUS Status ) /*++ Routine Description: This routine first looks to see if the specified range of sectors is already in the cache. If so, it increments the BCB PinCount, sets the BCB dirty, and returns TRUE with the location of the sectors. The IrpContext->Flags .. Wait == TRUE/FALSE actions of this routine are identical to FatPrepareWriteVolumeFile() above. Arguments: Dcb - Pointer to the DCB for the directory StartingVbo - The virtual offset of the first byte to be written ByteCount - Number of bytes to be written Bcb - Returns a pointer to the BCB which is valid until unpinned Buffer - Returns a pointer to the sectors, which is valid until unpinned Zero - Supplies TRUE if the specified range of bytes should be zeroed Reversible - Supplies TRUE if the specified range of modification should be repinned so that the operation can be reversed in a controlled fashion if errors are encountered. Status - Returns the status of the operation. --*/ { LARGE_INTEGER Vbo; ULONG InitialAllocation; BOOLEAN UnwindWeAllocatedDiskSpace = FALSE; ULONG ClusterSize; PVOID LocalBuffer; PAGED_CODE(); DebugTrace(+1, Dbg, "FatPrepareWriteDirectoryFile\n", 0); DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); *Bcb = NULL; *Buffer = NULL; // // If we need to create a directory file and initialize the // cachemap, do so. // FatOpenDirectoryFile( IrpContext, Dcb ); // // If the transfer is beyond the allocation size we need to // extend the directory's allocation. The call to // AddFileAllocation will raise a condition if // it runs out of disk space. Note that the root directory // cannot be extended. // Vbo.QuadPart = StartingVbo; try { if (StartingVbo + ByteCount > Dcb->Header.AllocationSize.LowPart) { if (NodeType(Dcb) == FAT_NTC_ROOT_DCB && !FatIsFat32(Dcb->Vcb)) { FatRaiseStatus( IrpContext, STATUS_DISK_FULL ); } DebugTrace(0, Dbg, "Try extending normal directory\n", 0); InitialAllocation = Dcb->Header.AllocationSize.LowPart; FatAddFileAllocation( IrpContext, Dcb, Dcb->Specific.Dcb.DirectoryFile, StartingVbo + ByteCount ); UnwindWeAllocatedDiskSpace = TRUE; // // Inform the cache manager of the new allocation // Dcb->Header.FileSize.LowPart = Dcb->Header.AllocationSize.LowPart; CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); // // Set up the Bitmap buffer if it is not big enough already // FatCheckFreeDirentBitmap( IrpContext, Dcb ); // // The newly allocated clusters should be zeroed starting at // the previous allocation size // Zero = TRUE; Vbo.QuadPart = InitialAllocation; ByteCount = Dcb->Header.AllocationSize.LowPart - InitialAllocation; } // // Call the Cache Manager to attempt the transfer, going one cluster // at a time to avoid pinning across a page boundary. // ClusterSize = 1 << Dcb->Vcb->AllocationSupport.LogOfBytesPerCluster; while (ByteCount > 0) { ULONG BytesToPin; *Bcb = NULL; if (ByteCount > ClusterSize) { BytesToPin = ClusterSize; } else { BytesToPin = ByteCount; } ASSERT( (Vbo.QuadPart / ClusterSize) == (Vbo.QuadPart + BytesToPin - 1)/ClusterSize ); if (!CcPinRead( Dcb->Specific.Dcb.DirectoryFile, &Vbo, BytesToPin, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), Bcb, &LocalBuffer )) { // // Could not read the data without waiting (cache miss). // FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } // // Update our caller with the beginning of their request. // if (*Buffer == NULL) { *Buffer = LocalBuffer; } DbgDoit( IrpContext->PinCount += 1 ) if (Zero) { // // We set this guy dirty right now so that we can raise CANT_WAIT when // it needs to be done. It'd be beautiful if we could noop the read IO // since we know we don't care about it. // RtlZeroMemory( LocalBuffer, BytesToPin ); CcSetDirtyPinnedData( *Bcb, NULL ); } ByteCount -= BytesToPin; Vbo.QuadPart += BytesToPin; if (ByteCount > 0) { FatUnpinBcb( IrpContext, *Bcb ); } } // // This lets us get the data pinned until we complete the request // and writes the dirty bit through to the disk. // FatSetDirtyBcb( IrpContext, *Bcb, Dcb->Vcb, Reversible ); *Status = STATUS_SUCCESS; } finally { DebugUnwind( FatPrepareWriteDirectoryFile ); if (AbnormalTermination()) { // // These steps are carefully arranged - FatTruncateFileAllocation can raise. // Make sure we unpin the buffer. If FTFA raises, the effect should be benign. // FatUnpinBcb(IrpContext, *Bcb); if (UnwindWeAllocatedDiskSpace == TRUE) { // // Inform the cache manager of the change. // FatTruncateFileAllocation( IrpContext, Dcb, InitialAllocation ); Dcb->Header.FileSize.LowPart = Dcb->Header.AllocationSize.LowPart; CcSetFileSizes( Dcb->Specific.Dcb.DirectoryFile, (PCC_FILE_SIZES)&Dcb->Header.AllocationSize ); } } DebugTrace(-1, Dbg, "FatPrepareWriteDirectoryFile -> (VOID), *Bcb = %08lx\n", *Bcb); } return; }
VOID FatReadDirectoryFile ( IN PIRP_CONTEXT IrpContext, IN PDCB Dcb, IN VBO StartingVbo, IN ULONG ByteCount, IN BOOLEAN Pin, OUT PBCB *Bcb, OUT PVOID *Buffer, OUT PNTSTATUS Status ) /*++ Routine Description: This routine is called when the specified range of sectors is to be read into the cache. If the desired range falls beyond the current cache mapping, the fat will be searched, and if the desired range can be satisfied, the cache mapping will be extended and the MCB updated accordingly. Arguments: Dcb - Pointer to the DCB for the directory StartingVbo - The virtual offset of the first desired byte ByteCount - Number of bytes desired Pin - Tells us if we should pin instead of just mapping. Bcb - Returns a pointer to the BCB which is valid until unpinned Buffer - Returns a pointer to the sectors, which is valid until unpinned Status - Returns the status of the operation. --*/ { LARGE_INTEGER Vbo; PAGED_CODE(); DebugTrace(+1, Dbg, "FatReadDirectoryFile\n", 0); DebugTrace( 0, Dbg, "Dcb = %08lx\n", Dcb); DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", StartingVbo); DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); // // Check for the zero case // if (ByteCount == 0) { DebugTrace(0, Dbg, "Nothing to read\n", 0); *Bcb = NULL; *Buffer = NULL; *Status = STATUS_SUCCESS; DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); return; } // // If we need to create a directory file and initialize the // cachemap, do so. // FatOpenDirectoryFile( IrpContext, Dcb ); // // Now if the transfer is beyond the allocation size return EOF. // if (StartingVbo >= Dcb->Header.AllocationSize.LowPart) { DebugTrace(0, Dbg, "End of file read for directory\n", 0); *Bcb = NULL; *Buffer = NULL; *Status = STATUS_END_OF_FILE; DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID\n", 0); return; } // // If the caller is trying to read past the EOF, truncate the // read. // ByteCount = (Dcb->Header.AllocationSize.LowPart - StartingVbo < ByteCount) ? Dcb->Header.AllocationSize.LowPart - StartingVbo : ByteCount; ASSERT( ByteCount != 0 ); // // Call the Cache manager to attempt the transfer. // Vbo.QuadPart = StartingVbo; if (Pin ? !CcPinRead( Dcb->Specific.Dcb.DirectoryFile, &Vbo, ByteCount, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), Bcb, Buffer ) : !CcMapData( Dcb->Specific.Dcb.DirectoryFile, &Vbo, ByteCount, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), Bcb, Buffer ) ) { // // Could not read the data without waiting (cache miss). // *Bcb = NULL; *Buffer = NULL; FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } DbgDoit( IrpContext->PinCount += 1 ) *Status = STATUS_SUCCESS; DebugTrace(-1, Dbg, "FatReadDirectoryFile -> VOID, *BCB = %08lx\n", *Bcb); return; }
VOID FatPrepareWriteVolumeFile ( IN PIRP_CONTEXT IrpContext, IN PVCB Vcb, IN VBO StartingVbo, IN ULONG ByteCount, OUT PBCB *Bcb, OUT PVOID *Buffer, IN BOOLEAN Reversible, IN BOOLEAN Zero ) /*++ Routine Description: This routine first looks to see if the specified range of sectors, is already in the cache. If so, it increments the BCB PinCount, sets the BCB dirty, and returns with the location of the sectors. If the sectors are not in the cache and Wait is TRUE, it finds a free BCB (potentially causing a flush), and clears out the entire buffer. Once this is done, it increments the BCB PinCount, sets the BCB dirty, and returns with the location of the sectors. If the sectors are not in the cache and Wait is FALSE, this routine raises STATUS_CANT_WAIT. Arguments: Vcb - Pointer to the VCB for the volume StartingVbo - The virtual offset of the first byte to be written ByteCount - Number of bytes to be written Bcb - Returns a pointer to the BCB which is valid until unpinned Buffer - Returns a pointer to the sectors, which is valid until unpinned Reversible - Supplies TRUE if the specified range of modification should be repinned so that the operation can be reversed in a controlled fashion if errors are encountered. Zero - Supplies TRUE if the specified range of bytes should be zeroed --*/ { LARGE_INTEGER Vbo; PAGED_CODE(); // // Check to see that all references are within the Bios Parameter Block // or the fat(s). // ASSERT( ((StartingVbo + ByteCount) <= (ULONG) (FatRootDirectoryLbo( &Vcb->Bpb )))); DebugTrace(+1, Dbg, "FatPrepareWriteVolumeFile\n", 0); DebugTrace( 0, Dbg, "Vcb = %08lx\n", Vcb); DebugTrace( 0, Dbg, "StartingVbo = %08lx\n", (ULONG)StartingVbo); DebugTrace( 0, Dbg, "ByteCount = %08lx\n", ByteCount); DebugTrace( 0, Dbg, "Zero = %08lx\n", Zero); // // Call the Cache manager to attempt the transfer. // Vbo.QuadPart = StartingVbo; if (!CcPinRead( Vcb->VirtualVolumeFile, &Vbo, ByteCount, BooleanFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT), Bcb, Buffer )) { ASSERT( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); // // Could not read the data without waiting (cache miss). // FatRaiseStatus( IrpContext, STATUS_CANT_WAIT ); } // // This keeps the data pinned until we complete the request // and writes the dirty bit through to the disk. // DbgDoit( IrpContext->PinCount += 1 ) try { if (Zero) { RtlZeroMemory( *Buffer, ByteCount ); } FatSetDirtyBcb( IrpContext, *Bcb, Vcb, Reversible ); } finally { if (AbnormalTermination()) { FatUnpinBcb(IrpContext, *Bcb); } } DebugTrace(-1, Dbg, "FatPrepareWriteVolumeFile -> VOID, *Bcb = %08lx\n", *Bcb); return; }
VOID NtfsPinStream ( IN PIRP_CONTEXT IrpContext, IN PSCB Scb, IN LONGLONG FileOffset, IN ULONG Length, OUT PVOID *Bcb, OUT PVOID *Buffer ) /*++ Routine Description: This routine is called to pin a range of bytes within the stream file for an Scb. The allowed range to pin is bounded by the allocation size for the Scb. This operation is only valid on a non-resident Scb. TEMPCODE - The following need to be resolved for this routine. - Can the caller specify either an empty range or an invalid range. In that case we need to able to return the actual length of the pinned range. Arguments: Scb - This is the Scb for the operation. FileOffset - This is the offset within the Scb where the data is to be pinned. Length - This is the number of bytes to pin. Bcb - Returns a pointer to the Bcb for this range of bytes. Buffer - Returns a pointer to the range of bytes pinned in memory. Return Value: None. --*/ { NTSTATUS OldStatus = IrpContext->ExceptionStatus; ASSERT_IRP_CONTEXT( IrpContext ); ASSERT_SCB( Scb ); ASSERT( Length != 0 ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsPinStream\n") ); DebugTrace( 0, Dbg, ("Scb = %08lx\n", Scb) ); DebugTrace( 0, Dbg, ("FileOffset = %016I64x\n", FileOffset) ); DebugTrace( 0, Dbg, ("Length = %08lx\n", Length) ); // // The file object should already exist in the Scb. // ASSERT( Scb->FileObject != NULL ); // // If we are trying to go beyond the end of the allocation, assume // we have some corruption. // if ((FileOffset + Length) > Scb->Header.AllocationSize.QuadPart) { NtfsRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR, NULL, Scb->Fcb ); } // // Call the cache manager to map the data. This call may raise, or // will return FALSE if waiting is required. // if (FlagOn( Scb->ScbPersist, SCB_PERSIST_USN_JOURNAL )) { FileOffset -= Scb->Vcb->UsnCacheBias; } if (!CcPinRead( Scb->FileObject, (PLARGE_INTEGER)&FileOffset, Length, FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ), Bcb, Buffer )) { ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT )); // // Could not pin the data without waiting (cache miss). // NtfsRaiseStatus( IrpContext, STATUS_CANT_WAIT, NULL, NULL ); } // // We don't want to propagate wether or not we hit eof. Its assumed the code pinning is // already filesize synchronized // if (IrpContext->ExceptionStatus == STATUS_END_OF_FILE) { IrpContext->ExceptionStatus = OldStatus; } #ifdef MAPCOUNT_DBG IrpContext->MapCount++; #endif DebugTrace( 0, Dbg, ("Bcb -> %08lx\n", *Bcb) ); DebugTrace( 0, Dbg, ("Buffer -> %08lx\n", *Buffer) ); DebugTrace( -1, Dbg, ("NtfsMapStream -> VOID\n") ); return; }
BOOLEAN CmpFileWriteThroughCache( PHHIVE Hive, ULONG FileType, PCMP_OFFSET_ARRAY offsetArray, ULONG offsetArrayCount ) /*++ Routine Description: This is routine writes dirty ranges of data using Cc mapped views. The benefit is that writes don't go through Cc Lazy Writer, so there is no danger to be throttled or deferred. It also flushes the cache for the written ranges, guaranteeing that the data was commited to the disk upon return. Arguments: Hive - Hive we are doing I/O for FileType - which supporting file to use offsetArray - array of structures where each structure holds a 32bit offset into the Hive file and pointer the a buffer written to that file offset. offsetArrayCount - number of elements in the offsetArray. Return Value: FALSE if failure TRUE if success Note: This routine is intended to deal only with paged bins (i.e. bins crossing the CM_VIEW_SIZE boundary or bins that were added after the last sync) Assumption: We work on the assumption that the data to be written at one iteration never spans over the CM_VIEW_SIZE boundary. HvpFindNextDirtyBlock takes care of that !!! --*/ { ULONG i; PVOID DataBuffer; ULONG DataLength; ULONG FileOffset; PCMHIVE CmHive; PVOID Bcb; PVOID FileBuffer; LARGE_INTEGER Offset; IO_STATUS_BLOCK IoStatus; ASSERT_PASSIVE_LEVEL(); #if !DBG UNREFERENCED_PARAMETER (FileType); #endif CmHive = (PCMHIVE)CONTAINING_RECORD(Hive, CMHIVE, Hive); ASSERT( ((FileType == HFILE_TYPE_EXTERNAL) && (CmHive->FileObject != NULL)) || HiveWritesThroughCache(Hive,FileType) ); Offset.HighPart = 0; // // iterate through the array of data // for(i=0;i<offsetArrayCount;i++) { DataBuffer = offsetArray[i].DataBuffer; DataLength = offsetArray[i].DataLength; FileOffset = offsetArray[i].FileOffset; // // data should never span over CM_VIEW_SIZE boundary // ASSERT( (FileOffset & (~(CM_VIEW_SIZE - 1))) == ((FileOffset + DataLength - 1) & (~(CM_VIEW_SIZE - 1))) ); // // unmap any possible mapped view that could overlap with this range ; not needed !!!! // // // map and pin data // Offset.LowPart = FileOffset; try { if( !CcPinRead (CmHive->FileObject,&Offset,DataLength,PIN_WAIT,&Bcb,&FileBuffer) ) { CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache - could not pin read view i= %lu\n",i)); #if DBG DbgBreakPoint(); #endif //DBG return FALSE; } // // copy data to pinned view; we need to do it inside try except, to protect against devices/volumes // dismounting from under us. // RtlCopyMemory(FileBuffer,DataBuffer, DataLength); } except (EXCEPTION_EXECUTE_HANDLER) { // // in low-memory scenarios, CcPinRead throws a STATUS_INSUFFICIENT_RESOURCES // We want to catch this and treat as a "not enough resources" problem, // rather than letting it to surface the kernel call // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWriteThroughCache : CcPinRead has raised :%08lx\n",GetExceptionCode())); return FALSE; } // // dirty, unpin and flush // CcSetDirtyPinnedData (Bcb,NULL); CcUnpinData( Bcb ); CcFlushCache (CmHive->FileObject->SectionObjectPointer,(PLARGE_INTEGER)(((ULONG_PTR)(&Offset)) + 1)/*we are private writers*/,DataLength,&IoStatus); if(!NT_SUCCESS(IoStatus.Status) ) { return FALSE; } } return TRUE; }
/* * rename an existing FAT entry */ NTSTATUS vfatRenameEntry( IN PDEVICE_EXTENSION DeviceExt, IN PVFATFCB pFcb, IN PUNICODE_STRING FileName, IN BOOLEAN CaseChangeOnly) { OEM_STRING NameA; ULONG StartIndex; PVOID Context = NULL; LARGE_INTEGER Offset; PFATX_DIR_ENTRY pDirEntry; NTSTATUS Status; DPRINT("vfatRenameEntry(%p, %p, %wZ, %d)\n", DeviceExt, pFcb, FileName, CaseChangeOnly); if (pFcb->Flags & FCB_IS_FATX_ENTRY) { VFAT_DIRENTRY_CONTEXT DirContext; /* Open associated dir entry */ StartIndex = pFcb->startIndex; Offset.u.HighPart = 0; Offset.u.LowPart = (StartIndex * sizeof(FATX_DIR_ENTRY) / PAGE_SIZE) * PAGE_SIZE; _SEH2_TRY { CcPinRead(pFcb->parentFcb->FileObject, &Offset, PAGE_SIZE, PIN_WAIT, &Context, (PVOID*)&pDirEntry); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { DPRINT1("CcPinRead(Offset %x:%x, Length %d) failed\n", Offset.u.HighPart, Offset.u.LowPart, PAGE_SIZE); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; pDirEntry = &pDirEntry[StartIndex % (PAGE_SIZE / sizeof(FATX_DIR_ENTRY))]; /* Set file name */ NameA.Buffer = (PCHAR)pDirEntry->Filename; NameA.Length = 0; NameA.MaximumLength = 42; RtlUnicodeStringToOemString(&NameA, FileName, FALSE); pDirEntry->FilenameLength = (unsigned char)NameA.Length; /* Update FCB */ DirContext.ShortNameU.Length = 0; DirContext.ShortNameU.MaximumLength = 0; DirContext.ShortNameU.Buffer = NULL; DirContext.LongNameU = *FileName; DirContext.DirEntry.FatX = *pDirEntry; CcSetDirtyPinnedData(Context, NULL); CcUnpinData(Context); Status = vfatUpdateFCB(DeviceExt, pFcb, &DirContext, pFcb->parentFcb); if (NT_SUCCESS(Status)) { CcPurgeCacheSection(&pFcb->parentFcb->SectionObjectPointers, NULL, 0, FALSE); } return Status; }
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 Ext2ExpandBlock( 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 PULONG Hint, IN PULONG Extra ) { ULONG i = 0; ULONG j; ULONG Slot; ULONG Block = 0; LARGE_INTEGER Offset; PBCB Bcb = NULL; PULONG pData = NULL; ULONG Skip = 0; ULONG Number; ULONG Wanted; NTSTATUS Status = STATUS_SUCCESS; if (Layer == 1) { /* * try to make all leaf block continuous to avoid fragments */ Number = min(SizeArray, ((*Extra + (Start & (BLOCK_SIZE/4 - 1))) * 4 / BLOCK_SIZE)); Wanted = 0; DEBUG(DL_BLK, ("Ext2ExpandBlock: SizeArray=%xh Extra=%xh Start=%xh %xh\n", SizeArray, *Extra, Start, Number )); for (i=0; i < Number; i++) { if (BlockArray[i] == 0) { Wanted += 1; } } i = 0; while (Wanted > 0) { Number = Wanted; Status = Ext2ExpandLast( IrpContext, Vcb, Mcb, Base, Layer, NULL, Hint, &Block, &Number ); if (!NT_SUCCESS(Status)) { goto errorout; } ASSERT(Number > 0); Wanted -= Number; while (Number) { if (BlockArray[i] == 0) { BlockArray[i] = Block++; Number--; } i++; } } } else if (Layer == 0) { /* * bulk allocation for inode data blocks */ i = 0; while (*Extra && i < SizeArray) { Wanted = 0; Number = 1; for (j = i; j < SizeArray && j < i + *Extra; j++) { if (BlockArray[j] >= TOTAL_BLOCKS) { DbgBreak(); BlockArray[j] = 0; } if (BlockArray[j] == 0) { Wanted += 1; } else { break; } } if (Wanted == 0) { /* add block extent into Mcb */ ASSERT(BlockArray[i] != 0); if (!Ext2AddBlockExtent(Vcb, Mcb, Base + i, BlockArray[i], 1)) { DbgBreak(); ClearFlag(Mcb->Flags, MCB_ZONE_INITED); Ext2ClearAllExtents(&Mcb->Extents); } } else { Number = Wanted; Status = Ext2ExpandLast( IrpContext, Vcb, Mcb, Base + i, 0, NULL, Hint, &Block, &Number ); if (!NT_SUCCESS(Status)) { goto errorout; } ASSERT(Number > 0); for (j = 0; j < Number; j++) { BlockArray[i + j] = Block++; } } *Extra -= Number; i += Number; } goto errorout; } /* * only for meta blocks allocation */ for (i = 0; *Extra && i < SizeArray; i++) { if (Layer <= 3) { if (BlockArray[i] >= TOTAL_BLOCKS) { DbgBreak(); BlockArray[i] = 0; } if (BlockArray[i] == 0) { Number = 1; Status = Ext2ExpandLast( IrpContext, Vcb, Mcb, Base, Layer, &pData, Hint, &BlockArray[i], &Number ); if (!NT_SUCCESS(Status)) { goto errorout; } } else { Offset.QuadPart = (((LONGLONG)BlockArray[i]) << BLOCK_BITS); if (!CcPinRead( Vcb->Volume, &Offset, BLOCK_SIZE, PIN_WAIT, &Bcb, (void **)&pData )) { DEBUG(DL_ERR, ( "Ext2ExpandInode: failed to PinLock offset :%I64xh...\n", Offset.QuadPart)); Status = STATUS_CANT_WAIT; DbgBreak(); goto errorout; } /* add block to meta extents */ if (!Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[i], 1)) { DbgBreak(); Ext2Sleep(500); Ext2AddMcbMetaExts(Vcb, Mcb, BlockArray[i], 1); } } Skip = Vcb->max_blocks_per_layer[Layer] * i; if (i == 0) { if (Layer > 1) { Slot = Start / Vcb->max_blocks_per_layer[Layer - 1]; Start = Start % Vcb->max_blocks_per_layer[Layer - 1]; Skip += Slot * Vcb->max_blocks_per_layer[Layer - 1]; } else { Slot = Start; Start = 0; Skip += Slot; } } else { Start = 0; Slot = 0; } Status = Ext2ExpandBlock( IrpContext, Vcb, Mcb, Base + Skip, Layer - 1, Start, BLOCK_SIZE/4 - Slot, &pData[Slot], Hint, Extra ); if (Bcb) { CcSetDirtyPinnedData(Bcb, NULL); if (!Ext2AddBlockExtent(Vcb, NULL, BlockArray[i], BlockArray[i], 1)) { DbgBreak(); Ext2Sleep(500); if (!Ext2AddBlockExtent(Vcb, NULL, BlockArray[i], BlockArray[i], 1)) { } } } else { Ext2SaveBlock(IrpContext, Vcb, BlockArray[i], (PVOID)pData); } if (pData) { if (Bcb) { CcUnpinData(Bcb); Bcb = NULL; } else { Ext2FreePool(pData, EXT2_DATA_MAGIC); DEC_MEM_COUNT(PS_BLOCK_DATA, pData, BLOCK_SIZE); } pData = NULL; } if (!NT_SUCCESS(Status)) { DbgBreak(); break; } } } errorout: 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; }