From 5775259453fd056f429776408c86c8114e3e2811 Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Tue, 10 Aug 2004 09:28:56 +0000 Subject: [PATCH] - 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 --- freeldr/freeldr/CHANGELOG | 5 ++ freeldr/freeldr/fs/ntfs.c | 107 ++++++++++++++++++++++-------- freeldr/freeldr/fs/ntfs.h | 104 +++++++++++++++-------------- freeldr/freeldr/include/version.h | 4 +- 4 files changed, 140 insertions(+), 80 deletions(-) diff --git a/freeldr/freeldr/CHANGELOG b/freeldr/freeldr/CHANGELOG index 3e9685cdcf2..0dc77fb381b 100644 --- a/freeldr/freeldr/CHANGELOG +++ b/freeldr/freeldr/CHANGELOG @@ -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. diff --git a/freeldr/freeldr/fs/ntfs.c b/freeldr/freeldr/fs/ntfs.c index 35c1106b811..76394364338 100644 --- a/freeldr/freeldr/fs/ntfs.c +++ b/freeldr/freeldr/fs/ntfs.c @@ -1,7 +1,4 @@ /* - * FreeLoader - * Copyright (C) 1998-2003 Brian Palmer - * * FreeLoader NTFS support * Copyright (C) 2004 Filip Navara * @@ -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 @@ -40,7 +33,6 @@ #include #include -#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; diff --git a/freeldr/freeldr/fs/ntfs.h b/freeldr/freeldr/fs/ntfs.h index e3229b5ea7c..af51534d365 100644 --- a/freeldr/freeldr/fs/ntfs.h +++ b/freeldr/freeldr/fs/ntfs.h @@ -1,7 +1,4 @@ /* - * FreeLoader - * Copyright (C) 1998-2003 Brian Palmer - * * FreeLoader NTFS support * Copyright (C) 2004 Filip Navara * @@ -23,76 +20,80 @@ #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 { - 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; diff --git a/freeldr/freeldr/include/version.h b/freeldr/freeldr/include/version.h index 93f0e6139d4..2229902b16f 100644 --- a/freeldr/freeldr/include/version.h +++ b/freeldr/freeldr/include/version.h @@ -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 " #define AUTHOR_EMAIL "" #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