[NTFS]
authorTrevor Thompson <tmt256@email.vccs.edu>
Wed, 29 Jun 2016 16:35:36 +0000 (16:35 +0000)
committerThomas Faber <thomas.faber@reactos.org>
Sun, 10 Dec 2017 10:13:33 +0000 (11:13 +0100)
Lay some groundwork for extending allocation size.
+AddRun() - Unimplemented
+GetLastClusterInDataRun()
+NtfsAllocateClusters()

svn path=/branches/GSoC_2016/NTFS/; revision=71696

drivers/filesystems/ntfs/attrib.c
drivers/filesystems/ntfs/mft.c
drivers/filesystems/ntfs/ntfs.h
drivers/filesystems/ntfs/volinfo.c

index 12474f6..9608d1d 100644 (file)
 
 /* 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)
index ae2a6cc..e1725f9 100644 (file)
@@ -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)
index 4435b48..66cb37f 100644 (file)
@@ -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);
 
index 13a3b7a..c893562 100644 (file)
@@ -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,