- Fixed lots of bugs in NTFS code and added correct update sequence
authorFilip Navara <filip.navara@gmail.com>
Tue, 10 Aug 2004 09:28:56 +0000 (09:28 +0000)
committerFilip Navara <filip.navara@gmail.com>
Tue, 10 Aug 2004 09:28:56 +0000 (09:28 +0000)
  fixups handling and untested NTFS 3+ sparse file support.

svn path=/trunk/; revision=10467

freeldr/freeldr/CHANGELOG
freeldr/freeldr/fs/ntfs.c
freeldr/freeldr/fs/ntfs.h
freeldr/freeldr/include/version.h

index 3e9685c..0dc77fb 100644 (file)
@@ -1,3 +1,8 @@
+Changes in v1.8.22 (21/05/2004) (navaraf)
+
+- Fixed lots of bugs in NTFS code and added correct update sequence
+  fixups handling and untested NTFS 3+ sparse file support.
+
 Changes in v1.8.21 (21/05/2004) (navaraf)
 
 - Experimental NTFS reading support with no boot code yet.
index 35c1106..7639436 100644 (file)
@@ -1,7 +1,4 @@
 /*
- *  FreeLoader
- *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
- *
  *  FreeLoader NTFS support
  *  Copyright (C) 2004       Filip Navara  <xnavara@volny.cz>
  *
  * - No support for compressed files.
  * - No attribute list support.
  * - May crash on currupted filesystem.
- *
- * Bugs:
- * - I encountered file names like 'KERNEL~1.E\ 4E' stored on
- *   the disk. These aren't handled correctly yet.
  */
 
 #include <freeldr.h>
@@ -40,7 +33,6 @@
 #include <debug.h>
 #include <cache.h>
 
-#define NTFS_DEFS
 #include "ntfs.h"
 
 PNTFS_BOOTSECTOR NtfsBootSector;
@@ -56,7 +48,7 @@ PUCHAR NtfsDecodeRun(PUCHAR DataRun, S64 *DataRunOffset, U64 *DataRunLength)
 {
     U8 DataRunOffsetSize;
     U8 DataRunLengthSize;
-    U8 i;
+    S8 i;
 
     DataRunOffsetSize = (*DataRun >> 4) & 0xF;
     DataRunLengthSize = *DataRun & 0xF;
@@ -68,16 +60,27 @@ PUCHAR NtfsDecodeRun(PUCHAR DataRun, S64 *DataRunOffset, U64 *DataRunLength)
         *DataRunLength += *DataRun << (i << 3);
         DataRun++;
     }
-    for (i = 0; i < DataRunOffsetSize; i++)
+
+    /* NTFS 3+ sparse files */
+    if (DataRunOffsetSize == 0)
     {
-        *DataRunOffset += *DataRun << (i << 3);
-        DataRun++;
+        *DataRunOffset = -1;
+    }
+    else
+    {
+        for (i = 0; i < DataRunOffsetSize - 1; i++)
+        {
+            *DataRunOffset += *DataRun << (i << 3);
+            DataRun++;
+        }
+        /* The last byte contains sign so we must process it different way. */
+        *DataRunOffset = ((S8)(*(DataRun++)) << (i << 3)) + *DataRunOffset;
     }
 
     DbgPrint((DPRINT_FILESYSTEM, "DataRunOffsetSize: %x\n", DataRunOffsetSize));
     DbgPrint((DPRINT_FILESYSTEM, "DataRunLengthSize: %x\n", DataRunLengthSize));
-    DbgPrint((DPRINT_FILESYSTEM, "DataRunOffset: %x\n", DataRunOffset));
-    DbgPrint((DPRINT_FILESYSTEM, "DataRunLength: %x\n", DataRunLength));
+    DbgPrint((DPRINT_FILESYSTEM, "DataRunOffset: %x\n", *DataRunOffset));
+    DbgPrint((DPRINT_FILESYSTEM, "DataRunLength: %x\n", *DataRunLength));
 
     return DataRun;
 }
@@ -97,7 +100,7 @@ BOOL NtfsFindAttribute(PNTFS_ATTR_CONTEXT Context, PNTFS_MFT_RECORD MftRecord, U
 
     while (AttrRecord < AttrRecordEnd)
     {
-        if (AttrRecord->Type == ATTR_TYPE_END)
+        if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
             break;
 
         if (AttrRecord->Type == Type)
@@ -117,6 +120,7 @@ BOOL NtfsFindAttribute(PNTFS_ATTR_CONTEXT Context, PNTFS_MFT_RECORD MftRecord, U
                         Context->CacheRun = (PUCHAR)Context->Record + Context->Record->NonResident.MappingPairsOffset;
                         Context->CacheRunOffset = 0;
                         Context->CacheRun = NtfsDecodeRun(Context->CacheRun, &DataRunOffset, &DataRunLength);
+                        Context->CacheRunLength = DataRunLength;
                         if (DataRunOffset != -1)
                         {
                             /* Normal run. */
@@ -147,6 +151,9 @@ BOOL NtfsDiskRead(U64 Offset, U64 Length, PCHAR Buffer)
 {
     U16 ReadLength;
 
+    DbgPrint((DPRINT_FILESYSTEM, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length));
+    RtlZeroMemory((PCHAR)DISKREADBUFFER, 0x1000);
+
     /* I. Read partial first sector if needed */
     if (Offset % NtfsBootSector->BytesPerSector)
     {
@@ -216,6 +223,7 @@ U64 NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, U64 Offset, PCHAR Buffer, U64
         DataRun = Context->CacheRun;
         LastLCN = Context->CacheRunLastLCN;
         DataRunStartLCN = Context->CacheRunStartLCN;
+        DataRunLength = Context->CacheRunLength;
         CurrentOffset = Context->CacheRunCurrentOffset;
     }
     else
@@ -239,6 +247,8 @@ U64 NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, U64 Offset, PCHAR Buffer, U64
                 DataRunStartLCN = -1;
             }
 
+            DbgPrint((DPRINT_FILESYSTEM, "YYY - %I64x\n", DataRunStartLCN));
+
             if (Offset >= CurrentOffset &&
                 Offset < CurrentOffset + (DataRunLength * NtfsClusterSize))
             {
@@ -270,6 +280,10 @@ U64 NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, U64 Offset, PCHAR Buffer, U64
        Buffer += ReadLength;
        AlreadyRead += ReadLength;
 
+       /* We finished this request, but there still data in this data run. */
+       if (Length == 0 && ReadLength != DataRunLength * NtfsClusterSize)
+           break;
+
        /*
         * Go to next run in the list.
          */
@@ -294,15 +308,47 @@ U64 NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, U64 Offset, PCHAR Buffer, U64
     Context->CacheRun = DataRun;
     Context->CacheRunOffset = Offset + AlreadyRead;
     Context->CacheRunStartLCN = DataRunStartLCN;
+    Context->CacheRunLength = DataRunLength;
     Context->CacheRunLastLCN = LastLCN;
     Context->CacheRunCurrentOffset = CurrentOffset;
 
     return AlreadyRead;
 }
 
+BOOL NtfsFixupRecord(PNTFS_RECORD Record)
+{
+    U16 *USA;
+    U16 USANumber;
+    U16 USACount;
+    U16 *Block;
+
+    USA = (U16*)((PCHAR)Record + Record->USAOffset);
+    USANumber = *(USA++);
+    USACount = Record->USACount - 1; /* Exclude the USA Number. */
+    Block = (U16*)((PCHAR)Record + NtfsBootSector->BytesPerSector - 2);
+
+    while (USACount)
+    {
+        if (*Block != USANumber)
+            return FALSE;
+        *Block = *(USA++);
+        Block = (U16*)((PCHAR)Block + NtfsBootSector->BytesPerSector);
+        USACount--;
+    }
+
+    return TRUE;
+}
+
 BOOL NtfsReadMftRecord(U32 MFTIndex, PNTFS_MFT_RECORD Buffer)
 {
-    return NtfsReadAttribute(&NtfsMFTContext, MFTIndex * NtfsMftRecordSize, (PCHAR)Buffer, NtfsMftRecordSize) == NtfsMftRecordSize;
+    U64 BytesRead;
+    
+    BytesRead = NtfsReadAttribute(&NtfsMFTContext, MFTIndex * NtfsMftRecordSize, (PCHAR)Buffer, NtfsMftRecordSize);
+    if (BytesRead != NtfsMftRecordSize)
+        return FALSE;
+
+    /* Apply update sequence array fixups. */
+    return NtfsFixupRecord((PNTFS_RECORD)Buffer);
 }
 
 #ifdef DEBUG
@@ -341,7 +387,7 @@ BOOL NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
         return FALSE;
 
     /* Do case-sensitive compares for Posix file names. */
-    if (IndexEntry->FileName.FileNameType == FILE_NAME_POSIX)
+    if (IndexEntry->FileName.FileNameType == NTFS_FILE_NAME_POSIX)
     {
         for (i = 0; i < EntryFileNameLength; i++)
             if (EntryFileName[i] != FileName[i])
@@ -383,7 +429,7 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
     {
         Magic = MftRecord->Magic;
 
-        if (!NtfsFindAttribute(&IndexRootCtx, MftRecord, ATTR_TYPE_INDEX_ROOT, L"$I30"))
+        if (!NtfsFindAttribute(&IndexRootCtx, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30"))
         {
             MmFreeMemory(MftRecord);
             return FALSE;
@@ -405,7 +451,7 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
         DbgPrint((DPRINT_FILESYSTEM, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize, IndexRoot->IndexBlockSize));
 
         while (IndexEntry < IndexEntryEnd &&
-               !(IndexEntry->Flags & INDEX_ENTRY_END))
+               !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
         {
             if (NtfsCompareFileName(FileName, IndexEntry))
             {
@@ -417,13 +463,13 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
            IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)IndexEntry + IndexEntry->Length);
         }
 
-        if (IndexRoot->IndexHeader.Flags & LARGE_INDEX)
+        if (IndexRoot->IndexHeader.Flags & NTFS_LARGE_INDEX)
         {
             DbgPrint((DPRINT_FILESYSTEM, "Large Index!\n"));
 
             IndexBlockSize = IndexRoot->IndexBlockSize;
 
-            if (!NtfsFindAttribute(&IndexBitmapCtx, MftRecord, ATTR_TYPE_BITMAP, L"$I30"))
+            if (!NtfsFindAttribute(&IndexBitmapCtx, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30"))
             {
                 DbgPrint((DPRINT_FILESYSTEM, "Corrupted filesystem!\n"));
                 MmFreeMemory(MftRecord);
@@ -443,7 +489,7 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
             }
             NtfsReadAttribute(&IndexBitmapCtx, 0, BitmapData, BitmapDataSize);
 
-            if (!NtfsFindAttribute(&IndexAllocationCtx, MftRecord, ATTR_TYPE_INDEX_ALLOCATION, L"$I30"))
+            if (!NtfsFindAttribute(&IndexAllocationCtx, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30"))
             {
                 DbgPrint((DPRINT_FILESYSTEM, "Corrupted filesystem!\n"));
                 MmFreeMemory(BitmapData);
@@ -463,7 +509,7 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
                 DbgPrint((DPRINT_FILESYSTEM, "RecordOffset: %x IndexAllocationSize: %x\n", RecordOffset, IndexAllocationSize));
                 for (; RecordOffset < IndexAllocationSize;)
                 {
-                    U8 Bit = 1 << ((RecordOffset / IndexBlockSize) & 3);
+                    U8 Bit = 1 << ((RecordOffset / IndexBlockSize) & 7);
                     U32 Byte = (RecordOffset / IndexBlockSize) >> 3;
                     if ((BitmapData[Byte] & Bit))
                         break;
@@ -471,16 +517,23 @@ BOOL NtfsFindMftRecord(U32 MFTIndex, PCHAR FileName, U32 *OutMFTIndex)
                 }
             
                 if (RecordOffset >= IndexAllocationSize)
+                {
                     break;
+                }
 
                 NtfsReadAttribute(&IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
 
+                if (!NtfsFixupRecord((PNTFS_RECORD)IndexRecord))
+                {
+                    break;
+                }
+
                 /* FIXME */
                 IndexEntry = (PNTFS_INDEX_ENTRY)(IndexRecord + 0x18 + *(U16 *)(IndexRecord + 0x18));
                IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexBlockSize);
 
                 while (IndexEntry < IndexEntryEnd &&
-                       !(IndexEntry->Flags & INDEX_ENTRY_END))
+                       !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
                 {        
                     if (NtfsCompareFileName(FileName, IndexEntry))
                     {
@@ -520,7 +573,7 @@ BOOL NtfsLookupFile(PUCHAR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONT
 
     DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile() FileName = %s\n", FileName));
 
-    CurrentMFTIndex = FILE_ROOT;
+    CurrentMFTIndex = NTFS_FILE_ROOT;
     NumberOfPathParts = FsGetNumPathParts(FileName);
     for (i = 0; i < NumberOfPathParts; i++)
     {
@@ -545,7 +598,7 @@ BOOL NtfsLookupFile(PUCHAR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONT
         return FALSE;
     }
 
-    if (!NtfsFindAttribute(DataContext, MftRecord, ATTR_TYPE_DATA, L""))
+    if (!NtfsFindAttribute(DataContext, MftRecord, NTFS_ATTR_TYPE_DATA, L""))
     {
         DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile: Can't find data attribute\n"));
         return FALSE;
@@ -619,7 +672,7 @@ BOOL NtfsOpenVolume(U32 DriveNumber, U32 VolumeStartSector)
     RtlCopyMemory(NtfsMasterFileTable, (PCHAR)DISKREADBUFFER, NtfsMftRecordSize);
 
     DbgPrint((DPRINT_FILESYSTEM, "Searching for DATA attribute...\n"));
-    if (!NtfsFindAttribute(&NtfsMFTContext, NtfsMasterFileTable, ATTR_TYPE_DATA, L""))
+    if (!NtfsFindAttribute(&NtfsMFTContext, NtfsMasterFileTable, NTFS_ATTR_TYPE_DATA, L""))
     {
         FileSystemError("Can't find data attribute for Master File Table.");
         return FALSE;
index e3229b5..af51534 100644 (file)
@@ -1,7 +1,4 @@
 /*
- *  FreeLoader
- *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
- *
  *  FreeLoader NTFS support
  *  Copyright (C) 2004       Filip Navara  <xnavara@volny.cz>
  *
 #ifndef __NTFS_H
 #define __NTFS_H
 
-#ifdef NTFS_DEFS
-
-#define FILE_MFT                               0
-#define FILE_MFTMIRR                           1
-#define FILE_LOGFILE                           2
-#define FILE_VOLUME                            3
-#define FILE_ATTRDEF                           4
-#define FILE_ROOT                              5
-#define FILE_BITMAP                            6
-#define FILE_BOOT                              7
-#define FILE_BADCLUS                           8
-#define FILE_QUOTA                             9
-#define FILE_UPCASE                            10
-
-#define ATTR_TYPE_STANDARD_INFORMATION         0x10
-#define ATTR_TYPE_ATTRIBUTE_LIST                       0x20
-#define ATTR_TYPE_FILENAME                             0x30
-#define ATTR_TYPE_SECURITY_DESCRIPTOR          0x50
-#define ATTR_TYPE_DATA                         0x80
-#define ATTR_TYPE_INDEX_ROOT                           0x90
-#define ATTR_TYPE_INDEX_ALLOCATION                     0xa0
-#define ATTR_TYPE_BITMAP                               0xb0
-#define ATTR_TYPE_SYMLINK                              0xc0
-#define ATTR_TYPE_END                                  0xffffffff
-
-#define ATTR_NORMAL                            0
-#define ATTR_COMPRESSED                                1
-#define ATTR_RESIDENT                          2
-#define ATTR_ENCRYPTED                         0x4000
-
-#define SMALL_INDEX                            0
-#define LARGE_INDEX                            1
-
-#define INDEX_ENTRY_NODE                       1
-#define INDEX_ENTRY_END                                2
-
-#define FILE_NAME_POSIX                                0
-#define FILE_NAME_WIN32                                1
-#define FILE_NAME_DOS                          2
-#define FILE_NAME_WIN32_AND_DOS                        3 
-#endif
+#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_ATTR_TYPE_STANDARD_INFORMATION    0x10
+#define NTFS_ATTR_TYPE_ATTRIBUTE_LIST          0x20
+#define NTFS_ATTR_TYPE_FILENAME                        0x30
+#define NTFS_ATTR_TYPE_SECURITY_DESCRIPTOR     0x50
+#define NTFS_ATTR_TYPE_DATA                    0x80
+#define NTFS_ATTR_TYPE_INDEX_ROOT              0x90
+#define NTFS_ATTR_TYPE_INDEX_ALLOCATION                0xa0
+#define NTFS_ATTR_TYPE_BITMAP                  0xb0
+#define NTFS_ATTR_TYPE_SYMLINK                 0xc0
+#define NTFS_ATTR_TYPE_END                     0xffffffff
+
+#define NTFS_ATTR_NORMAL                       0
+#define NTFS_ATTR_COMPRESSED                   1
+#define NTFS_ATTR_RESIDENT                     2
+#define NTFS_ATTR_ENCRYPTED                    0x4000
+
+#define NTFS_SMALL_INDEX                       0
+#define NTFS_LARGE_INDEX                       1
+
+#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
 {
-       U8              JumpBoot[3];                            // Jump to the boot loader routine
-       U8              SystemId[8];                            // System Id ("NTFS    ")
-       U16             BytesPerSector;                         // Bytes per sector
-       U8              SectorsPerCluster;                      // Number of sectors in a cluster
+       U8              JumpBoot[3];                    // Jump to the boot loader routine
+       U8              SystemId[8];                    // System Id ("NTFS    ")
+       U16             BytesPerSector;                 // Bytes per sector
+       U8              SectorsPerCluster;              // Number of sectors in a cluster
        U8              Unused1[7];
-       U8              MediaDescriptor;                        // Media descriptor byte
+       U8              MediaDescriptor;                // Media descriptor byte
        U8              Unused2[2];
-       U16             SectorsPerTrack;                        // Number of sectors in a track
-       U16             NumberOfHeads;                          // Number of heads on the disk
+       U16             SectorsPerTrack;                // Number of sectors in a track
+       U16             NumberOfHeads;                  // Number of heads on the disk
        U8              Unused3[8];
-       U8              DriveNumber;                            // Int 0x13 drive number (e.g. 0x80)
+       U8              DriveNumber;                    // Int 0x13 drive number (e.g. 0x80)
        U8              CurrentHead;
-       U8              BootSignature;                          // Extended boot signature (0x80)
+       U8              BootSignature;                  // Extended boot signature (0x80)
        U8              Unused4;
-       U64             VolumeSectorCount;                      // Number of sectors in the volume
+       U64             VolumeSectorCount;              // Number of sectors in the volume
        U64             MftLocation;
        U64             MftMirrorLocation;
        S8              ClustersPerMftRecord;           // Clusters per MFT Record
        U8              Unused5[3];
        S8              ClustersPerIndexRecord;         // Clusters per Index Record
        U8              Unused6[3];
-       U64             VolumeSerialNumber;                     // Volume serial number
+       U64             VolumeSerialNumber;             // Volume serial number
        U8              BootCodeAndData[430];           // The remainder of the boot sector
-       U16             BootSectorMagic;                        // 0xAA55
+       U16             BootSectorMagic;                // 0xAA55
 } PACKED NTFS_BOOTSECTOR, *PNTFS_BOOTSECTOR;
 
+typedef struct
+{
+       U32             Magic;
+       U16             USAOffset;                                      // Offset to the Update Sequence Array from the start of the ntfs record
+       U16             USACount;
+} PACKED NTFS_RECORD, *PNTFS_RECORD;
+
 typedef struct
 {
        U32             Magic;
@@ -206,6 +207,7 @@ typedef struct
        PUCHAR                  CacheRun;
        U64                     CacheRunOffset;
        S64                     CacheRunStartLCN;
+       U64                     CacheRunLength;
        S64                     CacheRunLastLCN;
        U64                     CacheRunCurrentOffset;
 } NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
index 93f0e61..2229902 100644 (file)
@@ -22,7 +22,7 @@
 
 
 /* just some stuff */
-#define VERSION                        "FreeLoader v1.8.21"
+#define VERSION                        "FreeLoader v1.8.22"
 #define COPYRIGHT              "Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>"
 #define AUTHOR_EMAIL   "<brianp@sginet.com>"
 #define BY_AUTHOR              "by Brian Palmer"
@@ -36,7 +36,7 @@
 //
 #define FREELOADER_MAJOR_VERSION       1
 #define FREELOADER_MINOR_VERSION       8
-#define FREELOADER_PATCH_VERSION       21
+#define FREELOADER_PATCH_VERSION       22
 
 
 #ifndef ASM