[freeldr] Rework NTFS filesystem to not be tied to boot filesystem

svn path=/trunk/; revision=43115
This commit is contained in:
Hervé Poussineau 2009-09-22 19:37:11 +00:00
parent 9b86293dce
commit 4648a3e59d
2 changed files with 212 additions and 166 deletions

View file

@ -26,15 +26,19 @@
#include <freeldr.h> #include <freeldr.h>
#include <debug.h> #include <debug.h>
PNTFS_BOOTSECTOR NtfsBootSector; typedef struct _NTFS_VOLUME_INFO
ULONG NtfsClusterSize; {
ULONG NtfsMftRecordSize; NTFS_BOOTSECTOR BootSector;
ULONG NtfsIndexRecordSize; ULONG ClusterSize;
ULONG NtfsDriveNumber; ULONG MftRecordSize;
ULONG NtfsSectorOfClusterZero; ULONG IndexRecordSize;
PNTFS_MFT_RECORD NtfsMasterFileTable; PNTFS_MFT_RECORD MasterFileTable;
/* FIXME: NtfsMFTContext is never freed. */ /* FIXME: MFTContext is never freed. */
PNTFS_ATTR_CONTEXT NtfsMFTContext; PNTFS_ATTR_CONTEXT MFTContext;
ULONG DeviceId;
} NTFS_VOLUME_INFO;
PNTFS_VOLUME_INFO NtfsVolumes[MAX_FDS];
static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord) static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
{ {
@ -123,50 +127,80 @@ static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
MmHeapFree(Context); MmHeapFree(Context);
} }
/* FIXME: Optimize for multisector reads. */ static BOOLEAN NtfsDiskRead(PNTFS_VOLUME_INFO Volume, ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
static BOOLEAN NtfsDiskRead(ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
{ {
LARGE_INTEGER Position;
ULONG Count;
USHORT ReadLength; USHORT ReadLength;
LONG ret;
DPRINTM(DPRINT_FILESYSTEM, "NtfsDiskRead - Offset: %I64d Length: %I64d\n", Offset, Length); DPRINTM(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) // I. Read partial first sector if needed
//
if (Offset % Volume->BootSector.BytesPerSector)
{ {
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 1, (PCHAR)DISKREADBUFFER)) Position.HighPart = 0;
Position.LowPart = Offset & ~(Volume->BootSector.BytesPerSector - 1);
ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
if (ret != ESUCCESS)
return FALSE; return FALSE;
ReadLength = min(Length, NtfsBootSector->BytesPerSector - (Offset % NtfsBootSector->BytesPerSector)); ReadLength = min(Length, Volume->BootSector.BytesPerSector - (Offset % Volume->BootSector.BytesPerSector));
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER + (Offset % NtfsBootSector->BytesPerSector), ReadLength); ret = ArcRead(Volume->DeviceId, Buffer, ReadLength, &Count);
if (ret != ESUCCESS || Count != ReadLength)
return FALSE;
//
// Move to unfilled buffer part
//
Buffer += ReadLength; Buffer += ReadLength;
Length -= ReadLength; Length -= ReadLength;
Offset += ReadLength; Offset += ReadLength;
} }
/* II. Read all complete 64-sector blocks. */ //
while (Length >= (ULONGLONG)64 * (ULONGLONG)NtfsBootSector->BytesPerSector) // II. Read all complete blocks
//
if (Length >= Volume->BootSector.BytesPerSector)
{ {
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 64, (PCHAR)DISKREADBUFFER)) Position.HighPart = 0;
Position.LowPart = Offset;
ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
if (ret != ESUCCESS)
return FALSE; return FALSE;
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, 64 * NtfsBootSector->BytesPerSector); ReadLength = Length & ~(Volume->BootSector.BytesPerSector - 1);
Buffer += 64 * NtfsBootSector->BytesPerSector; ret = ArcRead(Volume->DeviceId, Buffer, ReadLength, &Count);
Length -= 64 * NtfsBootSector->BytesPerSector; if (ret != ESUCCESS || Count != ReadLength)
Offset += 64 * NtfsBootSector->BytesPerSector; return FALSE;
//
// Move to unfilled buffer part
//
Buffer += ReadLength;
Length -= ReadLength;
Offset += ReadLength;
} }
/* III. Read the rest of data */ //
// III. Read the rest of data
//
if (Length) if (Length)
{ {
ReadLength = ((Length + NtfsBootSector->BytesPerSector - 1) / NtfsBootSector->BytesPerSector); Position.HighPart = 0;
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), ReadLength, (PCHAR)DISKREADBUFFER)) Position.LowPart = Offset;
ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
if (ret != ESUCCESS)
return FALSE;
ret = ArcRead(Volume->DeviceId, Buffer, Length, &Count);
if (ret != ESUCCESS || Count != Length)
return FALSE; return FALSE;
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, Length);
} }
return TRUE; return TRUE;
} }
static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONGLONG Length) static ULONGLONG NtfsReadAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONGLONG Length)
{ {
ULONGLONG LastLCN; ULONGLONG LastLCN;
PUCHAR DataRun; PUCHAR DataRun;
@ -197,7 +231,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
AlreadyRead = 0; AlreadyRead = 0;
if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * NtfsClusterSize) if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * Volume->ClusterSize)
{ {
DataRun = Context->CacheRun; DataRun = Context->CacheRun;
LastLCN = Context->CacheRunLastLCN; LastLCN = Context->CacheRunLastLCN;
@ -227,7 +261,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
} }
if (Offset >= CurrentOffset && if (Offset >= CurrentOffset &&
Offset < CurrentOffset + (DataRunLength * NtfsClusterSize)) Offset < CurrentOffset + (DataRunLength * Volume->ClusterSize))
{ {
break; break;
} }
@ -237,7 +271,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
return AlreadyRead; return AlreadyRead;
} }
CurrentOffset += DataRunLength * NtfsClusterSize; CurrentOffset += DataRunLength * Volume->ClusterSize;
} }
} }
@ -245,18 +279,18 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
* II. Go through the run list and read the data * II. Go through the run list and read the data
*/ */
ReadLength = min(DataRunLength * NtfsClusterSize - (Offset - CurrentOffset), Length); ReadLength = min(DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset), Length);
if (DataRunStartLCN == -1) if (DataRunStartLCN == -1)
RtlZeroMemory(Buffer, ReadLength); RtlZeroMemory(Buffer, ReadLength);
if (NtfsDiskRead(DataRunStartLCN * NtfsClusterSize + Offset - CurrentOffset, ReadLength, Buffer)) if (NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
{ {
Length -= ReadLength; Length -= ReadLength;
Buffer += ReadLength; Buffer += ReadLength;
AlreadyRead += ReadLength; AlreadyRead += ReadLength;
if (ReadLength == DataRunLength * NtfsClusterSize - (Offset - CurrentOffset)) if (ReadLength == DataRunLength * Volume->ClusterSize - (Offset - CurrentOffset))
{ {
CurrentOffset += DataRunLength * NtfsClusterSize; CurrentOffset += DataRunLength * Volume->ClusterSize;
DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength); DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
if (DataRunLength != (ULONGLONG)-1) if (DataRunLength != (ULONGLONG)-1)
{ {
@ -272,10 +306,10 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
while (Length > 0) while (Length > 0)
{ {
ReadLength = min(DataRunLength * NtfsClusterSize, Length); ReadLength = min(DataRunLength * Volume->ClusterSize, Length);
if (DataRunStartLCN == -1) if (DataRunStartLCN == -1)
RtlZeroMemory(Buffer, ReadLength); RtlZeroMemory(Buffer, ReadLength);
else if (!NtfsDiskRead(DataRunStartLCN * NtfsClusterSize, ReadLength, Buffer)) else if (!NtfsDiskRead(Volume, DataRunStartLCN * Volume->ClusterSize, ReadLength, Buffer))
break; break;
Length -= ReadLength; Length -= ReadLength;
@ -283,7 +317,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
AlreadyRead += ReadLength; AlreadyRead += ReadLength;
/* We finished this request, but there still data in this data run. */ /* We finished this request, but there still data in this data run. */
if (Length == 0 && ReadLength != DataRunLength * NtfsClusterSize) if (Length == 0 && ReadLength != DataRunLength * Volume->ClusterSize)
break; break;
/* /*
@ -292,7 +326,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
if (*DataRun == 0) if (*DataRun == 0)
break; break;
CurrentOffset += DataRunLength * NtfsClusterSize; CurrentOffset += DataRunLength * Volume->ClusterSize;
DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength); DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
if (DataRunOffset != -1) if (DataRunOffset != -1)
{ {
@ -319,7 +353,7 @@ static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset,
return AlreadyRead; return AlreadyRead;
} }
static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength) static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_VOLUME_INFO Volume, PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength)
{ {
while (AttrRecord < AttrRecordEnd) while (AttrRecord < AttrRecordEnd)
{ {
@ -343,9 +377,9 @@ static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord,
ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer; ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize); ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
if (NtfsReadAttribute(ListContext, 0, ListBuffer, ListSize) == ListSize) if (NtfsReadAttribute(Volume, ListContext, 0, ListBuffer, ListSize) == ListSize)
{ {
Context = NtfsFindAttributeHelper(ListAttrRecord, ListAttrRecordEnd, Context = NtfsFindAttributeHelper(Volume, ListAttrRecord, ListAttrRecordEnd,
Type, Name, NameLength); Type, Name, NameLength);
NtfsReleaseAttributeContext(ListContext); NtfsReleaseAttributeContext(ListContext);
@ -371,27 +405,29 @@ static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord,
} }
} }
if (AttrRecord->Length == 0)
return NULL;
AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length); AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
} }
return NULL; return NULL;
} }
static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name) static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_VOLUME_INFO Volume, PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
{ {
PNTFS_ATTR_RECORD AttrRecord; PNTFS_ATTR_RECORD AttrRecord;
PNTFS_ATTR_RECORD AttrRecordEnd; PNTFS_ATTR_RECORD AttrRecordEnd;
ULONG NameLength; ULONG NameLength;
AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset); AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset);
AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + NtfsMftRecordSize); AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + Volume->MftRecordSize);
for (NameLength = 0; Name[NameLength] != 0; NameLength++) for (NameLength = 0; Name[NameLength] != 0; NameLength++)
; ;
return NtfsFindAttributeHelper(AttrRecord, AttrRecordEnd, Type, Name, NameLength); return NtfsFindAttributeHelper(Volume, AttrRecord, AttrRecordEnd, Type, Name, NameLength);
} }
static BOOLEAN NtfsFixupRecord(PNTFS_RECORD Record) static BOOLEAN NtfsFixupRecord(PNTFS_VOLUME_INFO Volume, PNTFS_RECORD Record)
{ {
USHORT *USA; USHORT *USA;
USHORT USANumber; USHORT USANumber;
@ -401,30 +437,30 @@ static BOOLEAN NtfsFixupRecord(PNTFS_RECORD Record)
USA = (USHORT*)((PCHAR)Record + Record->USAOffset); USA = (USHORT*)((PCHAR)Record + Record->USAOffset);
USANumber = *(USA++); USANumber = *(USA++);
USACount = Record->USACount - 1; /* Exclude the USA Number. */ USACount = Record->USACount - 1; /* Exclude the USA Number. */
Block = (USHORT*)((PCHAR)Record + NtfsBootSector->BytesPerSector - 2); Block = (USHORT*)((PCHAR)Record + Volume->BootSector.BytesPerSector - 2);
while (USACount) while (USACount)
{ {
if (*Block != USANumber) if (*Block != USANumber)
return FALSE; return FALSE;
*Block = *(USA++); *Block = *(USA++);
Block = (USHORT*)((PCHAR)Block + NtfsBootSector->BytesPerSector); Block = (USHORT*)((PCHAR)Block + Volume->BootSector.BytesPerSector);
USACount--; USACount--;
} }
return TRUE; return TRUE;
} }
static BOOLEAN NtfsReadMftRecord(ULONG MFTIndex, PNTFS_MFT_RECORD Buffer) static BOOLEAN NtfsReadMftRecord(PNTFS_VOLUME_INFO Volume, ULONG MFTIndex, PNTFS_MFT_RECORD Buffer)
{ {
ULONGLONG BytesRead; ULONGLONG BytesRead;
BytesRead = NtfsReadAttribute(NtfsMFTContext, MFTIndex * NtfsMftRecordSize, (PCHAR)Buffer, NtfsMftRecordSize); BytesRead = NtfsReadAttribute(Volume, Volume->MFTContext, MFTIndex * Volume->MftRecordSize, (PCHAR)Buffer, Volume->MftRecordSize);
if (BytesRead != NtfsMftRecordSize) if (BytesRead != Volume->MftRecordSize)
return FALSE; return FALSE;
/* Apply update sequence array fixups. */ /* Apply update sequence array fixups. */
return NtfsFixupRecord((PNTFS_RECORD)Buffer); return NtfsFixupRecord(Volume, (PNTFS_RECORD)Buffer);
} }
#if DBG #if DBG
@ -479,7 +515,7 @@ static BOOLEAN NtfsCompareFileName(PCHAR FileName, PNTFS_INDEX_ENTRY IndexEntry)
return TRUE; return TRUE;
} }
static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIndex) static BOOLEAN NtfsFindMftRecord(PNTFS_VOLUME_INFO Volume, ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIndex)
{ {
PNTFS_MFT_RECORD MftRecord; PNTFS_MFT_RECORD MftRecord;
ULONG Magic; ULONG Magic;
@ -495,38 +531,38 @@ static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIn
ULONG RecordOffset; ULONG RecordOffset;
ULONG IndexBlockSize; ULONG IndexBlockSize;
MftRecord = MmHeapAlloc(NtfsMftRecordSize); MftRecord = MmHeapAlloc(Volume->MftRecordSize);
if (MftRecord == NULL) if (MftRecord == NULL)
{ {
return FALSE; return FALSE;
} }
if (NtfsReadMftRecord(MFTIndex, MftRecord)) if (NtfsReadMftRecord(Volume, MFTIndex, MftRecord))
{ {
Magic = MftRecord->Magic; Magic = MftRecord->Magic;
IndexRootCtx = NtfsFindAttribute(MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30"); IndexRootCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ROOT, L"$I30");
if (IndexRootCtx == NULL) if (IndexRootCtx == NULL)
{ {
MmHeapFree(MftRecord); MmHeapFree(MftRecord);
return FALSE; return FALSE;
} }
IndexRecord = MmHeapAlloc(NtfsIndexRecordSize); IndexRecord = MmHeapAlloc(Volume->IndexRecordSize);
if (IndexRecord == NULL) if (IndexRecord == NULL)
{ {
MmHeapFree(MftRecord); MmHeapFree(MftRecord);
return FALSE; return FALSE;
} }
NtfsReadAttribute(IndexRootCtx, 0, IndexRecord, NtfsIndexRecordSize); NtfsReadAttribute(Volume, IndexRootCtx, 0, IndexRecord, Volume->IndexRecordSize);
IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord; IndexRoot = (PNTFS_INDEX_ROOT)IndexRecord;
IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset); IndexEntry = (PNTFS_INDEX_ENTRY)((PCHAR)&IndexRoot->IndexHeader + IndexRoot->IndexHeader.EntriesOffset);
/* Index root is always resident. */ /* Index root is always resident. */
IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength); IndexEntryEnd = (PNTFS_INDEX_ENTRY)(IndexRecord + IndexRootCtx->Record.Resident.ValueLength);
NtfsReleaseAttributeContext(IndexRootCtx); NtfsReleaseAttributeContext(IndexRootCtx);
DPRINTM(DPRINT_FILESYSTEM, "NtfsIndexRecordSize: %x IndexBlockSize: %x\n", NtfsIndexRecordSize, IndexRoot->IndexBlockSize); DPRINTM(DPRINT_FILESYSTEM, "IndexRecordSize: %x IndexBlockSize: %x\n", Volume->IndexRecordSize, IndexRoot->IndexBlockSize);
while (IndexEntry < IndexEntryEnd && while (IndexEntry < IndexEntryEnd &&
!(IndexEntry->Flags & NTFS_INDEX_ENTRY_END)) !(IndexEntry->Flags & NTFS_INDEX_ENTRY_END))
@ -547,7 +583,7 @@ static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIn
IndexBlockSize = IndexRoot->IndexBlockSize; IndexBlockSize = IndexRoot->IndexBlockSize;
IndexBitmapCtx = NtfsFindAttribute(MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30"); IndexBitmapCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_BITMAP, L"$I30");
if (IndexBitmapCtx == NULL) if (IndexBitmapCtx == NULL)
{ {
DPRINTM(DPRINT_FILESYSTEM, "Corrupted filesystem!\n"); DPRINTM(DPRINT_FILESYSTEM, "Corrupted filesystem!\n");
@ -563,10 +599,10 @@ static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIn
MmHeapFree(MftRecord); MmHeapFree(MftRecord);
return FALSE; return FALSE;
} }
NtfsReadAttribute(IndexBitmapCtx, 0, BitmapData, BitmapDataSize); NtfsReadAttribute(Volume, IndexBitmapCtx, 0, BitmapData, BitmapDataSize);
NtfsReleaseAttributeContext(IndexBitmapCtx); NtfsReleaseAttributeContext(IndexBitmapCtx);
IndexAllocationCtx = NtfsFindAttribute(MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30"); IndexAllocationCtx = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_INDEX_ALLOCATION, L"$I30");
if (IndexAllocationCtx == NULL) if (IndexAllocationCtx == NULL)
{ {
DPRINTM(DPRINT_FILESYSTEM, "Corrupted filesystem!\n"); DPRINTM(DPRINT_FILESYSTEM, "Corrupted filesystem!\n");
@ -596,9 +632,9 @@ static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIn
break; break;
} }
NtfsReadAttribute(IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize); NtfsReadAttribute(Volume, IndexAllocationCtx, RecordOffset, IndexRecord, IndexBlockSize);
if (!NtfsFixupRecord((PNTFS_RECORD)IndexRecord)) if (!NtfsFixupRecord(Volume, (PNTFS_RECORD)IndexRecord))
{ {
break; break;
} }
@ -641,7 +677,7 @@ static BOOLEAN NtfsFindMftRecord(ULONG MFTIndex, PCHAR FileName, ULONG *OutMFTIn
return FALSE; return FALSE;
} }
static BOOLEAN NtfsLookupFile(PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext) static BOOLEAN NtfsLookupFile(PNTFS_VOLUME_INFO Volume, PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_ATTR_CONTEXT *DataContext)
{ {
ULONG NumberOfPathParts; ULONG NumberOfPathParts;
CHAR PathPart[261]; CHAR PathPart[261];
@ -661,7 +697,7 @@ static BOOLEAN NtfsLookupFile(PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_
FileName++; FileName++;
DPRINTM(DPRINT_FILESYSTEM, "- Lookup: %s\n", PathPart); DPRINTM(DPRINT_FILESYSTEM, "- Lookup: %s\n", PathPart);
if (!NtfsFindMftRecord(CurrentMFTIndex, PathPart, &CurrentMFTIndex)) if (!NtfsFindMftRecord(Volume, CurrentMFTIndex, PathPart, &CurrentMFTIndex))
{ {
DPRINTM(DPRINT_FILESYSTEM, "- Failed\n"); DPRINTM(DPRINT_FILESYSTEM, "- Failed\n");
return FALSE; return FALSE;
@ -669,13 +705,13 @@ static BOOLEAN NtfsLookupFile(PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_
DPRINTM(DPRINT_FILESYSTEM, "- Lookup: %x\n", CurrentMFTIndex); DPRINTM(DPRINT_FILESYSTEM, "- Lookup: %x\n", CurrentMFTIndex);
} }
if (!NtfsReadMftRecord(CurrentMFTIndex, MftRecord)) if (!NtfsReadMftRecord(Volume, CurrentMFTIndex, MftRecord))
{ {
DPRINTM(DPRINT_FILESYSTEM, "NtfsLookupFile: Can't read MFT record\n"); DPRINTM(DPRINT_FILESYSTEM, "NtfsLookupFile: Can't read MFT record\n");
return FALSE; return FALSE;
} }
*DataContext = NtfsFindAttribute(MftRecord, NTFS_ATTR_TYPE_DATA, L""); *DataContext = NtfsFindAttribute(Volume, MftRecord, NTFS_ATTR_TYPE_DATA, L"");
if (*DataContext == NULL) if (*DataContext == NULL)
{ {
DPRINTM(DPRINT_FILESYSTEM, "NtfsLookupFile: Can't find data attribute\n"); DPRINTM(DPRINT_FILESYSTEM, "NtfsLookupFile: Can't find data attribute\n");
@ -685,81 +721,6 @@ static BOOLEAN NtfsLookupFile(PCSTR FileName, PNTFS_MFT_RECORD MftRecord, PNTFS_
return TRUE; return TRUE;
} }
BOOLEAN NtfsOpenVolume(UCHAR DriveNumber, ULONGLONG VolumeStartSector, ULONGLONG PartitionSectorCount)
{
NtfsBootSector = (PNTFS_BOOTSECTOR)DISKREADBUFFER;
DPRINTM(DPRINT_FILESYSTEM, "NtfsOpenVolume() DriveNumber = 0x%x VolumeStartSector = 0x%x\n", DriveNumber, VolumeStartSector);
if (!MachDiskReadLogicalSectors(DriveNumber, VolumeStartSector, 1, (PCHAR)DISKREADBUFFER))
{
FileSystemError("Failed to read the boot sector.");
return FALSE;
}
if (!RtlEqualMemory(NtfsBootSector->SystemId, "NTFS", 4))
{
FileSystemError("Invalid NTFS signature.");
return FALSE;
}
NtfsBootSector = MmHeapAlloc(NtfsBootSector->BytesPerSector);
if (NtfsBootSector == NULL)
{
return FALSE;
}
RtlCopyMemory(NtfsBootSector, (PCHAR)DISKREADBUFFER, ((PNTFS_BOOTSECTOR)DISKREADBUFFER)->BytesPerSector);
NtfsClusterSize = NtfsBootSector->SectorsPerCluster * NtfsBootSector->BytesPerSector;
if (NtfsBootSector->ClustersPerMftRecord > 0)
NtfsMftRecordSize = NtfsBootSector->ClustersPerMftRecord * NtfsClusterSize;
else
NtfsMftRecordSize = 1 << (-NtfsBootSector->ClustersPerMftRecord);
if (NtfsBootSector->ClustersPerIndexRecord > 0)
NtfsIndexRecordSize = NtfsBootSector->ClustersPerIndexRecord * NtfsClusterSize;
else
NtfsIndexRecordSize = 1 << (-NtfsBootSector->ClustersPerIndexRecord);
DPRINTM(DPRINT_FILESYSTEM, "NtfsClusterSize: 0x%x\n", NtfsClusterSize);
DPRINTM(DPRINT_FILESYSTEM, "ClustersPerMftRecord: %d\n", NtfsBootSector->ClustersPerMftRecord);
DPRINTM(DPRINT_FILESYSTEM, "ClustersPerIndexRecord: %d\n", NtfsBootSector->ClustersPerIndexRecord);
DPRINTM(DPRINT_FILESYSTEM, "NtfsMftRecordSize: 0x%x\n", NtfsMftRecordSize);
DPRINTM(DPRINT_FILESYSTEM, "NtfsIndexRecordSize: 0x%x\n", NtfsIndexRecordSize);
NtfsDriveNumber = DriveNumber;
NtfsSectorOfClusterZero = VolumeStartSector;
DPRINTM(DPRINT_FILESYSTEM, "Reading MFT index...\n");
if (!MachDiskReadLogicalSectors(DriveNumber,
NtfsSectorOfClusterZero +
(NtfsBootSector->MftLocation * NtfsBootSector->SectorsPerCluster),
NtfsMftRecordSize / NtfsBootSector->BytesPerSector, (PCHAR)DISKREADBUFFER))
{
FileSystemError("Failed to read the Master File Table record.");
return FALSE;
}
NtfsMasterFileTable = MmHeapAlloc(NtfsMftRecordSize);
if (NtfsMasterFileTable == NULL)
{
MmHeapFree(NtfsBootSector);
return FALSE;
}
RtlCopyMemory(NtfsMasterFileTable, (PCHAR)DISKREADBUFFER, NtfsMftRecordSize);
DPRINTM(DPRINT_FILESYSTEM, "Searching for DATA attribute...\n");
NtfsMFTContext = NtfsFindAttribute(NtfsMasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
if (NtfsMFTContext == NULL)
{
FileSystemError("Can't find data attribute for Master File Table.");
return FALSE;
}
return TRUE;
}
LONG NtfsClose(ULONG FileId) LONG NtfsClose(ULONG FileId)
{ {
PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId); PNTFS_FILE_HANDLE FileHandle = FsGetDeviceSpecific(FileId);
@ -788,6 +749,7 @@ LONG NtfsGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
LONG NtfsOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId) LONG NtfsOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
{ {
PNTFS_VOLUME_INFO Volume;
PNTFS_FILE_HANDLE FileHandle; PNTFS_FILE_HANDLE FileHandle;
PNTFS_MFT_RECORD MftRecord; PNTFS_MFT_RECORD MftRecord;
ULONG DeviceId; ULONG DeviceId;
@ -802,24 +764,26 @@ LONG NtfsOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
// Get underlying device // Get underlying device
// //
DeviceId = FsGetDeviceId(*FileId); DeviceId = FsGetDeviceId(*FileId);
Volume = NtfsVolumes[DeviceId];
DPRINTM(DPRINT_FILESYSTEM, "NtfsOpen() FileName = %s\n", Path); DPRINTM(DPRINT_FILESYSTEM, "NtfsOpen() FileName = %s\n", Path);
// //
// Allocate file structure // Allocate file structure
// //
FileHandle = MmHeapAlloc(sizeof(NTFS_FILE_HANDLE) + NtfsMftRecordSize); FileHandle = MmHeapAlloc(sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
if (!FileHandle) if (!FileHandle)
{ {
return ENOMEM; return ENOMEM;
} }
RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + NtfsMftRecordSize); RtlZeroMemory(FileHandle, sizeof(NTFS_FILE_HANDLE) + Volume->MftRecordSize);
FileHandle->Volume = Volume;
// //
// Search file entry // Search file entry
// //
MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1); MftRecord = (PNTFS_MFT_RECORD)(FileHandle + 1);
if (!NtfsLookupFile(Path, MftRecord, &FileHandle->DataContext)) if (!NtfsLookupFile(Volume, Path, MftRecord, &FileHandle->DataContext))
{ {
MmHeapFree(FileHandle); MmHeapFree(FileHandle);
return ENOENT; return ENOENT;
@ -836,7 +800,7 @@ LONG NtfsRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
// //
// Read file // Read file
// //
BytesRead64 = NtfsReadAttribute(FileHandle->DataContext, FileHandle->Offset, Buffer, N); BytesRead64 = NtfsReadAttribute(FileHandle->Volume, FileHandle->DataContext, FileHandle->Offset, Buffer, N);
*Count = (ULONG)BytesRead64; *Count = (ULONG)BytesRead64;
// //
@ -876,11 +840,19 @@ const DEVVTBL NtfsFuncTable =
const DEVVTBL* NtfsMount(ULONG DeviceId) const DEVVTBL* NtfsMount(ULONG DeviceId)
{ {
NTFS_BOOTSECTOR BootSector; PNTFS_VOLUME_INFO Volume;
LARGE_INTEGER Position; LARGE_INTEGER Position;
ULONG Count; ULONG Count;
LONG ret; LONG ret;
//
// Allocate data for volume information
//
Volume = MmHeapAlloc(sizeof(NTFS_VOLUME_INFO));
if (!Volume)
return NULL;
RtlZeroMemory(Volume, sizeof(NTFS_VOLUME_INFO));
// //
// Read the BootSector // Read the BootSector
// //
@ -888,28 +860,99 @@ const DEVVTBL* NtfsMount(ULONG DeviceId)
Position.LowPart = 0; Position.LowPart = 0;
ret = ArcSeek(DeviceId, &Position, SeekAbsolute); ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
if (ret != ESUCCESS) if (ret != ESUCCESS)
{
MmHeapFree(Volume);
return NULL; return NULL;
ret = ArcRead(DeviceId, &BootSector, sizeof(BootSector), &Count); }
if (ret != ESUCCESS || Count != sizeof(BootSector)) ret = ArcRead(DeviceId, &Volume->BootSector, sizeof(Volume->BootSector), &Count);
if (ret != ESUCCESS || Count != sizeof(Volume->BootSector))
{
MmHeapFree(Volume);
return NULL; return NULL;
}
// //
// Check if BootSector is valid. If yes, return NTFS function table // Check if BootSector is valid. If no, return early
// //
if (RtlEqualMemory(BootSector.SystemId, "NTFS", 4)) if (!RtlEqualMemory(Volume->BootSector.SystemId, "NTFS", 4))
{ {
// MmHeapFree(Volume);
// Compatibility hack as long as FS is not using underlying device DeviceId
//
ULONG DriveNumber;
ULONGLONG StartSector;
ULONGLONG SectorCount;
int Type;
if (!DiskGetBootVolume(&DriveNumber, &StartSector, &SectorCount, &Type))
return NULL;
NtfsOpenVolume(DriveNumber, StartSector, SectorCount);
return &NtfsFuncTable;
}
else
return NULL; return NULL;
}
//
// Calculate cluster size and MFT record size
//
Volume->ClusterSize = Volume->BootSector.SectorsPerCluster * Volume->BootSector.BytesPerSector;
if (Volume->BootSector.ClustersPerMftRecord > 0)
Volume->MftRecordSize = Volume->BootSector.ClustersPerMftRecord * Volume->ClusterSize;
else
Volume->MftRecordSize = 1 << (-Volume->BootSector.ClustersPerMftRecord);
if (Volume->BootSector.ClustersPerIndexRecord > 0)
Volume->IndexRecordSize = Volume->BootSector.ClustersPerIndexRecord * Volume->ClusterSize;
else
Volume->IndexRecordSize = 1 << (-Volume->BootSector.ClustersPerIndexRecord);
DPRINTM(DPRINT_FILESYSTEM, "ClusterSize: 0x%x\n", Volume->ClusterSize);
DPRINTM(DPRINT_FILESYSTEM, "ClustersPerMftRecord: %d\n", Volume->BootSector.ClustersPerMftRecord);
DPRINTM(DPRINT_FILESYSTEM, "ClustersPerIndexRecord: %d\n", Volume->BootSector.ClustersPerIndexRecord);
DPRINTM(DPRINT_FILESYSTEM, "MftRecordSize: 0x%x\n", Volume->MftRecordSize);
DPRINTM(DPRINT_FILESYSTEM, "IndexRecordSize: 0x%x\n", Volume->IndexRecordSize);
//
// Read MFT index
//
DPRINTM(DPRINT_FILESYSTEM, "Reading MFT index...\n");
Volume->MasterFileTable = MmHeapAlloc(Volume->MftRecordSize);
if (!Volume->MasterFileTable)
{
MmHeapFree(Volume);
return NULL;
}
Position.HighPart = 0;
Position.LowPart = Volume->BootSector.MftLocation * Volume->ClusterSize;
ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
if (ret != ESUCCESS)
{
FileSystemError("Failed to seek to Master File Table record.");
MmHeapFree(Volume->MasterFileTable);
MmHeapFree(Volume);
return NULL;
}
ret = ArcRead(DeviceId, Volume->MasterFileTable, Volume->MftRecordSize, &Count);
if (ret != ESUCCESS || Count != Volume->MftRecordSize)
{
FileSystemError("Failed to read the Master File Table record.");
MmHeapFree(Volume->MasterFileTable);
MmHeapFree(Volume);
return NULL;
}
//
// Keep device id
//
Volume->DeviceId = DeviceId;
//
// Search DATA attribute
//
DPRINTM(DPRINT_FILESYSTEM, "Searching for DATA attribute...\n");
Volume->MFTContext = NtfsFindAttribute(Volume, Volume->MasterFileTable, NTFS_ATTR_TYPE_DATA, L"");
if (!Volume->MFTContext)
{
FileSystemError("Can't find data attribute for Master File Table.");
MmHeapFree(Volume->MasterFileTable);
MmHeapFree(Volume);
return NULL;
}
//
// Remember NTFS volume information
//
NtfsVolumes[DeviceId] = Volume;
//
// Return success
//
return &NtfsFuncTable;
} }

View file

@ -214,11 +214,14 @@ typedef struct
NTFS_ATTR_RECORD Record; NTFS_ATTR_RECORD Record;
} NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT; } NTFS_ATTR_CONTEXT, *PNTFS_ATTR_CONTEXT;
typedef struct _NTFS_VOLUME_INFO *PNTFS_VOLUME_INFO;
#include <pshpack1.h> #include <pshpack1.h>
typedef struct typedef struct
{ {
PNTFS_ATTR_CONTEXT DataContext; PNTFS_ATTR_CONTEXT DataContext;
ULONGLONG Offset; ULONGLONG Offset;
PNTFS_VOLUME_INFO Volume;
} NTFS_FILE_HANDLE, *PNTFS_FILE_HANDLE; } NTFS_FILE_HANDLE, *PNTFS_FILE_HANDLE;
#include <poppack.h> #include <poppack.h>