[NTFS]
[reactos.git] / drivers / filesystems / ntfs / attrib.c
index e334ba9..9608d1d 100644 (file)
  * PROJECT:          ReactOS kernel
  * FILE:             drivers/filesystem/ntfs/attrib.c
  * PURPOSE:          NTFS filesystem driver
- * PROGRAMMER:       Eric Kohl
- * Updated     by       Valentin Verkhovsky  2003/09/12
+ * PROGRAMMERS:      Eric Kohl
+ *                   Valentin Verkhovsky
+ *                   HervĂ© Poussineau (hpoussin@reactos.org)
+ *                   Pierre Schweitzer (pierre@reactos.org)
  */
 
 /* INCLUDES *****************************************************************/
 #define NDEBUG
 #include <debug.h>
 
-/* GLOBALS *****************************************************************/
-
-
 /* FUNCTIONS ****************************************************************/
 
+NTSTATUS
+AddRun(PNTFS_ATTR_CONTEXT AttrContext,
+       ULONGLONG NextAssignedCluster,
+       ULONG RunLength)
+{
+    UNIMPLEMENTED;
 
+    if (!AttrContext->Record.IsNonResident)
+        return STATUS_INVALID_PARAMETER;
 
-static ULONG
-RunLength(PUCHAR run)
-{
-  return(*run & 0x0f) + ((*run >> 4) & 0x0f) + 1;
+    return STATUS_NOT_IMPLEMENTED;
 }
 
-
-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;
+    }
 
+    DPRINT("DataRunOffsetSize: %x\n", DataRunOffsetSize);
+    DPRINT("DataRunLengthSize: %x\n", DataRunLengthSize);
+    DPRINT("DataRunOffset: %x\n", *DataRunOffset);
+    DPRINT("DataRunLength: %x\n", *DataRunLength);
 
-static ULONGLONG
-RunCount(PUCHAR run)
+    return DataRun;
+}
+
+BOOLEAN
+FindRun(PNTFS_ATTR_RECORD NresAttr,
+        ULONGLONG vcn,
+        PULONGLONG lcn,
+        PULONGLONG count)
 {
-       UCHAR n =  *run & 0xf;
-       ULONGLONG count = 0;
-       ULONG i = 0;
+    if (vcn < NresAttr->NonResident.LowestVCN || vcn > NresAttr->NonResident.HighestVCN)
+        return FALSE;
 
-       for (i = n; i > 0; i--)
-               count = (count << 8) + run[i];
-       return count;
-}
+    DecodeRun((PUCHAR)((ULONG_PTR)NresAttr + NresAttr->NonResident.MappingPairsOffset), (PLONGLONG)lcn, count);
 
+    return TRUE;
+}
 
-BOOLEAN
-FindRun (PNONRESIDENT_ATTRIBUTE NresAttr,
-        ULONGLONG vcn,
-        PULONGLONG lcn,
-        PULONGLONG count)
+static
+NTSTATUS
+InternalReadNonResidentAttributes(PFIND_ATTR_CONTXT Context)
 {
-  PUCHAR run;
+    ULONGLONG ListSize;
+    PNTFS_ATTR_RECORD Attribute;
+    PNTFS_ATTR_CONTEXT ListContext;
 
-  ULONGLONG base = NresAttr->StartVcn;
+    DPRINT("InternalReadNonResidentAttributes(%p)\n", Context);
 
-  if (vcn < NresAttr->StartVcn || vcn > NresAttr->LastVcn)
-    return FALSE;
+    Attribute = Context->CurrAttr;
+    ASSERT(Attribute->Type == AttributeAttributeList);
 
-  *lcn = 0;
+    if (Context->OnlyResident)
+    {
+        Context->NonResidentStart = NULL;
+        Context->NonResidentEnd = NULL;
+        return STATUS_SUCCESS;
+    }
 
-  for (run = (PUCHAR)((ULONG_PTR)NresAttr + NresAttr->RunArrayOffset);
-       *run != 0; run += RunLength(run))
+    if (Context->NonResidentStart != NULL)
     {
-      *lcn += RunLCN(run);
-      *count = RunCount(run);
+        return STATUS_FILE_CORRUPT_ERROR;
+    }
 
-      if (base <= vcn && vcn < base + *count)
-       {
-         *lcn = (RunLCN(run) == 0) ? 0 : *lcn + vcn - base;
-         *count -= (ULONG)(vcn - base);
+    ListContext = PrepareAttributeContext(Attribute);
+    ListSize = AttributeDataLength(&ListContext->Record);
+    if (ListSize > 0xFFFFFFFF)
+    {
+        ReleaseAttributeContext(ListContext);
+        return STATUS_BUFFER_OVERFLOW;
+    }
 
-         return TRUE;
-       }
-      else
-       {
-         base += *count;
-       }
+    Context->NonResidentStart = ExAllocatePoolWithTag(NonPagedPool, (ULONG)ListSize, TAG_NTFS);
+    if (Context->NonResidentStart == NULL)
+    {
+        ReleaseAttributeContext(ListContext);
+        return STATUS_INSUFFICIENT_RESOURCES;
     }
 
-  return FALSE;
-}
+    if (ReadAttribute(Context->Vcb, ListContext, 0, (PCHAR)Context->NonResidentStart, (ULONG)ListSize) != ListSize)
+    {
+        ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
+        Context->NonResidentStart = NULL;
+        ReleaseAttributeContext(ListContext);
+        return STATUS_FILE_CORRUPT_ERROR;
+    }
 
+    ReleaseAttributeContext(ListContext);
+    Context->NonResidentEnd = (PNTFS_ATTR_RECORD)((PCHAR)Context->NonResidentStart + ListSize);
+    return STATUS_SUCCESS;
+}
 
-static VOID
-NtfsDumpFileNameAttribute(PATTRIBUTE Attribute)
+static
+PNTFS_ATTR_RECORD
+InternalGetNextAttribute(PFIND_ATTR_CONTXT Context)
 {
-  PRESIDENT_ATTRIBUTE ResAttr;
-  PFILENAME_ATTRIBUTE FileNameAttr;
+    PNTFS_ATTR_RECORD NextAttribute;
+
+    if (Context->CurrAttr == (PVOID)-1)
+    {
+        return NULL;
+    }
 
-  DbgPrint("  $FILE_NAME ");
+    if (Context->CurrAttr >= Context->FirstAttr &&
+        Context->CurrAttr < Context->LastAttr)
+    {
+        if (Context->CurrAttr->Length == 0)
+        {
+            DPRINT1("Broken length!\n");
+            Context->CurrAttr = (PVOID)-1;
+            return NULL;
+        }
+
+        NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
+        Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
+        Context->CurrAttr = NextAttribute;
+
+        if (Context->CurrAttr < Context->LastAttr &&
+            Context->CurrAttr->Type != AttributeEnd)
+        {
+            return Context->CurrAttr;
+        }
+    }
+
+    if (Context->NonResidentStart == NULL)
+    {
+        Context->CurrAttr = (PVOID)-1;
+        return NULL;
+    }
+
+    if (Context->CurrAttr < Context->NonResidentStart ||
+        Context->CurrAttr >= Context->NonResidentEnd)
+    {
+        Context->CurrAttr = Context->NonResidentStart;
+    }
+    else if (Context->CurrAttr->Length != 0)
+    {
+        NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)Context->CurrAttr + Context->CurrAttr->Length);
+        Context->Offset += ((ULONG_PTR)NextAttribute - (ULONG_PTR)Context->CurrAttr);
+        Context->CurrAttr = NextAttribute;
+    }
+    else
+    {
+        DPRINT1("Broken length!\n");
+        Context->CurrAttr = (PVOID)-1;
+        return NULL;
+    }
 
-  ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-//  DbgPrint(" Length %lu  Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset);
+    if (Context->CurrAttr < Context->NonResidentEnd &&
+        Context->CurrAttr->Type != AttributeEnd)
+    {
+        return Context->CurrAttr;
+    }
 
-  FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
-  DbgPrint(" '%.*S' ", FileNameAttr->NameLength, FileNameAttr->Name);
+    Context->CurrAttr = (PVOID)-1;
+    return NULL;
 }
 
+NTSTATUS
+FindFirstAttribute(PFIND_ATTR_CONTXT Context,
+                   PDEVICE_EXTENSION Vcb,
+                   PFILE_RECORD_HEADER FileRecord,
+                   BOOLEAN OnlyResident,
+                   PNTFS_ATTR_RECORD * Attribute)
+{
+    NTSTATUS Status;
+
+    DPRINT("FindFistAttribute(%p, %p, %p, %p, %u, %p)\n", Context, Vcb, FileRecord, OnlyResident, Attribute);
 
-static VOID
-NtfsDumpVolumeNameAttribute(PATTRIBUTE Attribute)
+    Context->Vcb = Vcb;
+    Context->OnlyResident = OnlyResident;
+    Context->FirstAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
+    Context->CurrAttr = Context->FirstAttr;
+    Context->LastAttr = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->BytesInUse);
+    Context->NonResidentStart = NULL;
+    Context->NonResidentEnd = NULL;
+    Context->Offset = FileRecord->AttributeOffset;
+
+    if (Context->FirstAttr->Type == AttributeEnd)
+    {
+        Context->CurrAttr = (PVOID)-1;
+        return STATUS_END_OF_FILE;
+    }
+    else if (Context->FirstAttr->Type == AttributeAttributeList)
+    {
+        Status = InternalReadNonResidentAttributes(Context);
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
+
+        *Attribute = InternalGetNextAttribute(Context);
+        if (*Attribute == NULL)
+        {
+            return STATUS_END_OF_FILE;
+        }
+    }
+    else
+    {
+        *Attribute = Context->CurrAttr;
+        Context->Offset = (UCHAR*)Context->CurrAttr - (UCHAR*)FileRecord;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+FindNextAttribute(PFIND_ATTR_CONTXT Context,
+                  PNTFS_ATTR_RECORD * Attribute)
 {
-  PRESIDENT_ATTRIBUTE ResAttr;
-  PWCHAR VolumeName;
+    NTSTATUS Status;
+
+    DPRINT("FindNextAttribute(%p, %p)\n", Context, Attribute);
+
+    *Attribute = InternalGetNextAttribute(Context);
+    if (*Attribute == NULL)
+    {
+        return STATUS_END_OF_FILE;
+    }
+
+    if (Context->CurrAttr->Type != AttributeAttributeList)
+    {
+        return STATUS_SUCCESS;
+    }
 
-  DbgPrint("  $VOLUME_NAME ");
+    Status = InternalReadNonResidentAttributes(Context);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
 
-  ResAttr = (PRESIDENT_ATTRIBUTE)Attribute;
-//  DbgPrint(" Length %lu  Offset %hu ", ResAttr->ValueLength, ResAttr->ValueOffset);
+    *Attribute = InternalGetNextAttribute(Context);
+    if (*Attribute == NULL)
+    {
+        return STATUS_END_OF_FILE;
+    }
 
-  VolumeName = (PWCHAR)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
-  DbgPrint(" '%.*S' ", ResAttr->ValueLength / sizeof(WCHAR), VolumeName);
+    return STATUS_SUCCESS;
 }
 
+VOID
+FindCloseAttribute(PFIND_ATTR_CONTXT Context)
+{
+    if (Context->NonResidentStart != NULL)
+    {
+        ExFreePoolWithTag(Context->NonResidentStart, TAG_NTFS);
+        Context->NonResidentStart = NULL;
+    }
+}
 
-static VOID
-NtfsDumpVolumeInformationAttribute(PATTRIBUTE Attribute)
+static
+VOID
+NtfsDumpFileNameAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-  PRESIDENT_ATTRIBUTE ResAttr;
-  PVOLINFO_ATTRIBUTE VolInfoAttr;
+    PFILENAME_ATTRIBUTE FileNameAttr;
 
-  DbgPrint("  $VOLUME_INFORMATION ");
+    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);
 
-  VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)ResAttr + ResAttr->ValueOffset);
-  DbgPrint(" NTFS Version %u.%u  Flags 0x%04hx ",
-          VolInfoAttr->MajorVersion,
-          VolInfoAttr->MinorVersion,
-          VolInfoAttr->Flags);
+    FileNameAttr = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+    DbgPrint(" (%x) '%.*S' ", FileNameAttr->NameType, FileNameAttr->NameLength, FileNameAttr->Name);
+    DbgPrint(" '%x' \n", FileNameAttr->FileAttributes);
+    DbgPrint(" AllocatedSize: %I64u\nDataSize: %I64u\n", FileNameAttr->AllocatedSize, FileNameAttr->DataSize);
 }
 
 
-static VOID
-NtfsDumpAttribute (PATTRIBUTE Attribute)
+static
+VOID
+NtfsDumpStandardInformationAttribute(PNTFS_ATTR_RECORD Attribute)
 {
-  PNONRESIDENT_ATTRIBUTE NresAttr;
-  UNICODE_STRING Name;
+    PSTANDARD_INFORMATION StandardInfoAttr;
 
-  ULONGLONG lcn = 0;
-  ULONGLONG runcount = 0;
+    DbgPrint("  $STANDARD_INFORMATION ");
 
-  switch (Attribute->AttributeType)
-    {
-      case AttributeFileName:
-       NtfsDumpFileNameAttribute(Attribute);
-       break;
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-      case AttributeStandardInformation:
-       DbgPrint("  $STANDARD_INFORMATION ");
-       break;
+    StandardInfoAttr = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+    DbgPrint(" '%x' ", StandardInfoAttr->FileAttribute);
+}
 
-      case AttributeAttributeList:
-       DbgPrint("  $ATTRIBUTE_LIST ");
-       break;
 
-      case AttributeObjectId:
-       DbgPrint("  $OBJECT_ID ");
-       break;
+static
+VOID
+NtfsDumpVolumeNameAttribute(PNTFS_ATTR_RECORD Attribute)
+{
+    PWCHAR VolumeName;
+
+    DbgPrint("  $VOLUME_NAME ");
 
-      case AttributeSecurityDescriptor:
-       DbgPrint("  $SECURITY_DESCRIPTOR ");
-       break;
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-      case AttributeVolumeName:
-       NtfsDumpVolumeNameAttribute(Attribute);
-       break;
+    VolumeName = (PWCHAR)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+    DbgPrint(" '%.*S' ", Attribute->Resident.ValueLength / sizeof(WCHAR), VolumeName);
+}
 
-      case AttributeVolumeInformation:
-       NtfsDumpVolumeInformationAttribute(Attribute);
-       break;
 
-      case AttributeData:
-       DbgPrint("  $DATA ");
-       //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
-       break;
+static
+VOID
+NtfsDumpVolumeInformationAttribute(PNTFS_ATTR_RECORD Attribute)
+{
+    PVOLINFO_ATTRIBUTE VolInfoAttr;
 
-      case AttributeIndexRoot:
-       DbgPrint("  $INDEX_ROOT ");
-       break;
+    DbgPrint("  $VOLUME_INFORMATION ");
 
-      case AttributeIndexAllocation:
-       DbgPrint("  $INDEX_ALLOCATION ");
-       break;
+//    DbgPrint(" Length %lu  Offset %hu ", Attribute->Resident.ValueLength, Attribute->Resident.ValueOffset);
 
-      case AttributeBitmap:
-       DbgPrint("  $BITMAP ");
-       break;
+    VolInfoAttr = (PVOLINFO_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+    DbgPrint(" NTFS Version %u.%u  Flags 0x%04hx ",
+             VolInfoAttr->MajorVersion,
+             VolInfoAttr->MinorVersion,
+             VolInfoAttr->Flags);
+}
 
-      case AttributeReparsePoint:
-       DbgPrint("  $REPARSE_POINT ");
-       break;
 
-      case AttributeEAInformation:
-       DbgPrint("  $EA_INFORMATION ");
-       break;
+static
+VOID
+NtfsDumpIndexRootAttribute(PNTFS_ATTR_RECORD Attribute)
+{
+    PINDEX_ROOT_ATTRIBUTE IndexRootAttr;
 
-      case AttributeEA:
-       DbgPrint("  $EA ");
-       break;
+    IndexRootAttr = (PINDEX_ROOT_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
 
-      case AttributePropertySet:
-       DbgPrint("  $PROPERTY_SET ");
-       break;
+    if (IndexRootAttr->AttributeType == AttributeFileName)
+        ASSERT(IndexRootAttr->CollationRule == COLLATION_FILE_NAME);
 
-      case AttributeLoggedUtilityStream:
-       DbgPrint("  $LOGGED_UTILITY_STREAM ");
-       break;
+    DbgPrint("  $INDEX_ROOT (%uB, %u) ", IndexRootAttr->SizeOfEntry, IndexRootAttr->ClustersPerIndexRecord);
 
-      default:
-       DbgPrint("  Attribute %lx ",
-                Attribute->AttributeType);
-       break;
+    if (IndexRootAttr->Header.Flags == INDEX_ROOT_SMALL)
+    {
+        DbgPrint(" (small) ");
+    }
+    else
+    {
+        ASSERT(IndexRootAttr->Header.Flags == INDEX_ROOT_LARGE);
+        DbgPrint(" (large) ");
     }
+}
+
+
+static
+VOID
+NtfsDumpAttribute(PDEVICE_EXTENSION Vcb,
+                  PNTFS_ATTR_RECORD Attribute)
+{
+    UNICODE_STRING Name;
+
+    ULONGLONG lcn = 0;
+    ULONGLONG runcount = 0;
 
-  if (Attribute->NameLength != 0)
+    switch (Attribute->Type)
     {
-      Name.Length = Attribute->NameLength * sizeof(WCHAR);
-      Name.MaximumLength = Name.Length;
-      Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
+        case AttributeFileName:
+            NtfsDumpFileNameAttribute(Attribute);
+            break;
+
+        case AttributeStandardInformation:
+            NtfsDumpStandardInformationAttribute(Attribute);
+            break;
+
+        case AttributeObjectId:
+            DbgPrint("  $OBJECT_ID ");
+            break;
+
+        case AttributeSecurityDescriptor:
+            DbgPrint("  $SECURITY_DESCRIPTOR ");
+            break;
+
+        case AttributeVolumeName:
+            NtfsDumpVolumeNameAttribute(Attribute);
+            break;
+
+        case AttributeVolumeInformation:
+            NtfsDumpVolumeInformationAttribute(Attribute);
+            break;
+
+        case AttributeData:
+            DbgPrint("  $DATA ");
+            //DataBuf = ExAllocatePool(NonPagedPool,AttributeLengthAllocated(Attribute));
+            break;
+
+        case AttributeIndexRoot:
+            NtfsDumpIndexRootAttribute(Attribute);
+            break;
+
+        case AttributeIndexAllocation:
+            DbgPrint("  $INDEX_ALLOCATION ");
+            break;
+
+        case AttributeBitmap:
+            DbgPrint("  $BITMAP ");
+            break;
+
+        case AttributeReparsePoint:
+            DbgPrint("  $REPARSE_POINT ");
+            break;
+
+        case AttributeEAInformation:
+            DbgPrint("  $EA_INFORMATION ");
+            break;
+
+        case AttributeEA:
+            DbgPrint("  $EA ");
+            break;
+
+        case AttributePropertySet:
+            DbgPrint("  $PROPERTY_SET ");
+            break;
+
+        case AttributeLoggedUtilityStream:
+            DbgPrint("  $LOGGED_UTILITY_STREAM ");
+            break;
+
+        default:
+            DbgPrint("  Attribute %lx ",
+                     Attribute->Type);
+            break;
+    }
 
-      DbgPrint("'%wZ' ", &Name);
+    if (Attribute->Type != AttributeAttributeList)
+    {
+        if (Attribute->NameLength != 0)
+        {
+            Name.Length = Attribute->NameLength * sizeof(WCHAR);
+            Name.MaximumLength = Name.Length;
+            Name.Buffer = (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset);
+
+            DbgPrint("'%wZ' ", &Name);
+        }
+
+        DbgPrint("(%s)\n",
+                 Attribute->IsNonResident ? "non-resident" : "resident");
+
+        if (Attribute->IsNonResident)
+        {
+            FindRun(Attribute,0,&lcn, &runcount);
+
+            DbgPrint("  AllocatedSize %I64u  DataSize %I64u InitilizedSize %I64u\n",
+                     Attribute->NonResident.AllocatedSize, Attribute->NonResident.DataSize, Attribute->NonResident.InitializedSize);
+            DbgPrint("  logical clusters: %I64u - %I64u\n",
+                     lcn, lcn + runcount - 1);
+        }
     }
+}
+
 
-  DbgPrint("(%s)\n",
-          Attribute->Nonresident ? "non-resident" : "resident");
+VOID
+NtfsDumpFileAttributes(PDEVICE_EXTENSION Vcb,
+                       PFILE_RECORD_HEADER FileRecord)
+{
+    NTSTATUS Status;
+    FIND_ATTR_CONTXT Context;
+    PNTFS_ATTR_RECORD Attribute;
 
-  if (Attribute->Nonresident)
+    Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
+    while (NT_SUCCESS(Status))
     {
-      NresAttr = (PNONRESIDENT_ATTRIBUTE)Attribute;
+        NtfsDumpAttribute(Vcb, Attribute);
 
-      FindRun (NresAttr,0,&lcn, &runcount);
+        Status = FindNextAttribute(&Context, &Attribute);
+    }
+
+    FindCloseAttribute(&Context);
+}
+
+PFILENAME_ATTRIBUTE
+GetFileNameFromRecord(PDEVICE_EXTENSION Vcb,
+                      PFILE_RECORD_HEADER FileRecord,
+                      UCHAR NameType)
+{
+    FIND_ATTR_CONTXT Context;
+    PNTFS_ATTR_RECORD Attribute;
+    PFILENAME_ATTRIBUTE Name;
+    NTSTATUS Status;
 
-      DbgPrint ("  AllocatedSize %I64u  DataSize %I64u\n",
-               NresAttr->AllocatedSize, NresAttr->DataSize);
-      DbgPrint ("  logical clusters: %I64u - %I64u\n",
-               lcn, lcn + runcount - 1);
+    Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
+    while (NT_SUCCESS(Status))
+    {
+        if (Attribute->Type == AttributeFileName)
+        {
+            Name = (PFILENAME_ATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+            if (Name->NameType == NameType ||
+                (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_WIN32) ||
+                (Name->NameType == NTFS_FILE_NAME_WIN32_AND_DOS && NameType == NTFS_FILE_NAME_DOS))
+            {
+                FindCloseAttribute(&Context);
+                return Name;
+            }
+        }
+
+        Status = FindNextAttribute(&Context, &Attribute);
     }
+
+    FindCloseAttribute(&Context);
+    return NULL;
 }
 
+NTSTATUS
+GetLastClusterInDataRun(PDEVICE_EXTENSION Vcb, PNTFS_ATTR_RECORD Attribute, PULONGLONG LastCluster)
+{
+    LONGLONG DataRunOffset;
+    ULONGLONG DataRunLength;
+    LONGLONG DataRunStartLCN;
 
-VOID
-NtfsDumpFileAttributes (PFILE_RECORD_HEADER FileRecord)
+    ULONGLONG LastLCN = 0;
+    PUCHAR DataRun = (PUCHAR)Attribute + Attribute->NonResident.MappingPairsOffset;
+
+    if (!Attribute->IsNonResident)
+        return STATUS_INVALID_PARAMETER;
+
+    while (1)
+    {
+        DataRun = DecodeRun(DataRun, &DataRunOffset, &DataRunLength);
+       
+        if (DataRunOffset == -1)
+        {
+            // sparse run
+            if (*DataRun == 0)
+            {
+                // if it's the last run, return the last cluster of the last run
+                *LastCluster = LastLCN + DataRunLength - 1;
+                break;
+            }
+        }
+        else
+        {
+            // Normal data run.
+            DataRunStartLCN = LastLCN + DataRunOffset;
+            LastLCN = DataRunStartLCN;
+        }             
+
+        if (*DataRun == 0)
+        {
+            *LastCluster = LastLCN + DataRunLength - 1;
+            break;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+PSTANDARD_INFORMATION
+GetStandardInformationFromRecord(PDEVICE_EXTENSION Vcb,
+                                 PFILE_RECORD_HEADER FileRecord)
 {
-  PATTRIBUTE Attribute;
+    NTSTATUS Status;
+    FIND_ATTR_CONTXT Context;
+    PNTFS_ATTR_RECORD Attribute;
+    PSTANDARD_INFORMATION StdInfo;
 
-  Attribute = (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
-  while (Attribute < (PATTRIBUTE)((ULONG_PTR)FileRecord + FileRecord->BytesInUse) &&
-         Attribute->AttributeType != (ATTRIBUTE_TYPE)-1)
+    Status = FindFirstAttribute(&Context, Vcb, FileRecord, FALSE, &Attribute);
+    while (NT_SUCCESS(Status))
     {
-      NtfsDumpAttribute (Attribute);
+        if (Attribute->Type == AttributeStandardInformation)
+        {
+            StdInfo = (PSTANDARD_INFORMATION)((ULONG_PTR)Attribute + Attribute->Resident.ValueOffset);
+            FindCloseAttribute(&Context);
+            return StdInfo;
+        }
+
+        Status = FindNextAttribute(&Context, &Attribute);
+    }
+
+    FindCloseAttribute(&Context);
+    return NULL;
+}
 
-      Attribute = (PATTRIBUTE)((ULONG_PTR)Attribute + Attribute->Length);
+PFILENAME_ATTRIBUTE
+GetBestFileNameFromRecord(PDEVICE_EXTENSION Vcb,
+                          PFILE_RECORD_HEADER FileRecord)
+{
+    PFILENAME_ATTRIBUTE FileName;
+
+    FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_POSIX);
+    if (FileName == NULL)
+    {
+        FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_WIN32);
+        if (FileName == NULL)
+        {
+            FileName = GetFileNameFromRecord(Vcb, FileRecord, NTFS_FILE_NAME_DOS);
+        }
     }
+
+    return FileName;
 }
 
 /* EOF */