PNTFS_ATTR_CONTEXT FindAttributeHelper(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, PCWSTR Name, ULONG NameLength) { DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %S, %u)\n", Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength); while (AttrRecord < AttrRecordEnd) { DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord->Type); if (AttrRecord->Type == AttributeEnd) break; if (AttrRecord->Type == AttributeAttributeList) { PNTFS_ATTR_CONTEXT Context; PNTFS_ATTR_CONTEXT ListContext; PVOID ListBuffer; ULONGLONG ListSize; PNTFS_ATTR_RECORD ListAttrRecord; PNTFS_ATTR_RECORD ListAttrRecordEnd; ListContext = PrepareAttributeContext(AttrRecord); ListSize = AttributeDataLength(&ListContext->Record); if(ListSize <= 0xFFFFFFFF) ListBuffer = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS); else ListBuffer = NULL; if(!ListBuffer) { DPRINT("Failed to allocate memory: %x\n", (ULONG)ListSize); continue; } ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer; ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize); if (ReadAttribute(Vcb, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize) { Context = FindAttributeHelper(Vcb, ListAttrRecord, ListAttrRecordEnd, Type, Name, NameLength); ReleaseAttributeContext(ListContext); ExFreePoolWithTag(ListBuffer, TAG_NTFS); if (Context != NULL) { if (AttrRecord->IsNonResident) DPRINT("Found context = %p\n", Context); return Context; } } } if (AttrRecord->Type == Type) { if (AttrRecord->NameLength == NameLength) { PWCHAR AttrName; AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset); DPRINT("%.*S, %.*S\n", AttrRecord->NameLength, AttrName, NameLength, Name); if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1)) { /* Found it, fill up the context and return. */ DPRINT("Found context\n"); return PrepareAttributeContext(AttrRecord); } } } if (AttrRecord->Length == 0) { DPRINT("Null length attribute record\n"); return NULL; } AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length); } DPRINT("Ended\n"); return NULL; }
NTSTATUS NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, PULONG FirstEntry, BOOLEAN DirSearch, ULONGLONG *OutMFTIndex) { PFILE_RECORD_HEADER MftRecord; PNTFS_ATTR_CONTEXT IndexRootCtx; PNTFS_ATTR_CONTEXT IndexBitmapCtx; PNTFS_ATTR_CONTEXT IndexAllocationCtx; PINDEX_ROOT_ATTRIBUTE IndexRoot; PINDEX_BUFFER IndexBuffer; ULONGLONG BitmapDataSize; ULONGLONG IndexAllocationSize; PCHAR BitmapData; PCHAR IndexRecord; PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd; ULONG RecordOffset; ULONG IndexBlockSize; NTSTATUS Status; ULONG CurrentEntry = 0; DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %p, %u, %p)\n", Vcb, MFTIndex, FileName, FirstEntry, DirSearch, OutMFTIndex); MftRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (MftRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(ReadFileRecord(Vcb, MFTIndex, MftRecord))) { ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE); Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, L"$I30", 4, &IndexRootCtx); if (!NT_SUCCESS(Status)) { ExFreePoolWithTag(MftRecord, TAG_NTFS); return Status; } IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS); if (IndexRecord == NULL) { ExFreePoolWithTag(MftRecord, TAG_NTFS); return STATUS_INSUFFICIENT_RESOURCES; } ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord); IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord; IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset); /* Index root is always resident. */ IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRoot->Header.TotalSizeOfEntries); ReleaseAttributeContext(IndexRootCtx); DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry); Status = BrowseIndexEntries(IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex); if (NT_SUCCESS(Status)) { ExFreePoolWithTag(IndexRecord, TAG_NTFS); ExFreePoolWithTag(MftRecord, TAG_NTFS); return Status; } if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE) { DPRINT("Large Index!\n"); IndexBlockSize = IndexRoot->SizeOfEntry; Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, L"$I30", 4, &IndexBitmapCtx); if (!NT_SUCCESS(Status)) { DPRINT1("Corrupted filesystem!\n"); ExFreePoolWithTag(MftRecord, TAG_NTFS); return Status; } BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record); DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize); if(BitmapDataSize <= 0xFFFFFFFF) BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS); else BitmapData = NULL; if (BitmapData == NULL) { ExFreePoolWithTag(IndexRecord, TAG_NTFS); ExFreePoolWithTag(MftRecord, TAG_NTFS); return STATUS_INSUFFICIENT_RESOURCES; } ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize); ReleaseAttributeContext(IndexBitmapCtx); Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx); if (!NT_SUCCESS(Status)) { DPRINT("Corrupted filesystem!\n"); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreePoolWithTag(IndexRecord, TAG_NTFS); ExFreePoolWithTag(MftRecord, TAG_NTFS); return Status; } IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record); RecordOffset = 0; for (;;) { DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize); for (; RecordOffset < IndexAllocationSize;) { UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7); ULONG Byte = (RecordOffset / IndexBlockSize) >> 3; if ((BitmapData[Byte] & Bit)) break; RecordOffset += IndexBlockSize; } if (RecordOffset >= IndexAllocationSize) { break; } ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize); if (!NT_SUCCESS(FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs))) { break; } IndexBuffer = (PINDEX_BUFFER)IndexRecord; ASSERT(IndexBuffer->Ntfs.Type == NRH_INDX_TYPE); ASSERT(IndexBuffer->Header.AllocatedSize + 0x18 == IndexBlockSize); IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.FirstEntryOffset); IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)&IndexBuffer->Header + IndexBuffer->Header.TotalSizeOfEntries); ASSERT(IndexEntryEnd <= (PINDEX_ENTRY_ATTRIBUTE)((ULONG_PTR)IndexBuffer + IndexBlockSize)); Status = BrowseIndexEntries(IndexEntry, IndexEntryEnd, FileName, FirstEntry, &CurrentEntry, DirSearch, OutMFTIndex); if (NT_SUCCESS(Status)) { ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreePoolWithTag(IndexRecord, TAG_NTFS); ExFreePoolWithTag(MftRecord, TAG_NTFS); ReleaseAttributeContext(IndexAllocationCtx); return Status; } RecordOffset += IndexBlockSize; } ReleaseAttributeContext(IndexAllocationCtx); ExFreePoolWithTag(BitmapData, TAG_NTFS); } ExFreePoolWithTag(IndexRecord, TAG_NTFS); }
/** * NtfsAllocateClusters * Allocates a run of clusters. The run allocated might be smaller than DesiredClusters. */ NTSTATUS NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, ULONG FirstDesiredCluster, ULONG DesiredClusters, PULONG FirstAssignedCluster, PULONG AssignedClusters) { NTSTATUS Status; PFILE_RECORD_HEADER BitmapRecord; PNTFS_ATTR_CONTEXT DataContext; ULONGLONG BitmapDataSize; PUCHAR BitmapData; ULONGLONG FreeClusters = 0; RTL_BITMAP Bitmap; ULONG AssignedRun; ULONG LengthWritten; DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p, %p)\n", DeviceExt, FirstDesiredCluster, DesiredClusters, FirstAssignedCluster, AssignedClusters); BitmapRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (BitmapRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; } Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; } BitmapDataSize = AttributeDataLength(DataContext->pRecord); BitmapDataSize = min(BitmapDataSize, 0xffffffff); ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount); BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS); if (BitmapData == NULL) { ReleaseAttributeContext(DataContext); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return STATUS_INSUFFICIENT_RESOURCES; } DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount); DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8); DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector); ReadAttribute(DeviceExt, DataContext, 0, (PCHAR)BitmapData, (ULONG)BitmapDataSize); RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); FreeClusters = RtlNumberOfClearBits(&Bitmap); if (FreeClusters < DesiredClusters) { ReleaseAttributeContext(DataContext); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return STATUS_DISK_FULL; } // TODO: Observe MFT reservation zone // Can we get one contiguous run? AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster); if (AssignedRun != 0xFFFFFFFF) { *FirstAssignedCluster = AssignedRun; *AssignedClusters = DesiredClusters; } else { // we can't get one contiguous run *AssignedClusters = RtlFindNextForwardRunClear(&Bitmap, FirstDesiredCluster, FirstAssignedCluster); if (*AssignedClusters == 0) { // we couldn't find any runs starting at DesiredFirstCluster *AssignedClusters = RtlFindLongestRunClear(&Bitmap, FirstAssignedCluster); } } Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, (ULONG)BitmapDataSize, &LengthWritten, BitmapRecord); ReleaseAttributeContext(DataContext); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return Status; }
ULONGLONG NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt) { NTSTATUS Status; PFILE_RECORD_HEADER BitmapRecord; PNTFS_ATTR_CONTEXT DataContext; ULONGLONG BitmapDataSize; PCHAR BitmapData; ULONGLONG FreeClusters = 0; ULONG Read = 0; RTL_BITMAP Bitmap; DPRINT1("NtfsGetFreeClusters(%p)\n", DeviceExt); BitmapRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList); if (BitmapRecord == NULL) { return 0; } Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); if (!NT_SUCCESS(Status)) { ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } BitmapDataSize = AttributeDataLength(DataContext->pRecord); ASSERT((BitmapDataSize * 8) >= DeviceExt->NtfsInfo.ClusterCount); BitmapData = ExAllocatePoolWithTag(NonPagedPool, ROUND_UP(BitmapDataSize, DeviceExt->NtfsInfo.BytesPerSector), TAG_NTFS); if (BitmapData == NULL) { ReleaseAttributeContext(DataContext); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return 0; } /* FIXME: Totally underoptimized! */ for (; Read < BitmapDataSize; Read += DeviceExt->NtfsInfo.BytesPerSector) { ReadAttribute(DeviceExt, DataContext, Read, (PCHAR)((ULONG_PTR)BitmapData + Read), DeviceExt->NtfsInfo.BytesPerSector); } ReleaseAttributeContext(DataContext); DPRINT1("Total clusters: %I64x\n", DeviceExt->NtfsInfo.ClusterCount); DPRINT1("Total clusters in bitmap: %I64x\n", BitmapDataSize * 8); DPRINT1("Diff in size: %I64d B\n", ((BitmapDataSize * 8) - DeviceExt->NtfsInfo.ClusterCount) * DeviceExt->NtfsInfo.SectorsPerCluster * DeviceExt->NtfsInfo.BytesPerSector); RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); FreeClusters = RtlNumberOfClearBits(&Bitmap); ExFreePoolWithTag(BitmapData, TAG_NTFS); ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, BitmapRecord); return FreeClusters; }
/* * FUNCTION: Reads data from a file */ static NTSTATUS NtfsReadFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject, PUCHAR Buffer, ULONG Length, ULONG ReadOffset, ULONG IrpFlags, PULONG LengthRead) { NTSTATUS Status = STATUS_SUCCESS; PNTFS_FCB Fcb; PFILE_RECORD_HEADER FileRecord; PNTFS_ATTR_CONTEXT DataContext; ULONG RealLength; ULONG RealReadOffset; ULONG RealLengthRead; ULONG ToRead; BOOLEAN AllocatedBuffer = FALSE; PCHAR ReadBuffer = (PCHAR)Buffer; DPRINT1("NtfsReadFile(%p, %p, %p, %u, %u, %x, %p)\n", DeviceExt, FileObject, Buffer, Length, ReadOffset, IrpFlags, LengthRead); *LengthRead = 0; if (Length == 0) { DPRINT1("Null read!\n"); return STATUS_SUCCESS; } Fcb = (PNTFS_FCB)FileObject->FsContext; if (ReadOffset >= Fcb->Entry.AllocatedSize) { DPRINT1("Reading beyond file end!\n"); return STATUS_END_OF_FILE; } ToRead = Length; if (ReadOffset + Length > Fcb->Entry.AllocatedSize) ToRead = Fcb->Entry.AllocatedSize - ReadOffset; RealReadOffset = ReadOffset; RealLength = ToRead; if ((ReadOffset % DeviceExt->NtfsInfo.BytesPerSector) != 0 || (ToRead % DeviceExt->NtfsInfo.BytesPerSector) != 0) { RealReadOffset = ROUND_DOWN(ReadOffset, DeviceExt->NtfsInfo.BytesPerSector); RealLength = ROUND_UP(ToRead, DeviceExt->NtfsInfo.BytesPerSector); ReadBuffer = ExAllocatePoolWithTag(NonPagedPool, RealLength + DeviceExt->NtfsInfo.BytesPerSector, TAG_NTFS); if (ReadBuffer == NULL) { DPRINT1("Not enough memory!\n"); return STATUS_INSUFFICIENT_RESOURCES; } AllocatedBuffer = TRUE; } FileRecord = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS); if (FileRecord == NULL) { DPRINT1("Not enough memory!\n"); return STATUS_INSUFFICIENT_RESOURCES; } Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord); if (!NT_SUCCESS(Status)) { DPRINT1("Can't find record!\n"); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } Status = FindAttribute(DeviceExt, FileRecord, AttributeData, L"", 0, &DataContext); if (!NT_SUCCESS(Status)) { DPRINT1("No data associated with file!\n"); ExFreePoolWithTag(FileRecord, TAG_NTFS); return Status; } DPRINT1("Effective read: %lu at %lu\n", RealLength, RealReadOffset); RealLengthRead = ReadAttribute(DeviceExt, DataContext, RealReadOffset, (PCHAR)ReadBuffer, RealLength); if (RealLengthRead != RealLength) { DPRINT1("Read failure!\n"); ReleaseAttributeContext(DataContext); ExFreePoolWithTag(FileRecord, TAG_NTFS); if (AllocatedBuffer) { ExFreePoolWithTag(ReadBuffer, TAG_NTFS); } return Status; } ReleaseAttributeContext(DataContext); ExFreePoolWithTag(FileRecord, TAG_NTFS); *LengthRead = ToRead; DPRINT1("%lu got read\n", *LengthRead); if (AllocatedBuffer) { RtlCopyMemory(Buffer, ReadBuffer + (ReadOffset - RealReadOffset), ToRead); } if (ToRead != Length) { RtlZeroMemory(Buffer + ToRead, Length - ToRead); } if (AllocatedBuffer) { ExFreePoolWithTag(ReadBuffer, TAG_NTFS); } return STATUS_SUCCESS; }