static NTSTATUS NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject, PDEVICE_EXTENSION DeviceExt) { DISK_GEOMETRY DiskGeometry; PFILE_RECORD_HEADER MftRecord; PFILE_RECORD_HEADER VolumeRecord; PVOLINFO_ATTRIBUTE VolumeInfo; PBOOT_SECTOR BootSector; PATTRIBUTE Attribute; ULONG Size; PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo; NTSTATUS Status; DPRINT("NtfsGetVolumeData() called\n"); Size = sizeof(DISK_GEOMETRY); Status = NtfsDeviceIoControl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &DiskGeometry, &Size, TRUE); if (!NT_SUCCESS(Status)) { DPRINT("NtfsDeviceIoControl() failed (Status %lx)\n", Status); return Status; } DPRINT("BytesPerSector: %lu\n", DiskGeometry.BytesPerSector); BootSector = ExAllocatePoolWithTag(NonPagedPool, DiskGeometry.BytesPerSector, TAG_NTFS); if (BootSector == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = NtfsReadSectors(DeviceObject, 0, /* Partition boot sector */ 1, DiskGeometry.BytesPerSector, (PVOID)BootSector, TRUE); if (!NT_SUCCESS(Status)) { ExFreePool(BootSector); return Status; } /* Read data from the bootsector */ NtfsInfo->BytesPerSector = BootSector->BPB.BytesPerSector; NtfsInfo->SectorsPerCluster = BootSector->BPB.SectorsPerCluster; NtfsInfo->BytesPerCluster = BootSector->BPB.BytesPerSector * BootSector->BPB.SectorsPerCluster; NtfsInfo->SectorCount = BootSector->EBPB.SectorCount; NtfsInfo->MftStart.QuadPart = BootSector->EBPB.MftLocation; NtfsInfo->MftMirrStart.QuadPart = BootSector->EBPB.MftMirrLocation; NtfsInfo->SerialNumber = BootSector->EBPB.SerialNumber; if (BootSector->EBPB.ClustersPerMftRecord > 0) NtfsInfo->BytesPerFileRecord = BootSector->EBPB.ClustersPerMftRecord * NtfsInfo->BytesPerCluster; else NtfsInfo->BytesPerFileRecord = 1 << (-BootSector->EBPB.ClustersPerMftRecord); DPRINT("Boot sector information:\n"); DPRINT(" BytesPerSector: %hu\n", BootSector->BPB.BytesPerSector); DPRINT(" SectorsPerCluster: %hu\n", BootSector->BPB.SectorsPerCluster); DPRINT(" SectorCount: %I64u\n", BootSector->EBPB.SectorCount); DPRINT(" MftStart: %I64u\n", BootSector->EBPB.MftLocation); DPRINT(" MftMirrStart: %I64u\n", BootSector->EBPB.MftMirrLocation); DPRINT(" ClustersPerMftRecord: %lx\n", BootSector->EBPB.ClustersPerMftRecord); DPRINT(" ClustersPerIndexRecord: %lx\n", BootSector->EBPB.ClustersPerIndexRecord); DPRINT(" SerialNumber: %I64x\n", BootSector->EBPB.SerialNumber); ExFreePool(BootSector); MftRecord = ExAllocatePoolWithTag(NonPagedPool, NtfsInfo->BytesPerFileRecord, TAG_NTFS); if (MftRecord == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Status = NtfsReadSectors(DeviceObject, NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster, NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector, NtfsInfo->BytesPerSector, (PVOID)MftRecord, TRUE); if (!NT_SUCCESS(Status)) { ExFreePool(MftRecord); return Status; } VolumeRecord = ExAllocatePoolWithTag(NonPagedPool, NtfsInfo->BytesPerFileRecord, TAG_NTFS); if (VolumeRecord == NULL) { ExFreePool(MftRecord); return STATUS_INSUFFICIENT_RESOURCES; } /* Read Volume File (MFT index 3) */ DeviceExt->StorageDevice = DeviceObject; Status = ReadFileRecord(DeviceExt, 3, VolumeRecord, MftRecord); if (!NT_SUCCESS(Status)) { ExFreePool(MftRecord); return Status; } /* Enumerate attributes */ NtfsDumpFileAttributes (MftRecord); /* Enumerate attributes */ NtfsDumpFileAttributes (VolumeRecord); /* Get volume name */ Attribute = FindAttribute (VolumeRecord, AttributeVolumeName, NULL); DPRINT("Attribute %p\n", Attribute); if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) { DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); NtfsInfo->VolumeLabelLength = min (((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH); RtlCopyMemory(NtfsInfo->VolumeLabel, (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset), NtfsInfo->VolumeLabelLength); } else { NtfsInfo->VolumeLabelLength = 0; } /* Get volume information */ Attribute = FindAttribute (VolumeRecord, AttributeVolumeInformation, NULL); DPRINT("Attribute %p\n", Attribute); if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0) { DPRINT("Data length %lu\n", AttributeDataLength (Attribute)); VolumeInfo = (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset); NtfsInfo->MajorVersion = VolumeInfo->MajorVersion; NtfsInfo->MinorVersion = VolumeInfo->MinorVersion; NtfsInfo->Flags = VolumeInfo->Flags; } ExFreePool(MftRecord); ExFreePool(VolumeRecord); return Status; }
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; }
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; }
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; }