mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
826 lines
22 KiB
C
826 lines
22 KiB
C
/** @file
|
|
Copyright (c) 2022, Mikhail Krichanov. All rights reserved.
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
Functional and structural descriptions follow NTFS Documentation
|
|
by Richard Russon and Yuval Fledel
|
|
**/
|
|
|
|
#include "NTFS.h"
|
|
#include "Helper.h"
|
|
|
|
EFI_STATUS
|
|
NtfsDir (
|
|
IN EFI_FS *FileSystem,
|
|
IN CONST CHAR16 *Path,
|
|
OUT EFI_NTFS_FILE *File,
|
|
IN FUNCTION_TYPE FunctionType
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
NTFS_FILE *Dir;
|
|
|
|
ASSERT (FileSystem != NULL);
|
|
ASSERT (Path != NULL);
|
|
ASSERT (File != NULL);
|
|
|
|
Dir = NULL;
|
|
|
|
CopyMem (&File->RootFile, FileSystem->RootIndex, sizeof (File->RootFile));
|
|
CopyMem (&File->MftFile, FileSystem->MftStart, sizeof (File->MftFile));
|
|
|
|
Status = FsHelpFindFile (
|
|
Path,
|
|
&File->RootFile,
|
|
&Dir,
|
|
FSHELP_DIR
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = IterateDir (Dir, File, FunctionType);
|
|
|
|
if (Dir != &File->RootFile) {
|
|
FreeFile (Dir);
|
|
FreePool (Dir);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
NtfsOpen (
|
|
IN EFI_NTFS_FILE *File
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
NTFS_FILE *BaseMftRecord;
|
|
|
|
ASSERT (File != NULL);
|
|
|
|
BaseMftRecord = NULL;
|
|
|
|
CopyMem (&File->RootFile, File->FileSystem->RootIndex, sizeof (File->RootFile));
|
|
CopyMem (&File->MftFile, File->FileSystem->MftStart, sizeof (File->MftFile));
|
|
|
|
Status = FsHelpFindFile (
|
|
File->Path,
|
|
&File->RootFile,
|
|
&BaseMftRecord,
|
|
FSHELP_REG
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (BaseMftRecord != &File->RootFile) {
|
|
CopyMem (&File->RootFile, BaseMftRecord, sizeof (*BaseMftRecord));
|
|
|
|
if (!File->RootFile.InodeRead) {
|
|
Status = InitFile (&File->RootFile, File->RootFile.Inode);
|
|
}
|
|
|
|
FreeFile (BaseMftRecord);
|
|
FreePool (BaseMftRecord);
|
|
}
|
|
|
|
File->Offset = 0;
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
NtfsMount (
|
|
IN EFI_FS *FileSystem
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
BOOT_FILE_DATA Boot;
|
|
UINTN Size;
|
|
EFI_NTFS_FILE *RootFile;
|
|
|
|
ASSERT (FileSystem != NULL);
|
|
|
|
Status = DiskRead (FileSystem, 0, sizeof (Boot), &Boot);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if ( (Boot.SystemId[0] != SIGNATURE_32 ('N', 'T', 'F', 'S'))
|
|
|| (Boot.SectorsPerCluster == 0)
|
|
|| ((Boot.SectorsPerCluster & (Boot.SectorsPerCluster - 1U)) != 0)
|
|
|| (Boot.BytesPerSector == 0)
|
|
|| ((Boot.BytesPerSector & (Boot.BytesPerSector - 1U)) != 0))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "NTFS: (NtfsMount #1) BIOS Parameter Block is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
FileSystem->SectorSize = (UINTN)Boot.BytesPerSector;
|
|
FileSystem->ClusterSize = (UINTN)Boot.SectorsPerCluster * FileSystem->SectorSize;
|
|
|
|
if (Boot.MftRecordClusters > 0) {
|
|
Size = (UINTN)Boot.MftRecordClusters * FileSystem->ClusterSize;
|
|
} else if (-Boot.MftRecordClusters >= 31) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (NtfsMount #2) BIOS Parameter Block is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
} else {
|
|
Size = (UINTN)LShiftU64 (1ULL, -Boot.MftRecordClusters);
|
|
}
|
|
|
|
FileSystem->FileRecordSize = Size;
|
|
if (FileSystem->FileRecordSize < FileSystem->SectorSize) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: File Record is smaller than Sector.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (Boot.IndexRecordClusters > 0) {
|
|
Size = (UINTN)Boot.IndexRecordClusters * FileSystem->ClusterSize;
|
|
} else if (-Boot.IndexRecordClusters >= 31) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (NtfsMount #3) BIOS Parameter Block is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
} else {
|
|
Size = (UINTN)LShiftU64 (1ULL, -Boot.IndexRecordClusters);
|
|
}
|
|
|
|
FileSystem->IndexRecordSize = Size;
|
|
if (FileSystem->IndexRecordSize < FileSystem->SectorSize) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Index Record is smaller than Sector.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
FileSystem->FirstMftRecord = Boot.MftLcn * FileSystem->ClusterSize;
|
|
//
|
|
// Driver limitations
|
|
//
|
|
if ( (FileSystem->FileRecordSize > NTFS_MAX_MFT)
|
|
|| (FileSystem->IndexRecordSize > NTFS_MAX_IDX))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "NTFS: (NtfsMount #4) BIOS Parameter Block is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
RootFile = AllocateZeroPool (sizeof (*RootFile));
|
|
if (RootFile == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
CopyMem (RootFile, &FileSystem->EfiFile, sizeof (FileSystem->EfiFile));
|
|
|
|
RootFile->IsDir = TRUE;
|
|
RootFile->Path = L"/";
|
|
RootFile->RefCount = 1U;
|
|
RootFile->FileSystem = FileSystem;
|
|
RootFile->RootFile.File = RootFile;
|
|
RootFile->MftFile.File = RootFile;
|
|
|
|
RootFile->MftFile.FileRecord = AllocateZeroPool (FileSystem->FileRecordSize);
|
|
if (RootFile->MftFile.FileRecord == NULL) {
|
|
FreePool (RootFile);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = DiskRead (
|
|
FileSystem,
|
|
FileSystem->FirstMftRecord,
|
|
FileSystem->FileRecordSize,
|
|
RootFile->MftFile.FileRecord
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (RootFile->MftFile.FileRecord);
|
|
FreePool (RootFile);
|
|
return Status;
|
|
}
|
|
|
|
Status = Fixup (
|
|
RootFile->MftFile.FileRecord,
|
|
FileSystem->FileRecordSize,
|
|
SIGNATURE_32 ('F', 'I', 'L', 'E'),
|
|
FileSystem->SectorSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (RootFile->MftFile.FileRecord);
|
|
FreePool (RootFile);
|
|
return Status;
|
|
}
|
|
|
|
if (LocateAttr (&RootFile->MftFile.Attr, &RootFile->MftFile, AT_DATA) == NULL) {
|
|
FreePool (RootFile->MftFile.FileRecord);
|
|
FreePool (RootFile);
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
Status = InitFile (&RootFile->RootFile, ROOT_FILE);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (RootFile->MftFile.FileRecord);
|
|
FreePool (RootFile);
|
|
return Status;
|
|
}
|
|
|
|
FileSystem->RootIndex = &RootFile->RootFile;
|
|
FileSystem->MftStart = &RootFile->MftFile;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Table 4.21. Layout of a File Record
|
|
____________________________________________________________________
|
|
Offset | Size | Description
|
|
--------------------------------------------------------------------
|
|
0x00 | 4 | Magic number 'FILE'
|
|
0x04 | 2 | Offset to the update sequence
|
|
0x06 | 2 | Size in words of Update Sequence Number & Array (S)
|
|
...
|
|
| 2 | Update Sequence Number (a)
|
|
| 2S-2 | Update Sequence Array (a)
|
|
--------------------------------------------------------------------
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Fixup (
|
|
IN UINT8 *Buffer,
|
|
IN UINT64 Length,
|
|
IN UINT32 Magic,
|
|
IN UINTN SectorSize
|
|
)
|
|
{
|
|
FILE_RECORD_HEADER *Record;
|
|
UINT8 *UpdateSequencePointer;
|
|
UINT16 UpdateSequenceNumber;
|
|
UINT64 USCounter;
|
|
UINT8 *BufferEnd;
|
|
|
|
ASSERT (Buffer != NULL);
|
|
|
|
Record = (FILE_RECORD_HEADER *)Buffer;
|
|
|
|
if (Length < sizeof (*Record)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #1) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (Record->Magic != Magic) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #2) Record is corrupted.\n"));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
if ((Record->UpdateSequenceOffset + sizeof (UINT16)) > Length) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #3) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (((UINT64)Record->S_Size - 1U) != DivU64x64Remainder (Length, SectorSize, NULL)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #4) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
UpdateSequencePointer = Buffer + Record->UpdateSequenceOffset;
|
|
UpdateSequenceNumber = ReadUnaligned16 ((UINT16 *)UpdateSequencePointer);
|
|
USCounter = Record->UpdateSequenceOffset;
|
|
|
|
if (Length < (SectorSize - sizeof (UINT16))) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #5) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
BufferEnd = Buffer + Length;
|
|
Buffer += SectorSize - sizeof (UINT16);
|
|
while ((Buffer + sizeof (UINT16)) <= BufferEnd) {
|
|
UpdateSequencePointer += sizeof (UINT16);
|
|
|
|
USCounter += sizeof (UINT16);
|
|
if ((USCounter + sizeof (UINT16)) > Length) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #6) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (ReadUnaligned16 ((UINT16 *)Buffer) != UpdateSequenceNumber) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (Fixup #7) Record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
Buffer[0] = UpdateSequencePointer[0];
|
|
Buffer[1] = UpdateSequencePointer[1];
|
|
Buffer += SectorSize;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InitAttr (
|
|
OUT NTFS_ATTR *Attr,
|
|
IN NTFS_FILE *File
|
|
)
|
|
{
|
|
FILE_RECORD_HEADER *Record;
|
|
UINT64 AttrEnd;
|
|
|
|
ASSERT (Attr != NULL);
|
|
ASSERT (File != NULL);
|
|
|
|
Record = (FILE_RECORD_HEADER *)File->FileRecord;
|
|
|
|
Attr->BaseMftRecord = File;
|
|
Attr->Flags = (File == &File->File->MftFile) ? NTFS_AF_MFT_FILE : 0;
|
|
|
|
AttrEnd = Record->AttributeOffset + sizeof (ATTR_HEADER_RES);
|
|
if ( (AttrEnd > File->File->FileSystem->FileRecordSize)
|
|
|| (AttrEnd > Record->RealSize)
|
|
|| (Record->RealSize > Record->AllocatedSize))
|
|
{
|
|
DEBUG ((DEBUG_INFO, "NTFS: (InitAttr) File record is corrupted.\n"));
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
Attr->Next = File->FileRecord + Record->AttributeOffset;
|
|
Attr->Last = NULL;
|
|
Attr->ExtensionMftRecord = NULL;
|
|
Attr->NonResAttrList = NULL;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
UINT8 *
|
|
LocateAttr (
|
|
IN NTFS_ATTR *Attr,
|
|
IN NTFS_FILE *Mft,
|
|
IN UINT32 Type
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *AttrStart;
|
|
|
|
ASSERT (Attr != NULL);
|
|
ASSERT (Mft != NULL);
|
|
|
|
Status = InitAttr (Attr, Mft);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
AttrStart = FindAttr (Attr, Type);
|
|
if (AttrStart == NULL) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if ((Attr->Flags & NTFS_AF_ALST) == 0) {
|
|
while (TRUE) {
|
|
AttrStart = FindAttr (Attr, Type);
|
|
if (AttrStart == NULL) {
|
|
break;
|
|
}
|
|
|
|
if ((Attr->Flags & NTFS_AF_ALST) != 0) {
|
|
return AttrStart;
|
|
}
|
|
}
|
|
|
|
FreeAttr (Attr);
|
|
|
|
Status = InitAttr (Attr, Mft);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
AttrStart = FindAttr (Attr, Type);
|
|
if (AttrStart == NULL) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return AttrStart;
|
|
}
|
|
|
|
UINT8 *
|
|
FindAttr (
|
|
IN NTFS_ATTR *Attr,
|
|
IN UINT32 Type
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *AttrStart;
|
|
ATTR_HEADER_RES *Res;
|
|
ATTR_HEADER_NONRES *NonRes;
|
|
ATTR_LIST_RECORD *LRecord;
|
|
FILE_RECORD_HEADER *FRecord;
|
|
UINT64 BufferSize;
|
|
UINTN FileRecordSize;
|
|
UINTN SectorSize;
|
|
|
|
ASSERT (Attr != NULL);
|
|
|
|
BufferSize = 0;
|
|
FileRecordSize = Attr->BaseMftRecord->File->FileSystem->FileRecordSize;
|
|
SectorSize = Attr->BaseMftRecord->File->FileSystem->SectorSize;
|
|
|
|
if ((Attr->Flags & NTFS_AF_ALST) != 0) {
|
|
retry:
|
|
while ((Attr->Next + sizeof (*LRecord)) <= Attr->Last) {
|
|
Attr->Current = Attr->Next;
|
|
LRecord = (ATTR_LIST_RECORD *)Attr->Current;
|
|
|
|
if (BufferSize < LRecord->RecordLength) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #0) $ATTRIBUTE_LIST is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Attr->Next += LRecord->RecordLength;
|
|
BufferSize -= LRecord->RecordLength;
|
|
if (Attr->Next <= Attr->Current) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #1) $ATTRIBUTE_LIST is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if ((LRecord->Type == Type) || (Type == 0)) {
|
|
if (Attr->Flags & NTFS_AF_MFT_FILE) {
|
|
Status = DiskRead (
|
|
Attr->BaseMftRecord->File->FileSystem,
|
|
ReadUnaligned32 ((UINT32 *)(Attr->Current + 0x10U)),
|
|
FileRecordSize / 2U,
|
|
Attr->ExtensionMftRecord
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Could not read first part of extension record.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Status = DiskRead (
|
|
Attr->BaseMftRecord->File->FileSystem,
|
|
ReadUnaligned32 ((UINT32 *)(Attr->Current + 0x14U)),
|
|
FileRecordSize / 2U,
|
|
Attr->ExtensionMftRecord + FileRecordSize / 2U
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Could not read second part of extension record.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Status = Fixup (
|
|
Attr->ExtensionMftRecord,
|
|
FileRecordSize,
|
|
SIGNATURE_32 ('F', 'I', 'L', 'E'),
|
|
Attr->BaseMftRecord->File->FileSystem->SectorSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Fixup failed.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
Status = ReadMftRecord (
|
|
Attr->BaseMftRecord->File,
|
|
Attr->ExtensionMftRecord,
|
|
(UINT32)LRecord->BaseFileReference
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Could not read extension record.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
FRecord = (FILE_RECORD_HEADER *)Attr->ExtensionMftRecord;
|
|
BufferSize = FileRecordSize;
|
|
if ((FRecord->AttributeOffset + sizeof (*Res)) <= BufferSize) {
|
|
Res = (ATTR_HEADER_RES *)((UINT8 *)FRecord + FRecord->AttributeOffset);
|
|
BufferSize -= FRecord->AttributeOffset;
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #1) Extension record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
while ((BufferSize >= sizeof (UINT32)) && (Res->Type != ATTRIBUTES_END_MARKER)) {
|
|
if (BufferSize < sizeof (*Res)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #2) Extension record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if ( (Res->Type == LRecord->Type)
|
|
&& (Res->AttributeId == LRecord->AttributeId))
|
|
{
|
|
return (UINT8 *)Res;
|
|
}
|
|
|
|
if ((Res->Length == 0) || (Res->Length >= BufferSize)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #3) Extension record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
BufferSize -= Res->Length;
|
|
Res = (ATTR_HEADER_RES *)((UINT8 *)Res + Res->Length);
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "NTFS: Can\'t find 0x%X in attribute list\n", Attr->Current));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Attr->Current = Attr->Next;
|
|
Res = (ATTR_HEADER_RES *)Attr->Current;
|
|
BufferSize = FileRecordSize - (Attr->Current - Attr->BaseMftRecord->FileRecord);
|
|
|
|
while ((BufferSize >= sizeof (UINT32)) && (Res->Type != ATTRIBUTES_END_MARKER)) {
|
|
if (BufferSize < sizeof (*Res)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #1) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if ((Res->Length == 0) || (Res->Length >= BufferSize)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #2) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
BufferSize -= Res->Length;
|
|
Attr->Next += Res->Length;
|
|
|
|
if (Res->Type == AT_ATTRIBUTE_LIST) {
|
|
Attr->Last = Attr->Current;
|
|
}
|
|
|
|
if ((Res->Type == Type) || (Type == 0)) {
|
|
return Attr->Current;
|
|
}
|
|
|
|
Attr->Current = Attr->Next;
|
|
Res = (ATTR_HEADER_RES *)Attr->Current;
|
|
}
|
|
|
|
//
|
|
// Continue search in $ATTRIBUTE_LIST
|
|
//
|
|
if (Attr->Last != NULL) {
|
|
if (Attr->ExtensionMftRecord != NULL) {
|
|
FreePool (Attr->ExtensionMftRecord);
|
|
}
|
|
|
|
Attr->ExtensionMftRecord = AllocateZeroPool (FileRecordSize);
|
|
if (Attr->ExtensionMftRecord == NULL) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
AttrStart = Attr->Last;
|
|
Res = (ATTR_HEADER_RES *)Attr->Last;
|
|
BufferSize = FileRecordSize - (Attr->Last - Attr->BaseMftRecord->FileRecord);
|
|
|
|
if (BufferSize < sizeof (*Res)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #3) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (Res->NonResFlag != 0) {
|
|
NonRes = (ATTR_HEADER_NONRES *)Res;
|
|
Attr->Current = (UINT8 *)NonRes;
|
|
|
|
if (BufferSize < sizeof (*NonRes)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #4) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (NonRes->RealSize > MAX_FILE_SIZE) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr) File is too huge.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (Attr->NonResAttrList != NULL) {
|
|
FreePool (Attr->NonResAttrList);
|
|
}
|
|
|
|
Attr->NonResAttrList = AllocateZeroPool ((UINTN)NonRes->RealSize);
|
|
if (Attr->NonResAttrList == NULL) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Status = ReadData (Attr, AttrStart, Attr->NonResAttrList, 0, (UINTN)NonRes->RealSize);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: Failed to read non-resident attribute list\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Attr->Next = Attr->NonResAttrList;
|
|
Attr->Last = Attr->NonResAttrList + NonRes->RealSize;
|
|
BufferSize = NonRes->RealSize;
|
|
} else {
|
|
if ((Res->InfoOffset < Res->Length) && (Res->Length <= BufferSize)) {
|
|
Attr->Next = (UINT8 *)Res + Res->InfoOffset;
|
|
Attr->Last = (UINT8 *)Res + Res->Length;
|
|
BufferSize -= Res->InfoOffset;
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #5) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Attr->Flags |= NTFS_AF_ALST;
|
|
LRecord = (ATTR_LIST_RECORD *)Attr->Next;
|
|
while ((Attr->Next + sizeof (*LRecord)) < Attr->Last) {
|
|
if ((LRecord->Type == Type) || (Type == 0)) {
|
|
break;
|
|
}
|
|
|
|
if (BufferSize < LRecord->RecordLength) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #2) $ATTRIBUTE_LIST is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (LRecord->RecordLength == 0) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #3) $ATTRIBUTE_LIST is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
Attr->Next += LRecord->RecordLength;
|
|
BufferSize -= LRecord->RecordLength;
|
|
LRecord = (ATTR_LIST_RECORD *)Attr->Next;
|
|
}
|
|
|
|
if ((Attr->Next + sizeof (*LRecord)) >= Attr->Last) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if ((Attr->Flags & NTFS_AF_MFT_FILE) && (Type == AT_DATA)) {
|
|
Attr->Flags |= NTFS_AF_GPOS;
|
|
Attr->Current = Attr->Next;
|
|
AttrStart = Attr->Current;
|
|
|
|
if (BufferSize >= 0x18U) {
|
|
WriteUnaligned32 (
|
|
(UINT32 *)(AttrStart + 0x10U),
|
|
(UINT32)DivU64x64Remainder (Attr->BaseMftRecord->File->FileSystem->FirstMftRecord, SectorSize, NULL)
|
|
);
|
|
WriteUnaligned32 (
|
|
(UINT32 *)(AttrStart + 0x14U),
|
|
(UINT32)DivU64x64Remainder (Attr->BaseMftRecord->File->FileSystem->FirstMftRecord, SectorSize, NULL) + 1U
|
|
);
|
|
} else {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #7) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
AttrStart = Attr->Next + ReadUnaligned16 ((UINT16 *)(AttrStart + 0x04U));
|
|
while ((AttrStart + sizeof (UINT32) + sizeof (UINT16)) < Attr->Last) {
|
|
if (ReadUnaligned32 ((UINT32 *)AttrStart) != Type) {
|
|
break;
|
|
}
|
|
|
|
Status = ReadAttr (
|
|
Attr,
|
|
AttrStart + 0x10U,
|
|
ReadUnaligned32 ((UINT32 *)(AttrStart + 0x10U)) * FileRecordSize,
|
|
FileRecordSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
if (ReadUnaligned16 ((UINT16 *)(AttrStart + 4U)) == 0) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: (FindAttr #8) File record is corrupted.\n"));
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
AttrStart += ReadUnaligned16 ((UINT16 *)(AttrStart + 4U));
|
|
}
|
|
|
|
Attr->Next = Attr->Current;
|
|
Attr->Flags &= ~NTFS_AF_GPOS;
|
|
}
|
|
|
|
goto retry;
|
|
}
|
|
|
|
FreeAttr (Attr);
|
|
return NULL;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitFile (
|
|
IN OUT NTFS_FILE *File,
|
|
IN UINT64 RecordNumber
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
ATTR_HEADER_RES *Attr;
|
|
FILE_RECORD_HEADER *Record;
|
|
|
|
ASSERT (File != NULL);
|
|
|
|
File->InodeRead = TRUE;
|
|
|
|
File->FileRecord = AllocateZeroPool (File->File->FileSystem->FileRecordSize);
|
|
if (File->FileRecord == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Status = ReadMftRecord (File->File, File->FileRecord, RecordNumber);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (File->FileRecord);
|
|
File->FileRecord = NULL;
|
|
return Status;
|
|
}
|
|
|
|
Record = (FILE_RECORD_HEADER *)File->FileRecord;
|
|
|
|
if ((Record->Flags & IS_IN_USE) == 0) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: MFT Record 0x%Lx is not in use\n", RecordNumber));
|
|
FreePool (File->FileRecord);
|
|
File->FileRecord = NULL;
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if ((Record->Flags & IS_A_DIRECTORY) == 0) {
|
|
Attr = (ATTR_HEADER_RES *)LocateAttr (&File->Attr, File, AT_DATA);
|
|
if (Attr == NULL) {
|
|
DEBUG ((DEBUG_INFO, "NTFS: No $DATA in MFT Record 0x%Lx\n", RecordNumber));
|
|
FreePool (File->FileRecord);
|
|
File->FileRecord = NULL;
|
|
return EFI_VOLUME_CORRUPTED;
|
|
}
|
|
|
|
if (Attr->NonResFlag == 0) {
|
|
File->DataAttributeSize = Attr->InfoLength;
|
|
} else {
|
|
File->DataAttributeSize = ((ATTR_HEADER_NONRES *)Attr)->RealSize;
|
|
}
|
|
|
|
if ((File->Attr.Flags & NTFS_AF_ALST) == 0) {
|
|
File->Attr.Last = 0;
|
|
}
|
|
} else {
|
|
Status = InitAttr (&File->Attr, File);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (File->FileRecord);
|
|
File->FileRecord = NULL;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
FreeAttr (
|
|
IN NTFS_ATTR *Attr
|
|
)
|
|
{
|
|
ASSERT (Attr != NULL);
|
|
|
|
if (Attr->ExtensionMftRecord != NULL) {
|
|
FreePool (Attr->ExtensionMftRecord);
|
|
Attr->ExtensionMftRecord = NULL;
|
|
}
|
|
|
|
if (Attr->NonResAttrList != NULL) {
|
|
FreePool (Attr->NonResAttrList);
|
|
Attr->NonResAttrList = NULL;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FreeFile (
|
|
IN NTFS_FILE *File
|
|
)
|
|
{
|
|
ASSERT (File != NULL);
|
|
|
|
FreeAttr (&File->Attr);
|
|
|
|
if ( (File->FileRecord != NULL)
|
|
&& (File->FileRecord != File->File->FileSystem->RootIndex->FileRecord)
|
|
&& (File->FileRecord != File->File->FileSystem->MftStart->FileRecord))
|
|
{
|
|
FreePool (File->FileRecord);
|
|
File->FileRecord = NULL;
|
|
}
|
|
}
|