[NTFS] - Add some fixes and improvements to mft.c from CR-123:
[reactos.git] / drivers / filesystems / ntfs / mft.c
index e83c33e..539b8f2 100644 (file)
@@ -119,7 +119,7 @@ FindAttribute(PDEVICE_EXTENSION Vcb,
     FIND_ATTR_CONTXT Context;
     PNTFS_ATTR_RECORD Attribute;
 
-    DPRINT("FindAttribute(%p, %p, 0x%x, %S, %u, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx);
+    DPRINT("FindAttribute(%p, %p, 0x%x, %S, %lu, %p, %p)\n", Vcb, MftRecord, Type, Name, NameLength, AttrCtx, Offset);
 
     Found = FALSE;
     Status = FindFirstAttribute(&Context, Vcb, MftRecord, FALSE, &Attribute);
@@ -133,7 +133,7 @@ FindAttribute(PDEVICE_EXTENSION Vcb,
 
                 AttrName = (PWCHAR)((PCHAR)Attribute + Attribute->NameOffset);
                 DPRINT("%.*S, %.*S\n", Attribute->NameLength, AttrName, NameLength, Name);
-                if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
+                if (RtlCompareMemory(AttrName, Name, NameLength * sizeof(WCHAR)) == (NameLength  * sizeof(WCHAR)))
                 {
                     Found = TRUE;
                 }
@@ -268,7 +268,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
     }
 
     // Zero the bytes we'll be adding
-    RtlZeroMemory((PUCHAR)((ULONG_PTR)BitmapBuffer), NewBitmapSize);
+    RtlZeroMemory(BitmapBuffer, NewBitmapSize);
 
     // Read the bitmap attribute
     BytesRead = ReadAttribute(Vcb,
@@ -337,6 +337,7 @@ IncreaseMftSize(PDEVICE_EXTENSION Vcb, BOOLEAN CanWait)
         ExFreePoolWithTag(BitmapBuffer, TAG_NTFS);
         ReleaseAttributeContext(BitmapContext);
         DPRINT1("ERROR: Couldn't write to bitmap attribute of $MFT!\n");
+        return Status;
     }
 
     // Cleanup
@@ -358,6 +359,8 @@ InternalSetResidentAttributeLength(PNTFS_ATTR_CONTEXT AttrContext,
 
     DPRINT("InternalSetResidentAttributeLength( %p, %p, %lu, %lu )\n", AttrContext, FileRecord, AttrOffset, DataSize);
 
+    ASSERT(!AttrContext->Record.IsNonResident);
+
     // update ValueLength Field
     AttrContext->Record.Resident.ValueLength =
     Destination->Resident.ValueLength = DataSize;
@@ -396,6 +399,14 @@ SetAttributeDataLength(PFILE_OBJECT FileObject,
 {
     NTSTATUS Status = STATUS_SUCCESS;
 
+    DPRINT1("SetAttributeDataLenth(%p, %p, %p, %lu, %p, %I64u)\n",
+            FileObject,
+            Fcb,
+            AttrContext,
+            AttrOffset,
+            FileRecord,
+            DataSize->QuadPart);
+
     // are we truncating the file?
     if (DataSize->QuadPart < AttributeDataLength(&AttrContext->Record))
     {
@@ -529,11 +540,7 @@ SetNonResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
     PNTFS_ATTR_RECORD DestinationAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + AttrOffset);
     ULONG ExistingClusters = AttrContext->Record.NonResident.AllocatedSize / BytesPerCluster;
 
-    if (!AttrContext->Record.IsNonResident)
-    {
-        DPRINT1("ERROR: SetNonResidentAttributeDataLength() called for resident attribute!\n");
-        return STATUS_INVALID_PARAMETER;
-    }
+    ASSERT(AttrContext->Record.IsNonResident);
 
     // do we need to increase the allocation size?
     if (AttrContext->Record.NonResident.AllocatedSize < AllocationSize)
@@ -663,11 +670,7 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
     ULONG NextAttributeOffset = AttrOffset + AttrContext->Record.Length;
     PNTFS_ATTR_RECORD NextAttribute = (PNTFS_ATTR_RECORD)((PCHAR)FileRecord + NextAttributeOffset);
 
-    if (AttrContext->Record.IsNonResident)
-    {
-        DPRINT1("ERROR: SetResidentAttributeDataLength() called for non-resident attribute!\n");
-        return STATUS_INVALID_PARAMETER;
-    }
+    ASSERT(!AttrContext->Record.IsNonResident);
 
     //NtfsDumpFileAttributes(Vcb, FileRecord);
 
@@ -725,9 +728,6 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
                 // update the mapping pairs offset, which will be 0x40 + length in bytes of the name
                 AttrContext->Record.NonResident.MappingPairsOffset = Destination->NonResident.MappingPairsOffset = 0x40 + (Destination->NameLength * 2);
 
-                // mark the attribute as non-resident
-                AttrContext->Record.IsNonResident = Destination->IsNonResident = 1;
-
                 // update the end of the file record
                 // calculate position of end markers (1 byte for empty data run)
                 EndAttributeOffset = AttrOffset + AttrContext->Record.NonResident.MappingPairsOffset + 1;
@@ -742,7 +742,22 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
                                  (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + EndAttributeOffset),
                                  FILE_RECORD_END);
 
-                // update file record on disk
+                // Initialize the MCB, potentially catch an exception
+                _SEH2_TRY
+                {
+                    FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
+                }
+                _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 
+                {
+                    DPRINT1("Unable to create LargeMcb!\n");
+                    ExFreePoolWithTag(AttribData, TAG_NTFS);
+                    _SEH2_YIELD(return _SEH2_GetExceptionCode());
+                } _SEH2_END;
+
+                // Mark the attribute as non-resident (we wait until after we know the LargeMcb was initialized)
+                AttrContext->Record.IsNonResident = Destination->IsNonResident = 1;
+
+                // Update file record on disk
                 Status = UpdateFileRecord(Vcb, AttrContext->FileMFTIndex, FileRecord);
                 if (!NT_SUCCESS(Status))
                 {
@@ -752,13 +767,6 @@ SetResidentAttributeDataLength(PDEVICE_EXTENSION Vcb,
                     return Status;
                 }
 
-                // Initialize the MCB, potentially catch an exception
-                _SEH2_TRY{
-                    FsRtlInitializeLargeMcb(&AttrContext->DataRunsMCB, NonPagedPool);
-                } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
-                    _SEH2_YIELD(return _SEH2_GetExceptionCode());
-                } _SEH2_END;
-
                 // Now we can treat the attribute as non-resident and enlarge it normally
                 Status = SetNonResidentAttributeDataLength(Vcb, AttrContext, AttrOffset, FileRecord, DataSize);
                 if (!NT_SUCCESS(Status))
@@ -1336,7 +1344,7 @@ ReadFileRecord(PDEVICE_EXTENSION Vcb,
     BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
     if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
     {
-        DPRINT1("ReadFileRecord failed: %I64u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
+        DPRINT1("ReadFileRecord failed: %I64u read, %lu expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
         return STATUS_PARTIAL_COPY;
     }
 
@@ -1370,11 +1378,11 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
     NTSTATUS Status;
     ULONG CurrentEntry = 0;
 
-    DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %u, %I64u, %I64u, %s)\n",
+    DPRINT("UpdateFileNameRecord(%p, %I64d, %wZ, %s, %I64u, %I64u, %s)\n",
            Vcb,
            ParentMFTIndex,
            FileName,
-           DirSearch,
+           DirSearch ? "TRUE" : "FALSE",
            NewDataSize,
            NewAllocationSize,
            CaseSensitive ? "TRUE" : "FALSE");
@@ -1410,7 +1418,15 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-    ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
+    Status = ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(&IndexRootCtx->Record));
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("ERROR: Failed to read Index Root!\n");
+        ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+        ReleaseAttributeContext(IndexRootCtx);
+        ExFreePoolWithTag(MftRecord, TAG_NTFS);
+    }
+
     IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
     IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
     // Index root is always resident. 
@@ -1432,6 +1448,18 @@ UpdateFileNameRecord(PDEVICE_EXTENSION Vcb,
                                           NewAllocationSize,
                                           CaseSensitive);
 
+    if (Status == STATUS_PENDING)
+    {
+        // we need to write the index root attribute back to disk
+        ULONG LengthWritten;
+        Status = WriteAttribute(Vcb, IndexRootCtx, 0, IndexRecord, AttributeDataLength(&IndexRootCtx->Record), &LengthWritten);
+        if (!NT_SUCCESS(Status))
+        {
+            DPRINT1("ERROR: Couldn't update Index Root!\n");
+        }
+
+    }
+
     ReleaseAttributeContext(IndexRootCtx);
     ExFreePoolWithTag(IndexRecord, TAG_NTFS);
     ExFreePoolWithTag(MftRecord, TAG_NTFS);
@@ -1466,7 +1494,20 @@ UpdateIndexEntryFileNameSize(PDEVICE_EXTENSION Vcb,
     ULONGLONG IndexAllocationSize;
     PINDEX_BUFFER IndexBuffer;
 
-    DPRINT("UpdateIndexEntrySize(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %u, %I64u, %I64u)\n", Vcb, MftRecord, IndexRecord, IndexBlockSize, FirstEntry, LastEntry, FileName, *StartEntry, *CurrentEntry, DirSearch, NewDataSize, NewAllocatedSize);
+    DPRINT("UpdateIndexEntrySize(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %I64u, %I64u, %s)\n",
+           Vcb,
+           MftRecord,
+           IndexRecord,
+           IndexBlockSize,
+           FirstEntry,
+           LastEntry,
+           FileName,
+           *StartEntry,
+           *CurrentEntry,
+           DirSearch ? "TRUE" : "FALSE",
+           NewDataSize,
+           NewAllocatedSize,
+           CaseSensitive ? "TRUE" : "FALSE");
 
     // find the index entry responsible for the file we're trying to update
     IndexEntry = FirstEntry;
@@ -1688,7 +1729,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
     RTL_BITMAP Bitmap;
     ULONGLONG BitmapDataSize;
     ULONGLONG AttrBytesRead;
-    PVOID BitmapData;
+    PUCHAR BitmapData;
     ULONG LengthWritten;
     PNTFS_ATTR_CONTEXT BitmapContext;
     LARGE_INTEGER BitmapBits;
@@ -1724,17 +1765,20 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
         return STATUS_OBJECT_NAME_NOT_FOUND;
     }
 
-    // we need to backup the bits for records 0x10 - 0x17 and leave them unassigned if they aren't assigned
-    RtlCopyMemory(&SystemReservedBits, (PVOID)((ULONG_PTR)BitmapData + 2), 1);
-    RtlFillMemory((PVOID)((ULONG_PTR)BitmapData + 2), 1, (UCHAR)0xFF);
+    // We need to backup the bits for records 0x10 - 0x17 (3rd byte of bitmap) and mark these records
+    // as in-use so we don't assign files to those indices. They're reserved for the system (e.g. ChkDsk).
+    SystemReservedBits = BitmapData[2];
+    BitmapData[2] = 0xff;
 
     // Calculate bit count
     BitmapBits.QuadPart = AttributeDataLength(&(DeviceExt->MFTContext->Record)) /
                           DeviceExt->NtfsInfo.BytesPerFileRecord;
     if (BitmapBits.HighPart != 0)
     {
-        DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported!\n");
-        BitmapBits.LowPart = 0xFFFFFFFF;
+        DPRINT1("\tFIXME: bitmap sizes beyond 32bits are not yet supported! (Your NTFS volume is too large)\n");
+        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        ReleaseAttributeContext(BitmapContext);
+        return STATUS_NOT_IMPLEMENTED;
     }
 
     // convert buffer into bitmap
@@ -1768,7 +1812,7 @@ AddNewMftEntry(PFILE_RECORD_HEADER FileRecord,
     // [BitmapData should have been updated via RtlFindClearBitsAndSet()]
 
     // Restore the system reserved bits
-    RtlCopyMemory((PVOID)((ULONG_PTR)BitmapData + 2), &SystemReservedBits, 1);
+    BitmapData[2] = SystemReservedBits;
 
     // write the bitmap back to the MFT's $Bitmap attribute
     Status = WriteAttribute(DeviceExt, BitmapContext, 0, BitmapData, BitmapDataSize, &LengthWritten);
@@ -1804,9 +1848,9 @@ AddFixupArray(PDEVICE_EXTENSION Vcb,
               PNTFS_RECORD_HEADER Record)
 {
     USHORT *pShortToFixUp;
-    unsigned int ArrayEntryCount = Record->UsaCount - 1;
-    unsigned int Offset = Vcb->NtfsInfo.BytesPerSector - 2;
-    int i;
+    ULONG ArrayEntryCount = Record->UsaCount - 1;
+    ULONG Offset = Vcb->NtfsInfo.BytesPerSector - 2;
+    ULONG i;
 
     PFIXUP_ARRAY fixupArray = (PFIXUP_ARRAY)((UCHAR*)Record + Record->UsaOffset);
 
@@ -1933,7 +1977,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
     ULONGLONG IndexAllocationSize;
     PINDEX_BUFFER IndexBuffer;
 
-    DPRINT("BrowseIndexEntries(%p, %p, %p, %u, %p, %p, %wZ, %u, %u, %s, %s, %p)\n",
+    DPRINT("BrowseIndexEntries(%p, %p, %p, %lu, %p, %p, %wZ, %lu, %lu, %s, %s, %p)\n",
            Vcb,
            MftRecord,
            IndexRecord,
@@ -1981,7 +2025,7 @@ BrowseIndexEntries(PDEVICE_EXTENSION Vcb,
     Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, L"$I30", 4, &IndexAllocationCtx, NULL);
     if (!NT_SUCCESS(Status))
     {
-        DPRINT("Corrupted filesystem!\n");
+        DPRINT1("Corrupted filesystem!\n");
         return Status;
     }
 
@@ -2031,8 +2075,8 @@ NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
                   PUNICODE_STRING FileName,
                   PULONG FirstEntry,
                   BOOLEAN DirSearch,
-                  ULONGLONG *OutMFTIndex,
-                  BOOLEAN CaseSensitive)
+                  BOOLEAN CaseSensitive,
+                  ULONGLONG *OutMFTIndex)
 {
     PFILE_RECORD_HEADER MftRecord;
     PNTFS_ATTR_CONTEXT IndexRootCtx;
@@ -2042,7 +2086,14 @@ NtfsFindMftRecord(PDEVICE_EXTENSION Vcb,
     NTSTATUS Status;
     ULONG CurrentEntry = 0;
 
-    DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %u, %u, %p)\n", Vcb, MFTIndex, FileName, *FirstEntry, DirSearch, OutMFTIndex);
+    DPRINT("NtfsFindMftRecord(%p, %I64d, %wZ, %lu, %s, %s, %p)\n",
+           Vcb,
+           MFTIndex,
+           FileName,
+           *FirstEntry,
+           DirSearch ? "TRUE" : "FALSE",
+           CaseSensitive ? "TRUE" : "FALSE",
+           OutMFTIndex);
 
     MftRecord = ExAllocatePoolWithTag(NonPagedPool,
                                       Vcb->NtfsInfo.BytesPerFileRecord,
@@ -2129,7 +2180,7 @@ NtfsLookupFileAt(PDEVICE_EXTENSION Vcb,
     {
         DPRINT("Current: %wZ\n", &Current);
 
-        Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, &CurrentMFTIndex, CaseSensitive);
+        Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &FirstEntry, FALSE, CaseSensitive, &CurrentMFTIndex);
         if (!NT_SUCCESS(Status))
         {
             return Status;
@@ -2222,7 +2273,7 @@ NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
 {
     NTSTATUS Status;
 
-    DPRINT("NtfsFindFileAt(%p, %wZ, %u, %p, %p, %I64x, %s)\n",
+    DPRINT("NtfsFindFileAt(%p, %wZ, %lu, %p, %p, %I64x, %s)\n",
            Vcb,
            SearchPattern,
            *FirstEntry,
@@ -2231,7 +2282,7 @@ NtfsFindFileAt(PDEVICE_EXTENSION Vcb,
            CurrentMFTIndex,
            (CaseSensitive ? "TRUE" : "FALSE"));
 
-    Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, &CurrentMFTIndex, CaseSensitive);
+    Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, SearchPattern, FirstEntry, TRUE, CaseSensitive, &CurrentMFTIndex);
     if (!NT_SUCCESS(Status))
     {
         DPRINT("NtfsFindFileAt: NtfsFindMftRecord() failed with status 0x%08lx\n", Status);