/*
- * 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>
#include <debug.h>
#include <cache.h>
-#define NTFS_DEFS
#include "ntfs.h"
PNTFS_BOOTSECTOR NtfsBootSector;
{
U8 DataRunOffsetSize;
U8 DataRunLengthSize;
- U8 i;
+ S8 i;
DataRunOffsetSize = (*DataRun >> 4) & 0xF;
DataRunLengthSize = *DataRun & 0xF;
*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;
}
while (AttrRecord < AttrRecordEnd)
{
- if (AttrRecord->Type == ATTR_TYPE_END)
+ if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
break;
if (AttrRecord->Type == Type)
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. */
{
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)
{
DataRun = Context->CacheRun;
LastLCN = Context->CacheRunLastLCN;
DataRunStartLCN = Context->CacheRunStartLCN;
+ DataRunLength = Context->CacheRunLength;
CurrentOffset = Context->CacheRunCurrentOffset;
}
else
DataRunStartLCN = -1;
}
+ DbgPrint((DPRINT_FILESYSTEM, "YYY - %I64x\n", DataRunStartLCN));
+
if (Offset >= CurrentOffset &&
Offset < CurrentOffset + (DataRunLength * NtfsClusterSize))
{
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.
*/
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
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])
{
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;
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))
{
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);
}
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);
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;
}
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))
{
DbgPrint((DPRINT_FILESYSTEM, "NtfsLookupFile() FileName = %s\n", FileName));
- CurrentMFTIndex = FILE_ROOT;
+ CurrentMFTIndex = NTFS_FILE_ROOT;
NumberOfPathParts = FsGetNumPathParts(FileName);
for (i = 0; i < NumberOfPathParts; i++)
{
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;
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;
/*
- * 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;
PUCHAR CacheRun;
U64 CacheRunOffset;
S64 CacheRunStartLCN;
+ U64 CacheRunLength;
S64 CacheRunLastLCN;
U64 CacheRunCurrentOffset;
} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;