[NTFS]
[reactos.git] / drivers / filesystems / ntfs / mft.c
index 4e10584..f3ac318 100644 (file)
@@ -178,45 +178,44 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
                                    ULONG AttrOffset,
                                    ULONG DataSize)
 {
-    ULONG EndMarker = AttributeEnd;
-    ULONG FinalMarker = FILE_RECORD_END;
+    PNTFS_ATTR_RECORD Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
     ULONG NextAttributeOffset;
-    ULONG Offset;
-    USHORT Padding;
 
     DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
 
     // update ValueLength Field
-    AttrContext->Record.Resident.ValueLength = DataSize;
-    Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Resident.ValueLength);
-    RtlCopyMemory((PCHAR)FileRecord + Offset, &DataSize, sizeof(ULONG));
+    AttrContext->Record.Resident.ValueLength =
+    Destination->Resident.ValueLength = DataSize;
 
     // calculate the record length and end marker offset
-    AttrContext->Record.Length = DataSize + AttrContext->Record.Resident.ValueOffset;
+    AttrContext->Record.Length =
+    Destination->Length = DataSize + AttrContext->Record.Resident.ValueOffset;
     NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
 
     // Ensure NextAttributeOffset is aligned to an 8-byte boundary
     if (NextAttributeOffset % 8 != 0)
     {
-        Padding = 8 - (NextAttributeOffset % 8);
+        USHORT Padding = 8 - (NextAttributeOffset % 8);
         NextAttributeOffset += Padding;
         AttrContext->Record.Length += Padding;
+        Destination->Length += Padding;
     }
+    
+    // advance Destination to the final "attribute" and write the end type
+    Destination = (PNTFS_ATTR_RECORD)((ULONG_PTR)Destination + Destination->Length);
+    Destination->Type = AttributeEnd;
 
-    // update the record length
-    Offset = AttrOffset + FIELD_OFFSET(NTFS_ATTR_RECORD, Length);
-    RtlCopyMemory((PCHAR)FileRecord + Offset, &AttrContext->Record.Length, sizeof(ULONG));
+    // write the final marker (which shares the same offset and type as the Length field)
+    Destination->Length = FILE_RECORD_END;
 
-    // write the end marker 
-    RtlCopyMemory((PCHAR)FileRecord + NextAttributeOffset, &EndMarker, sizeof(ULONG));
-
-    // write the final marker
-    Offset = NextAttributeOffset + sizeof(ULONG);
-    RtlCopyMemory((PCHAR)FileRecord + Offset, &FinalMarker, sizeof(ULONG));
-
-    FileRecord->BytesInUse = Offset + sizeof(ULONG);
+    FileRecord->BytesInUse = NextAttributeOffset + (sizeof(ULONG) * 2);
 }
 
+/**
+*   @parameter FileRecord
+*   Pointer to a file record. Must be a full record at least 
+*   Fcb->Vcb->NtfsInfo.BytesPerFileRecord bytes large, not just the header.
+*/
 NTSTATUS
 SetAttributeDataLength(PFILE_OBJECT FileObject,
                        PNTFS_FCB Fcb,
@@ -226,11 +225,12 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
                        PLARGE_INTEGER DataSize)
 {
     NTSTATUS Status = STATUS_SUCCESS;
+    ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
 
     // are we truncating the file?
-    if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record)
+    if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
     {
-        if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, (PLARGE_INTEGER)&AllocationSize))
+        if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer, DataSize))
         {
             DPRINT1("Can't truncate a memory-mapped file!\n");
             return STATUS_USER_MAPPED_FILE;
@@ -239,19 +239,19 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
 
     if (AttrContext->Record.IsNonResident)
     {
-        ULONG BytesPerCluster = Fcb->Vcb->NtfsInfo.BytesPerCluster;
         ULONGLONG AllocationSize = ROUND_UP(DataSize->QuadPart, BytesPerCluster);
+        PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
+        ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
 
         // do we need to increase the allocation size?
         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);
+            NTSTATUS Status = GetLastClusterInDataRun(Fcb->Vcb, &AttrContext->Record, (PULONGLONG)&LastClusterInDataRun.QuadPart);
 
             DPRINT1("GetLastClusterInDataRun returned: %I64u\n", LastClusterInDataRun.QuadPart);
             DPRINT1("Highest VCN of record: %I64u\n", AttrContext->Record.NonResident.HighestVCN);
@@ -271,7 +271,7 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
                 }
 
                 // now we need to add the clusters we allocated to the data run
-                Status = AddRun(AttrContext, NextAssignedCluster, AssignedClusters);
+                Status = AddRun(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, NextAssignedCluster, AssignedClusters);
                 if (!NT_SUCCESS(Status))
                 {
                     DPRINT1("Error: Unable to add data run!\n");
@@ -281,23 +281,28 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
                 ClustersNeeded -= AssignedClusters;
                 LastClusterInDataRun.LowPart = NextAssignedCluster + AssignedClusters - 1;
             }
-
-            DPRINT1("FixMe: Increasing allocation size is unimplemented!\n");
-            return STATUS_NOT_IMPLEMENTED;
+        }
+        else if (AttrContext->Record.NonResident.AllocatedSize > AllocationSize)
+        {
+            // shrink allocation size
+            ULONG ClustersToFree = ExistingClusters - (AllocationSize / BytesPerCluster);
+            Status = FreeClusters(Fcb->Vcb, AttrContext, AttrOffset, FileRecord, ClustersToFree);
         }
 
         // TODO: is the file compressed, encrypted, or sparse?
 
         // NOTE: we need to have acquired the main resource exclusively, as well as(?) the PagingIoResource
 
-        // TODO: update the allocated size on-disk
-        DPRINT("Allocated Size: %I64u\n", AttrContext->Record.NonResident.AllocatedSize);
-
+        Fcb->RFCB.AllocationSize.QuadPart = AllocationSize;
+        AttrContext->Record.NonResident.AllocatedSize = AllocationSize;
         AttrContext->Record.NonResident.DataSize = DataSize->QuadPart;
         AttrContext->Record.NonResident.InitializedSize = DataSize->QuadPart;
 
-        // copy the attribute record back into the FileRecord
-        RtlCopyMemory((PCHAR)FileRecord + AttrOffset, &AttrContext->Record, AttrContext->Record.Length);
+        DestinationAttribute->NonResident.AllocatedSize = AllocationSize;
+        DestinationAttribute->NonResident.DataSize = DataSize->QuadPart;
+        DestinationAttribute->NonResident.InitializedSize = DataSize->QuadPart;
+
+        DPRINT("Allocated Size: %I64u\n", DestinationAttribute->NonResident.AllocatedSize);
     }
     else
     {
@@ -1087,9 +1092,12 @@ UpdateFileRecord(PDEVICE_EXTENSION Vcb,
 
     if (!NT_SUCCESS(Status))
     {
-        DPRINT1("UpdateFileRecord failed: %I64u written, %u expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
+        DPRINT1("UpdateFileRecord failed: %I64u written, %lu expected\n", BytesWritten, Vcb->NtfsInfo.BytesPerFileRecord);
     }
 
+    // remove the fixup array (so the file record pointer can still be used)
+    FixupUpdateSequenceArray(Vcb, &file->Ntfs);
+
     return Status;
 }