2022-05-15 00:55:52 +03:00

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;
}
}