- Fixed lots of bugs in NTFS code and added correct update sequence

fixups handling and untested NTFS 3+ sparse file support.

svn path=/trunk/; revision=10467
This commit is contained in:
Filip Navara 2004-08-10 09:28:56 +00:00
parent 99e5dd3e01
commit 5775259453
4 changed files with 140 additions and 80 deletions

View 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.

View 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>
*
@ -25,10 +22,6 @@
* - No support for compressed files.
* - No attribute list support.
* - May crash on currupted filesystem.
*
* Bugs:
* - I encountered file names like 'KERNEL~1.EE' 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 = -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;

View 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>
*
@ -23,47 +20,44 @@
#ifndef __NTFS_H
#define __NTFS_H
#ifdef NTFS_DEFS
#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 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 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 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 NTFS_ATTR_NORMAL 0
#define NTFS_ATTR_COMPRESSED 1
#define NTFS_ATTR_RESIDENT 2
#define NTFS_ATTR_ENCRYPTED 0x4000
#define ATTR_NORMAL 0
#define ATTR_COMPRESSED 1
#define ATTR_RESIDENT 2
#define ATTR_ENCRYPTED 0x4000
#define NTFS_SMALL_INDEX 0
#define NTFS_LARGE_INDEX 1
#define SMALL_INDEX 0
#define LARGE_INDEX 1
#define NTFS_INDEX_ENTRY_NODE 1
#define NTFS_INDEX_ENTRY_END 2
#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_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
{
@ -93,6 +87,13 @@ typedef struct
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;

View 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