[NTFS]
authorPierre Schweitzer <pierre@reactos.org>
Fri, 26 Sep 2014 13:57:29 +0000 (13:57 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Fri, 26 Sep 2014 13:57:29 +0000 (13:57 +0000)
- Import structures and defines from FreeLdr NTFS driver header
- Import lot of code from FreeLdr NTFS driver to handle MFT & attributes
- Remove code that has been overtaken by FreeLdr code
- Adapt & port code from FreeLdr (allocation, status, data structures)
- Adapt old code to the new functions defined from FreeLdr code

- Open the MFT during volume mount and keep in VCB for further use
- Implement a function to lookup a file in the MFT given its full path

svn path=/trunk/; revision=64313

reactos/drivers/filesystems/ntfs/attrib.c
reactos/drivers/filesystems/ntfs/fsctl.c
reactos/drivers/filesystems/ntfs/mft.c
reactos/drivers/filesystems/ntfs/ntfs.h

index 5ccbe21..572f6aa 100644 (file)
 
 /* FUNCTIONS ****************************************************************/
 
-static
-ULONG
-RunLength(PUCHAR run)
-{
-    return(*run & 0x0f) + ((*run >> 4) & 0x0f) + 1;
-}
-
-
-static
-LONGLONG
-RunLCN(PUCHAR run)
+PUCHAR
+DecodeRun(PUCHAR DataRun,
+          LONGLONG *DataRunOffset,
+          ULONGLONG *DataRunLength)
 {
-    UCHAR n1 = *run & 0x0f;
-    UCHAR n2 = (*run >> 4) & 0x0f;
-    LONGLONG lcn = (n2 == 0) ? 0 : (CHAR)(run[n1 + n2]);
-    LONG i = 0;
-
-    for (i = n1 +n2 - 1; i > n1; i--)
-        lcn = (lcn << 8) + run[i];
-    return lcn;
-}
+    UCHAR DataRunOffsetSize;
+    UCHAR DataRunLengthSize;
+    CHAR i;
+
+    DataRunOffsetSize = (*DataRun >> 4) & 0xF;
+    DataRunLengthSize = *DataRun & 0xF;
+    *DataRunOffset = 0;
+    *DataRunLength = 0;
+    DataRun++;
+    for (i = 0; i < DataRunLengthSize; i++)
+    {
+        *DataRunLength += ((ULONG64)*DataRun) << (i * 8);
+        DataRun++;
+    }
 
+    /* NTFS 3+ sparse files */
+    if (DataRunOffsetSize == 0)
+    {
+        *DataRunOffset = -1;
+    }
+    else
+    {
+        for (i = 0; i < DataRunOffsetSize - 1; i++)
+        {
+            *DataRunOffset += ((ULONG64)*DataRun) << (i * 8);
+            DataRun++;
+        }
+        /* The last byte contains sign so we must process it different way. */
+        *DataRunOffset = ((LONG64)(CHAR)(*(DataRun++)) << (i * 8)) + *DataRunOffset;
+    }
 
-static
-ULONGLONG
-RunCount(PUCHAR run)
-{
-    UCHAR n =  *run & 0xf;
-    ULONGLONG count = 0;
-    ULONG i = 0;
+    DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
+    DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
+    DPRINT("DataRunOffset: %x\n", *DataRunOffset);
+    DPRINT("DataRunLength: %x\n", *DataRunLength);
 
-    for (i = n; i > 0; i--)
-        count = (count << 8) + run[i];
-    return count;
+    return DataRun;
 }
 
-
 BOOLEAN
-FindRun(PNONRESIDENT_ATTRIBUTE NresAttr,
+FindRun(PNTFS_ATTR_RECORD NresAttr,
         ULONGLONG vcn,
         PULONGLONG lcn,
         PULONGLONG count)
 {
-    PUCHAR run;
-    ULONGLONG base = NresAttr->StartVcn;
-
-    if (vcn < NresAttr->StartVcn || vcn > NresAttr->LastVcn)
+    if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
         return FALSE;
 
-    *lcn = 0;
-
-    for (run = (PUCHAR)((ULONG_PTR)NresAttr + NresAttr->RunArrayOffset);
-         *run != 0; run += RunLength(run))
-    {
-        *lcn += RunLCN(run);
-        *count = RunCount(run);
+    DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
 
-        if (base <= vcn && vcn < base + *count)
-        {
-            *lcn = (RunLCN(run) == 0) ? 0 : *lcn + vcn - base;
-            *count -= (ULONG)(vcn - base);
-
-            return TRUE;
-        }
-        else
-        {
-            base += *count;
-        }
-    }
-
-    return FALSE;
+    return TRUE;
 }
 
 
 static
 VOID
-NtfsDumpFileNameAttribute(PATTRIBUTE Attribute)
+NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-    PRESIDENT_ATTRIBUTE ResAttr;
     PFILENAME_ATTRIBUTE FileNameAttr;
 
     DbgPrint("  $FILE_NAME ");
 
-    ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-//    DbgPrint(" Length %lu  Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset);
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-    FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
+    FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
     DbgPrint(" '%.*S' ", FileNameAttr->NameLength, FileNameAttr->Name);
 }
 
 
 static
 VOID
-NtfsDumpVolumeNameAttribute(PATTRIBUTE Attribute)
+NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-    PRESIDENT_ATTRIBUTE ResAttr;
     PWCHAR VolumeName;
 
     DbgPrint("  $VOLUME_NAME ");
 
-    ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-//    DbgPrint(" Length %lu  Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset);
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-    VolumeName = (PWCHAR)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
-    DbgPrint(" '%.*S' ", ResAttr->ValueLength / sizeof(WCHAR), VolumeName);
+    VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+    DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
 }
 
 
 static
 VOID
-NtfsDumpVolumeInformationAttribute(PATTRIBUTE Attribute)
+NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-    PRESIDENT_ATTRIBUTE ResAttr;
     PVOLINFO_ATTRIBUTE VolInfoAttr;
 
     DbgPrint("  $VOLUME_INFORMATION ");
 
-    ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-//    DbgPrint(" Length %lu  Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset);
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-    VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
+    VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
     DbgPrint(" NTFS Version %u.%u  Flags 0x%04hx ",
              VolInfoAttr->MajorVersion,
              VolInfoAttr->MinorVersion,
@@ -163,13 +142,11 @@ NtfsDumpVolumeInformationAttribute(PATTRIBUTE Attribute)
 
 static
 VOID
-NtfsDumpIndexRootAttribute(PATTRIBUTE Attribute)
+NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-    PRESIDENT_ATTRIBUTE ResAttr;
     PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
 
-    ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-    IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
+    IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
 
     if (IndexRootAttr->AttributeType == AttributeFileName)
         ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
@@ -190,15 +167,14 @@ NtfsDumpIndexRootAttribute(PATTRIBUTE Attribute)
 
 static
 VOID
-NtfsDumpAttribute (PATTRIBUTE Attribute)
+NtfsDumpAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-    PNONRESIDENT_ATTRIBUTE NresAttr;
     UNICODE_STRING Name;
 
     ULONGLONG lcn = 0;
     ULONGLONG runcount = 0;
 
-    switch (Attribute->AttributeType)
+    switch (Attribute->Type)
     {
         case AttributeFileName:
             NtfsDumpFileNameAttribute(Attribute);
@@ -267,7 +243,7 @@ NtfsDumpAttribute (PATTRIBUTE Attribute)
 
         default:
             DbgPrint("  Attribute %lx ",
-                     Attribute->AttributeType);
+                     Attribute->Type);
             break;
     }
 
@@ -281,16 +257,14 @@ NtfsDumpAttribute (PATTRIBUTE Attribute)
     }
 
     DbgPrint("(%s)\n",
-             Attribute->Nonresident ? "non-resident" : "resident");
+             Attribute->IsNonResident ? "non-resident" : "resident");
 
-    if (Attribute->Nonresident)
+    if (Attribute->IsNonResident)
     {
-        NresAttr = (PNONRESIDENT_ATTRIBUTE)Attribute;
-
-        FindRun(NresAttr,0,&lcn, &runcount);
+        FindRun(Attribute,0,&lcn, &runcount);
 
         DbgPrint("  AllocatedSize %I64u  DataSize %I64u\n",
-                 NresAttr->AllocatedSize, NresAttr->DataSize);
+                 Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize);
         DbgPrint("  logical clusters: %I64u - %I64u\n",
                  lcn, lcn + runcount - 1);
     }
@@ -300,15 +274,15 @@ NtfsDumpAttribute (PATTRIBUTE Attribute)
 VOID
 NtfsDumpFileAttributes(PFILE_RECORD_HEADER FileRecord)
 {
-    PATTRIBUTE Attribute;
+    PNTFS_ATTR_RECORD Attribute;
 
-    Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
-    while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
-           Attribute->AttributeType != (ATTRIBUTE_TYPE)-1)
+    Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
+    while (Attribute < (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
+           Attribute->Type != (ATTRIBUTE_TYPE)-1)
     {
         NtfsDumpAttribute(Attribute);
 
-        Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length);
+        Attribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Attribute + Attribute->Length);
     }
 }
 
index 6110741..c822709 100644 (file)
@@ -34,6 +34,8 @@
 #define NDEBUG
 #include <debug.h>
 
+UNICODE_STRING EmptyName = RTL_CONSTANT_STRING(L"");
+
 /* FUNCTIONS ****************************************************************/
 
 /*
@@ -173,14 +175,14 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
                   PDEVICE_EXTENSION DeviceExt)
 {
     DISK_GEOMETRY DiskGeometry;
-    PFILE_RECORD_HEADER MftRecord;
     PFILE_RECORD_HEADER VolumeRecord;
     PVOLINFO_ATTRIBUTE VolumeInfo;
     PBOOT_SECTOR BootSector;
-    PATTRIBUTE Attribute;
     ULONG Size;
     PNTFS_INFO NtfsInfo = &DeviceExt->NtfsInfo;
     NTSTATUS Status;
+    PNTFS_ATTR_CONTEXT AttrCtxt;
+    PNTFS_ATTR_RECORD Attribute;
     PNTFS_FCB VolumeFcb;
     PWSTR VolumeNameU;
 
@@ -251,10 +253,10 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
 
     ExFreePool(BootSector);
 
-    MftRecord = ExAllocatePoolWithTag(NonPagedPool,
-                                      NtfsInfo->BytesPerFileRecord,
-                                      TAG_NTFS);
-    if (MftRecord == NULL)
+    DeviceExt->MasterFileTable = ExAllocatePoolWithTag(NonPagedPool,
+                                                       NtfsInfo->BytesPerFileRecord,
+                                                       TAG_NTFS);
+    if (DeviceExt->MasterFileTable == NULL)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
     }
@@ -263,11 +265,20 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
                              NtfsInfo->MftStart.u.LowPart * NtfsInfo->SectorsPerCluster,
                              NtfsInfo->BytesPerFileRecord / NtfsInfo->BytesPerSector,
                              NtfsInfo->BytesPerSector,
-                             (PVOID)MftRecord,
+                             (PVOID)DeviceExt->MasterFileTable,
                              TRUE);
     if (!NT_SUCCESS(Status))
     {
-        ExFreePool(MftRecord);
+        DPRINT1("Failed reading MFT.\n");
+        ExFreePool(DeviceExt->MasterFileTable);
+        return Status;
+    }
+
+    Status = FindAttribute(DeviceExt, DeviceExt->MasterFileTable, AttributeData, &EmptyName, &DeviceExt->MFTContext);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("Can't find data attribute for Master File Table.\n");
+        ExFreePool(DeviceExt->MasterFileTable);
         return Status;
     }
 
@@ -276,40 +287,41 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
                                          TAG_NTFS);
     if (VolumeRecord == NULL)
     {
-        ExFreePool(MftRecord);
+        DPRINT1("Allocation failed for volume record\n");
+        ExFreePool(DeviceExt->MasterFileTable);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
     /* Read Volume File (MFT index 3) */
     DeviceExt->StorageDevice = DeviceObject;
     Status = ReadFileRecord(DeviceExt,
-                            3,
-                            VolumeRecord,
-                            MftRecord);
+                            NTFS_FILE_VOLUME,
+                            VolumeRecord);
     if (!NT_SUCCESS(Status))
     {
+        DPRINT1("Failed reading volume file\n");
         ExFreePool(VolumeRecord);
-        ExFreePool(MftRecord);
+        ExFreePool(DeviceExt->MasterFileTable);
         return Status;
     }
 
     /* Enumerate attributes */
-    NtfsDumpFileAttributes (MftRecord);
+    NtfsDumpFileAttributes(DeviceExt->MasterFileTable);
 
     /* Enumerate attributes */
-    NtfsDumpFileAttributes (VolumeRecord);
+    NtfsDumpFileAttributes(VolumeRecord);
 
     /* Get volume name */
-    Attribute = FindAttribute (VolumeRecord, AttributeVolumeName, NULL);
-    DPRINT("Attribute %p\n", Attribute);
+    Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeName, &EmptyName, &AttrCtxt);
 
-    if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0)
+    if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
     {
-        DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
+        Attribute = &AttrCtxt->Record;
+        DPRINT("Data length %lu\n", AttributeDataLength(Attribute));
         NtfsInfo->VolumeLabelLength =
-           min (((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
+           min (Attribute->Resident.ValueLength, MAXIMUM_VOLUME_LABEL_LENGTH);
         RtlCopyMemory(NtfsInfo->VolumeLabel,
-                      (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset),
+                      (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset),
                       NtfsInfo->VolumeLabelLength);
         VolumeNameU = NtfsInfo->VolumeLabel;
     }
@@ -322,8 +334,9 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
     VolumeFcb = NtfsCreateFCB(VolumeNameU, DeviceExt);
     if (VolumeFcb == NULL)
     {
+        DPRINT1("Failed allocating volume FCB\n");
         ExFreePool(VolumeRecord);
-        ExFreePool(MftRecord);
+        ExFreePool(DeviceExt->MasterFileTable);
         return STATUS_INSUFFICIENT_RESOURCES;
     }
 
@@ -334,20 +347,19 @@ NtfsGetVolumeData(PDEVICE_OBJECT DeviceObject,
     DeviceExt->VolumeFcb = VolumeFcb;
 
     /* Get volume information */
-    Attribute = FindAttribute (VolumeRecord, AttributeVolumeInformation, NULL);
-    DPRINT("Attribute %p\n", Attribute);
+    Status = FindAttribute(DeviceExt, VolumeRecord, AttributeVolumeInformation, &EmptyName, &AttrCtxt);
 
-    if (Attribute != NULL && ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength != 0)
+    if (NT_SUCCESS(Status) && AttrCtxt->Record.Resident.ValueLength != 0)
     {
+        Attribute = &AttrCtxt->Record;
         DPRINT("Data length %lu\n", AttributeDataLength (Attribute));
-        VolumeInfo = (PVOID)((ULONG_PTR)Attribute + ((PRESIDENT_ATTRIBUTE)Attribute)->ValueOffset);
+        VolumeInfo = (PVOID)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
 
         NtfsInfo->MajorVersion = VolumeInfo->MajorVersion;
         NtfsInfo->MinorVersion = VolumeInfo->MinorVersion;
         NtfsInfo->Flags = VolumeInfo->Flags;
     }
 
-    ExFreePool(MftRecord);
     ExFreePool(VolumeRecord);
 
     return Status;
index d40d919..7e99ab2 100644 (file)
 #define NDEBUG
 #include <debug.h>
 
+UNICODE_STRING IndexOfFileNames = RTL_CONSTANT_STRING(L"$I30");
+
 /* FUNCTIONS ****************************************************************/
 
-NTSTATUS
-NtfsOpenMft(PDEVICE_EXTENSION Vcb)
+PNTFS_ATTR_CONTEXT
+PrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
 {
-//    PVOID Bitmap;
-    PFILE_RECORD_HEADER MftRecord;
-    PFILE_RECORD_HEADER FileRecord;
-//    PATTRIBUTE Attribute;
-//    PATTRIBUTE AttrData;
-//    PRESIDENT_ATTRIBUTE ResAttr;
-
-    NTSTATUS Status;
-    ULONG BytesPerFileRecord;
-    ULONG n;
-    ULONG i;
+    PNTFS_ATTR_CONTEXT Context;
 
-    DPRINT1("NtfsOpenMft() called\n");
-
-    BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord;
-
-    MftRecord = ExAllocatePoolWithTag(NonPagedPool,
-                                      BytesPerFileRecord,
-                                      TAG_NTFS);
-    if (MftRecord == NULL)
+    Context = ExAllocatePoolWithTag(NonPagedPool,
+                                    FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length,
+                                    TAG_NTFS);
+    RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
+    if (AttrRecord->IsNonResident)
     {
-        return STATUS_INSUFFICIENT_RESOURCES;
+        LONGLONG DataRunOffset;
+        ULONGLONG DataRunLength;
+
+        Context->CacheRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
+        Context->CacheRunOffset = 0;
+        Context->CacheRun = DecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
+        Context->CacheRunLength = DataRunLength;
+        if (DataRunOffset != -1)
+        {
+            /* Normal run. */
+            Context->CacheRunStartLCN =
+            Context->CacheRunLastLCN = DataRunOffset;
+        }
+        else
+        {
+            /* Sparse run. */
+            Context->CacheRunStartLCN = -1;
+            Context->CacheRunLastLCN = 0;
+        }
+        Context->CacheRunCurrentOffset = 0;
     }
 
-    Status = NtfsReadSectors(Vcb->StorageDevice,
-                             Vcb->NtfsInfo.MftStart.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
-                             BytesPerFileRecord / Vcb->NtfsInfo.BytesPerSector,
-                             Vcb->NtfsInfo.BytesPerSector,
-                             (PVOID)MftRecord,
-                             FALSE);
-    if (!NT_SUCCESS(Status))
-    {
-        ExFreePool(MftRecord);
-        return Status;
-    }
+    return Context;
+}
 
-    FixupUpdateSequenceArray(MftRecord);
 
-//    Attribute = FindAttribute(MftRecord, AttributeBitmap, 0);
+VOID
+ReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
+{
+    ExFreePoolWithTag(Context, TAG_NTFS);
+}
 
-    /* Get number of file records*/
-    n = AttributeDataLength(FindAttribute(MftRecord, AttributeData, 0)) / BytesPerFileRecord;
 
-    FileRecord = ExAllocatePoolWithTag(NonPagedPool,
-                                       BytesPerFileRecord,
-                                       TAG_NTFS);
-    if (FileRecord == NULL)
-    {
-        ExFreePool(MftRecord);
-        return STATUS_INSUFFICIENT_RESOURCES;
-    }
+PNTFS_ATTR_CONTEXT
+FindAttributeHelper(PDEVICE_EXTENSION Vcb,
+                    PNTFS_ATTR_RECORD AttrRecord,
+                    PNTFS_ATTR_RECORD AttrRecordEnd,
+                    ULONG Type,
+                    const WCHAR *Name,
+                    ULONG NameLength)
+{
+    DPRINT("FindAttributeHelper(%p, %p, %p, 0x%x, %s, %u)\n", Vcb, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
 
-    /* Enumerate MFT Records */
-    DPRINT("Enumerate MFT records\n");
-    for (i = 0; i < n; i++)
+    while (AttrRecord < AttrRecordEnd)
     {
-        ReadFileRecord(Vcb,
-                       i,
-                       FileRecord,
-                       MftRecord);
+        DPRINT("AttrRecord->Type = 0x%x\n", AttrRecord->Type);
+
+        if (AttrRecord->Type == AttributeEnd)
+            break;
+
+        if (AttrRecord->Type == AttributeAttributeList)
+        {
+            PNTFS_ATTR_CONTEXT Context;
+            PNTFS_ATTR_CONTEXT ListContext;
+            PVOID ListBuffer;
+            ULONGLONG ListSize;
+            PNTFS_ATTR_RECORD ListAttrRecord;
+            PNTFS_ATTR_RECORD ListAttrRecordEnd;
+
+            // Do not handle non-resident yet
+            ASSERT(!(AttrRecord->IsNonResident & 1));
+
+            ListContext = PrepareAttributeContext(AttrRecord);
+
+            ListSize = AttributeDataLength(&ListContext->Record);
+            if(ListSize <= 0xFFFFFFFF)
+                ListBuffer = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
+            else
+                ListBuffer = NULL;
+
+            if(!ListBuffer)
+            {
+                DPRINT("Failed to allocate memory: %x\n", (ULONG)ListSize);
+                continue;
+            }
+
+            ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
+            ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
+
+            if (ReadAttribute(Vcb, ListContext, 0, ListBuffer, (ULONG)ListSize) == ListSize)
+            {
+                Context = FindAttributeHelper(Vcb, ListAttrRecord, ListAttrRecordEnd,
+                                              Type, Name, NameLength);
+
+                ReleaseAttributeContext(ListContext);
+                ExFreePoolWithTag(ListBuffer, TAG_NTFS);
+
+                if (Context != NULL)
+                {
+                    DPRINT("Found context = %p\n", Context);
+                    return Context;
+                }
+            }
+        }
 
-        if (FileRecord->Ntfs.Type == NRH_FILE_TYPE &&
-            (FileRecord->Flags & FRH_IN_USE))
+        if (AttrRecord->Type == Type)
         {
-            DPRINT("\nFile  %lu\n\n", i);
+            DPRINT("%d, %d\n", AttrRecord->NameLength, NameLength);
+            if (AttrRecord->NameLength == NameLength)
+            {
+                PWCHAR AttrName;
+
+                AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
+                DPRINT("%s, %s\n", AttrName, Name);
+                if (RtlCompareMemory(AttrName, Name, NameLength << 1) == (NameLength << 1))
+                {
+                    /* Found it, fill up the context and return. */
+                    DPRINT("Found context\n");
+                    return PrepareAttributeContext(AttrRecord);
+                }
+            }
+        }
 
-            /* Enumerate attributtes */
-            NtfsDumpFileAttributes (FileRecord);
-            DbgPrint("\n\n");
+        if (AttrRecord->Length == 0)
+        {
+            DPRINT("Null length attribute record\n");
+            return NULL;
         }
+        AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
     }
 
-    ExFreePool(FileRecord);
-    ExFreePool(MftRecord);
-
-    return Status;
+    DPRINT("Ended\n");
+    return NULL;
 }
 
 
-PATTRIBUTE
-FindAttribute(PFILE_RECORD_HEADER FileRecord,
-              ATTRIBUTE_TYPE Type,
-              PWSTR name)
+NTSTATUS
+FindAttribute(PDEVICE_EXTENSION Vcb,
+              PFILE_RECORD_HEADER MftRecord,
+              ULONG Type,
+              PUNICODE_STRING Name,
+              PNTFS_ATTR_CONTEXT * AttrCtx)
 {
-    PATTRIBUTE Attribute;
+    PNTFS_ATTR_RECORD AttrRecord;
+    PNTFS_ATTR_RECORD AttrRecordEnd;
 
-    UNREFERENCED_PARAMETER(name);
+    DPRINT("NtfsFindAttribute(%p, %p, %u, %s)\n", Vcb, MftRecord, Type, Name);
 
-    Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
-    while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
-           Attribute->AttributeType != (ATTRIBUTE_TYPE)-1)
-    {
-        if (Attribute->AttributeType == Type)
-        {
-            return Attribute;
-        }
+    AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributeOffset);
+    AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Vcb->NtfsInfo.BytesPerFileRecord);
 
-        Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length);
+    *AttrCtx = FindAttributeHelper(Vcb, AttrRecord, AttrRecordEnd, Type, Name->Buffer, Name->Length);
+    if (*AttrCtx == NULL)
+    {
+        return STATUS_OBJECT_NAME_NOT_FOUND;
     }
 
-    return NULL;
+    return STATUS_SUCCESS;
 }
 
 
 ULONG
-AttributeAllocatedLength(PATTRIBUTE Attribute)
+AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord)
 {
-    if (Attribute->Nonresident)
-    {
-        return ((PNONRESIDENT_ATTRIBUTE)Attribute)->AllocatedSize;
-    }
+    if (AttrRecord->IsNonResident)
+        return AttrRecord->NonResident.AllocatedSize;
+    else
+        return AttrRecord->Resident.ValueLength;
+}
+
 
-    return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength;
+ULONGLONG
+AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord)
+{
+    if (AttrRecord->IsNonResident)
+        return AttrRecord->NonResident.DataSize;
+    else
+        return AttrRecord->Resident.ValueLength;
 }
 
 
 ULONG
-AttributeDataLength(PATTRIBUTE Attribute)
+ReadAttribute(PDEVICE_EXTENSION Vcb,
+              PNTFS_ATTR_CONTEXT Context,
+              ULONGLONG Offset,
+              PCHAR Buffer,
+              ULONG Length)
 {
-    if (Attribute->Nonresident)
+    ULONGLONG LastLCN;
+    PUCHAR DataRun;
+    LONGLONG DataRunOffset;
+    ULONGLONG DataRunLength;
+    LONGLONG DataRunStartLCN;
+    ULONGLONG CurrentOffset;
+    ULONG ReadLength;
+    ULONG AlreadyRead;
+    NTSTATUS Status;
+
+    if (!Context->Record.IsNonResident)
     {
-        return ((PNONRESIDENT_ATTRIBUTE)Attribute)->DataSize;
+        if (Offset > Context->Record.Resident.ValueLength)
+            return 0;
+        if (Offset + Length > Context->Record.Resident.ValueLength)
+            Length = (ULONG)(Context->Record.Resident.ValueLength - Offset);
+        RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
+        return Length;
     }
 
-    return ((PRESIDENT_ATTRIBUTE)Attribute)->ValueLength;
-}
+    /*
+     * Non-resident attribute
+     */
 
+    /*
+     * I. Find the corresponding start data run.
+     */
 
-VOID
-ReadAttribute(PATTRIBUTE attr,
-              PVOID buffer,
-              PDEVICE_EXTENSION Vcb,
-              PDEVICE_OBJECT DeviceObject)
-{
-    PNONRESIDENT_ATTRIBUTE NresAttr = (PNONRESIDENT_ATTRIBUTE)attr;
+    AlreadyRead = 0;
 
-    UNREFERENCED_PARAMETER(DeviceObject);
-
-    if (attr->Nonresident == FALSE)
+    // FIXME: Cache seems to be non-working. Disable it for now
+    //if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
+    if (0)
+    {
+        DataRun = Context->CacheRun;
+        LastLCN = Context->CacheRunLastLCN;
+        DataRunStartLCN = Context->CacheRunStartLCN;
+        DataRunLength = Context->CacheRunLength;
+        CurrentOffset = Context->CacheRunCurrentOffset;
+    }
+    else
     {
-        memcpy(buffer,
-               (PVOID)((ULONG_PTR)attr + ((PRESIDENT_ATTRIBUTE)attr)->ValueOffset),
-               ((PRESIDENT_ATTRIBUTE)attr)->ValueLength);
+        LastLCN = 0;
+        DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
+        CurrentOffset = 0;
+
+        while (1)
+        {
+            DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
+            if (DataRunOffset != -1)
+            {
+                /* Normal data run. */
+                DataRunStartLCN = LastLCN + DataRunOffset;
+                LastLCN = DataRunStartLCN;
+            }
+            else
+            {
+                /* Sparse data run. */
+                DataRunStartLCN = -1;
+            }
+
+            if (Offset >= CurrentOffset &&
+                Offset < CurrentOffset + (DataRunLength * Vcb->NtfsInfo.BytesPerCluster))
+            {
+                break;
+            }
+
+            if (*DataRun == 0)
+            {
+                return AlreadyRead;
+            }
+
+            CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
+        }
     }
 
-    ReadExternalAttribute(Vcb,
-                          NresAttr,
-                          0,
-                          (ULONG)(NresAttr->LastVcn) + 1,
-                          buffer);
+    /*
+     * II. Go through the run list and read the data
+     */
+
+    ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset), Length);
+    if (DataRunStartLCN == -1)
+        RtlZeroMemory(Buffer, ReadLength);
+    Status = NtfsReadDisk(Vcb->StorageDevice,
+                          DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster + Offset - CurrentOffset,
+                          ReadLength,
+                          (PVOID)Buffer,
+                          FALSE);
+    if (NT_SUCCESS(Status))
+    {
+        Length -= ReadLength;
+        Buffer += ReadLength;
+        AlreadyRead += ReadLength;
+
+        if (ReadLength == DataRunLength * Vcb->NtfsInfo.BytesPerCluster - (Offset - CurrentOffset))
+        {
+            CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
+            DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
+            if (DataRunLength != (ULONGLONG)-1)
+            {
+                DataRunStartLCN = LastLCN + DataRunOffset;
+                LastLCN = DataRunStartLCN;
+            }
+            else
+                DataRunStartLCN = -1;
+
+            if (*DataRun == 0)
+                return AlreadyRead;
+        }
+
+        while (Length > 0)
+        {
+            ReadLength = (ULONG)min(DataRunLength * Vcb->NtfsInfo.BytesPerCluster, Length);
+            if (DataRunStartLCN == -1)
+                RtlZeroMemory(Buffer, ReadLength);
+            else
+            {
+                Status = NtfsReadDisk(Vcb->StorageDevice,
+                                      DataRunStartLCN * Vcb->NtfsInfo.BytesPerCluster,
+                                      ReadLength,
+                                      (PVOID)Buffer,
+                                      FALSE);
+                if (!NT_SUCCESS(Status))
+                    break;
+            }
+
+            Length -= ReadLength;
+            Buffer += ReadLength;
+            AlreadyRead += ReadLength;
+
+            /* We finished this request, but there still data in this data run. */
+            if (Length == 0 && ReadLength != DataRunLength * Vcb->NtfsInfo.BytesPerCluster)
+                break;
+
+            /*
+             * Go to next run in the list.
+             */
+
+            if (*DataRun == 0)
+                break;
+            CurrentOffset += DataRunLength * Vcb->NtfsInfo.BytesPerCluster;
+            DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
+            if (DataRunOffset != -1)
+            {
+                /* Normal data run. */
+                DataRunStartLCN = LastLCN + DataRunOffset;
+                LastLCN = DataRunStartLCN;
+            }
+            else
+            {
+                /* Sparse data run. */
+                DataRunStartLCN = -1;
+            }
+        } /* while */
+
+    } /* if Disk */
+
+    Context->CacheRun = DataRun;
+    Context->CacheRunOffset = Offset + AlreadyRead;
+    Context->CacheRunStartLCN = DataRunStartLCN;
+    Context->CacheRunLength = DataRunLength;
+    Context->CacheRunLastLCN = LastLCN;
+    Context->CacheRunCurrentOffset = CurrentOffset;
+
+    return AlreadyRead;
 }
 
 
 NTSTATUS
 ReadFileRecord(PDEVICE_EXTENSION Vcb,
-               ULONG index,
-               PFILE_RECORD_HEADER file,
-               PFILE_RECORD_HEADER Mft)
+               ULONGLONG index,
+               PFILE_RECORD_HEADER file)
 {
-    PVOID p;
-    ULONG BytesPerFileRecord = Vcb->NtfsInfo.BytesPerFileRecord;
-    ULONG clusters = max(BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster, 1);
-    ULONGLONG vcn = index * BytesPerFileRecord / Vcb->NtfsInfo.BytesPerCluster;
-    LONG m = (Vcb->NtfsInfo.BytesPerCluster / BytesPerFileRecord) - 1;
-    ULONG n = m > 0 ? (index & m) : 0;
+    ULONGLONG BytesRead;
 
-    p = ExAllocatePoolWithTag(NonPagedPool,
-                              clusters * Vcb->NtfsInfo.BytesPerCluster,
-                              TAG_NTFS);
+    BytesRead = ReadAttribute(Vcb, Vcb->MFTContext, index * Vcb->NtfsInfo.BytesPerFileRecord, (PCHAR)file, Vcb->NtfsInfo.BytesPerFileRecord);
+    if (BytesRead != Vcb->NtfsInfo.BytesPerFileRecord)
+    {
+        DPRINT1("ReadFileRecord failed: %u read, %u expected\n", BytesRead, Vcb->NtfsInfo.BytesPerFileRecord);
+        return STATUS_PARTIAL_COPY;
+    }
 
-    ReadVCN (Vcb, Mft, AttributeData, vcn, clusters, p);
+    /* Apply update sequence array fixups. */
+    return FixupUpdateSequenceArray(Vcb, &file->Ntfs);
+}
 
-    memcpy(file,
-           (PVOID)((ULONG_PTR)p + n * BytesPerFileRecord),
-           BytesPerFileRecord);
 
-    ExFreePool(p);
+NTSTATUS 
+FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
+                         PNTFS_RECORD_HEADER Record)
+{
+    USHORT *USA;
+    USHORT USANumber;
+    USHORT USACount;
+    USHORT *Block;
+
+    USA = (USHORT*)((PCHAR)Record + Record->UsaOffset);
+    USANumber = *(USA++);
+    USACount = Record->UsaCount - 1; /* Exclude the USA Number. */
+    Block = (USHORT*)((PCHAR)Record + Vcb->NtfsInfo.BytesPerSector - 2);
 
-    FixupUpdateSequenceArray(file);
+    while (USACount)
+    {
+        if (*Block != USANumber)
+        {
+            DPRINT1("Mismatch with USA: %u read, %u expected\n" , *Block, USANumber);
+            return STATUS_UNSUCCESSFUL;
+        }
+        *Block = *(USA++);
+        Block = (USHORT*)((PCHAR)Block + Vcb->NtfsInfo.BytesPerSector);
+        USACount--;
+    }
 
     return STATUS_SUCCESS;
 }
 
 
-VOID
-ReadExternalAttribute(PDEVICE_EXTENSION Vcb,
-                      PNONRESIDENT_ATTRIBUTE NresAttr,
-                      ULONGLONG vcn,
-                      ULONG count,
-                      PVOID buffer)
+NTSTATUS
+ReadLCN(PDEVICE_EXTENSION Vcb,
+        ULONGLONG lcn,
+        ULONG count,
+        PVOID buffer)
 {
-    ULONGLONG lcn;
-    ULONGLONG runcount;
-    ULONG readcount;
-    ULONG left;
-    ULONG n;
-
-    PUCHAR bytes = (PUCHAR)buffer;
+    LARGE_INTEGER DiskSector;
 
-    for (left = count; left > 0; left -= readcount)
-    {
-        FindRun(NresAttr, vcn, &lcn, &runcount);
+    DiskSector.QuadPart = lcn;
 
-//        readcount = (ULONG)(__min(runcount, left));
-        readcount = (ULONG)min(runcount, left);
+    return NtfsReadSectors(Vcb->StorageDevice,
+                           DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
+                           count * Vcb->NtfsInfo.SectorsPerCluster,
+                           Vcb->NtfsInfo.BytesPerSector,
+                           buffer,
+                           FALSE);
+}
 
 
-        n = readcount * Vcb->NtfsInfo.BytesPerCluster;
+BOOLEAN
+CompareFileName(PUNICODE_STRING FileName,
+                PINDEX_ENTRY_ATTRIBUTE IndexEntry)
+{
+    UNICODE_STRING EntryName;
 
-        if (lcn == 0)
-            memset(bytes, 0, n);
-        else
-            ReadLCN(Vcb, lcn, readcount, bytes);
+    EntryName.Buffer = IndexEntry->FileName.Name;
+    EntryName.Length = 
+    EntryName.MaximumLength = IndexEntry->FileName.NameLength;
 
-        vcn += readcount;
-        bytes += n;
-    }
+    return (RtlCompareUnicodeString(FileName, &EntryName, !!(IndexEntry->FileName.NameType != NTFS_FILE_NAME_POSIX)) == TRUE);
 }
 
 
-VOID
-ReadVCN(PDEVICE_EXTENSION Vcb,
-        PFILE_RECORD_HEADER file,
-        ATTRIBUTE_TYPE type,
-        ULONGLONG vcn,
-        ULONG count,
-        PVOID buffer)
+NTSTATUS
+NtfsFindMftRecord(PDEVICE_EXTENSION Vcb, ULONGLONG MFTIndex, PUNICODE_STRING FileName, ULONGLONG *OutMFTIndex)
 {
-    PNONRESIDENT_ATTRIBUTE NresAttr;
-    PATTRIBUTE attr;
+    PFILE_RECORD_HEADER MftRecord;
+    //ULONG Magic;
+    PNTFS_ATTR_CONTEXT IndexRootCtx;
+    PNTFS_ATTR_CONTEXT IndexBitmapCtx;
+    PNTFS_ATTR_CONTEXT IndexAllocationCtx;
+    PINDEX_ROOT_ATTRIBUTE IndexRoot;
+    ULONGLONG BitmapDataSize;
+    ULONGLONG IndexAllocationSize;
+    PCHAR BitmapData;
+    PCHAR IndexRecord;
+    PINDEX_ENTRY_ATTRIBUTE IndexEntry, IndexEntryEnd;
+    ULONG RecordOffset;
+    ULONG IndexBlockSize;
+    NTSTATUS Status;
 
-    attr = FindAttribute(file, type, 0);
+    MftRecord = ExAllocatePoolWithTag(NonPagedPool,
+                                      Vcb->NtfsInfo.BytesPerFileRecord,
+                                      TAG_NTFS);
+    if (MftRecord == NULL)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    if (ReadFileRecord(Vcb, MFTIndex, MftRecord))
+    {
+        //Magic = MftRecord->Magic;
+
+        Status = FindAttribute(Vcb, MftRecord, AttributeIndexRoot, &IndexOfFileNames, &IndexRootCtx);
+        if (!NT_SUCCESS(Status))
+        {
+            ExFreePoolWithTag(MftRecord, TAG_NTFS);
+            return Status;
+        }
+
+        IndexRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerIndexRecord, TAG_NTFS);
+        if (IndexRecord == NULL)
+        {
+            ExFreePoolWithTag(MftRecord, TAG_NTFS);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        ReadAttribute(Vcb, IndexRootCtx, 0, IndexRecord, Vcb->NtfsInfo.BytesPerIndexRecord);
+        IndexRoot = (PINDEX_ROOT_ATTRIBUTE)IndexRecord;
+        IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)&IndexRoot->Header + IndexRoot->Header.FirstEntryOffset);
+        /* Index root is always resident. */
+        IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
+        ReleaseAttributeContext(IndexRootCtx);
 
-    NresAttr = (PNONRESIDENT_ATTRIBUTE) attr;
+        DPRINT("IndexRecordSize: %x IndexBlockSize: %x\n", Vcb->NtfsInfo.BytesPerIndexRecord, IndexRoot->SizeOfEntry);
+
+        while (IndexEntry < IndexEntryEnd &&
+               !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
+        {
+            if (CompareFileName(FileName, IndexEntry))
+            {
+                *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
+                ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+                ExFreePoolWithTag(MftRecord, TAG_NTFS);
+                return STATUS_SUCCESS;
+            }
+        IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
+        }
+
+        if (IndexRoot->Header.Flags & INDEX_ROOT_LARGE)
+        {
+            DPRINT("Large Index!\n");
+
+            IndexBlockSize = IndexRoot->SizeOfEntry;
+
+            Status = FindAttribute(Vcb, MftRecord, AttributeBitmap, &IndexOfFileNames, &IndexBitmapCtx);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT("Corrupted filesystem!\n");
+                ExFreePoolWithTag(MftRecord, TAG_NTFS);
+                return Status;
+            }
+            BitmapDataSize = AttributeDataLength(&IndexBitmapCtx->Record);
+            DPRINT("BitmapDataSize: %x\n", (ULONG)BitmapDataSize);
+            if(BitmapDataSize <= 0xFFFFFFFF)
+                BitmapData = ExAllocatePoolWithTag(NonPagedPool, (ULONG)BitmapDataSize, TAG_NTFS);
+            else
+                BitmapData = NULL;
+
+            if (BitmapData == NULL)
+            {
+                ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+                ExFreePoolWithTag(MftRecord, TAG_NTFS);
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+            ReadAttribute(Vcb, IndexBitmapCtx, 0, BitmapData, (ULONG)BitmapDataSize);
+            ReleaseAttributeContext(IndexBitmapCtx);
+
+            Status = FindAttribute(Vcb, MftRecord, AttributeIndexAllocation, &IndexOfFileNames, &IndexAllocationCtx);
+            if (!NT_SUCCESS(Status))
+            {
+                DPRINT("Corrupted filesystem!\n");
+                ExFreePoolWithTag(BitmapData, TAG_NTFS);
+                ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+                ExFreePoolWithTag(MftRecord, TAG_NTFS);
+                return Status;
+            }
+            IndexAllocationSize = AttributeDataLength(&IndexAllocationCtx->Record);
+
+            RecordOffset = 0;
+
+            for (;;)
+            {
+                DPRINT("RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize);
+                for (; RecordOffset < IndexAllocationSize;)
+                {
+                    UCHAR Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
+                    ULONG Byte = (RecordOffset / IndexBlockSize) >> 3;
+                    if ((BitmapData[Byte] & Bit))
+                        break;
+                    RecordOffset += IndexBlockSize;
+                }
+
+                if (RecordOffset >= IndexAllocationSize)
+                {
+                    break;
+                }
+
+                ReadAttribute(Vcb, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
+
+                if (!FixupUpdateSequenceArray(Vcb, &((PFILE_RECORD_HEADER)IndexRecord)->Ntfs))
+                {
+                    break;
+                }
+
+                /* FIXME */
+                IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + 0x18 + *(USHORT *)(IndexRecord + 0x18));
+                IndexEntryEnd = (PINDEX_ENTRY_ATTRIBUTE)(IndexRecord + IndexBlockSize);
+
+                while (IndexEntry < IndexEntryEnd &&
+                       !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
+                {
+                    if (CompareFileName(FileName, IndexEntry))
+                    {
+                        DPRINT("File found\n");
+                        *OutMFTIndex = IndexEntry->Data.Directory.IndexedFile;
+                        ExFreePoolWithTag(BitmapData, TAG_NTFS);
+                        ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+                        ExFreePoolWithTag(MftRecord, TAG_NTFS);
+                        ReleaseAttributeContext(IndexAllocationCtx);
+                        return STATUS_SUCCESS;
+                    }
+                    IndexEntry = (PINDEX_ENTRY_ATTRIBUTE)((PCHAR)IndexEntry + IndexEntry->Length);
+                }
+
+                RecordOffset += IndexBlockSize;
+            }
+
+            ReleaseAttributeContext(IndexAllocationCtx);
+            ExFreePoolWithTag(BitmapData, TAG_NTFS);
+        }
 
-    if (NresAttr == 0 || (vcn < NresAttr->StartVcn ||vcn > NresAttr->LastVcn))
+        ExFreePoolWithTag(IndexRecord, TAG_NTFS);
+    }
+    else
     {
-//      PATTRIBUTE attrList = FindAttribute(file,AttributeAttributeList,0);
-        DbgPrint("Exeption \n");
-//      KeDebugCheck(0);
+        DPRINT("Can't read MFT record\n");
     }
+    ExFreePoolWithTag(MftRecord, TAG_NTFS);
 
-    ReadExternalAttribute(Vcb, NresAttr, vcn, count, buffer);
+    return STATUS_OBJECT_PATH_NOT_FOUND;
 }
 
-
-#if 0
-BOOL bitset(PUCHAR bitmap, ULONG i)
+NTSTATUS
+NtfsLookupFile(PDEVICE_EXTENSION Vcb,
+               PUNICODE_STRING PathName,
+               PFILE_RECORD_HEADER *FileRecord,
+               PNTFS_ATTR_CONTEXT *DataContext)
 {
-    return (bitmap[i>>3] & (1 << (i & 7))) !=0;
-}
-#endif
+    ULONGLONG CurrentMFTIndex;
+    UNICODE_STRING Current, Remaining;
+    NTSTATUS Status;
 
+    DPRINT1("NtfsLookupFile(%p, %wZ, %p)\n", Vcb, PathName, FileRecord);
 
-VOID
-FixupUpdateSequenceArray(PFILE_RECORD_HEADER file)
-{
-    PUSHORT usa = (PUSHORT)((ULONG_PTR)file + file->Ntfs.UsaOffset);
-    PUSHORT sector = (PUSHORT)file;
-    ULONG i;
+    CurrentMFTIndex = NTFS_FILE_ROOT;
+    FsRtlDissectName(*PathName, &Current, &Remaining);
 
-    for (i = 1; i < file->Ntfs.UsaCount; i++)
+    while (Current.Length != 0)
     {
-        sector[255] = usa[i];
-        sector += 256;
+        DPRINT1("Lookup: %wZ\n", &Current);
+
+        Status = NtfsFindMftRecord(Vcb, CurrentMFTIndex, &Current, &CurrentMFTIndex);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+
+        FsRtlDissectName(*PathName, &Current, &Remaining);
     }
-}
 
+    *FileRecord = ExAllocatePoolWithTag(NonPagedPool, Vcb->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
+    if (*FileRecord == NULL)
+    {
+        DPRINT("NtfsLookupFile: Can't allocate MFT record\n");
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-NTSTATUS
-ReadLCN(PDEVICE_EXTENSION Vcb,
-        ULONGLONG lcn,
-        ULONG count,
-        PVOID buffer)
-{
-    LARGE_INTEGER DiskSector;
+    Status = ReadFileRecord(Vcb, CurrentMFTIndex, *FileRecord);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT("NtfsLookupFile: Can't read MFT record\n");
+        return Status;
+    }
 
-    DiskSector.QuadPart = lcn;
+    Status = FindAttribute(Vcb, *FileRecord, AttributeData, PathName, DataContext);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT("NtfsLookupFile: Can't find data attribute\n");
+        return Status;
+    }
 
-    return NtfsReadSectors(Vcb->StorageDevice,
-                           DiskSector.u.LowPart * Vcb->NtfsInfo.SectorsPerCluster,
-                           count * Vcb->NtfsInfo.SectorsPerCluster,
-                           Vcb->NtfsInfo.BytesPerSector,
-                           buffer,
-                           FALSE);
+    return STATUS_SUCCESS;
 }
-
 /* EOF */
index 0da7175..abd3a45 100644 (file)
@@ -86,7 +86,6 @@ typedef struct
     ULONG Size;
 } NTFSIDENTIFIER, *PNTFSIDENTIFIER;
 
-
 typedef struct
 {
     NTFSIDENTIFIER Identifier;
@@ -101,6 +100,8 @@ typedef struct
     PDEVICE_OBJECT StorageDevice;
     PFILE_OBJECT StreamFileObject;
 
+    struct _NTFS_ATTR_CONTEXT* MFTContext;
+    struct _FILE_RECORD_HEADER* MasterFileTable;
     struct _FCB *VolumeFcb;
 
     NTFS_INFO NtfsInfo;
@@ -186,9 +187,23 @@ typedef enum
     AttributeEAInformation = 0xD0,
     AttributeEA = 0xE0,
     AttributePropertySet = 0xF0,
-    AttributeLoggedUtilityStream = 0x100
+    AttributeLoggedUtilityStream = 0x100,
+    AttributeEnd = 0xFFFFFFFF
 } ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;
 
+#define NTFS_FILE_MFT                0
+#define NTFS_FILE_MFTMIRR            1
+#define NTFS_FILE_LOGFILE            2
+#define NTFS_FILE_VOLUME            3
+#define NTFS_FILE_ATTRDEF            4
+#define NTFS_FILE_ROOT                5
+#define NTFS_FILE_BITMAP            6
+#define NTFS_FILE_BOOT                7
+#define NTFS_FILE_BADCLUS            8
+#define NTFS_FILE_QUOTA                9
+#define NTFS_FILE_UPCASE            10
+#define NTFS_FILE_EXTEND            11
+
 #define COLLATION_BINARY              0x00
 #define COLLATION_FILE_NAME           0x01
 #define COLLATION_UNICODE_STRING      0x02
@@ -200,6 +215,14 @@ typedef enum
 #define INDEX_ROOT_SMALL 0x0
 #define INDEX_ROOT_LARGE 0x1
 
+#define NTFS_INDEX_ENTRY_NODE            1
+#define NTFS_INDEX_ENTRY_END            2
+
+#define NTFS_FILE_NAME_POSIX            0
+#define NTFS_FILE_NAME_WIN32            1
+#define NTFS_FILE_NAME_DOS            2
+#define NTFS_FILE_NAME_WIN32_AND_DOS        3
+
 typedef struct
 {
     ULONG Type;             /* Magic number 'FILE' */
@@ -212,7 +235,7 @@ typedef struct
 #define NRH_FILE_TYPE  0x454C4946  /* 'FILE' */
 
 
-typedef struct
+typedef struct _FILE_RECORD_HEADER
 {
     NTFS_RECORD_HEADER Ntfs;
     USHORT SequenceNumber;        /* Sequence number */
@@ -236,39 +259,38 @@ typedef struct
 
 typedef struct
 {
-    ATTRIBUTE_TYPE AttributeType;
-    ULONG Length;
-    BOOLEAN Nonresident;
-    UCHAR NameLength;
-    USHORT NameOffset;
-    USHORT Flags;
-    USHORT AttributeNumber;
-} ATTRIBUTE, *PATTRIBUTE;
-
-typedef struct
-{
-    ATTRIBUTE Attribute;
-    ULONG ValueLength;
-    USHORT ValueOffset;
-    UCHAR Flags;
-//    UCHAR Padding0;
-} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;
-
-typedef struct
-{
-    ATTRIBUTE Attribute;
-    ULONGLONG StartVcn; // LowVcn
-    ULONGLONG LastVcn; // HighVcn
-    USHORT RunArrayOffset;
-    USHORT CompressionUnit;
-    ULONG Padding0;
-    UCHAR IndexedFlag;
-    ULONGLONG AllocatedSize;
-    ULONGLONG DataSize;
-    ULONGLONG InitializedSize;
-    ULONGLONG CompressedSize;
-} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;
-
+    ULONG        Type;
+    ULONG        Length;
+    UCHAR        IsNonResident;
+    UCHAR        NameLength;
+    USHORT        NameOffset;
+    USHORT        Flags;
+    USHORT        Instance;
+    union
+    {
+        // Resident attributes
+        struct
+        {
+            ULONG        ValueLength;
+            USHORT        ValueOffset;
+            UCHAR        Flags;
+            UCHAR        Reserved;
+        } Resident;
+        // Non-resident attributes
+        struct
+        {
+            ULONGLONG        LowestVCN;
+            ULONGLONG        HighestVCN;
+            USHORT        MappingPairsOffset;
+            USHORT        CompressionUnit;
+            UCHAR        Reserved[4];
+            LONGLONG        AllocatedSize;
+            LONGLONG        DataSize;
+            LONGLONG        InitializedSize;
+            LONGLONG        CompressedSize;
+        } NonResident;
+    };
+} NTFS_ATTR_RECORD, *PNTFS_ATTR_RECORD;
 
 typedef struct
 {
@@ -335,6 +357,28 @@ typedef struct
     INDEX_HEADER_ATTRIBUTE Header;
 } INDEX_ROOT_ATTRIBUTE, *PINDEX_ROOT_ATTRIBUTE;
 
+typedef struct
+{
+    union
+    {
+        struct
+        {
+            ULONGLONG    IndexedFile;
+        } Directory;
+        struct
+        {
+            USHORT    DataOffset;
+            USHORT    DataLength;
+            ULONG    Reserved;
+        } ViewIndex;
+    } Data;
+    USHORT            Length;
+    USHORT            KeyLength;
+    USHORT            Flags;
+    USHORT            Reserved;
+    FILENAME_ATTRIBUTE    FileName;
+} INDEX_ENTRY_ATTRIBUTE, *PINDEX_ENTRY_ATTRIBUTE;
+
 typedef struct
 {
     ULONGLONG Unknown1;
@@ -357,6 +401,16 @@ typedef struct
     NTSTATUS SavedExceptionCode;
 } NTFS_IRP_CONTEXT, *PNTFS_IRP_CONTEXT;
 
+typedef struct _NTFS_ATTR_CONTEXT
+{
+    PUCHAR            CacheRun;
+    ULONGLONG            CacheRunOffset;
+    LONGLONG            CacheRunStartLCN;
+    ULONGLONG            CacheRunLength;
+    LONGLONG            CacheRunLastLCN;
+    ULONGLONG            CacheRunCurrentOffset;
+    NTFS_ATTR_RECORD    Record;
+} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
 
 extern PNTFS_GLOBAL_DATA NtfsGlobalData;
 
@@ -369,15 +423,10 @@ extern PNTFS_GLOBAL_DATA NtfsGlobalData;
 //VOID
 //NtfsDumpAttribute(PATTRIBUTE Attribute);
 
-//LONGLONG RunLCN(PUCHAR run);
-
-//ULONG RunLength(PUCHAR run);
-
-BOOLEAN
-FindRun(PNONRESIDENT_ATTRIBUTE NresAttr,
-        ULONGLONG vcn,
-        PULONGLONG lcn,
-        PULONGLONG count);
+PUCHAR
+DecodeRun(PUCHAR DataRun,
+          LONGLONG *DataRunOffset,
+          ULONGLONG *DataRunLength);
 
 VOID
 NtfsDumpFileAttributes(PFILE_RECORD_HEADER FileRecord);
@@ -529,35 +578,30 @@ NtfsFsdFileSystemControl(PDEVICE_OBJECT DeviceObject,
 
 
 /* mft.c */
-NTSTATUS
-NtfsOpenMft(PDEVICE_EXTENSION Vcb);
-
-
-VOID
-ReadAttribute(PATTRIBUTE attr,
-              PVOID buffer,
-              PDEVICE_EXTENSION Vcb,
-              PDEVICE_OBJECT DeviceObject);
-
 ULONG
-AttributeDataLength(PATTRIBUTE attr);
+ReadAttribute(PDEVICE_EXTENSION Vcb,
+              PNTFS_ATTR_CONTEXT Context,
+              ULONGLONG Offset,
+              PCHAR Buffer,
+              ULONG Length);
+
+ULONGLONG
+AttributeDataLength(PNTFS_ATTR_RECORD AttrRecord);
 
 ULONG
-AttributeAllocatedLength(PATTRIBUTE Attribute);
+AttributeAllocatedLength(PNTFS_ATTR_RECORD AttrRecord);
 
 NTSTATUS
 ReadFileRecord(PDEVICE_EXTENSION Vcb,
-               ULONG index,
-               PFILE_RECORD_HEADER file,
-               PFILE_RECORD_HEADER Mft);
+               ULONGLONG index,
+               PFILE_RECORD_HEADER file);
 
-PATTRIBUTE
-FindAttribute(PFILE_RECORD_HEADER file,
-              ATTRIBUTE_TYPE type,
-              PWSTR name);
-
-ULONG
-AttributeLengthAllocated(PATTRIBUTE attr);
+NTSTATUS
+FindAttribute(PDEVICE_EXTENSION Vcb,
+              PFILE_RECORD_HEADER MftRecord,
+              ULONG Type,
+              PUNICODE_STRING Name,
+              PNTFS_ATTR_CONTEXT * AttrCtx);
 
 VOID
 ReadVCN(PDEVICE_EXTENSION Vcb,
@@ -567,15 +611,9 @@ ReadVCN(PDEVICE_EXTENSION Vcb,
         ULONG count,
         PVOID buffer);
 
-VOID
-FixupUpdateSequenceArray(PFILE_RECORD_HEADER file);
-
-VOID
-ReadExternalAttribute(PDEVICE_EXTENSION Vcb,
-                      PNONRESIDENT_ATTRIBUTE NresAttr,
-                      ULONGLONG vcn,
-                      ULONG count,
-                      PVOID buffer);
+NTSTATUS 
+FixupUpdateSequenceArray(PDEVICE_EXTENSION Vcb,
+                         PNTFS_RECORD_HEADER Record);
 
 NTSTATUS
 ReadLCN(PDEVICE_EXTENSION Vcb,
@@ -588,6 +626,11 @@ EnumerAttribute(PFILE_RECORD_HEADER file,
                 PDEVICE_EXTENSION Vcb,
                 PDEVICE_OBJECT DeviceObject);
 
+NTSTATUS
+NtfsLookupFile(PDEVICE_EXTENSION Vcb,
+               PUNICODE_STRING PathName,
+               PFILE_RECORD_HEADER *FileRecord,
+               PNTFS_ATTR_CONTEXT *DataContext);
 
 /* misc.c */