From 77fc65dc0e4f7b08cec056bb3f017c70036748f6 Mon Sep 17 00:00:00 2001 From: Trevor Thompson Date: Wed, 29 Jun 2016 16:35:36 +0000 Subject: [PATCH] [NTFS] Lay some groundwork for extending allocation size. +AddRun() - Unimplemented +GetLastClusterInDataRun() +NtfsAllocateClusters() svn path=/branches/GSoC_2016/NTFS/; revision=71696 --- drivers/filesystems/ntfs/attrib.c | 57 ++++++++++++++++ drivers/filesystems/ntfs/mft.c | 48 +++++++++++-- drivers/filesystems/ntfs/ntfs.h | 19 +++++- drivers/filesystems/ntfs/volinfo.c | 104 +++++++++++++++++++++++++++++ 4 files changed, 223 insertions(+), 5 deletions(-) diff --git a/drivers/filesystems/ntfs/attrib.c b/drivers/filesystems/ntfs/attrib.c index 12474f6c813..9608d1d17be 100644 --- a/drivers/filesystems/ntfs/attrib.c +++ b/drivers/filesystems/ntfs/attrib.c @@ -35,6 +35,19 @@ /* FUNCTIONS ****************************************************************/ +NTSTATUS +AddRun(PNTFS_ATTR_CONTEXT AttrContext, + ULONGLONG NextAssignedCluster, + ULONG RunLength) +{ + UNIMPLEMENTED; + + if (!AttrContext->Record.IsNonResident) + return STATUS_INVALID_PARAMETER; + + return STATUS_NOT_IMPLEMENTED; +} + PUCHAR DecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, @@ -551,6 +564,50 @@ GetFileNameFromRecord(PDEVICE_EXTENSION Vcb, return NULL; } +NTSTATUS +GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster) +{ + LONGLONG DataRunOffset; + ULONGLONG DataRunLength; + LONGLONG DataRunStartLCN; + + ULONGLONG LastLCN = 0; + PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset; + + if (!Attribute->IsNonResident) + return STATUS_INVALID_PARAMETER; + + while (1) + { + DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength); + + if (DataRunOffset == -1) + { + // sparse run + if (*DataRun == 0) + { + // if it's the last run, return the last cluster of the last run + *LastCluster = LastLCN + DataRunLength - 1; + break; + } + } + else + { + // Normal data run. + DataRunStartLCN = LastLCN + DataRunOffset; + LastLCN = DataRunStartLCN; + } + + if (*DataRun == 0) + { + *LastCluster = LastLCN + DataRunLength - 1; + break; + } + } + + return STATUS_SUCCESS; +} + PSTANDARD_INFORMATION GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord) diff --git a/drivers/filesystems/ntfs/mft.c b/drivers/filesystems/ntfs/mft.c index ae2a6cc2baf..e1725f9a50e 100644 --- a/drivers/filesystems/ntfs/mft.c +++ b/drivers/filesystems/ntfs/mft.c @@ -151,7 +151,7 @@ FindAttribute(PDEVICE_EXTENSION Vcb, } -ULONG +ULONGLONG AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord) { if (AttrRecord->IsNonResident) @@ -181,9 +181,49 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, { if (AttrContext->Record.IsNonResident) { + ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster; + ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster); + // do we need to increase the allocation size? - if (AttrContext->Record.NonResident.AllocatedSize < DataSize->QuadPart) - { + if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize) + { + ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster; + ULONG ClustersNeeded = (AllocationSize / BytesPerCluster) - ExistingClusters; + LARGE_INTEGER LastClusterInDataRun; + ULONG NextAssignedCluster; + ULONG AssignedClusters; + + NTSTATUS Status = GetLastClusterInDataRun(Fcb->Vcb, &AttrContext->Record, &LastClusterInDataRun.QuadPart); + + DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun.QuadPart); + DPRINT1("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN); + + while (ClustersNeeded > 0) + { + Status = NtfsAllocateClusters(Fcb->Vcb, + LastClusterInDataRun.LowPart + 1, + ClustersNeeded, + &NextAssignedCluster, + &AssignedClusters); + + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Unable to allocate requested clusters!\n"); + return Status; + } + + // now we need to add the clusters we allocated to the data run + Status = AddRun(AttrContext, NextAssignedCluster, AssignedClusters); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Unable to add data run!\n"); + return Status; + } + + ClustersNeeded -= AssignedClusters; + LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1; + } + DPRINT1("FixMe: Increasing allocation size is unimplemented!\n"); return STATUS_NOT_IMPLEMENTED; } @@ -459,7 +499,7 @@ WriteAttribute(PDEVICE_EXTENSION Vcb, PUCHAR SourceBuffer = Buffer; LONGLONG StartingOffset; - DPRINT("WriteAttribute(%p, %p, %I64U, %p, %lu)\n", Vcb, Context, Offset, Buffer, Length); + DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context, Offset, Buffer, Length, RealLengthWritten); // is this a resident attribute? if (!Context->Record.IsNonResident) diff --git a/drivers/filesystems/ntfs/ntfs.h b/drivers/filesystems/ntfs/ntfs.h index 4435b4866e6..66cb37f4bd1 100644 --- a/drivers/filesystems/ntfs/ntfs.h +++ b/drivers/filesystems/ntfs/ntfs.h @@ -511,6 +511,11 @@ NtfsMarkIrpContextForQueue(PNTFS_IRP_CONTEXT IrpContext) //VOID //NtfsDumpAttribute(PATTRIBUTE Attribute); +NTSTATUS +AddRun(PNTFS_ATTR_CONTEXT AttrContext, + ULONGLONG NextAssignedCluster, + ULONG RunLength); + PUCHAR DecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, @@ -529,6 +534,11 @@ GetFileNameFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord, UCHAR NameType); +NTSTATUS +GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, + PNTFS_ATTR_RECORD Attribute, + PULONGLONG LastCluster); + PFILENAME_ATTRIBUTE GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb, PFILE_RECORD_HEADER FileRecord); @@ -774,7 +784,7 @@ SetAttributeDataLength(PFILE_OBJECT FileObject, PFILE_RECORD_HEADER FileRecord, PLARGE_INTEGER DataSize); -ULONG +ULONGLONG AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord); BOOLEAN @@ -915,6 +925,13 @@ NtfsWrite(PNTFS_IRP_CONTEXT IrpContext); /* volinfo.c */ +NTSTATUS +NtfsAllocateClusters(PDEVICE_EXTENSION DeviceExt, + ULONG FirstDesiredCluster, + ULONG DesiredClusters, + PULONG FirstAssignedCluster, + PULONG AssignedClusters); + ULONGLONG NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt); diff --git a/drivers/filesystems/ntfs/volinfo.c b/drivers/filesystems/ntfs/volinfo.c index 13a3b7af7ff..c8935621894 100644 --- a/drivers/filesystems/ntfs/volinfo.c +++ b/drivers/filesystems/ntfs/volinfo.c @@ -99,6 +99,110 @@ NtfsGetFreeClusters(PDEVICE_EXTENSION DeviceExt) return FreeClusters; } +/** +* 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; + PCHAR BitmapData; + ULONGLONG FreeClusters = 0; + ULONG Read = 0; + RTL_BITMAP Bitmap; + + DPRINT1("NtfsAllocateClusters(%p, %lu, %lu, %p)\n", DeviceExt, DesiredClusters, FirstDesiredCluster, FirstAssignedCluster, AssignedClusters); + + BitmapRecord = ExAllocatePoolWithTag(NonPagedPool, + DeviceExt->NtfsInfo.BytesPerFileRecord, + TAG_NTFS); + if (BitmapRecord == NULL) + { + return 0; + } + + Status = ReadFileRecord(DeviceExt, NTFS_FILE_BITMAP, BitmapRecord); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(BitmapRecord, TAG_NTFS); + return 0; + } + + Status = FindAttribute(DeviceExt, BitmapRecord, AttributeData, L"", 0, &DataContext, NULL); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(BitmapRecord, TAG_NTFS); + return 0; + } + + BitmapDataSize = AttributeDataLength(&DataContext->Record); + 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); + ExFreePoolWithTag(BitmapRecord, TAG_NTFS); + return 0; + } + + 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, Read, (PCHAR)((ULONG_PTR)BitmapData + Read), (ULONG)BitmapDataSize); + + RtlInitializeBitMap(&Bitmap, (PULONG)BitmapData, DeviceExt->NtfsInfo.ClusterCount); + FreeClusters = RtlNumberOfClearBits(&Bitmap); + + if (FreeClusters >= DesiredClusters) + { + // TODO: Observe MFT reservation zone + + // Can we get one contiguous run? + ULONG AssignedRun = RtlFindClearBitsAndSet(&Bitmap, DesiredClusters, FirstDesiredCluster); + ULONG LengthWritten; + + 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); + } + else + Status = STATUS_DISK_FULL; + + + ReleaseAttributeContext(DataContext); + + ExFreePoolWithTag(BitmapData, TAG_NTFS); + ExFreePoolWithTag(BitmapRecord, TAG_NTFS); + + return Status; +} + static NTSTATUS NtfsGetFsVolumeInformation(PDEVICE_OBJECT DeviceObject, -- 2.17.1