Author: tthompson
Date: Tue Aug 15 19:32:20 2017
New Revision: 75554

URL: http://svn.reactos.org/svn/reactos?rev=75554&view=rev
Log:
[NTFS] - Allow for resizing an attribute in the middle of a file record. Add a 
helper function and minor improvements:
AddRun() - Allow for resizing the size of the data runs when the attribute 
isn't the last in the file record. Fix some comments.
CreateIndexBufferFromBTreeNode(), CreateIndexRootFromBTree - Fix math of 
IndexSize when checking if the index buffer is too large.
InternalSetResidentAttributeLength() - Allow changing the length of an 
attribute in the middle of a file record. Adjust the position of every 
attribute after the one being resized.
+MoveAttributes() - Moves a block of attributes to a new location in the file 
Record.
PrintAllVCNs() - Add consideration for an index allocation with a size of 0.
WriteAttribute() - Add optional parameter for a pointer to the file record 
being written to. If passed a file record, WriteAttribute() will skip reading 
the file record from disk, and will update the file record in memory before 
returning. This helps callers that use the file record after writing an 
attribute to stay in-sync with what's on disk.

Modified:
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/rw.c
    branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c   [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/attrib.c   [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -352,16 +352,33 @@
         PNTFS_ATTR_RECORD NextAttribute = 
(PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + NextAttributeOffset);
         PNTFS_ATTR_RECORD NewRecord;
 
-        DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - 
NextAttributeOffset - (sizeof(ULONG) * 2);
-
-        // Can we move the end of the attribute?
-        if (NextAttribute->Type != AttributeEnd || DataRunMaxLength < 
RunBufferSize - 1)
-        {
-            DPRINT1("FIXME: Need to create attribute list! Max Data Run Length 
available: %d\n", DataRunMaxLength);
-            if (NextAttribute->Type != AttributeEnd)
-                DPRINT1("There's another attribute after this one with type 
%0xlx\n", NextAttribute->Type);
+        // Add free space at the end of the file record to DataRunMaxLength
+        DataRunMaxLength += Vcb->NtfsInfo.BytesPerFileRecord - 
FileRecord->BytesInUse;
+
+        // Can we resize the attribute?
+        if (DataRunMaxLength < RunBufferSize)
+        {
+            DPRINT1("FIXME: Need to create attribute list! Max Data Run Length 
available: %d, RunBufferSize: %d\n", DataRunMaxLength, RunBufferSize);
             ExFreePoolWithTag(RunBuffer, TAG_NTFS);
             return STATUS_NOT_IMPLEMENTED;
+        }
+
+        // Are there more attributes after the one we're resizing?
+        if (NextAttribute->Type != AttributeEnd)
+        {
+            PNTFS_ATTR_RECORD FinalAttribute;
+
+            // Calculate where to move the trailing attributes
+            ULONG_PTR MoveTo = (ULONG_PTR)DestinationAttribute + 
AttrContext->pRecord->NonResident.MappingPairsOffset + RunBufferSize;
+            MoveTo = ALIGN_UP_BY(MoveTo, ATTR_RECORD_ALIGNMENT);
+
+            DPRINT1("Moving attribute(s) after this one starting with type 
0x%lx\n", NextAttribute->Type);
+
+            // Move the trailing attributes; FinalAttribute will point to the 
end marker
+            FinalAttribute = MoveAttributes(Vcb, NextAttribute, 
NextAttributeOffset, MoveTo);
+
+            // set the file record end
+            SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
         }
 
         // calculate position of end markers
@@ -371,20 +388,24 @@
         // Update the length of the destination attribute
         DestinationAttribute->Length = NextAttributeOffset - AttrOffset;
 
-        // Create a new copy of the attribute
+        // Create a new copy of the attribute record
         NewRecord = ExAllocatePoolWithTag(NonPagedPool, 
DestinationAttribute->Length, TAG_NTFS);
         RtlCopyMemory(NewRecord, AttrContext->pRecord, 
AttrContext->pRecord->Length);
         NewRecord->Length = DestinationAttribute->Length;
 
-        // Free the old copy of the attribute, which won't be large enough
+        // Free the old copy of the attribute record, which won't be large 
enough
         ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
 
         // Set the attribute context's record to the new copy
         AttrContext->pRecord = NewRecord;
 
-        // End the file record
-        NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + 
NextAttributeOffset);
-        SetFileRecordEnd(FileRecord, NextAttribute, FILE_RECORD_END);
+        // if NextAttribute is the AttributeEnd marker
+        if (NextAttribute->Type == AttributeEnd)
+        {
+            // End the file record
+            NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + 
NextAttributeOffset);
+            SetFileRecordEnd(FileRecord, NextAttribute, FILE_RECORD_END);
+        }
     }
 
     // Update HighestVCN
@@ -397,7 +418,7 @@
                   RunBuffer, 
                   RunBufferSize);
 
-    // Update the attribute copy in the attribute context
+    // Update the attribute record in the attribute context
     RtlCopyMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + 
AttrContext->pRecord->NonResident.MappingPairsOffset),
                   RunBuffer,
                   RunBufferSize);
@@ -827,7 +848,7 @@
     }
 
     // update $BITMAP file on disk
-    Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, 
(ULONG)BitmapDataSize, &LengthWritten);
+    Status = WriteAttribute(Vcb, DataContext, 0, BitmapData, 
(ULONG)BitmapDataSize, &LengthWritten, FileRecord);
     if (!NT_SUCCESS(Status))
     {
         ReleaseAttributeContext(DataContext);

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c    [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/btree.c    [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -45,6 +45,12 @@
     ULONG BytesRead;
     ULONGLONG i;
     int Count = 0;
+
+    if (BufferSize == 0)
+    {
+        DPRINT1("Index Allocation is empty.\n");
+        return;
+    }
 
     Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, TAG_NTFS);
 
@@ -281,10 +287,10 @@
             {
                 DPRINT1("TODO: Only a node with a single-level is supported 
right now!\n");
                 // Needs debugging:
-                /*CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
+                CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
                                                                        
IndexRoot,
                                                                        
IndexAllocationAttributeCtx,
-                                                                       
CurrentNodeEntry);*/
+                                                                       
CurrentKey->IndexEntry);
             }
 
             CurrentKey = NextKey;
@@ -300,10 +306,10 @@
             {
                 DPRINT1("TODO: Only a node with a single-level is supported 
right now!\n");
                 // Needs debugging:
-                /*CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
-                                                                         
IndexRoot,
-                                                                         
IndexAllocationAttributeCtx,
-                                                                         
CurrentNodeEntry);*/
+                CurrentKey->LesserChild = CreateBTreeNodeFromIndexNode(Vcb,
+                                                                       
IndexRoot,
+                                                                       
IndexAllocationAttributeCtx,
+                                                                       
CurrentKey->IndexEntry);
             }
 
             break;
@@ -575,7 +581,6 @@
     {
         // Would adding the current entry to the index increase the index size 
beyond the limit we've set?
         ULONG IndexSize = FIELD_OFFSET(INDEX_ROOT_ATTRIBUTE, Header)
-                          + NewIndexRoot->Header.FirstEntryOffset
                           + NewIndexRoot->Header.TotalSizeOfEntries
                           + CurrentNodeEntry->Length;
         if (IndexSize > MaxIndexSize)
@@ -649,7 +654,6 @@
     {
         // Would adding the current entry to the index increase the node size 
beyond the allocation size?
         ULONG IndexSize = FIELD_OFFSET(INDEX_BUFFER, Header)
-            + IndexBuffer->Header.FirstEntryOffset
             + IndexBuffer->Header.TotalSizeOfEntries
             + CurrentNodeEntry->Length;
         if (IndexSize > BufferSize)
@@ -776,7 +780,7 @@
         NodeOffset = GetAllocationOffsetFromVCN(DeviceExt, IndexBufferSize, 
Node->NodeNumber);
 
         // Write the buffer to the index allocation
-        Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, 
(const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten);
+        Status = WriteAttribute(DeviceExt, IndexAllocationContext, NodeOffset, 
(const PUCHAR)IndexBuffer, IndexBufferSize, &LengthWritten, NULL);
         if (!NT_SUCCESS(Status) || LengthWritten != IndexBufferSize)
         {
             DPRINT1("ERROR: Failed to update index allocation!\n");
@@ -945,10 +949,10 @@
     ULONG i;
     for (i = 0; i < Depth; i++)
         DbgPrint(" ");
-    DbgPrint("Node #%d, Depth %d, has %d keys\n", Number, Depth, 
Node->KeyCount);
+    DbgPrint("Node #%d, Depth %d, has %d key%s\n", Number, Depth, 
Node->KeyCount, Node->KeyCount == 1 ? "" : "s");
 
     CurrentKey = Node->FirstKey;
-    for (i = 0; i < Node->KeyCount; i++)
+    for (i = 1; i <= Node->KeyCount; i++)
     {
         DumpBTreeKey(CurrentKey, i, Depth);
         CurrentKey = CurrentKey->NextKey;

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c      [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/mft.c      [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -351,7 +351,7 @@
     }
 
     // Write out the new bitmap
-    Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, 
BitmapSize.LowPart, &LengthWritten);
+    Status = WriteAttribute(Vcb, BitmapContext, BitmapOffset, BitmapBuffer, 
BitmapSize.LowPart, &LengthWritten, Vcb->MasterFileTable);
     if (!NT_SUCCESS(Status))
     {
         ExReleaseResourceLite(&(Vcb->DirResource));
@@ -369,17 +369,72 @@
     return STATUS_SUCCESS;
 }
 
+/**
+* @name MoveAttributes
+* @implemented
+*
+* Moves a block of attributes to a new location in the file Record. The 
attribute at FirstAttributeToMove
+* and every attribute after that will be moved to MoveTo.
+*
+* @param DeviceExt
+* Pointer to the DEVICE_EXTENSION (VCB) of the target volume.
+*
+* @param FirstAttributeToMove
+* Pointer to the first NTFS_ATTR_RECORD that needs to be moved. This pointer 
must reside within a file record.
+*
+* @param FirstAttributeOffset
+* Offset of FirstAttributeToMove relative to the beginning of the file record.
+*
+* @param MoveTo
+* ULONG_PTR with the memory location that will be the new location of the 
first attribute being moved.
+*
+* @return
+* The new location of the final attribute (i.e. AttributeEnd marker).
+*/
+PNTFS_ATTR_RECORD
+MoveAttributes(PDEVICE_EXTENSION DeviceExt,
+               PNTFS_ATTR_RECORD FirstAttributeToMove,
+               ULONG FirstAttributeOffset,
+               ULONG_PTR MoveTo)
+{
+    // Get the size of all attributes after this one
+    ULONG MemBlockSize = 0;
+    PNTFS_ATTR_RECORD CurrentAttribute = FirstAttributeToMove;
+    ULONG CurrentOffset = FirstAttributeOffset;
+    PNTFS_ATTR_RECORD FinalAttribute;
+
+    while (CurrentAttribute->Type != AttributeEnd && CurrentOffset < 
DeviceExt->NtfsInfo.BytesPerFileRecord)
+    {
+        CurrentOffset += CurrentAttribute->Length;
+        MemBlockSize += CurrentAttribute->Length;
+        CurrentAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)CurrentAttribute + 
CurrentAttribute->Length);
+    }
+
+    FinalAttribute = (PNTFS_ATTR_RECORD)(MoveTo + MemBlockSize);
+    MemBlockSize += sizeof(ULONG) * 2;  // Add the AttributeEnd and file 
record end
+
+    ASSERT(MemBlockSize % ATTR_RECORD_ALIGNMENT == 0);
+
+    // Move the attributes after this one
+    RtlMoveMemory((PCHAR)MoveTo, FirstAttributeToMove, MemBlockSize);
+
+    return FinalAttribute;
+}
+
 NTSTATUS
-InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
+                                   PNTFS_ATTR_CONTEXT AttrContext,
                                    PFILE_RECORD_HEADER FileRecord,
                                    ULONG AttrOffset,
                                    ULONG DataSize)
 {
     PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord 
+ AttrOffset);
+    PNTFS_ATTR_RECORD NextAttribute = 
(PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
+    PNTFS_ATTR_RECORD FinalAttribute;
+    ULONG OldAttributeLength = Destination->Length;
     ULONG NextAttributeOffset;
-    USHORT ValueOffset;
-
-    DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", 
AttrContext, FileRecord, AttrOffset, DataSize);
+
+    DPRINT1("InternalSetResidentAttributeLength( %p, %p, %p, %lu, %lu )\n", 
DeviceExt, AttrContext, FileRecord, AttrOffset, DataSize);
 
     ASSERT(!AttrContext->pRecord->IsNonResident);
 
@@ -390,30 +445,46 @@
     Destination->Length = ALIGN_UP_BY(DataSize + 
AttrContext->pRecord->Resident.ValueOffset, ATTR_RECORD_ALIGNMENT);
     NextAttributeOffset = AttrOffset + Destination->Length;
 
-    // Ensure FileRecord has an up-to-date copy of the attribute
-    ValueOffset = AttrContext->pRecord->Resident.ValueOffset;
-    RtlCopyMemory((PCHAR)((ULONG_PTR)FileRecord + AttrOffset + ValueOffset),
-                  (PCHAR)((ULONG_PTR)AttrContext->pRecord + ValueOffset),
-                  min(DataSize, AttrContext->pRecord->Resident.ValueLength));
-
-    // Free the old copy of the attribute in the context, as it will be the 
wrong length
-    ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
-
-    // Create a new copy of the attribute for the context
-    AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, 
Destination->Length, TAG_NTFS);
-    if (!AttrContext->pRecord)
-    {
-        DPRINT1("Unable to allocate memory for attribute!\n");
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
-    RtlCopyMemory(AttrContext->pRecord, Destination, Destination->Length);
-
     // Ensure NextAttributeOffset is aligned to an 8-byte boundary
     ASSERT(NextAttributeOffset % ATTR_RECORD_ALIGNMENT == 0);
-    
-    // advance Destination to the final "attribute" and set the file record end
-    Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + 
Destination->Length);
-    SetFileRecordEnd(FileRecord, Destination, FILE_RECORD_END);
+
+    // Will the new attribute be larger than the old one?
+    if (Destination->Length > OldAttributeLength)
+    {
+        // Free the old copy of the attribute in the context, as it will be 
the wrong length
+        ExFreePoolWithTag(AttrContext->pRecord, TAG_NTFS);
+
+        // Create a new copy of the attribute record for the context
+        AttrContext->pRecord = ExAllocatePoolWithTag(NonPagedPool, 
Destination->Length, TAG_NTFS);
+        if (!AttrContext->pRecord)
+        {
+            DPRINT1("Unable to allocate memory for attribute!\n");
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+        RtlZeroMemory((PVOID)((ULONG_PTR)AttrContext->pRecord + 
OldAttributeLength), Destination->Length - OldAttributeLength);
+        RtlCopyMemory(AttrContext->pRecord, Destination, OldAttributeLength);
+    }
+
+    // Are there attributes after this one that need to be moved?
+    if (NextAttribute->Type != AttributeEnd)
+    {
+        // Move the attributes after this one
+        FinalAttribute = MoveAttributes(DeviceExt, NextAttribute, 
NextAttributeOffset, (ULONG_PTR)Destination + Destination->Length);
+    }
+    else
+    {
+        // advance to the final "attribute," adjust for the changed length of 
the attribute we're resizing
+        FinalAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute - 
OldAttributeLength + Destination->Length);
+    }
+
+    // Update pRecord's length
+    AttrContext->pRecord->Length = Destination->Length;
+    AttrContext->pRecord->Resident.ValueLength = DataSize;
+
+    // set the file record end
+    SetFileRecordEnd(FileRecord, FinalAttribute, FILE_RECORD_END);
+
+    //NtfsDumpFileRecord(DeviceExt, FileRecord);
 
     return STATUS_SUCCESS;
 }
@@ -519,6 +590,9 @@
                  PNTFS_ATTR_RECORD AttrEnd,
                  ULONG EndMarker)
 {
+    // Ensure AttrEnd is aligned on an 8-byte boundary, relative to FileRecord
+    ASSERT(((ULONG_PTR)AttrEnd - (ULONG_PTR)FileRecord) % 
ATTR_RECORD_ALIGNMENT == 0);
+
     // mark the end of attributes
     AttrEnd->Type = AttributeEnd;
 
@@ -841,7 +915,7 @@
                 // restore the back-up attribute, if we made one
                 if (AttribDataSize.QuadPart > 0)
                 {
-                    Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, 
AttribDataSize.QuadPart, &LengthWritten);
+                    Status = WriteAttribute(Vcb, AttrContext, 0, AttribData, 
AttribDataSize.QuadPart, &LengthWritten, FileRecord);
                     if (!NT_SUCCESS(Status))
                     {
                         DPRINT1("ERROR: Unable to write attribute data to 
non-resident clusters during migration!\n");
@@ -855,19 +929,10 @@
             }
         }
     }
-    else if (DataSize->LowPart < AttrContext->pRecord->Resident.ValueLength)
-    {
-        // we need to decrease the length
-        if (NextAttribute->Type != AttributeEnd)
-        {
-            DPRINT1("FIXME: Don't know how to decrease length of resident 
attribute unless it's the final attribute!\n");
-            return STATUS_NOT_IMPLEMENTED;
-        }
-    }
 
     // set the new length of the resident attribute (if we didn't migrate it)
     if (!AttrContext->pRecord->IsNonResident)
-        return InternalSetResidentAttributeLength(AttrContext, FileRecord, 
AttrOffset, DataSize->LowPart);
+        return InternalSetResidentAttributeLength(Vcb, AttrContext, 
FileRecord, AttrOffset, DataSize->LowPart);
 
     return STATUS_SUCCESS;
 }
@@ -1102,6 +1167,12 @@
 * @param RealLengthWritten
 * Pointer to a ULONG which will receive how much data was written, in bytes
 *
+* @param FileRecord
+* Optional pointer to a FILE_RECORD_HEADER that contains a copy of the file 
record
+* being written to. Can be NULL, in which case the file record will be read 
from disk.
+* If not-null, WriteAttribute() will skip reading from disk, and FileRecord
+* will be updated with the newly-written attribute before the function returns.
+*
 * @return
 * STATUS_SUCCESS if successful, an error code otherwise. 
STATUS_NOT_IMPLEMENTED if
 * writing to a sparse file.
@@ -1117,7 +1188,8 @@
                ULONGLONG Offset,
                const PUCHAR Buffer,
                ULONG Length,
-               PULONG RealLengthWritten)
+               PULONG RealLengthWritten,
+               PFILE_RECORD_HEADER FileRecord)
 {
     ULONGLONG LastLCN;
     PUCHAR DataRun;
@@ -1129,12 +1201,13 @@
     NTSTATUS Status;
     PUCHAR SourceBuffer = Buffer;
     LONGLONG StartingOffset;
+    BOOLEAN FileRecordAllocated = FALSE;
     
     //TEMPTEMP
     PUCHAR TempBuffer;
         
 
-    DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p)\n", Vcb, Context, 
Offset, Buffer, Length, RealLengthWritten);
+    DPRINT("WriteAttribute(%p, %p, %I64u, %p, %lu, %p, %p)\n", Vcb, Context, 
Offset, Buffer, Length, RealLengthWritten, FileRecord);
 
     *RealLengthWritten = 0;
 
@@ -1143,7 +1216,10 @@
     {
         ULONG AttributeOffset;
         PNTFS_ATTR_CONTEXT FoundContext;
-        PFILE_RECORD_HEADER FileRecord;
+        PNTFS_ATTR_RECORD Destination;
+
+        // Ensure requested data is within the bounds of the attribute
+        ASSERT(Offset + Length <= Context->pRecord->Resident.ValueLength);
 
         if (Offset + Length > Context->pRecord->Resident.ValueLength)
         {
@@ -1151,16 +1227,21 @@
             return STATUS_INVALID_PARAMETER;
         }
 
-        FileRecord = ExAllocatePoolWithTag(NonPagedPool, 
Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
-
-        if (!FileRecord)
-        {
-            DPRINT1("Error: Couldn't allocate file record!\n");
-            return STATUS_NO_MEMORY;
-        }
-
-        // read the file record
-        ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+        // Do we need to read the file record?
+        if (FileRecord == NULL)
+        {
+            FileRecord = ExAllocatePoolWithTag(NonPagedPool, 
Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+            if (!FileRecord)
+            {
+                DPRINT1("Error: Couldn't allocate file record!\n");
+                return STATUS_NO_MEMORY;
+            }
+
+            FileRecordAllocated = TRUE;
+
+            // read the file record
+            ReadFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+        }
 
         // find where to write the attribute data to
         Status = FindAttribute(Vcb, FileRecord,
@@ -1173,28 +1254,37 @@
         if (!NT_SUCCESS(Status))
         {
             DPRINT1("ERROR: Couldn't find matching attribute!\n");
-            ExFreePoolWithTag(FileRecord, TAG_NTFS);
+            if(FileRecordAllocated)
+                ExFreePoolWithTag(FileRecord, TAG_NTFS);
             return Status;
         }
 
+        Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + 
AttributeOffset);
+
         DPRINT("Offset: %I64u, AttributeOffset: %u, ValueOffset: %u\n", 
Offset, AttributeOffset, Context->pRecord->Resident.ValueLength);
-        Offset += AttributeOffset + Context->pRecord->Resident.ValueOffset;
-        
-        if (Offset + Length > Vcb->NtfsInfo.BytesPerFileRecord)
+
+        // Will we be writing past the end of the allocated file record?
+        if (Offset + Length + AttributeOffset + 
Context->pRecord->Resident.ValueOffset > Vcb->NtfsInfo.BytesPerFileRecord)
         {
             DPRINT1("DRIVER ERROR: Data being written extends past end of file 
record!\n");
             ReleaseAttributeContext(FoundContext);
+            if (FileRecordAllocated)
+                ExFreePoolWithTag(FileRecord, TAG_NTFS);
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        // copy the data being written into the file record. We cast Offset to 
ULONG, which is safe because it's range has been verified.
+        RtlCopyMemory((PCHAR)((ULONG_PTR)Destination + 
Context->pRecord->Resident.ValueOffset + (ULONG)Offset), Buffer, Length);
+
+        Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
+
+        // Update the context's copy of the resident attribute
+        ASSERT(Context->pRecord->Length == Destination->Length);
+        RtlCopyMemory((PVOID)Context->pRecord, Destination, 
Context->pRecord->Length);
+
+        ReleaseAttributeContext(FoundContext);
+        if (FileRecordAllocated)
             ExFreePoolWithTag(FileRecord, TAG_NTFS);
-            return STATUS_INVALID_PARAMETER;
-        }
-
-        // copy the data being written into the file record
-        RtlCopyMemory((PCHAR)FileRecord + Offset, Buffer, Length);
-
-        Status = UpdateFileRecord(Vcb, Context->FileMFTIndex, FileRecord);
-
-        ReleaseAttributeContext(FoundContext);
-        ExFreePoolWithTag(FileRecord, TAG_NTFS);
 
         if (NT_SUCCESS(Status))
             *RealLengthWritten = Length;
@@ -1519,7 +1609,7 @@
     {
         // we need to write the index root attribute back to disk
         ULONG LengthWritten;
-        Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, 
AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten);
+        Status = WriteAttribute(Vcb, IndexRootCtx, 0, (PUCHAR)IndexRecord, 
AttributeDataLength(IndexRootCtx->pRecord), &LengthWritten, MftRecord);
         if (!NT_SUCCESS(Status))
         {
             DPRINT1("ERROR: Couldn't update Index Root!\n");
@@ -1661,7 +1751,7 @@
                 break;
             }
 
-            Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, 
(const PUCHAR)IndexRecord, IndexBlockSize, &Written);
+            Status = WriteAttribute(Vcb, IndexAllocationCtx, RecordOffset, 
(const PUCHAR)IndexRecord, IndexBlockSize, &Written, MftRecord);
             if (!NT_SUCCESS(Status))
             {
                 DPRINT1("ERROR Performing write!\n");
@@ -1714,7 +1804,13 @@
     AddFixupArray(Vcb, &FileRecord->Ntfs);
 
     // write the file record to the master file table
-    Status = WriteAttribute(Vcb, Vcb->MFTContext, MftIndex * 
Vcb->NtfsInfo.BytesPerFileRecord, (const PUCHAR)FileRecord, 
Vcb->NtfsInfo.BytesPerFileRecord, &BytesWritten);
+    Status = WriteAttribute(Vcb, 
+                            Vcb->MFTContext,
+                            MftIndex * Vcb->NtfsInfo.BytesPerFileRecord,
+                            (const PUCHAR)FileRecord,
+                            Vcb->NtfsInfo.BytesPerFileRecord,
+                            &BytesWritten,
+                            FileRecord);
 
     if (!NT_SUCCESS(Status))
     {
@@ -1882,7 +1978,7 @@
     BitmapData[2] = SystemReservedBits;
 
     // write the bitmap back to the MFT's $Bitmap attribute
-    Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, 
BitmapDataSize, &LengthWritten);
+    Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, 
BitmapDataSize, &LengthWritten, FileRecord);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("ERROR encountered when writing $Bitmap attribute!\n");
@@ -1958,10 +2054,8 @@
     ULONG IndexRootOffset;
     ULONGLONG I30IndexRootLength;
     ULONG LengthWritten;
-    PNTFS_ATTR_RECORD DestinationAttribute;
     PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
     ULONG AttributeLength;
-    PNTFS_ATTR_RECORD NextAttribute;
     PB_TREE NewTree;
     ULONG BtreeIndexLength;
     ULONG MaxIndexSize;
@@ -2065,7 +2159,7 @@
 
     DumpBTree(NewTree);
 
-    // Convert B*Tree back to Index
+    // Convert B*Tree back to Index, starting with the index allocation
     Status = UpdateIndexAllocation(DeviceExt, NewTree, 
I30IndexRoot->SizeOfEntry, ParentFileRecord);
     if (!NT_SUCCESS(Status))
     {
@@ -2103,22 +2197,9 @@
     
     if (AttributeLength != IndexRootContext->pRecord->Resident.ValueLength)
     {
-        DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)ParentFileRecord 
+ IndexRootOffset);
-
-        // Find the attribute (or attribute-end marker) after the index root
-        NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)DestinationAttribute + 
DestinationAttribute->Length);
-        if (NextAttribute->Type != AttributeEnd)
-        {
-            DPRINT1("FIXME: For now, only resizing index root at the end of a 
file record is supported!\n");
-            ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
-            ReleaseAttributeContext(IndexRootContext);
-            ExFreePoolWithTag(I30IndexRoot, TAG_NTFS);
-            ExFreePoolWithTag(ParentFileRecord, TAG_NTFS);
-            return STATUS_NOT_IMPLEMENTED;
-        }
-
         // Update the length of the attribute in the file record of the parent 
directory
-        Status = InternalSetResidentAttributeLength(IndexRootContext,
+        Status = InternalSetResidentAttributeLength(DeviceExt,
+                                                    IndexRootContext,
                                                     ParentFileRecord,
                                                     IndexRootOffset,
                                                     AttributeLength);
@@ -2153,7 +2234,8 @@
                             0,
                             (PUCHAR)NewIndexRoot,
                             AttributeLength,
-                            &LengthWritten);
+                            &LengthWritten,
+                            ParentFileRecord);
     if (!NT_SUCCESS(Status))
     {
         DPRINT1("ERROR: Unable to write new index root attribute to parent 
directory!\n");

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h     [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/ntfs.h     [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -954,16 +954,24 @@
                ULONGLONG Offset,
                const PUCHAR Buffer,
                ULONG Length,
-               PULONG LengthWritten);
+               PULONG LengthWritten,
+               PFILE_RECORD_HEADER FileRecord);
 
 ULONGLONG
 AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
 
 NTSTATUS
-InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
+InternalSetResidentAttributeLength(PDEVICE_EXTENSION DeviceExt,
+                                   PNTFS_ATTR_CONTEXT AttrContext,
                                    PFILE_RECORD_HEADER FileRecord,
                                    ULONG AttrOffset,
                                    ULONG DataSize);
+
+PNTFS_ATTR_RECORD
+MoveAttributes(PDEVICE_EXTENSION DeviceExt,
+               PNTFS_ATTR_RECORD FirstAttributeToMove,
+               ULONG FirstAttributeOffset,
+               ULONG_PTR MoveTo);
 
 NTSTATUS
 SetAttributeDataLength(PFILE_OBJECT FileObject,

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/rw.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/rw.c?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/rw.c       [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/rw.c       [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -478,7 +478,7 @@
     DPRINT("Length: %lu\tWriteOffset: %lu\tStreamSize: %I64u\n", Length, 
WriteOffset, StreamSize);
 
     // Write the data to the attribute
-    Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, 
Length, LengthWritten);
+    Status = WriteAttribute(DeviceExt, DataContext, WriteOffset, Buffer, 
Length, LengthWritten, FileRecord);
 
     // Did the write fail?
     if (!NT_SUCCESS(Status))

Modified: branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c
URL: 
http://svn.reactos.org/svn/reactos/branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c?rev=75554&r1=75553&r2=75554&view=diff
==============================================================================
--- branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c  [iso-8859-1] 
(original)
+++ branches/GSoC_2016/NTFS/drivers/filesystems/ntfs/volinfo.c  [iso-8859-1] 
Tue Aug 15 19:32:20 2017
@@ -196,7 +196,7 @@
             
     }
                 
-    Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, 
(ULONG)BitmapDataSize, &LengthWritten);
+    Status = WriteAttribute(DeviceExt, DataContext, 0, BitmapData, 
(ULONG)BitmapDataSize, &LengthWritten, BitmapRecord);
     
     ReleaseAttributeContext(DataContext);
 


Reply via email to