📄 ntfs.c
字号:
/*
* FreeLoader NTFS support
* Copyright (C) 2004 Filip Navara <xnavara@volny.cz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Limitations:
* - No support for compressed files.
* - May crash on corrupted filesystem.
*/
#include <freeldr.h>
#include <debug.h>
PNTFS_BOOTSECTOR NtfsBootSector;
ULONG NtfsClusterSize;
ULONG NtfsMftRecordSize;
ULONG NtfsIndexRecordSize;
ULONG NtfsDriveNumber;
ULONG NtfsSectorOfClusterZero;
PNTFS_MFT_RECORD NtfsMasterFileTable;
/* FIXME: NtfsMFTContext is never freed. */
PNTFS_ATTR_CONTEXT NtfsMFTContext;
static ULONGLONG NtfsGetAttributeSize(PNTFS_ATTR_RECORD AttrRecord)
{
if (AttrRecord->IsNonResident)
return AttrRecord->NonResident.DataSize;
else
return AttrRecord->Resident.ValueLength;
}
static PUCHAR NtfsDecodeRun(PUCHAR DataRun, LONGLONG *DataRunOffset, ULONGLONG *DataRunLength)
{
UCHAR DataRunOffsetSize;
UCHAR DataRunLengthSize;
CHAR i;
DataRunOffsetSize = (*DataRun >> 4) & 0xF;
DataRunLengthSize = *DataRun & 0xF;
*DataRunOffset = 0;
*DataRunLength = 0;
DataRun++;
for (i = 0; i < DataRunLengthSize; i++)
{
*DataRunLength += *DataRun << (i << 3);
DataRun++;
}
/* 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 = ((CHAR)(*(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));
return DataRun;
}
static PNTFS_ATTR_CONTEXT NtfsPrepareAttributeContext(PNTFS_ATTR_RECORD AttrRecord)
{
PNTFS_ATTR_CONTEXT Context;
Context = MmAllocateMemory(FIELD_OFFSET(NTFS_ATTR_CONTEXT, Record) + AttrRecord->Length);
RtlCopyMemory(&Context->Record, AttrRecord, AttrRecord->Length);
if (AttrRecord->IsNonResident)
{
LONGLONG DataRunOffset;
ULONGLONG DataRunLength;
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. */
Context->CacheRunStartLCN =
Context->CacheRunLastLCN = DataRunOffset;
}
else
{
/* Sparse run. */
Context->CacheRunStartLCN = -1;
Context->CacheRunLastLCN = 0;
}
Context->CacheRunCurrentOffset = 0;
}
return Context;
}
static VOID NtfsReleaseAttributeContext(PNTFS_ATTR_CONTEXT Context)
{
MmFreeMemory(Context);
}
/* FIXME: Optimize for multisector reads. */
static BOOLEAN NtfsDiskRead(ULONGLONG Offset, ULONGLONG Length, PCHAR Buffer)
{
USHORT 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)
{
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 1, (PCHAR)DISKREADBUFFER))
return FALSE;
ReadLength = min(Length, NtfsBootSector->BytesPerSector - (Offset % NtfsBootSector->BytesPerSector));
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER + (Offset % NtfsBootSector->BytesPerSector), ReadLength);
Buffer += ReadLength;
Length -= ReadLength;
Offset += ReadLength;
}
/* II. Read all complete 64-sector blocks. */
while (Length >= (ULONGLONG)64 * (ULONGLONG)NtfsBootSector->BytesPerSector)
{
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), 64, (PCHAR)DISKREADBUFFER))
return FALSE;
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, 64 * NtfsBootSector->BytesPerSector);
Buffer += 64 * NtfsBootSector->BytesPerSector;
Length -= 64 * NtfsBootSector->BytesPerSector;
Offset += 64 * NtfsBootSector->BytesPerSector;
}
/* III. Read the rest of data */
if (Length)
{
ReadLength = ((Length + NtfsBootSector->BytesPerSector - 1) / NtfsBootSector->BytesPerSector);
if (!MachDiskReadLogicalSectors(NtfsDriveNumber, NtfsSectorOfClusterZero + (Offset / NtfsBootSector->BytesPerSector), ReadLength, (PCHAR)DISKREADBUFFER))
return FALSE;
RtlCopyMemory(Buffer, (PCHAR)DISKREADBUFFER, Length);
}
return TRUE;
}
static ULONGLONG NtfsReadAttribute(PNTFS_ATTR_CONTEXT Context, ULONGLONG Offset, PCHAR Buffer, ULONGLONG Length)
{
ULONGLONG LastLCN;
PUCHAR DataRun;
LONGLONG DataRunOffset;
ULONGLONG DataRunLength;
LONGLONG DataRunStartLCN;
ULONGLONG CurrentOffset;
ULONGLONG ReadLength;
ULONGLONG AlreadyRead;
if (!Context->Record.IsNonResident)
{
if (Offset > Context->Record.Resident.ValueLength)
return 0;
if (Offset + Length > Context->Record.Resident.ValueLength)
Length = Context->Record.Resident.ValueLength - Offset;
RtlCopyMemory(Buffer, (PCHAR)&Context->Record + Context->Record.Resident.ValueOffset + Offset, Length);
return Length;
}
/*
* Non-resident attribute
*/
/*
* I. Find the corresponding start data run.
*/
AlreadyRead = 0;
if(Context->CacheRunOffset <= Offset && Offset < Context->CacheRunOffset + Context->CacheRunLength * NtfsClusterSize)
{
DataRun = Context->CacheRun;
LastLCN = Context->CacheRunLastLCN;
DataRunStartLCN = Context->CacheRunStartLCN;
DataRunLength = Context->CacheRunLength;
CurrentOffset = Context->CacheRunCurrentOffset;
}
else
{
LastLCN = 0;
DataRun = (PUCHAR)&Context->Record + Context->Record.NonResident.MappingPairsOffset;
CurrentOffset = 0;
while (1)
{
DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
if (DataRunOffset != -1)
{
/* Normal data run. */
DataRunStartLCN = LastLCN + DataRunOffset;
LastLCN = DataRunStartLCN;
}
else
{
/* Sparse data run. */
DataRunStartLCN = -1;
}
if (Offset >= CurrentOffset &&
Offset < CurrentOffset + (DataRunLength * NtfsClusterSize))
{
break;
}
if (*DataRun == 0)
{
return AlreadyRead;
}
CurrentOffset += DataRunLength * NtfsClusterSize;
}
}
/*
* II. Go through the run list and read the data
*/
ReadLength = min(DataRunLength * NtfsClusterSize - (Offset - CurrentOffset), Length);
if (DataRunStartLCN == -1)
RtlZeroMemory(Buffer, ReadLength);
if (NtfsDiskRead(DataRunStartLCN * NtfsClusterSize + Offset - CurrentOffset, ReadLength, Buffer))
{
Length -= ReadLength;
Buffer += ReadLength;
AlreadyRead += ReadLength;
if (ReadLength == DataRunLength * NtfsClusterSize - (Offset - CurrentOffset))
{
CurrentOffset += DataRunLength * NtfsClusterSize;
DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
if (DataRunLength != -1)
{
DataRunStartLCN = LastLCN + DataRunOffset;
LastLCN = DataRunStartLCN;
}
else
DataRunStartLCN = -1;
if (*DataRun == 0)
return AlreadyRead;
}
while (Length > 0)
{
ReadLength = min(DataRunLength * NtfsClusterSize, Length);
if (DataRunStartLCN == -1)
RtlZeroMemory(Buffer, ReadLength);
else if (!NtfsDiskRead(DataRunStartLCN * NtfsClusterSize, ReadLength, Buffer))
break;
Length -= ReadLength;
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.
*/
if (*DataRun == 0)
break;
CurrentOffset += DataRunLength * NtfsClusterSize;
DataRun = NtfsDecodeRun(DataRun, &DataRunOffset, &DataRunLength);
if (DataRunOffset != -1)
{
/* Normal data run. */
DataRunStartLCN = LastLCN + DataRunOffset;
LastLCN = DataRunStartLCN;
}
else
{
/* Sparse data run. */
DataRunStartLCN = -1;
}
} /* while */
} /* if Disk */
Context->CacheRun = DataRun;
Context->CacheRunOffset = Offset + AlreadyRead;
Context->CacheRunStartLCN = DataRunStartLCN;
Context->CacheRunLength = DataRunLength;
Context->CacheRunLastLCN = LastLCN;
Context->CacheRunCurrentOffset = CurrentOffset;
return AlreadyRead;
}
static PNTFS_ATTR_CONTEXT NtfsFindAttributeHelper(PNTFS_ATTR_RECORD AttrRecord, PNTFS_ATTR_RECORD AttrRecordEnd, ULONG Type, const WCHAR *Name, ULONG NameLength)
{
while (AttrRecord < AttrRecordEnd)
{
if (AttrRecord->Type == NTFS_ATTR_TYPE_END)
break;
if (AttrRecord->Type == NTFS_ATTR_TYPE_ATTRIBUTE_LIST)
{
PNTFS_ATTR_CONTEXT Context;
PNTFS_ATTR_CONTEXT ListContext;
PVOID ListBuffer;
ULONGLONG ListSize;
PNTFS_ATTR_RECORD ListAttrRecord;
PNTFS_ATTR_RECORD ListAttrRecordEnd;
ListContext = NtfsPrepareAttributeContext(AttrRecord);
ListSize = NtfsGetAttributeSize(&ListContext->Record);
ListBuffer = MmAllocateMemory(ListSize);
ListAttrRecord = (PNTFS_ATTR_RECORD)ListBuffer;
ListAttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)ListBuffer + ListSize);
if (NtfsReadAttribute(ListContext, 0, ListBuffer, ListSize) == ListSize)
{
Context = NtfsFindAttributeHelper(ListAttrRecord, ListAttrRecordEnd,
Type, Name, NameLength);
NtfsReleaseAttributeContext(ListContext);
MmFreeMemory(ListBuffer);
if (Context != NULL)
return Context;
}
}
if (AttrRecord->Type == Type)
{
if (AttrRecord->NameLength == NameLength)
{
PWCHAR AttrName;
AttrName = (PWCHAR)((PCHAR)AttrRecord + AttrRecord->NameOffset);
if (RtlEqualMemory(AttrName, Name, NameLength << 1))
{
/* Found it, fill up the context and return. */
return NtfsPrepareAttributeContext(AttrRecord);
}
}
}
AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)AttrRecord + AttrRecord->Length);
}
return NULL;
}
static PNTFS_ATTR_CONTEXT NtfsFindAttribute(PNTFS_MFT_RECORD MftRecord, ULONG Type, const WCHAR *Name)
{
PNTFS_ATTR_RECORD AttrRecord;
PNTFS_ATTR_RECORD AttrRecordEnd;
ULONG NameLength;
AttrRecord = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + MftRecord->AttributesOffset);
AttrRecordEnd = (PNTFS_ATTR_RECORD)((PCHAR)MftRecord + NtfsMftRecordSize);
for (NameLength = 0; Name[NameLength] != 0; NameLength++)
;
return NtfsFindAttributeHelper(AttrRecord, AttrRecordEnd, Type, Name, NameLength);
}
static BOOLEAN NtfsFixupRecord(PNTFS_RECORD Record)
{
USHORT *USA;
USHORT USANumber;
USHORT USACount;
USHORT *Block;
USA = (USHORT*)((PCHAR)Record + Record->USAOffset);
USANumber = *(USA++);
USACount = Record->USACount - 1; /* Exclude the USA Number. */
Block = (USHORT*)((PCHAR)Record + NtfsBootSector->BytesPerSector - 2);
while (USACount)
{
if (*Block != USANumber)
return FALSE;
*Block = *(USA++);
Block = (USHORT*)((PCHAR)Block + NtfsBootSector->BytesPerSector);
USACount--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -