mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2026-02-01 15:59:39 +00:00
OpenNtfsDxe: Implement NTFS read-only driver. (#332)
This commit is contained in:
parent
d0b7eb3603
commit
e9136a3a0b
1
.gitignore
vendored
1
.gitignore
vendored
@ -80,3 +80,4 @@ Utilities/TestMacho/Macho
|
||||
Utilities/TestRsaPreprocess/RsaPreprocess
|
||||
Utilities/TestSmbios/Smbios
|
||||
Utilities/TestPeCoff/PeCoff
|
||||
Utilities/TestNtfsDxe/TestNtfsDxe
|
||||
|
||||
@ -6378,6 +6378,9 @@ even cause permanent firmware damage. Some of the known drivers are listed below
|
||||
& \hyperref[uefilinux]{OpenCore plugin} implementing \texttt{OC\_BOOT\_ENTRY\_PROTOCOL}
|
||||
to allow direct detection and booting of Linux distributiuons from OpenCore, without
|
||||
chainloading via GRUB. \\
|
||||
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenNtfsDxe}}\textbf{*}
|
||||
& New Technologies File System (NTFS) read-only driver.
|
||||
NTFS is the primary file system for Microsoft Windows versions that are based on Windows NT. \\
|
||||
\href{https://github.com/acidanthera/OpenCorePkg}{\texttt{OpenUsbKbDxe}}\textbf{*}
|
||||
& USB keyboard driver adding support for \texttt{AppleKeyMapAggregator} protocols
|
||||
on top of a custom USB keyboard driver implementation. This is an alternative to
|
||||
|
||||
@ -268,6 +268,7 @@
|
||||
OpenCorePkg/Platform/CrScreenshotDxe/CrScreenshotDxe.inf
|
||||
OpenCorePkg/Platform/OpenCanopy/OpenCanopy.inf
|
||||
OpenCorePkg/Platform/OpenLinuxBoot/OpenLinuxBoot.inf
|
||||
OpenCorePkg/Platform/OpenNtfsDxe/OpenNtfsDxe.inf
|
||||
OpenCorePkg/Platform/OpenPartitionDxe/PartitionDxe.inf
|
||||
OpenCorePkg/Platform/OpenRuntime/OpenRuntime.inf
|
||||
OpenCorePkg/Platform/OpenUsbKbDxe/UsbKbDxe.inf
|
||||
|
||||
693
Platform/OpenNtfsDxe/Compression.c
Normal file
693
Platform/OpenNtfsDxe/Compression.c
Normal file
@ -0,0 +1,693 @@
|
||||
/** @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"
|
||||
|
||||
#define IS_COMPRESSED_BLOCK 0x8000
|
||||
#define BLOCK_LENGTH_BITS 0xFFF
|
||||
#define UNIT_MASK 0xF
|
||||
|
||||
extern UINT64 mUnitSize;
|
||||
STATIC UINT64 mBufferSize;
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GetNextCluster (
|
||||
IN OUT COMPRESSED *Clusters
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINTN ClusterSize;
|
||||
|
||||
ClusterSize = Clusters->FileSystem->ClusterSize;
|
||||
|
||||
if (Clusters->Head >= Clusters->Tail) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Compression block overflown\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Status = DiskRead (
|
||||
Clusters->FileSystem,
|
||||
(Clusters->Elements[Clusters->Head].Lcn - Clusters->Elements[Clusters->Head].Vcn + Clusters->CurrentVcn) * ClusterSize,
|
||||
ClusterSize,
|
||||
Clusters->Cluster
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
++Clusters->CurrentVcn;
|
||||
|
||||
if (Clusters->CurrentVcn >= Clusters->Elements[Clusters->Head].Vcn) {
|
||||
++Clusters->Head;
|
||||
}
|
||||
|
||||
Clusters->ClusterOffset = 0;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GetDataRunByte (
|
||||
IN COMPRESSED *Clusters,
|
||||
OUT UINT8 *Result
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
ASSERT (Clusters != NULL);
|
||||
ASSERT (Result != NULL);
|
||||
|
||||
if (Clusters->ClusterOffset >= Clusters->FileSystem->ClusterSize) {
|
||||
Status = GetNextCluster (Clusters);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
*Result = Clusters->Cluster[Clusters->ClusterOffset++];
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GetTwoDataRunBytes (
|
||||
IN COMPRESSED *Clusters,
|
||||
OUT UINT16 *Result
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 ByteLow;
|
||||
UINT8 ByteHigh;
|
||||
|
||||
ASSERT (Clusters != NULL);
|
||||
ASSERT (Result != NULL);
|
||||
|
||||
Status = GetDataRunByte (Clusters, &ByteLow);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = GetDataRunByte (Clusters, &ByteHigh);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
*Result = (((UINT16)ByteHigh) << 8U) | ByteLow;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* The basic idea is that substrings of the block which have been seen before
|
||||
are compressed by referencing the string rather than mentioning it again.
|
||||
|
||||
#include <ntfs.h>\n
|
||||
#include <stdio.h>\n
|
||||
|
||||
This is compressed to #include <ntfs.h>\n(-18,10)stdio(-17,4)
|
||||
|
||||
* Pairs like (-18,10) are recorded in two bytes. ->
|
||||
The shortest possible substring is 3 bytes long. ->
|
||||
One can subtract 3 from the length before encoding it.
|
||||
|
||||
* The references are always backward, and never 0. ->
|
||||
One can store them as positive numbers, and subtract one. ->
|
||||
(-18,10) -> (17,7); (-17,4) -> (16,1).
|
||||
|
||||
* Given that a block is 4096 in size, you might need 12 bits to encode the back reference.
|
||||
This means that you have only 4 bits left to encode the length.
|
||||
|
||||
* Dynamic allocation of bits for the back-reference.
|
||||
|
||||
for (i = clear_pos - 1, lmask = 0xFFF, dshift = 12; i >= 0x10; i >>= 1) {
|
||||
lmask >>= 1; // bit mask for length
|
||||
dshift--; // shift width for delta
|
||||
}
|
||||
|
||||
* Now that we can encode a (offset,length) pair as 2 bytes,
|
||||
we still have to know whether a token is back-reference, or plain-text.
|
||||
This is 1 bit per token. 8 tokens are grouped together
|
||||
and preceded with the tags byte.
|
||||
|
||||
">\n(-18,10)stdio" would be encoded as "00000100"
|
||||
(the 1 bit indicates the back reference).
|
||||
|
||||
"00000000"#include"00000000" <ntfs.h"00000100">\n(17,7)stdio"00000001"(16,1)
|
||||
|
||||
* As a compression unit consists of 16 clusters (default), it usually contains more than one of these blocks.
|
||||
If you want to access the second block, it would be a waste of time to decompress the first one.
|
||||
Instead, each block is preceded by a 2-byte length.
|
||||
The lower 12 bits are the length, the higher 4 bits are of unknown purpose.
|
||||
Actually, (n-1) is stored in the low 12 bits.
|
||||
|
||||
* The compression method is based on independently compressing blocks of X clusters,
|
||||
where X = 2 ^ ATTR_HEADER_NONRES.CompressionUnitSize.
|
||||
|
||||
* If the block grows in size, it will be stored uncompressed.
|
||||
A length of exactly 4095 is used to indicate this case.
|
||||
|
||||
* Bit 0x8000 is the flag specifying that the block is compressed.
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
DecompressBlock (
|
||||
IN COMPRESSED *Clusters,
|
||||
OUT UINT8 *Dest OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT16 BlockParameters;
|
||||
UINTN BlockLength;
|
||||
UINT8 TagsByte;
|
||||
UINT8 Tokens;
|
||||
UINT16 ClearTextPointer;
|
||||
UINT8 PlainText;
|
||||
UINT16 Index;
|
||||
UINT16 Length;
|
||||
UINT16 Lmask;
|
||||
UINT16 Delta;
|
||||
UINT16 Dshift;
|
||||
UINT16 BackReference;
|
||||
UINTN SpareBytes;
|
||||
|
||||
ASSERT (Clusters != NULL);
|
||||
|
||||
Status = GetTwoDataRunBytes (Clusters, &BlockParameters);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
BlockLength = (BlockParameters & BLOCK_LENGTH_BITS) + 1U;
|
||||
|
||||
if (Dest != NULL) {
|
||||
if ((BlockParameters & IS_COMPRESSED_BLOCK) != 0) {
|
||||
ClearTextPointer = 0;
|
||||
Tokens = 0;
|
||||
TagsByte = 0;
|
||||
while (BlockLength > 0) {
|
||||
if (ClearTextPointer > COMPRESSION_BLOCK) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Compression block too large\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (Tokens == 0) {
|
||||
Status = GetDataRunByte (Clusters, &TagsByte);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Tokens = 8U;
|
||||
--BlockLength;
|
||||
if (BlockLength == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((TagsByte & 1U) != 0) {
|
||||
//
|
||||
// Back-reference
|
||||
//
|
||||
Status = GetTwoDataRunBytes (Clusters, &BackReference);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
BlockLength -= 2U;
|
||||
|
||||
if (ClearTextPointer == 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Nontext window empty\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Lmask = BLOCK_LENGTH_BITS;
|
||||
Dshift = 12U;
|
||||
for (Index = ClearTextPointer - 1U; Index >= 0x10U; Index >>= 1U) {
|
||||
Lmask >>= 1U;
|
||||
--Dshift;
|
||||
}
|
||||
|
||||
Delta = BackReference >> Dshift;
|
||||
Length = (BackReference & Lmask) + 3U;
|
||||
|
||||
if ((Delta > (ClearTextPointer - 1U)) || (Length >= COMPRESSION_BLOCK)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid back-reference.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (mBufferSize < Length) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #1) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
for (Index = 0; Index < Length; ++Index) {
|
||||
Dest[ClearTextPointer] = Dest[ClearTextPointer - Delta - 1U];
|
||||
++ClearTextPointer;
|
||||
}
|
||||
|
||||
mBufferSize -= Length;
|
||||
} else {
|
||||
//
|
||||
// Plain text
|
||||
//
|
||||
Status = GetDataRunByte (Clusters, &PlainText);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (mBufferSize == 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #2) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Dest[ClearTextPointer++] = PlainText;
|
||||
|
||||
--BlockLength;
|
||||
--mBufferSize;
|
||||
}
|
||||
|
||||
TagsByte >>= 1U;
|
||||
--Tokens;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (BlockLength != COMPRESSION_BLOCK) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid compression block size %d\n", BlockLength));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
while (BlockLength > 0) {
|
||||
SpareBytes = Clusters->FileSystem->ClusterSize - Clusters->ClusterOffset;
|
||||
if (SpareBytes > BlockLength) {
|
||||
SpareBytes = BlockLength;
|
||||
}
|
||||
|
||||
if ((Dest != NULL) && (SpareBytes != 0)) {
|
||||
if (mBufferSize < SpareBytes) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (DecompressBlock #3) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
CopyMem (Dest, &Clusters->Cluster[Clusters->ClusterOffset], SpareBytes);
|
||||
Dest += SpareBytes;
|
||||
mBufferSize -= SpareBytes;
|
||||
}
|
||||
|
||||
BlockLength -= SpareBytes;
|
||||
Clusters->ClusterOffset += SpareBytes;
|
||||
if (BlockLength != 0) {
|
||||
Status = GetNextCluster (Clusters);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of VCNs containing the stream of a compressed file attribute is divided
|
||||
in compression units (also called chunks) of 16 cluster.
|
||||
|
||||
* The alpha stage: if all the 16 clusters of a compression unit are full of zeroes,
|
||||
this compression unit is called a Sparse unit and is not physically stored.
|
||||
Instead, an element with no Offset field (F=0, the Offset is assumed to be 0 too)
|
||||
and a Length of 16 clusters is put in the Runlist.
|
||||
|
||||
* The beta stage: if the compression of the unit is possible,
|
||||
N (< 16) clusters are physically stored, and an element with a Length of N
|
||||
is put in the Runlist, followed by another element with no Offset field
|
||||
(F=0, the Offset is assumed to be 0 too) and a Length of 16 - N.
|
||||
|
||||
* If the unit is not compressed, 16 clusters are physically stored,
|
||||
and an element with a Length of 16 is put in the Runlist.
|
||||
|
||||
Example
|
||||
|
||||
Runlist:
|
||||
21 14 00 01 11 10 18 11 05 15 01 27 11 20 05
|
||||
|
||||
Decode
|
||||
0x14 at 0x100
|
||||
0x10 at + 0x18
|
||||
0x05 at + 0x15
|
||||
0x27 at + none
|
||||
0x20 at + 0x05
|
||||
|
||||
Absolute LCNs
|
||||
0x14 at 0x100
|
||||
0x10 at 0x118
|
||||
0x05 at 0x12D
|
||||
0x27 at none
|
||||
0x20 at 0x132
|
||||
|
||||
Regroup
|
||||
0x10 at 0x100 Unit not compressed
|
||||
|
||||
0x04 at 0x110 Unit not compressed
|
||||
0x0C at 0x118
|
||||
|
||||
0x04 at 0x118 Compressed unit
|
||||
0x05 at 0x12D
|
||||
0x07 at none
|
||||
|
||||
0x10 at none Sparse unit
|
||||
|
||||
0x10 at none Sparse unit
|
||||
|
||||
0x10 at 0x132 Unit not compressed
|
||||
|
||||
0x10 at 0x142 Unit not compressed
|
||||
**/
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
ReadCompressedBlock (
|
||||
IN RUNLIST *Runlist,
|
||||
OUT UINT8 *Dest OPTIONAL,
|
||||
IN UINTN BlocksTotal
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINTN SpareBlocks;
|
||||
UINT64 SpareClusters;
|
||||
UINT64 BlocksPerCluster;
|
||||
UINT64 ClustersPerBlock;
|
||||
UINT64 ClearTextClusters;
|
||||
UINTN ClusterSize;
|
||||
|
||||
ASSERT (Runlist != NULL);
|
||||
|
||||
BlocksPerCluster = 0;
|
||||
ClustersPerBlock = 0;
|
||||
ClusterSize = Runlist->Unit.FileSystem->ClusterSize;
|
||||
|
||||
mBufferSize = BlocksTotal * COMPRESSION_BLOCK;
|
||||
|
||||
if (ClusterSize >= COMPRESSION_BLOCK) {
|
||||
BlocksPerCluster = DivU64x64Remainder (ClusterSize, COMPRESSION_BLOCK, NULL);
|
||||
} else {
|
||||
ClustersPerBlock = DivU64x64Remainder (COMPRESSION_BLOCK, ClusterSize, NULL);
|
||||
}
|
||||
|
||||
while (BlocksTotal != 0) {
|
||||
if ((Runlist->TargetVcn & UNIT_MASK) == 0) {
|
||||
if ((Runlist->Unit.Head != Runlist->Unit.Tail) && (Runlist->IsSparse == FALSE)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid compression block\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Runlist->Unit.Head = Runlist->Unit.Tail = 0;
|
||||
Runlist->Unit.CurrentVcn = Runlist->TargetVcn;
|
||||
Runlist->Unit.ClusterOffset = ClusterSize;
|
||||
if (Runlist->TargetVcn >= Runlist->NextVcn) {
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
while ((Runlist->TargetVcn + mUnitSize) > Runlist->NextVcn) {
|
||||
if (Runlist->IsSparse) {
|
||||
break;
|
||||
}
|
||||
|
||||
Runlist->Unit.Elements[Runlist->Unit.Tail].Vcn = Runlist->NextVcn;
|
||||
Runlist->Unit.Elements[Runlist->Unit.Tail].Lcn = Runlist->CurrentLcn + Runlist->NextVcn - Runlist->CurrentVcn;
|
||||
++Runlist->Unit.Tail;
|
||||
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ClusterSize >= COMPRESSION_BLOCK) {
|
||||
SpareBlocks = (UINTN)((mUnitSize - (Runlist->TargetVcn & UNIT_MASK)) * BlocksPerCluster);
|
||||
} else {
|
||||
SpareBlocks = (UINTN)DivU64x64Remainder (mUnitSize - (Runlist->TargetVcn & UNIT_MASK), ClustersPerBlock, NULL);
|
||||
}
|
||||
|
||||
if (SpareBlocks > BlocksTotal) {
|
||||
SpareBlocks = BlocksTotal;
|
||||
}
|
||||
|
||||
BlocksTotal -= SpareBlocks;
|
||||
|
||||
if (Runlist->IsSparse) {
|
||||
if (ClusterSize >= COMPRESSION_BLOCK) {
|
||||
Runlist->TargetVcn += DivU64x64Remainder (SpareBlocks, BlocksPerCluster, NULL);
|
||||
} else {
|
||||
Runlist->TargetVcn += SpareBlocks * ClustersPerBlock;
|
||||
}
|
||||
|
||||
if (Runlist->Unit.Tail == 0) {
|
||||
if (Dest != NULL) {
|
||||
if (mBufferSize < (SpareBlocks * COMPRESSION_BLOCK)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #1) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
ZeroMem (Dest, SpareBlocks * COMPRESSION_BLOCK);
|
||||
Dest += SpareBlocks * COMPRESSION_BLOCK;
|
||||
mBufferSize -= SpareBlocks * COMPRESSION_BLOCK;
|
||||
}
|
||||
} else {
|
||||
while (SpareBlocks != 0) {
|
||||
Status = DecompressBlock (&Runlist->Unit, Dest);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (Dest != NULL) {
|
||||
Dest += COMPRESSION_BLOCK;
|
||||
}
|
||||
|
||||
--SpareBlocks;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ClusterSize >= COMPRESSION_BLOCK) {
|
||||
SpareClusters = DivU64x64Remainder (SpareBlocks, BlocksPerCluster, NULL);
|
||||
} else {
|
||||
SpareClusters = SpareBlocks * ClustersPerBlock;
|
||||
}
|
||||
|
||||
while ((Runlist->Unit.Head < Runlist->Unit.Tail) && (SpareClusters != 0)) {
|
||||
ClearTextClusters = Runlist->Unit.Elements[Runlist->Unit.Head].Vcn - Runlist->TargetVcn;
|
||||
if (ClearTextClusters > SpareClusters) {
|
||||
ClearTextClusters = SpareClusters;
|
||||
}
|
||||
|
||||
Runlist->TargetVcn += ClearTextClusters;
|
||||
if (Dest != NULL) {
|
||||
if (mBufferSize < (ClearTextClusters * ClusterSize)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #2) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Status = DiskRead (
|
||||
Runlist->Unit.FileSystem,
|
||||
Runlist->Unit.Elements[Runlist->Unit.Head].Lcn * ClusterSize,
|
||||
(UINTN)(ClearTextClusters * ClusterSize),
|
||||
Dest
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Dest += ClearTextClusters * ClusterSize;
|
||||
mBufferSize -= ClearTextClusters * ClusterSize;
|
||||
}
|
||||
|
||||
SpareClusters -= ClearTextClusters;
|
||||
++Runlist->Unit.Head;
|
||||
}
|
||||
|
||||
if (SpareClusters != 0) {
|
||||
if (Dest != NULL) {
|
||||
if (mBufferSize < (SpareClusters * ClusterSize)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadCompressedBlock #3) Buffer overflow.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Status = DiskRead (
|
||||
Runlist->Unit.FileSystem,
|
||||
(Runlist->TargetVcn - Runlist->CurrentVcn + Runlist->CurrentLcn) * ClusterSize,
|
||||
(UINTN)(SpareClusters * ClusterSize),
|
||||
Dest
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Dest += SpareClusters * ClusterSize;
|
||||
mBufferSize -= SpareClusters * ClusterSize;
|
||||
}
|
||||
|
||||
Runlist->TargetVcn += SpareClusters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
Decompress (
|
||||
IN RUNLIST *Runlist,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length,
|
||||
OUT UINT8 *Dest
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT64 Vcn;
|
||||
UINT64 Target;
|
||||
UINTN SpareBytes;
|
||||
UINTN Residual;
|
||||
UINT64 BlocksPerCluster;
|
||||
UINT64 ClustersPerBlock;
|
||||
UINTN ClusterSize;
|
||||
|
||||
ASSERT (Runlist != NULL);
|
||||
ASSERT (Dest != NULL);
|
||||
|
||||
ClusterSize = Runlist->Unit.FileSystem->ClusterSize;
|
||||
|
||||
if (Runlist->Unit.ClearTextBlock != NULL) {
|
||||
if ((Offset & (~(COMPRESSION_BLOCK - 1U))) == Runlist->Unit.SavedPosition) {
|
||||
Residual = (UINTN)(COMPRESSION_BLOCK - (Offset - Runlist->Unit.SavedPosition));
|
||||
if (Residual > Length) {
|
||||
Residual = Length;
|
||||
}
|
||||
|
||||
CopyMem (Dest, Runlist->Unit.ClearTextBlock + Offset - Runlist->Unit.SavedPosition, Residual);
|
||||
if (Residual == Length) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
Dest += Residual;
|
||||
Length -= Residual;
|
||||
Offset += Residual;
|
||||
}
|
||||
} else {
|
||||
Runlist->Unit.ClearTextBlock = AllocateZeroPool (COMPRESSION_BLOCK);
|
||||
if (Runlist->Unit.ClearTextBlock == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Runlist->Unit.SavedPosition = 1U;
|
||||
}
|
||||
|
||||
Vcn = Runlist->TargetVcn;
|
||||
Runlist->TargetVcn &= ~(mUnitSize - 1U);
|
||||
while (Runlist->NextVcn <= Runlist->TargetVcn) {
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
Runlist->Unit.Head = Runlist->Unit.Tail = 0;
|
||||
Runlist->Unit.Cluster = AllocateZeroPool (ClusterSize);
|
||||
if (Runlist->Unit.Cluster == NULL) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
if (Vcn > Runlist->TargetVcn) {
|
||||
if (ClusterSize >= COMPRESSION_BLOCK) {
|
||||
BlocksPerCluster = DivU64x64Remainder (ClusterSize, COMPRESSION_BLOCK, NULL);
|
||||
Status = ReadCompressedBlock (
|
||||
Runlist,
|
||||
NULL,
|
||||
(UINTN)((Vcn - Runlist->TargetVcn) * BlocksPerCluster)
|
||||
);
|
||||
} else {
|
||||
ClustersPerBlock = DivU64x64Remainder (COMPRESSION_BLOCK, ClusterSize, NULL);
|
||||
Status = ReadCompressedBlock (
|
||||
Runlist,
|
||||
NULL,
|
||||
(UINTN)DivU64x64Remainder (Vcn - Runlist->TargetVcn, ClustersPerBlock, NULL)
|
||||
);
|
||||
}
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Offset % COMPRESSION_BLOCK) != 0) {
|
||||
Target = Runlist->TargetVcn * ClusterSize;
|
||||
|
||||
Status = ReadCompressedBlock (Runlist, Runlist->Unit.ClearTextBlock, 1U);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Runlist->Unit.SavedPosition = Target;
|
||||
|
||||
Residual = (UINTN)(Offset % COMPRESSION_BLOCK);
|
||||
SpareBytes = COMPRESSION_BLOCK - Residual;
|
||||
if (SpareBytes > Length) {
|
||||
SpareBytes = Length;
|
||||
}
|
||||
|
||||
CopyMem (Dest, &Runlist->Unit.ClearTextBlock[Residual], SpareBytes);
|
||||
if (SpareBytes == Length) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
Dest += SpareBytes;
|
||||
Length -= SpareBytes;
|
||||
}
|
||||
|
||||
Status = ReadCompressedBlock (Runlist, Dest, Length / COMPRESSION_BLOCK);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Dest += (Length / COMPRESSION_BLOCK) * COMPRESSION_BLOCK;
|
||||
Length %= COMPRESSION_BLOCK;
|
||||
if (Length != 0) {
|
||||
Target = Runlist->TargetVcn * ClusterSize;
|
||||
|
||||
Status = ReadCompressedBlock (Runlist, Runlist->Unit.ClearTextBlock, 1U);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Runlist->Unit.SavedPosition = Target;
|
||||
|
||||
CopyMem (Dest, Runlist->Unit.ClearTextBlock, Length);
|
||||
}
|
||||
|
||||
FreePool (Runlist->Unit.ClearTextBlock);
|
||||
FreePool (Runlist->Unit.Cluster);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
629
Platform/OpenNtfsDxe/Data.c
Normal file
629
Platform/OpenNtfsDxe/Data.c
Normal file
@ -0,0 +1,629 @@
|
||||
/** @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"
|
||||
|
||||
UINT64 mUnitSize;
|
||||
|
||||
STATIC
|
||||
UINT64
|
||||
GetLcn (
|
||||
IN RUNLIST *Runlist,
|
||||
IN UINT64 Vcn
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT64 Delta;
|
||||
|
||||
ASSERT (Runlist != NULL);
|
||||
|
||||
if (Vcn >= Runlist->NextVcn) {
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return (UINT64)(-1);
|
||||
}
|
||||
|
||||
return Runlist->CurrentLcn;
|
||||
}
|
||||
|
||||
Delta = Vcn - Runlist->CurrentVcn;
|
||||
|
||||
return Runlist->IsSparse ? 0 : (Runlist->CurrentLcn + Delta);
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
ReadClusters (
|
||||
IN RUNLIST *Runlist,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length,
|
||||
OUT UINT8 *Dest
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT64 Index;
|
||||
UINT64 ClustersTotal;
|
||||
UINT64 Cluster;
|
||||
UINT64 OffsetInsideCluster;
|
||||
UINTN Size;
|
||||
UINTN ClusterSize;
|
||||
|
||||
ASSERT (Runlist != NULL);
|
||||
ASSERT (Dest != NULL);
|
||||
|
||||
ClusterSize = Runlist->Unit.FileSystem->ClusterSize;
|
||||
OffsetInsideCluster = Offset & (ClusterSize - 1U);
|
||||
Size = ClusterSize;
|
||||
ClustersTotal = DivU64x64Remainder (Length + Offset + ClusterSize - 1U, ClusterSize, NULL);
|
||||
|
||||
for (Index = Runlist->TargetVcn; Index < ClustersTotal; ++Index) {
|
||||
Cluster = GetLcn (Runlist, Index);
|
||||
if (Cluster == (UINT64)(-1)) {
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
Cluster *= ClusterSize;
|
||||
|
||||
if (Index == (ClustersTotal - 1U)) {
|
||||
Size = (UINTN)((Length + Offset) & (ClusterSize - 1U));
|
||||
|
||||
if (Size == 0) {
|
||||
Size = ClusterSize;
|
||||
}
|
||||
|
||||
if (Index != Runlist->TargetVcn) {
|
||||
OffsetInsideCluster = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (Index == Runlist->TargetVcn) {
|
||||
Size -= (UINTN)OffsetInsideCluster;
|
||||
}
|
||||
|
||||
if ((Index != Runlist->TargetVcn) && (Index != (ClustersTotal - 1U))) {
|
||||
Size = ClusterSize;
|
||||
OffsetInsideCluster = 0;
|
||||
}
|
||||
|
||||
if (Cluster != 0) {
|
||||
Status = DiskRead (
|
||||
Runlist->Unit.FileSystem,
|
||||
Cluster + OffsetInsideCluster,
|
||||
Size,
|
||||
Dest
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
} else {
|
||||
SetMem (Dest, Size, 0);
|
||||
}
|
||||
|
||||
Dest += Size;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
DiskRead (
|
||||
IN EFI_FS *FileSystem,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Size,
|
||||
IN OUT VOID *Buffer
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_BLOCK_IO_MEDIA *Media;
|
||||
|
||||
ASSERT (FileSystem != NULL);
|
||||
ASSERT (FileSystem->DiskIo != NULL);
|
||||
ASSERT (FileSystem->BlockIo != NULL);
|
||||
|
||||
Media = FileSystem->BlockIo->Media;
|
||||
|
||||
Status = FileSystem->DiskIo->ReadDisk (
|
||||
FileSystem->DiskIo,
|
||||
Media->MediaId,
|
||||
Offset,
|
||||
Size,
|
||||
Buffer
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not read disk at address %Lx\n", Offset));
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadMftRecord (
|
||||
IN EFI_NTFS_FILE *File,
|
||||
OUT UINT8 *Buffer,
|
||||
IN UINT64 RecordNumber
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINTN FileRecordSize;
|
||||
|
||||
ASSERT (File != NULL);
|
||||
ASSERT (Buffer != NULL);
|
||||
|
||||
FileRecordSize = File->FileSystem->FileRecordSize;
|
||||
|
||||
Status = ReadAttr (
|
||||
&File->MftFile.Attr,
|
||||
Buffer,
|
||||
RecordNumber * FileRecordSize,
|
||||
FileRecordSize
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not read MFT Record 0x%Lx\n", RecordNumber));
|
||||
FreeAttr (&File->MftFile.Attr);
|
||||
return Status;
|
||||
}
|
||||
|
||||
return Fixup (
|
||||
Buffer,
|
||||
FileRecordSize,
|
||||
SIGNATURE_32 ('F', 'I', 'L', 'E'),
|
||||
File->FileSystem->SectorSize
|
||||
);
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadAttr (
|
||||
IN NTFS_ATTR *Attr,
|
||||
OUT UINT8 *Dest,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT8 *Current;
|
||||
UINT32 Type;
|
||||
UINT8 *AttrStart;
|
||||
UINT64 Vcn;
|
||||
ATTR_LIST_RECORD *Record;
|
||||
ATTR_HEADER_RES *Res;
|
||||
UINTN ClusterSize;
|
||||
|
||||
ASSERT (Attr != NULL);
|
||||
ASSERT (Dest != NULL);
|
||||
|
||||
Current = Attr->Current;
|
||||
Attr->Next = Attr->Current;
|
||||
ClusterSize = Attr->BaseMftRecord->File->FileSystem->ClusterSize;
|
||||
|
||||
if ((Attr->Flags & NTFS_AF_ALST) != 0) {
|
||||
if (Attr->Last < (Attr->Next + sizeof (ATTR_LIST_RECORD))) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: $ATTRIBUTE_LIST does not contain even a single record.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Record = (ATTR_LIST_RECORD *)Attr->Next;
|
||||
Type = Record->Type;
|
||||
|
||||
Vcn = DivU64x64Remainder (Offset, ClusterSize, NULL);
|
||||
if (COMPRESSION_BLOCK >= ClusterSize) {
|
||||
Vcn &= ~0xFULL;
|
||||
}
|
||||
|
||||
Record = (ATTR_LIST_RECORD *)((UINT8 *)Record + Record->RecordLength);
|
||||
while (((UINT8 *)Record + sizeof (ATTR_LIST_RECORD)) <= Attr->Last) {
|
||||
if (Record->Type != Type) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Record->StartingVCN > Vcn) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (Record->RecordLength == 0) {
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Attr->Next = (UINT8 *)Record;
|
||||
Record = (ATTR_LIST_RECORD *)((UINT8 *)Record + Record->RecordLength);
|
||||
}
|
||||
} else {
|
||||
Res = (ATTR_HEADER_RES *)Attr->Next;
|
||||
Type = Res->Type;
|
||||
}
|
||||
|
||||
AttrStart = FindAttr (Attr, Type);
|
||||
if (AttrStart != NULL) {
|
||||
Status = ReadData (Attr, AttrStart, Dest, Offset, Length);
|
||||
} else {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Attribute not found\n"));
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Attr->Current = Current;
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadData (
|
||||
IN NTFS_ATTR *Attr,
|
||||
IN UINT8 *AttrStart,
|
||||
OUT UINT8 *Dest,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
RUNLIST *Runlist;
|
||||
ATTR_HEADER_NONRES *NonRes;
|
||||
ATTR_HEADER_RES *Res;
|
||||
UINT64 Sector0;
|
||||
UINT64 Sector1;
|
||||
UINT64 OffsetInsideCluster;
|
||||
UINT64 BufferSize;
|
||||
UINTN FileRecordSize;
|
||||
UINTN SectorSize;
|
||||
UINTN ClusterSize;
|
||||
|
||||
if (Length == 0) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
FileRecordSize = Attr->BaseMftRecord->File->FileSystem->FileRecordSize;
|
||||
SectorSize = Attr->BaseMftRecord->File->FileSystem->SectorSize;
|
||||
ClusterSize = Attr->BaseMftRecord->File->FileSystem->ClusterSize;
|
||||
BufferSize = FileRecordSize - (Attr->Current - Attr->BaseMftRecord->FileRecord);
|
||||
//
|
||||
// Resident Attribute
|
||||
//
|
||||
if (BufferSize < sizeof (ATTR_HEADER_RES)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadData #1) Attribute is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Res = (ATTR_HEADER_RES *)AttrStart;
|
||||
|
||||
if (Res->NonResFlag == 0) {
|
||||
if ((Offset + Length) > Res->InfoLength) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Read out of range\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (BufferSize < (Res->InfoOffset + Offset + Length)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Read out of buffer.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
CopyMem (Dest, (UINT8 *)Res + Res->InfoOffset + Offset, Length);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Non-Resident Attribute
|
||||
//
|
||||
if (BufferSize < sizeof (ATTR_HEADER_NONRES)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadData #2) Attribute is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
NonRes = (ATTR_HEADER_NONRES *)AttrStart;
|
||||
|
||||
Runlist = (RUNLIST *)AllocateZeroPool (sizeof (RUNLIST));
|
||||
if (Runlist == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Runlist->Attr = Attr;
|
||||
Runlist->Unit.FileSystem = Attr->BaseMftRecord->File->FileSystem;
|
||||
|
||||
if ( (NonRes->DataRunsOffset > BufferSize)
|
||||
|| (NonRes->DataRunsOffset > NonRes->RealSize)
|
||||
|| (NonRes->RealSize > NonRes->AllocatedSize))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Non-Resident Attribute is corrupted.\n"));
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Runlist->NextDataRun = (UINT8 *)NonRes + NonRes->DataRunsOffset;
|
||||
Runlist->NextVcn = NonRes->StartingVCN;
|
||||
Runlist->CurrentLcn = 0;
|
||||
|
||||
Runlist->TargetVcn = NonRes->StartingVCN + DivU64x64Remainder (Offset, ClusterSize, NULL);
|
||||
if (Runlist->TargetVcn < NonRes->StartingVCN) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Overflow: StartingVCN is too large.\n"));
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if ((Offset + Length) > NonRes->RealSize) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Read out of range\n"));
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if ( ((NonRes->Flags & FLAG_COMPRESSED) != 0)
|
||||
&& ((Attr->Flags & NTFS_AF_GPOS) == 0)
|
||||
&& (NonRes->Type == AT_DATA))
|
||||
{
|
||||
if (NonRes->CompressionUnitSize != 4U) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid copmression unit size.\n"));
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mUnitSize = LShiftU64 (1ULL, NonRes->CompressionUnitSize);
|
||||
|
||||
Status = Decompress (Runlist, Offset, Length, Dest);
|
||||
|
||||
FreePool (Runlist);
|
||||
return Status;
|
||||
}
|
||||
|
||||
while (Runlist->NextVcn <= Runlist->TargetVcn) {
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (Attr->Flags & NTFS_AF_GPOS) {
|
||||
OffsetInsideCluster = Offset & (ClusterSize - 1U);
|
||||
|
||||
Sector0 = DivU64x64Remainder (
|
||||
(Runlist->TargetVcn - Runlist->CurrentVcn + Runlist->CurrentLcn) * ClusterSize + OffsetInsideCluster,
|
||||
SectorSize,
|
||||
NULL
|
||||
);
|
||||
|
||||
Sector1 = Sector0 + 1U;
|
||||
if (Sector1 == DivU64x64Remainder (
|
||||
(Runlist->NextVcn - Runlist->CurrentVcn + Runlist->CurrentLcn) * ClusterSize,
|
||||
SectorSize,
|
||||
NULL
|
||||
))
|
||||
{
|
||||
Status = ReadRunListElement (Runlist);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Runlist);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Sector1 = DivU64x64Remainder (
|
||||
Runlist->CurrentLcn * ClusterSize,
|
||||
SectorSize,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
WriteUnaligned32 ((UINT32 *)Dest, (UINT32)Sector0);
|
||||
WriteUnaligned32 ((UINT32 *)(Dest + 4U), (UINT32)Sector1);
|
||||
|
||||
FreePool (Runlist);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
Status = ReadClusters (
|
||||
Runlist,
|
||||
Offset,
|
||||
Length,
|
||||
Dest
|
||||
);
|
||||
|
||||
FreePool (Runlist);
|
||||
return Status;
|
||||
}
|
||||
|
||||
STATIC
|
||||
UINT64
|
||||
ReadField (
|
||||
IN CONST UINT8 *Run,
|
||||
IN UINT8 FieldSize,
|
||||
IN BOOLEAN Signed
|
||||
)
|
||||
{
|
||||
UINT64 Value;
|
||||
|
||||
ASSERT (Run != NULL);
|
||||
|
||||
Value = 0;
|
||||
//
|
||||
// Offset to the starting LCN of the previous element is a signed value.
|
||||
// So we must check the most significant bit.
|
||||
//
|
||||
if (Signed && (FieldSize != 0) && ((Run[FieldSize - 1U] & 0x80U) != 0)) {
|
||||
Value = (UINT64)(-1);
|
||||
}
|
||||
|
||||
CopyMem (&Value, Run, FieldSize);
|
||||
|
||||
return Value;
|
||||
}
|
||||
|
||||
/**
|
||||
Table 4.10. Layout of a Data Run
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0 | 0.5 | F=Size of the Offset field
|
||||
0.5 | 0.5 | L=Size of the Length field
|
||||
1 | L | Length of the run
|
||||
1+L | F | Offset to the starting LCN of the previous element (signed)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadRunListElement (
|
||||
IN OUT RUNLIST *Runlist
|
||||
)
|
||||
{
|
||||
UINT8 LengthSize;
|
||||
UINT8 OffsetSize;
|
||||
UINT64 OffsetLcn;
|
||||
UINT8 *Run;
|
||||
ATTR_HEADER_NONRES *Attr;
|
||||
UINT64 BufferSize;
|
||||
UINTN FileRecordSize;
|
||||
|
||||
ASSERT (Runlist != NULL);
|
||||
|
||||
Run = Runlist->NextDataRun;
|
||||
FileRecordSize = Runlist->Attr->BaseMftRecord->File->FileSystem->FileRecordSize;
|
||||
BufferSize = FileRecordSize - (Run - Runlist->Attr->BaseMftRecord->FileRecord);
|
||||
|
||||
retry:
|
||||
if (BufferSize == 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadRunListElement #1) Runlist is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
LengthSize = *Run & 0xFU;
|
||||
OffsetSize = (*Run >> 4U) & 0xFU;
|
||||
++Run;
|
||||
--BufferSize;
|
||||
|
||||
if ( (LengthSize > 8U)
|
||||
|| (OffsetSize > 8U)
|
||||
|| ((LengthSize == 0) && (OffsetSize != 0)))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadRunListElement #2) Runlist is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
//
|
||||
// End of Runlist: LengthSize == 0, OffsetSize == 0
|
||||
//
|
||||
if ((LengthSize == 0) && (OffsetSize == 0)) {
|
||||
if ( (Runlist->Attr != NULL)
|
||||
&& ((Runlist->Attr->Flags & NTFS_AF_ALST) != 0))
|
||||
{
|
||||
Attr = (ATTR_HEADER_NONRES *)Runlist->Attr->Current;
|
||||
Attr = (ATTR_HEADER_NONRES *)FindAttr (Runlist->Attr, Attr->Type);
|
||||
if (Attr != NULL) {
|
||||
if (Attr->NonResFlag == 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: $DATA should be non-resident\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Run = (UINT8 *)Attr + Attr->DataRunsOffset;
|
||||
Runlist->CurrentLcn = 0;
|
||||
BufferSize = FileRecordSize - Attr->DataRunsOffset;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Run list overflown\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Runlist->CurrentVcn = Runlist->NextVcn;
|
||||
|
||||
if (BufferSize < LengthSize) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadRunListElement #3) Runlist is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Runlist->NextVcn += ReadField (Run, LengthSize, FALSE);
|
||||
if (Runlist->NextVcn <= Runlist->CurrentVcn) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadRunListElement #3.1) Runlist is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Run += LengthSize;
|
||||
BufferSize -= LengthSize;
|
||||
|
||||
if (BufferSize < OffsetSize) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ReadRunListElement #4) Runlist is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
OffsetLcn = ReadField (Run, OffsetSize, TRUE);
|
||||
Runlist->CurrentLcn += OffsetLcn;
|
||||
|
||||
Run += OffsetSize;
|
||||
BufferSize -= OffsetSize;
|
||||
Runlist->NextDataRun = Run;
|
||||
|
||||
Runlist->IsSparse = (OffsetLcn == 0);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
CHAR16 *
|
||||
ReadSymlink (
|
||||
IN NTFS_FILE *File
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
SYMLINK Symlink;
|
||||
CHAR16 *Substitute;
|
||||
CHAR16 *Letter;
|
||||
UINT64 Offset;
|
||||
|
||||
ASSERT (File != NULL);
|
||||
|
||||
File->FileRecord = AllocateZeroPool (File->File->FileSystem->FileRecordSize);
|
||||
if (File->FileRecord == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Status = ReadMftRecord (File->File, File->FileRecord, File->Inode);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (LocateAttr (&File->Attr, File, AT_SYMLINK) == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: no $SYMLINK in MFT 0x%Lx\n", File->Inode));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Status = ReadAttr (&File->Attr, (UINT8 *)&Symlink, 0, sizeof (Symlink));
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeAttr (&File->Attr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (Symlink.Type) {
|
||||
case (IS_ALIAS | IS_MICROSOFT | S_SYMLINK):
|
||||
Offset = sizeof (Symlink) + 4U + (UINT64)Symlink.SubstituteOffset;
|
||||
break;
|
||||
case (IS_ALIAS | IS_MICROSOFT | S_FILENAME):
|
||||
Offset = sizeof (Symlink) + (UINT64)Symlink.SubstituteOffset;
|
||||
break;
|
||||
default:
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Symlink type invalid (%x)\n", Symlink.Type));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Substitute = AllocateZeroPool (Symlink.SubstituteLength + sizeof (CHAR16));
|
||||
if (Substitute == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Status = ReadAttr (&File->Attr, (UINT8 *)Substitute, Offset, Symlink.SubstituteLength);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeAttr (&File->Attr);
|
||||
FreePool (Substitute);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (Letter = Substitute; *Letter != L'\0'; ++Letter) {
|
||||
if (*Letter == L'\\') {
|
||||
*Letter = L'/';
|
||||
}
|
||||
}
|
||||
|
||||
return Substitute;
|
||||
}
|
||||
825
Platform/OpenNtfsDxe/Disc.c
Normal file
825
Platform/OpenNtfsDxe/Disc.c
Normal file
@ -0,0 +1,825 @@
|
||||
/** @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;
|
||||
}
|
||||
}
|
||||
682
Platform/OpenNtfsDxe/Driver.h
Normal file
682
Platform/OpenNtfsDxe/Driver.h
Normal file
@ -0,0 +1,682 @@
|
||||
/** @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
|
||||
**/
|
||||
|
||||
#ifndef DRIVER_H
|
||||
#define DRIVER_H
|
||||
|
||||
#define NTFS_MAX_MFT 4096
|
||||
#define NTFS_MAX_IDX 16384
|
||||
#define COMPRESSION_BLOCK 4096
|
||||
#define NTFS_DRIVER_VERSION 0x00020000
|
||||
#define MAX_PATH 1024
|
||||
#define MINIMUM_INFO_LENGTH (sizeof (EFI_FILE_INFO) + MAX_PATH * sizeof(CHAR16))
|
||||
#define MINIMUM_FS_INFO_LENGTH (sizeof (EFI_FILE_SYSTEM_INFO) + MAX_PATH * sizeof(CHAR16))
|
||||
#define ATTRIBUTES_END_MARKER 0xFFFFFFFF
|
||||
#define MAX_FILE_SIZE (MAX_UINT32 & ~7ULL)
|
||||
#define S_FILENAME 0x3
|
||||
#define S_SYMLINK 0xC
|
||||
|
||||
/**
|
||||
************
|
||||
*** FILE ***
|
||||
************
|
||||
* Everything on an NTFS volume is a File. Files are composed of Attributes
|
||||
(usually 4: $STANDARD_INFORMATION, $FILE_NAME, $SECURITY_DESCRIPTOR, $DATA).
|
||||
|
||||
* Information about all the Attributes available in a volume is stored in
|
||||
a File named $AttrDef.
|
||||
|
||||
* File named $MFT (Master File Table) is an index of every File on the volume.
|
||||
The description of each File is packed into FILE Records.
|
||||
A FILE Record is built up from a header, several variable length Attributes
|
||||
and an end marker (simply 0xFFFFFFFF).
|
||||
The first FILE Record that describes a given file is called
|
||||
the Base FILE Record and the others are called Extension FILE Records.
|
||||
Only the Base FILE Record is used for referencing the File it describes.
|
||||
Attribute named $ATTRIBUTE_LIST provides the references
|
||||
to all the Extension FILE Records.
|
||||
|
||||
* The $MFT lists the Boot Sector File ($Boot), located at the beginning of the disk.
|
||||
$Boot also lists where to find the $MFT. The $MFT also lists itself.
|
||||
|
||||
* Every Attribute has a standard header and specific fields.
|
||||
The header stores information about the Attribute's type, size,
|
||||
name (optional) and whether it is resident (in $MFT), or not.
|
||||
|
||||
* Non-resident Attributes are stored in intervals of Clusters called Data Runs.
|
||||
Each Data Run is represented by its starting Cluster and its length.
|
||||
|
||||
* The number of sectors that make up a Cluster is always a power of 2,
|
||||
and this number is fixed when the volume is formatted.
|
||||
The Cluster Size for a volume is stored in a File named $Boot.
|
||||
|
||||
* Each Cluster in a volume is given a sequential number.
|
||||
This is its Logical Cluster Number.
|
||||
LCN 0 (zero) refers to the first cluster in the volume (the Boot Sector).
|
||||
|
||||
* Each Cluster of a non-resident stream is given a sequential number.
|
||||
This is its Virtual Cluster Number.
|
||||
VCN 0 (zero) refers to the first cluster of the stream.
|
||||
|
||||
*************
|
||||
*** INDEX ***
|
||||
*************
|
||||
* Under NTFS every object on the volume is a file, even directories.
|
||||
A directory is a sequence of Index Entries containing a filename Attribute.
|
||||
An Index Entry is created for each $FILE_NAME Attribute
|
||||
of each file contained in the folder.
|
||||
|
||||
* $INDEX_ROOT Attribute is the root node of the B+ tree that implements
|
||||
an index (e.g. a directory). This file attribute is always resident.
|
||||
|
||||
* Every Index Record has a standard header and a set of blocks containing
|
||||
an Index Key and Index Data. The size of an Index Record is defined
|
||||
in $Boot File and always seems to be 4KB.
|
||||
|
||||
* $INDEX_ALLOCATION Attribute is the storage location for all sub-nodes
|
||||
of the B+ tree that implements an index (e.g. a directory). It is simply a
|
||||
sequence of all Index Records that belong to the Index.
|
||||
This file attribute is never resident.
|
||||
|
||||
* A sequence of Index Entries follows each INDEX_HEADER structure.
|
||||
Together they make up a complete index. The index follows either
|
||||
an $INDEX_ROOT Attribute or an $INDEX_ALLOCATION Attribute.
|
||||
|
||||
***************
|
||||
*** RUNLIST ***
|
||||
***************
|
||||
* Runlist is a table written in the content part of a non-resident file Attribute,
|
||||
which allows to have access to its stream. (Table 4.10.)
|
||||
|
||||
* The Runlist is a sequence of elements: each element stores an offset to the
|
||||
starting LCN of the previous element and the length in clusters of a Data Run.
|
||||
|
||||
* The layout of the Runlist must take account of the data compression:
|
||||
the set of VCNs containing the stream of a compressed file attribute
|
||||
is divided in compression units (also called chunks) of 16 clusters.
|
||||
|
||||
* Data are compressed using a modified LZ77 algorithm.
|
||||
The basic idea is that substrings of the block which have been seen before
|
||||
are compressed by referencing the string rather than mentioning it again.
|
||||
|
||||
* Only $DATA Attribute can be compressed, or sparse, and only when it is non-resident.
|
||||
**/
|
||||
|
||||
///
|
||||
/// Table 2.1. Standard NTFS Attribute Types
|
||||
///
|
||||
enum {
|
||||
AT_STANDARD_INFORMATION = 0x10,
|
||||
AT_ATTRIBUTE_LIST = 0x20,
|
||||
AT_FILENAME = 0x30,
|
||||
AT_VOLUME_VERSION = 0x40,
|
||||
AT_SECURITY_DESCRIPTOR = 0x50,
|
||||
AT_VOLUME_NAME = 0x60,
|
||||
AT_VOLUME_INFORMATION = 0x70,
|
||||
AT_DATA = 0x80,
|
||||
AT_INDEX_ROOT = 0x90,
|
||||
AT_INDEX_ALLOCATION = 0xA0,
|
||||
AT_BITMAP = 0xB0,
|
||||
AT_SYMLINK = 0xC0,
|
||||
AT_EA_INFORMATION = 0xD0,
|
||||
AT_EA = 0xE0,
|
||||
AT_PROPERTY_SET = 0xF0,
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 2.6. File Flags (Also called attributes in DOS terminology).
|
||||
///
|
||||
enum {
|
||||
ATTR_READ_ONLY = 0x1,
|
||||
ATTR_HIDDEN = 0x2,
|
||||
ATTR_SYSTEM = 0x4,
|
||||
ATTR_ARCHIVE = 0x20,
|
||||
ATTR_DEVICE = 0x40,
|
||||
ATTR_NORMAL = 0x80,
|
||||
ATTR_TEMPORARY = 0x100,
|
||||
ATTR_SPARSE = 0x200,
|
||||
ATTR_REPARSE = 0x400,
|
||||
ATTR_COMPRESSED = 0x800,
|
||||
ATTR_OFFLINE = 0x1000,
|
||||
ATTR_NOT_INDEXED = 0x2000,
|
||||
ATTR_ENCRYPTED = 0x4000,
|
||||
ATTR_DIRECTORY = 0x10000000,
|
||||
ATTR_INDEX_VIEW = 0x20000000
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 3.1. Layout of files on the Volume (Inodes).
|
||||
///
|
||||
enum {
|
||||
MFT_FILE = 0,
|
||||
MFTMIRR_FILE = 1,
|
||||
LOGFILE_FILE = 2,
|
||||
VOLUME_FILE = 3,
|
||||
ATTRDEF_FILE = 4,
|
||||
ROOT_FILE = 5,
|
||||
BITMAP_FILE = 6,
|
||||
BOOT_FILE = 7,
|
||||
BADCLUS_FILE = 8,
|
||||
QUOTA_FILE = 9,
|
||||
UPCASE_FILE = 10,
|
||||
};
|
||||
|
||||
enum {
|
||||
NTFS_AF_ALST = 1,
|
||||
NTFS_AF_MFT_FILE = 2,
|
||||
NTFS_AF_GPOS = 4,
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 4.6. Attribute flags
|
||||
///
|
||||
enum {
|
||||
FLAG_COMPRESSED = 0x0001,
|
||||
FLAG_ENCRYPTED = 0x4000,
|
||||
FLAG_SPARSE = 0x8000
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 4.22. File record flags
|
||||
///
|
||||
enum {
|
||||
IS_IN_USE = 0x01,
|
||||
IS_A_DIRECTORY = 0x02,
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 2.30. Data entry flags
|
||||
///
|
||||
enum {
|
||||
SUB_NODE = 0x01,
|
||||
LAST_INDEX_ENTRY = 0x02,
|
||||
};
|
||||
|
||||
///
|
||||
/// 13.2. Possible Namespaces
|
||||
///
|
||||
enum {
|
||||
POSIX = 0,
|
||||
WINDOWS32 = 1,
|
||||
DOS = 2,
|
||||
WIN32_DOS = 3,
|
||||
};
|
||||
|
||||
///
|
||||
/// Table 2.36. Reparse Tag Flags
|
||||
///
|
||||
enum {
|
||||
IS_ALIAS = 0x20000000,
|
||||
IS_HIGH_LATENCY = 0x40000000,
|
||||
IS_MICROSOFT = 0x80000000,
|
||||
NSS = 0x68000005,
|
||||
NSS_RECOVER = 0x68000006,
|
||||
SIS = 0x68000007,
|
||||
DFS = 0x68000008,
|
||||
MOUNT_POINT = 0x88000003,
|
||||
HSM = 0xA8000004,
|
||||
SYMBOLIC_LINK = 0xE8000000,
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
/**
|
||||
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)
|
||||
0x08 | 8 | $LogFile Sequence Number (LSN)
|
||||
0x10 | 2 | Sequence number
|
||||
0x12 | 2 | Hard link count
|
||||
0x14 | 2 | Offset to the first Attribute
|
||||
0x16 | 2 | Flags
|
||||
0x18 | 4 | Real size of the FILE record
|
||||
0x1C | 4 | Allocated size of the FILE record (a multiple of the Cluster size)
|
||||
0x20 | 8 | File reference to the base FILE record
|
||||
0x28 | 2 | Next Attribute Id
|
||||
| 2 | Update Sequence Number (a)
|
||||
| 2S-2 | Update Sequence Array (a)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Magic;
|
||||
UINT16 UpdateSequenceOffset;
|
||||
UINT16 S_Size;
|
||||
UINT64 LSN;
|
||||
UINT16 SequenceNumber;
|
||||
UINT16 HardLinkCount;
|
||||
UINT16 AttributeOffset;
|
||||
UINT16 Flags;
|
||||
UINT32 RealSize;
|
||||
UINT32 AllocatedSize;
|
||||
UINT64 BaseFileRecord;
|
||||
UINT16 NextAttributeId;
|
||||
} FILE_RECORD_HEADER;
|
||||
|
||||
/**
|
||||
Table 4.3. Layout of a resident named Attribute Header
|
||||
____________________________________________________________________
|
||||
Offset | Size | Value | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 4 | | Attribute Type (e.g. 0x90, 0xB0)
|
||||
0x04 | 4 | | Length (including this header)
|
||||
0x08 | 1 | 0x00 | Non-resident flag
|
||||
0x09 | 1 | N | Name length
|
||||
0x0A | 2 | 0x18 | Offset to the Name
|
||||
0x0C | 2 | 0x00 | Flags
|
||||
0x0E | 2 | | Attribute Id (a)
|
||||
0x10 | 4 | L | Length of the Attribute
|
||||
0x14 | 2 |2N+0x18| Offset to the Attribute (b)
|
||||
0x16 | 1 | | Indexed flag
|
||||
0x17 | 1 | 0x00 | Padding
|
||||
0x18 | 2N |Unicode| The Attribute's Name
|
||||
2N+0x18| L | | The Attribute (c)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Type;
|
||||
UINT32 Length;
|
||||
UINT8 NonResFlag;
|
||||
UINT8 NameLength;
|
||||
UINT16 NameOffset;
|
||||
UINT16 Flags;
|
||||
UINT16 AttributeId;
|
||||
UINT32 InfoLength;
|
||||
UINT16 InfoOffset;
|
||||
UINT8 IndexedFlag;
|
||||
UINT8 Padding;
|
||||
} ATTR_HEADER_RES;
|
||||
|
||||
/**
|
||||
Table 4.5. Layout of a non-resident named Attribute Header
|
||||
____________________________________________________________________
|
||||
Offset | Size | Value | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 4 | | Attribute Type (e.g. 0x80, 0xA0)
|
||||
0x04 | 4 | | Length (including this header)
|
||||
0x08 | 1 | 0x01 | Non-resident flag
|
||||
0x09 | 1 | N | Name length
|
||||
0x0A | 2 | 0x40 | Offset to the Name
|
||||
0x0C | 2 | | Flags
|
||||
0x0E | 2 | | Attribute Id (a)
|
||||
0x10 | 8 | | Starting VCN
|
||||
0x18 | 8 | | Last VCN
|
||||
0x20 | 2 |2N+0x40| Offset to the Data Runs (b)
|
||||
0x22 | 2 | | Compression Unit Size (c)
|
||||
0x24 | 4 | 0x00 | Padding
|
||||
0x28 | 8 | | Allocated size of the attribute (a multiple of the Cluster size)
|
||||
0x30 | 8 | | Real size of the attribute
|
||||
0x38 | 8 | | Initialized data size of the stream (e)
|
||||
0x40 | 2N |Unicode| The Attribute's Name
|
||||
2N+0x40| ... | | Data Runs (b)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Type;
|
||||
UINT32 Length;
|
||||
UINT8 NonResFlag;
|
||||
UINT8 NameLength;
|
||||
UINT16 NameOffset;
|
||||
UINT16 Flags;
|
||||
UINT16 AttributeId;
|
||||
UINT64 StartingVCN;
|
||||
UINT64 LastVCN;
|
||||
UINT16 DataRunsOffset;
|
||||
UINT16 CompressionUnitSize;
|
||||
UINT32 Padding;
|
||||
UINT64 AllocatedSize;
|
||||
UINT64 RealSize;
|
||||
UINT64 InitializedDataSize;
|
||||
} ATTR_HEADER_NONRES;
|
||||
|
||||
/**
|
||||
Table 2.4. Layout of the $ATTRIBUTE_LIST (0x20) attribute
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
~ | ~ | Standard Attribute Header
|
||||
0x00 | 4 | Type
|
||||
0x04 | 2 | Record length
|
||||
0x06 | 1 | Name length (N)
|
||||
0x07 | 1 | Offset to Name (a)
|
||||
0x08 | 8 | Starting VCN (b)
|
||||
0x10 | 8 | Base File Reference of the attribute (Table 4.23)
|
||||
0x18 | 2 | Attribute Id (c)
|
||||
0x1A | 2N | Name in Unicode (if N > 0)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Type;
|
||||
UINT16 RecordLength;
|
||||
UINT8 NameLength;
|
||||
UINT8 NameOffset;
|
||||
UINT64 StartingVCN;
|
||||
UINT64 BaseFileReference;
|
||||
UINT16 AttributeId;
|
||||
} ATTR_LIST_RECORD;
|
||||
|
||||
/**
|
||||
* Table 4.23. Layout of a file reference
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 6 | FILE record number
|
||||
0x06 | 2 | Sequence number
|
||||
--------------------------------------------------------------------
|
||||
|
||||
* Table 2.29. Index Entry
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 8 | File reference
|
||||
0x08 | 2 | Length of the index entry (L)
|
||||
0x0A | 2 | Length of the stream (S)
|
||||
0x0C | 1 | Flags
|
||||
--------------------------------------------------------------------
|
||||
The next field is only present when the last entry flag is not set
|
||||
--------------------------------------------------------------------
|
||||
0x10 | S | Stream - A copy of the body of the indexed attribute
|
||||
0x10+S | ? | Align 8
|
||||
--------------------------------------------------------------------
|
||||
The next field is only present when the sub-node flag is set
|
||||
--------------------------------------------------------------------
|
||||
L-8 | 8 | VCN of the sub-node in the Index Allocation
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT8 FileRecordNumber[6];
|
||||
UINT16 SequenceNumber;
|
||||
UINT16 IndexEntryLength;
|
||||
UINT16 StreamLength;
|
||||
UINT8 Flags;
|
||||
UINT8 Padding[3];
|
||||
} INDEX_ENTRY;
|
||||
|
||||
/**
|
||||
Table 4.26. Layout of a Standard Index Header
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 4 | Magic number 'INDX'
|
||||
0x04 | 2 | Offset to the Update Sequence
|
||||
0x06 | 2 | Size in words of the Update Sequence (S)
|
||||
0x08 | 8 | $LogFile sequence number
|
||||
0x10 | 8 | VCN of this Index record in the Index Allocation
|
||||
0x18 | 4 | Offset to the Index Entries (a)
|
||||
0x1C | 4 | Size of Index Entries (a)
|
||||
0x20 | 4 | Allocated size of the Index Entries (a)
|
||||
0x24 | 1 | 1 if not leaf node (b)
|
||||
0x25 | 3 | Padding (always zero)
|
||||
0x28 | 2 | Update sequence
|
||||
0x2A | 2S-2 | Update sequence array
|
||||
X | Y | Index Entry
|
||||
X+Y | Z | Next Index Entry
|
||||
~ | ~ | ...
|
||||
--------------------------------------------------------------------
|
||||
(a) These values are relative to 0x18
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Magic;
|
||||
UINT16 UpdateSequenceOffset;
|
||||
UINT16 S_Size;
|
||||
UINT64 LSN;
|
||||
UINT64 IndexRecordVCN;
|
||||
} INDEX_HEADER;
|
||||
|
||||
typedef struct {
|
||||
INDEX_HEADER Header;
|
||||
UINT32 IndexEntriesOffset;
|
||||
UINT32 IndexEntriesSize;
|
||||
UINT32 IndexEntriesAllocated;
|
||||
UINT8 IsNotLeafNode;
|
||||
UINT8 Padding[3];
|
||||
} INDEX_RECORD_HEADER;
|
||||
|
||||
/**
|
||||
* Table 2.24. Layout of the $INDEX_ROOT (0x90) Attribute (Index Root)
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
~ | ~ | Standard Attribute Header
|
||||
0x00 | 4 | Attribute Type
|
||||
0x04 | 4 | Collation Rule
|
||||
0x08 | 4 | Size of Index Allocation Entry (bytes)
|
||||
0x0C | 1 | Clusters per Index Record
|
||||
0x0D | 3 | Padding (Align to 8 bytes)
|
||||
--------------------------------------------------------------------
|
||||
|
||||
* Table 2.25. Layout of the $INDEX_ROOT (0x90) Attribute (Index Header)
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 4 | Offset to first Index Entry
|
||||
0x04 | 4 | Total size of the Index Entries
|
||||
0x08 | 4 | Allocated size of the Index Entries
|
||||
0x0C | 1 | Flags
|
||||
0x0D | 3 | Padding (align to 8 bytes)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Type;
|
||||
UINT32 CollationRule;
|
||||
UINT32 IndexAllocationSize;
|
||||
UINT8 IndexRecordClusters;
|
||||
UINT8 Padding[3];
|
||||
} INDEX_ROOT;
|
||||
|
||||
typedef struct {
|
||||
INDEX_ROOT Root;
|
||||
UINT32 FirstEntryOffset;
|
||||
UINT32 EntriesTotalSize;
|
||||
UINT32 EntriesAllocatedSize;
|
||||
UINT8 Flags;
|
||||
UINT8 Padding[3];
|
||||
} ATTR_INDEX_ROOT;
|
||||
|
||||
/**
|
||||
Table 2.5. Layout of the $FILE_NAME (0x30) attribute
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
~ | ~ | Standard Attribute Header
|
||||
0x00 | 8 | File reference to the base record of the parent directory.
|
||||
0x08 | 8 | C Time - File Creation
|
||||
0x10 | 8 | A Time - File Altered
|
||||
0x18 | 8 | M Time - MFT Changed
|
||||
0x20 | 8 | R Time - File Read
|
||||
0x28 | 8 | Allocated size of the file (a multiple of the Cluster size)
|
||||
0x30 | 8 | Real size of the file (size of the unnamed $Data Attribute)
|
||||
0x38 | 4 | Flags, e.g. Directory, compressed, hidden
|
||||
0x3c | 4 | Used by EAs and Reparse
|
||||
0x40 | 1 | Filename length in characters (L)
|
||||
0x41 | 1 | Filename namespace
|
||||
0x42 | 2L | File name in Unicode (not null terminated)
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT64 ParentDir;
|
||||
UINT64 CreationTime;
|
||||
UINT64 AlteredTime;
|
||||
UINT64 ChangedMftTime;
|
||||
UINT64 ReadTime;
|
||||
UINT64 AllocatedSize;
|
||||
UINT64 RealSize;
|
||||
UINT32 Flags;
|
||||
UINT32 Reparse;
|
||||
UINT8 FilenameLen;
|
||||
UINT8 Namespace;
|
||||
} ATTR_FILE_NAME;
|
||||
|
||||
/**
|
||||
Table 3.19. Layout of the $Boot File's $DATA Attribute
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x0000 | 3 | Jump to the boot loader routine
|
||||
0x0003 | 8 | System Id: "NTFS "
|
||||
0x000B | 2 | Bytes per sector
|
||||
0x000D | 1 | Sectors per cluster
|
||||
0x000E | 7 | Unused
|
||||
0x0015 | 1 | Media descriptor (a)
|
||||
0x0016 | 2 | Unused
|
||||
0x0018 | 2 | Sectors per track
|
||||
0x001A | 2 | Number of heads
|
||||
0x001C | 8 | Unused
|
||||
0x0024 | 4 | Usually 80 00 80 00 (b)
|
||||
0x0028 | 8 | Number of sectors in the volume
|
||||
0x0030 | 8 | LCN of VCN 0 of the $MFT
|
||||
0x0038 | 8 | LCN of VCN 0 of the $MFTMirr
|
||||
0x0040 | 4 | Clusters per MFT Record (c)
|
||||
0x0044 | 4 | Clusters per Index Record (c)
|
||||
0x0048 | 8 | Volume serial number
|
||||
~ | ~ | ~
|
||||
0x0200 | | Windows NT Loader
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT8 BootLoaderJump[3];
|
||||
UINT32 SystemId[2];
|
||||
UINT16 BytesPerSector;
|
||||
UINT8 SectorsPerCluster;
|
||||
UINT8 Unused1[7];
|
||||
UINT8 MediaDescriptor;
|
||||
UINT16 Unused2;
|
||||
UINT16 SectorsPerTrack;
|
||||
UINT16 HeadsNumber;
|
||||
UINT64 Unused3;
|
||||
UINT32 Usually;
|
||||
UINT64 VolumeSectorsNumber;
|
||||
UINT64 MftLcn;
|
||||
UINT64 MftMirrLcn;
|
||||
INT8 MftRecordClusters;
|
||||
UINT8 Unused4[3];
|
||||
INT8 IndexRecordClusters;
|
||||
UINT8 Unused5[3];
|
||||
UINT64 VolumeSerialNumber;
|
||||
} BOOT_FILE_DATA;
|
||||
|
||||
/**
|
||||
* Table 2.32. Layout of the $REPARSE_POINT (0xC0) attribute (Microsoft Reparse Point)
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
~ | ~ | Standard Attribute Header
|
||||
0x00 | 4 | Reparse Type (and Flags)
|
||||
0x04 | 2 | Reparse Data Length
|
||||
0x06 | 2 | Padding (align to 8 bytes)
|
||||
0x08 | V | Reparse Data (a)
|
||||
--------------------------------------------------------------------
|
||||
|
||||
* Table 2.34. Symbolic Link Reparse Data
|
||||
____________________________________________________________________
|
||||
Offset | Size | Description
|
||||
--------------------------------------------------------------------
|
||||
0x00 | 2 | Substitute Name Offset
|
||||
0x02 | 2 | Substitute Name Length
|
||||
0x04 | 2 | Print Name Offset
|
||||
0x08 | 2 | Print Name Length
|
||||
0x10 | V | Path Buffer
|
||||
--------------------------------------------------------------------
|
||||
**/
|
||||
typedef struct {
|
||||
UINT32 Type;
|
||||
UINT16 DataLength;
|
||||
UINT16 Padding;
|
||||
UINT16 SubstituteOffset;
|
||||
UINT16 SubstituteLength;
|
||||
UINT16 PrintOffset;
|
||||
UINT16 PrintLength;
|
||||
} SYMLINK;
|
||||
#pragma pack()
|
||||
|
||||
typedef struct _NTFS_FILE NTFS_FILE;
|
||||
typedef struct _EFI_NTFS_FILE EFI_NTFS_FILE;
|
||||
typedef struct _EFI_FS EFI_FS;
|
||||
|
||||
typedef struct {
|
||||
INT32 Flags;
|
||||
UINT8 *ExtensionMftRecord;
|
||||
UINT8 *NonResAttrList;
|
||||
UINT8 *Current;
|
||||
UINT8 *Next;
|
||||
UINT8 *Last;
|
||||
NTFS_FILE *BaseMftRecord;
|
||||
} NTFS_ATTR;
|
||||
|
||||
typedef struct _NTFS_FILE {
|
||||
UINT8 *FileRecord;
|
||||
UINT64 DataAttributeSize;
|
||||
UINT64 CreationTime;
|
||||
UINT64 AlteredTime;
|
||||
UINT64 ReadTime;
|
||||
UINT64 Inode;
|
||||
BOOLEAN InodeRead;
|
||||
NTFS_ATTR Attr;
|
||||
EFI_NTFS_FILE *File;
|
||||
} NTFS_FILE;
|
||||
|
||||
typedef struct _EFI_NTFS_FILE {
|
||||
EFI_FILE_PROTOCOL EfiFile;
|
||||
BOOLEAN IsDir;
|
||||
UINT64 DirIndex;
|
||||
CHAR16 *Path;
|
||||
CHAR16 *BaseName;
|
||||
UINT64 Offset;
|
||||
UINT32 RefCount;
|
||||
NTFS_FILE RootFile;
|
||||
NTFS_FILE MftFile;
|
||||
EFI_FS *FileSystem;
|
||||
} EFI_NTFS_FILE;
|
||||
|
||||
typedef struct _EFI_FS {
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL FileIoInterface;
|
||||
EFI_FILE_PROTOCOL EfiFile;
|
||||
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
||||
EFI_DISK_IO_PROTOCOL *DiskIo;
|
||||
EFI_DEVICE_PATH *DevicePath;
|
||||
UINT64 FirstMftRecord;
|
||||
NTFS_FILE *RootIndex;
|
||||
NTFS_FILE *MftStart;
|
||||
UINTN FileRecordSize;
|
||||
UINTN IndexRecordSize;
|
||||
UINTN SectorSize;
|
||||
UINTN ClusterSize;
|
||||
} EFI_FS;
|
||||
|
||||
typedef struct {
|
||||
UINT64 Vcn;
|
||||
UINT64 Lcn;
|
||||
} UNIT_ELEMENT;
|
||||
|
||||
typedef struct {
|
||||
EFI_FS *FileSystem;
|
||||
UINT8 Head;
|
||||
UINT8 Tail;
|
||||
UNIT_ELEMENT Elements[16];
|
||||
UINT64 CurrentVcn;
|
||||
UINT8 *Cluster;
|
||||
UINTN ClusterOffset;
|
||||
UINT64 SavedPosition;
|
||||
UINT8 *ClearTextBlock;
|
||||
} COMPRESSED;
|
||||
|
||||
typedef struct {
|
||||
BOOLEAN IsSparse;
|
||||
UINT64 TargetVcn;
|
||||
UINT64 CurrentVcn;
|
||||
UINT64 CurrentLcn;
|
||||
UINT64 NextVcn;
|
||||
UINT8 *NextDataRun;
|
||||
NTFS_ATTR *Attr;
|
||||
COMPRESSED Unit;
|
||||
} RUNLIST;
|
||||
|
||||
#endif // DRIVER_H
|
||||
205
Platform/OpenNtfsDxe/Helper.h
Normal file
205
Platform/OpenNtfsDxe/Helper.h
Normal file
@ -0,0 +1,205 @@
|
||||
/** @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
|
||||
**/
|
||||
|
||||
#ifndef HELPER_H
|
||||
#define HELPER_H
|
||||
|
||||
#include "Driver.h"
|
||||
|
||||
#define FSHELP_TYPE_MASK 0xff
|
||||
#define FSHELP_CASE_INSENSITIVE 0x100
|
||||
//
|
||||
// Leap year: is every 4 years, except every 100th isn't, and every 400th is.
|
||||
//
|
||||
#define LEAP_YEAR ((Year % 400U == 0) || ((Year % 4U == 0) && (Year % 100U != 0)))
|
||||
#define YEAR_IN_100NS (365ULL * DAY_IN_100NS)
|
||||
#define DAY_IN_100NS (24ULL * HOUR_IN_100NS)
|
||||
#define HOUR_IN_100NS (60ULL * MINUTE_IN_100NS)
|
||||
#define MINUTE_IN_100NS (60U * SECOND_IN_100NS)
|
||||
#define SECOND_IN_100NS 10000000U
|
||||
#define UNIT_IN_NS 100U
|
||||
#define GREGORIAN_START 1601U
|
||||
|
||||
typedef enum {
|
||||
FSHELP_UNKNOWN,
|
||||
FSHELP_REG,
|
||||
FSHELP_DIR,
|
||||
FSHELP_SYMLINK
|
||||
} FSHELP_FILETYPE;
|
||||
|
||||
typedef struct _STACK_ELEMENT {
|
||||
struct _STACK_ELEMENT *Parent;
|
||||
NTFS_FILE *Node;
|
||||
FSHELP_FILETYPE Type;
|
||||
} STACK_ELEMENT;
|
||||
|
||||
typedef struct {
|
||||
CONST CHAR16 *Path;
|
||||
NTFS_FILE *RootNode;
|
||||
UINT32 SymlinkDepth;
|
||||
STACK_ELEMENT *CurrentNode;
|
||||
} FSHELP_CTX;
|
||||
|
||||
typedef struct {
|
||||
CONST CHAR16 *Name;
|
||||
NTFS_FILE **FoundNode;
|
||||
FSHELP_FILETYPE *FoundType;
|
||||
} FSHELP_ITER_CTX;
|
||||
|
||||
typedef enum {
|
||||
INFO_HOOK,
|
||||
DIR_HOOK,
|
||||
FILE_ITER,
|
||||
} FUNCTION_TYPE;
|
||||
|
||||
VOID
|
||||
FreeAttr (
|
||||
IN NTFS_ATTR *Attr
|
||||
);
|
||||
|
||||
VOID
|
||||
FreeFile (
|
||||
IN NTFS_FILE *File
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
DiskRead (
|
||||
IN EFI_FS *FileSystem,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Size,
|
||||
IN OUT VOID *Buffer
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadMftRecord (
|
||||
IN EFI_NTFS_FILE *File,
|
||||
OUT UINT8 *Buffer,
|
||||
IN UINT64 RecordNumber
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadAttr (
|
||||
IN NTFS_ATTR *NtfsAttr,
|
||||
OUT UINT8 *Dest,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadData (
|
||||
IN NTFS_ATTR *NtfsAttr,
|
||||
IN UINT8 *pa,
|
||||
OUT UINT8 *Dest,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
ReadRunListElement (
|
||||
IN OUT RUNLIST *Runlist
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
NtfsDir (
|
||||
IN EFI_FS *FileSystem,
|
||||
IN CONST CHAR16 *Path,
|
||||
OUT EFI_NTFS_FILE *File,
|
||||
IN FUNCTION_TYPE FunctionType
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
NtfsOpen (
|
||||
IN EFI_NTFS_FILE *File
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
NtfsMount (
|
||||
IN EFI_FS *FileSystem
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
Fixup (
|
||||
IN UINT8 *Buffer,
|
||||
IN UINT64 Length,
|
||||
IN UINT32 Magic,
|
||||
IN UINTN SectorSize
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
InitAttr (
|
||||
OUT NTFS_ATTR *Attr,
|
||||
IN NTFS_FILE *File
|
||||
);
|
||||
|
||||
UINT8 *
|
||||
LocateAttr (
|
||||
IN NTFS_ATTR *Attr,
|
||||
IN NTFS_FILE *Mft,
|
||||
IN UINT32 Type
|
||||
);
|
||||
|
||||
UINT8 *
|
||||
FindAttr (
|
||||
IN NTFS_ATTR *Attr,
|
||||
IN UINT32 Type
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
InitFile (
|
||||
IN OUT NTFS_FILE *File,
|
||||
IN UINT64 RecordNumber
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
FsHelpFindFile (
|
||||
IN CONST CHAR16 *Path,
|
||||
IN NTFS_FILE *RootNode,
|
||||
OUT NTFS_FILE **FoundNode,
|
||||
IN FSHELP_FILETYPE Type
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
IterateDir (
|
||||
IN NTFS_FILE *dir,
|
||||
IN VOID *FileOrCtx,
|
||||
IN FUNCTION_TYPE FunctionType
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
RelativeToAbsolute (
|
||||
OUT CHAR16 *Dest,
|
||||
IN CHAR16 *Source
|
||||
);
|
||||
|
||||
CHAR16 *
|
||||
ReadSymlink (
|
||||
IN NTFS_FILE *Node
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
Decompress (
|
||||
IN RUNLIST *Runlist,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN Length,
|
||||
OUT UINT8 *Dest
|
||||
);
|
||||
|
||||
VOID
|
||||
NtfsToEfiTime (
|
||||
EFI_TIME *EfiTime,
|
||||
UINT64 NtfsTime
|
||||
);
|
||||
|
||||
#endif // HELPER_H
|
||||
998
Platform/OpenNtfsDxe/Index.c
Normal file
998
Platform/OpenNtfsDxe/Index.c
Normal file
@ -0,0 +1,998 @@
|
||||
/** @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"
|
||||
|
||||
STATIC UINT64 mBufferSize;
|
||||
STATIC UINT8 mDaysPerMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0 };
|
||||
INT64 mIndexCounter;
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
FreeNode (
|
||||
IN OUT NTFS_FILE *Node OPTIONAL,
|
||||
IN FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
if ((Node != NULL) && (Node != Context->RootNode)) {
|
||||
FreeFile (Node);
|
||||
FreePool (Node);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
PopElement (
|
||||
IN OUT FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
STACK_ELEMENT *Element;
|
||||
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
Element = Context->CurrentNode;
|
||||
if (Element != NULL) {
|
||||
Context->CurrentNode = Element->Parent;
|
||||
FreeNode (Element->Node, Context);
|
||||
FreePool (Element);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
FreeStack (
|
||||
IN OUT FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
while (Context->CurrentNode != NULL) {
|
||||
PopElement (Context);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC
|
||||
VOID
|
||||
GoUpALevel (
|
||||
IN OUT FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
if (Context->CurrentNode->Parent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
PopElement (Context);
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
PushNode (
|
||||
IN OUT FSHELP_CTX *Context,
|
||||
IN NTFS_FILE *Node,
|
||||
IN FSHELP_FILETYPE FileType
|
||||
)
|
||||
{
|
||||
STACK_ELEMENT *Next;
|
||||
|
||||
ASSERT (Context != NULL);
|
||||
ASSERT (Node != NULL);
|
||||
|
||||
Next = AllocateZeroPool (sizeof (*Next));
|
||||
if (Next == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Next->Node = Node;
|
||||
Next->Type = FileType & ~FSHELP_CASE_INSENSITIVE;
|
||||
Next->Parent = Context->CurrentNode;
|
||||
Context->CurrentNode = Next;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GoToRoot (
|
||||
IN OUT FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
FreeStack (Context);
|
||||
return PushNode (Context, Context->RootNode, FSHELP_DIR);
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
FindFileIter (
|
||||
IN CHAR16 *Name,
|
||||
IN FSHELP_FILETYPE FileType,
|
||||
IN NTFS_FILE *Node,
|
||||
IN FSHELP_ITER_CTX *Context
|
||||
)
|
||||
{
|
||||
INTN Result;
|
||||
|
||||
ASSERT (Name != NULL);
|
||||
ASSERT (Node != NULL);
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
if (FileType == FSHELP_UNKNOWN) {
|
||||
FreePool (Node);
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((FileType & FSHELP_CASE_INSENSITIVE) != 0) {
|
||||
Result = OcStriCmp (Context->Name, Name);
|
||||
} else {
|
||||
Result = StrCmp (Context->Name, Name);
|
||||
}
|
||||
|
||||
if (Result != 0) {
|
||||
FreePool (Node);
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
*Context->FoundNode = Node;
|
||||
*Context->FoundType = FileType;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
NtfsDirHook (
|
||||
IN CHAR16 *Name,
|
||||
IN FSHELP_FILETYPE FileType,
|
||||
IN NTFS_FILE *Node,
|
||||
IN OUT EFI_FILE_INFO *Info
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
|
||||
ASSERT (Name != NULL);
|
||||
ASSERT (Node != NULL);
|
||||
ASSERT (Info != NULL);
|
||||
|
||||
if ( (Name[0] == L'.')
|
||||
&& ((Name[1] == 0) || ( (Name[1] == L'.')
|
||||
&& (Name[2] == 0))))
|
||||
{
|
||||
FreeFile (Node);
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
if ((mIndexCounter)-- != 0) {
|
||||
FreeFile (Node);
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
Status = StrnCpyS (
|
||||
Info->FileName,
|
||||
MAX_PATH,
|
||||
Name,
|
||||
(UINTN)((Info->Size - sizeof (*Info)) / sizeof (CHAR16))
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not copy string.\n"));
|
||||
FreeFile (Node);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Info->Size = sizeof (*Info) + StrnLenS (Info->FileName, MAX_PATH) * sizeof (CHAR16);
|
||||
|
||||
NtfsToEfiTime (&Info->CreateTime, Node->CreationTime);
|
||||
NtfsToEfiTime (&Info->LastAccessTime, Node->ReadTime);
|
||||
NtfsToEfiTime (&Info->ModificationTime, Node->AlteredTime);
|
||||
|
||||
Info->Attribute = EFI_FILE_READ_ONLY;
|
||||
if ((FileType & FSHELP_TYPE_MASK) == FSHELP_DIR) {
|
||||
Info->Attribute |= EFI_FILE_DIRECTORY;
|
||||
}
|
||||
|
||||
FreeFile (Node);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
NtfsDirIter (
|
||||
IN CHAR16 *FileName,
|
||||
IN FSHELP_FILETYPE FileType,
|
||||
IN NTFS_FILE *Node,
|
||||
IN EFI_NTFS_FILE *File
|
||||
)
|
||||
{
|
||||
ASSERT (FileName != NULL);
|
||||
ASSERT (Node != NULL);
|
||||
ASSERT (File != NULL);
|
||||
|
||||
if (StrCmp (FileName, File->BaseName) != 0) {
|
||||
FreeFile (Node);
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
File->IsDir = (BOOLEAN)((FileType & FSHELP_TYPE_MASK) == FSHELP_DIR);
|
||||
FreeFile (Node);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
ListFile (
|
||||
IN NTFS_FILE *Dir,
|
||||
IN UINT8 *Position,
|
||||
OUT VOID *FileOrCtx,
|
||||
IN FUNCTION_TYPE FunctionType
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CHAR16 *Filename;
|
||||
FSHELP_FILETYPE Type;
|
||||
NTFS_FILE *DirFile;
|
||||
INDEX_ENTRY *IndexEntry;
|
||||
ATTR_FILE_NAME *AttrFileName;
|
||||
|
||||
ASSERT (Dir != NULL);
|
||||
ASSERT (Position != NULL);
|
||||
ASSERT (FileOrCtx != NULL);
|
||||
|
||||
IndexEntry = (INDEX_ENTRY *)Position;
|
||||
|
||||
while (TRUE) {
|
||||
if (mBufferSize < sizeof (*IndexEntry)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ListFile #1) INDEX_ENTRY is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if ((IndexEntry->Flags & LAST_INDEX_ENTRY) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (mBufferSize < (sizeof (*IndexEntry) + sizeof (*AttrFileName))) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ListFile #2) INDEX_ENTRY is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
AttrFileName = (ATTR_FILE_NAME *)((UINT8 *)IndexEntry + sizeof (*IndexEntry));
|
||||
|
||||
//
|
||||
// Ignore files in DOS namespace, as they will reappear as Win32 names.
|
||||
//
|
||||
if ((AttrFileName->FilenameLen != 0) && (AttrFileName->Namespace != DOS)) {
|
||||
if ((AttrFileName->Flags & ATTR_REPARSE) != 0) {
|
||||
Type = FSHELP_SYMLINK;
|
||||
} else if ((AttrFileName->Flags & ATTR_DIRECTORY) != 0) {
|
||||
Type = FSHELP_DIR;
|
||||
} else {
|
||||
Type = FSHELP_REG;
|
||||
}
|
||||
|
||||
DirFile = AllocateZeroPool (sizeof (*DirFile));
|
||||
if (DirFile == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not allocate space for DirFile\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
DirFile->File = Dir->File;
|
||||
CopyMem (&DirFile->Inode, IndexEntry->FileRecordNumber, 6U);
|
||||
DirFile->CreationTime = AttrFileName->CreationTime;
|
||||
DirFile->AlteredTime = AttrFileName->AlteredTime;
|
||||
DirFile->ReadTime = AttrFileName->ReadTime;
|
||||
|
||||
if (mBufferSize < (sizeof (*IndexEntry) + sizeof (*AttrFileName) + AttrFileName->FilenameLen * sizeof (CHAR16))) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ListFile #3) INDEX_ENTRY is corrupted.\n"));
|
||||
FreePool (DirFile);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Filename = AllocateZeroPool ((AttrFileName->FilenameLen + 1U) * sizeof (CHAR16));
|
||||
if (Filename == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Failed to allocate buffer for Filename\n"));
|
||||
FreePool (DirFile);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (
|
||||
Filename,
|
||||
(UINT8 *)AttrFileName + sizeof (*AttrFileName),
|
||||
AttrFileName->FilenameLen * sizeof (CHAR16)
|
||||
);
|
||||
|
||||
if (AttrFileName->Namespace != POSIX) {
|
||||
Type |= FSHELP_CASE_INSENSITIVE;
|
||||
}
|
||||
|
||||
switch (FunctionType) {
|
||||
case INFO_HOOK:
|
||||
Status = NtfsDirIter (Filename, Type, DirFile, FileOrCtx);
|
||||
FreePool (DirFile);
|
||||
break;
|
||||
case DIR_HOOK:
|
||||
Status = NtfsDirHook (Filename, Type, DirFile, FileOrCtx);
|
||||
FreePool (DirFile);
|
||||
break;
|
||||
case FILE_ITER:
|
||||
Status = FindFileIter (Filename, Type, DirFile, FileOrCtx);
|
||||
break;
|
||||
}
|
||||
|
||||
FreePool (Filename);
|
||||
|
||||
if (Status == EFI_SUCCESS) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (mBufferSize < IndexEntry->IndexEntryLength)
|
||||
|| (IndexEntry->IndexEntryLength == 0))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (ListFile #4) INDEX_ENTRY is corrupted.\n"));
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mBufferSize -= IndexEntry->IndexEntryLength;
|
||||
IndexEntry = (INDEX_ENTRY *)((UINT8 *)IndexEntry + IndexEntry->IndexEntryLength);
|
||||
}
|
||||
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
FindFile (
|
||||
IN CHAR16 *CurrentPath,
|
||||
IN FSHELP_CTX *Context
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CHAR16 *Name;
|
||||
CHAR16 *Next;
|
||||
NTFS_FILE *FoundNode;
|
||||
FSHELP_FILETYPE FoundType;
|
||||
FSHELP_ITER_CTX IterCtx;
|
||||
CHAR16 *Symlink;
|
||||
CHAR16 *PathPart;
|
||||
|
||||
ASSERT (CurrentPath != NULL);
|
||||
ASSERT (Context != NULL);
|
||||
|
||||
for (Name = CurrentPath; ; Name = Next) {
|
||||
FoundNode = NULL;
|
||||
FoundType = FSHELP_UNKNOWN;
|
||||
|
||||
while (*Name == L'/') {
|
||||
++Name;
|
||||
}
|
||||
|
||||
if (*Name == L'\0') {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
for (Next = Name; (*Next != L'\0') && (*Next != L'/'); ++Next) {
|
||||
//
|
||||
// Search for L'\0' or L'/'.
|
||||
//
|
||||
}
|
||||
|
||||
if (Context->CurrentNode->Type != FSHELP_DIR) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Not a directory\n"));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((Next - Name == 1U) && (Name[0] == L'.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((Next - Name == 2U) && (Name[0] == L'.') && (Name[1] == L'.')) {
|
||||
GoUpALevel (Context);
|
||||
continue;
|
||||
}
|
||||
|
||||
PathPart = AllocateZeroPool ((Next - Name + 1U) * sizeof (CHAR16));
|
||||
if (PathPart == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (PathPart, Name, (Next - Name) * sizeof (CHAR16));
|
||||
|
||||
IterCtx.Name = PathPart;
|
||||
IterCtx.FoundNode = &FoundNode;
|
||||
IterCtx.FoundType = &FoundType;
|
||||
|
||||
Status = IterateDir (Context->CurrentNode->Node, &IterCtx, FILE_ITER);
|
||||
FreePool (PathPart);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (FoundNode == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
Status = PushNode (Context, FoundNode, FoundType);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (Context->CurrentNode->Type == FSHELP_SYMLINK) {
|
||||
if (++Context->SymlinkDepth == 8U) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Too deep nesting of symlinks\n"));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Symlink = ReadSymlink (Context->CurrentNode->Node);
|
||||
if (Symlink == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Symlink leeds nowhere\n"));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (Symlink[0] == L'/') {
|
||||
//
|
||||
// Symlink is an absolute path
|
||||
//
|
||||
Status = GoToRoot (Context);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
} else {
|
||||
GoUpALevel (Context);
|
||||
}
|
||||
|
||||
Status = FindFile (Symlink, Context);
|
||||
FreePool (Symlink);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG ((DEBUG_INFO, "NTFS: File `%s' not found\n", IterCtx.Name));
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
FsHelpFindFile (
|
||||
IN CONST CHAR16 *Path,
|
||||
IN NTFS_FILE *RootNode,
|
||||
OUT NTFS_FILE **FoundNode,
|
||||
IN FSHELP_FILETYPE Type
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
FSHELP_CTX Context;
|
||||
FSHELP_FILETYPE FoundType;
|
||||
|
||||
ASSERT (Path != NULL);
|
||||
ASSERT (RootNode != NULL);
|
||||
ASSERT (FoundNode != NULL);
|
||||
|
||||
Context.Path = Path;
|
||||
Context.RootNode = RootNode;
|
||||
Context.SymlinkDepth = 0;
|
||||
Context.CurrentNode = NULL;
|
||||
|
||||
if (Path[0] != L'/') {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid file name `%s'\n", Path));
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
Status = GoToRoot (&Context);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = FindFile ((CHAR16 *)Path, &Context);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeStack (&Context);
|
||||
return Status;
|
||||
}
|
||||
|
||||
*FoundNode = Context.CurrentNode->Node;
|
||||
FoundType = Context.CurrentNode->Type;
|
||||
//
|
||||
// Do not free the node
|
||||
//
|
||||
Context.CurrentNode->Node = NULL;
|
||||
FreeStack (&Context);
|
||||
|
||||
if (FoundType != Type) {
|
||||
if (*FoundNode != RootNode) {
|
||||
FreeFile (*FoundNode);
|
||||
FreePool (*FoundNode);
|
||||
}
|
||||
|
||||
if (Type == FSHELP_REG) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Not a regular file\n"));
|
||||
} else if (Type == FSHELP_DIR) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Not a directory\n"));
|
||||
}
|
||||
|
||||
Status = EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
IterateDir (
|
||||
IN NTFS_FILE *Dir,
|
||||
IN VOID *FileOrCtx,
|
||||
IN FUNCTION_TYPE FunctionType
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
NTFS_ATTR Attr;
|
||||
ATTR_HEADER_RES *Res;
|
||||
ATTR_HEADER_NONRES *Non;
|
||||
ATTR_INDEX_ROOT *Index;
|
||||
INDEX_RECORD_HEADER *IndexRecord;
|
||||
UINT8 *BitIndex;
|
||||
UINT8 *BitMap;
|
||||
UINTN BitMapLen;
|
||||
UINT8 Bit;
|
||||
UINTN Number;
|
||||
UINTN FileRecordSize;
|
||||
UINTN IndexRecordSize;
|
||||
|
||||
ASSERT (Dir != NULL);
|
||||
ASSERT (FileOrCtx != NULL);
|
||||
|
||||
FileRecordSize = Dir->File->FileSystem->FileRecordSize;
|
||||
IndexRecordSize = Dir->File->FileSystem->IndexRecordSize;
|
||||
|
||||
if (!Dir->InodeRead) {
|
||||
Status = InitFile (Dir, Dir->Inode);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
IndexRecord = NULL;
|
||||
BitMap = NULL;
|
||||
|
||||
Status = InitAttr (&Attr, Dir);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Search in $INDEX_ROOT
|
||||
//
|
||||
while (TRUE) {
|
||||
Res = (ATTR_HEADER_RES *)FindAttr (&Attr, AT_INDEX_ROOT);
|
||||
if (Res == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: no $INDEX_ROOT\n"));
|
||||
FreeAttr (&Attr);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mBufferSize = FileRecordSize - (Attr.Current - Attr.BaseMftRecord->FileRecord);
|
||||
|
||||
if ( (mBufferSize < sizeof (*Res))
|
||||
|| (mBufferSize < (Res->NameOffset + 8U))
|
||||
|| (mBufferSize < Res->InfoOffset))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #1) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mBufferSize -= Res->InfoOffset;
|
||||
|
||||
if ( (Res->NonResFlag != 0)
|
||||
|| (Res->NameLength != 4U)
|
||||
|| (Res->NameOffset != sizeof (*Res))
|
||||
|| (CompareMem ((UINT8 *)Res + Res->NameOffset, L"$I30", 8U) != 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mBufferSize < sizeof (*Index)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #1.1) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
Index = (ATTR_INDEX_ROOT *)((UINT8 *)Res + Res->InfoOffset);
|
||||
if (Index->Root.Type != AT_FILENAME) {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (mBufferSize < (sizeof (INDEX_ROOT) + Index->FirstEntryOffset)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #2) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mBufferSize -= sizeof (INDEX_ROOT) + Index->FirstEntryOffset;
|
||||
|
||||
Status = ListFile (
|
||||
Dir,
|
||||
(UINT8 *)Index + sizeof (INDEX_ROOT) + Index->FirstEntryOffset,
|
||||
FileOrCtx,
|
||||
FunctionType
|
||||
);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
FreeAttr (&Attr);
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
//
|
||||
// Search in $INDEX_ALLOCATION
|
||||
//
|
||||
BitIndex = NULL;
|
||||
BitMapLen = 0;
|
||||
FreeAttr (&Attr);
|
||||
|
||||
Status = InitAttr (&Attr, Dir);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
while ((Non = (ATTR_HEADER_NONRES *)FindAttr (&Attr, AT_BITMAP)) != NULL) {
|
||||
mBufferSize = FileRecordSize - (Attr.Current - Attr.BaseMftRecord->FileRecord);
|
||||
|
||||
if ( (mBufferSize < sizeof (*Non))
|
||||
|| (mBufferSize < (Non->NameOffset + 8U)))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #3) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if ( (Non->NameLength == 4U)
|
||||
&& (CompareMem ((UINT8 *)Non + Non->NameOffset, L"$I30", 8U) == 0))
|
||||
{
|
||||
BitMapLen = (Non->NonResFlag == 0) ?
|
||||
((ATTR_HEADER_RES *)Non)->InfoLength :
|
||||
(UINTN)Non->AllocatedSize;
|
||||
|
||||
if (BitMapLen > MAX_FILE_SIZE) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir) File is too huge.\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
BitMap = AllocateZeroPool (BitMapLen);
|
||||
if (BitMap == NULL) {
|
||||
FreeAttr (&Attr);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
if (Non->NonResFlag == 0) {
|
||||
if (mBufferSize < (((ATTR_HEADER_RES *)Non)->InfoOffset + BitMapLen)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #4) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
CopyMem (
|
||||
BitMap,
|
||||
(UINT8 *)Non + ((ATTR_HEADER_RES *)Non)->InfoOffset,
|
||||
BitMapLen
|
||||
);
|
||||
} else {
|
||||
Status = ReadData (&Attr, (UINT8 *)Non, BitMap, 0, BitMapLen);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Failed to read non-resident $BITMAP\n"));
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
return Status;
|
||||
}
|
||||
|
||||
BitMapLen = (UINTN)Non->RealSize;
|
||||
}
|
||||
|
||||
BitIndex = BitMap;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FreeAttr (&Attr);
|
||||
Non = (ATTR_HEADER_NONRES *)LocateAttr (&Attr, Dir, AT_INDEX_ALLOCATION);
|
||||
|
||||
while (Non != NULL) {
|
||||
mBufferSize = FileRecordSize - (Attr.Current - Attr.BaseMftRecord->FileRecord);
|
||||
|
||||
if ( (mBufferSize < sizeof (*Non))
|
||||
|| (mBufferSize < (Non->NameOffset + 8U)))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: (IterateDir #5) $INDEX_ROOT is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
if (BitIndex != NULL) {
|
||||
FreePool (BitMap);
|
||||
}
|
||||
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if ( (Non->NonResFlag == 1U)
|
||||
&& (Non->NameLength == 4U)
|
||||
&& (Non->NameOffset == sizeof (*Non))
|
||||
&& (CompareMem ((UINT8 *)Non + Non->NameOffset, L"$I30", 8U) == 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Non = (ATTR_HEADER_NONRES *)FindAttr (&Attr, AT_INDEX_ALLOCATION);
|
||||
}
|
||||
|
||||
if ((Non == NULL) && (BitIndex != NULL)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: $BITMAP without $INDEX_ALLOCATION\n"));
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (BitIndex != NULL) {
|
||||
IndexRecord = AllocateZeroPool (IndexRecordSize);
|
||||
if (IndexRecord == NULL) {
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Bit = 1U;
|
||||
for (Number = 0; Number < (BitMapLen * 8U); Number++) {
|
||||
if ((*BitIndex & Bit) != 0) {
|
||||
Status = ReadAttr (
|
||||
&Attr,
|
||||
(UINT8 *)IndexRecord,
|
||||
Number * IndexRecordSize,
|
||||
IndexRecordSize
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
FreePool (IndexRecord);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = Fixup (
|
||||
(UINT8 *)IndexRecord,
|
||||
IndexRecordSize,
|
||||
SIGNATURE_32 ('I', 'N', 'D', 'X'),
|
||||
Dir->File->FileSystem->SectorSize
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
FreePool (IndexRecord);
|
||||
return Status;
|
||||
}
|
||||
|
||||
if ( (IndexRecordSize < sizeof (*IndexRecord))
|
||||
|| (IndexRecordSize < (sizeof (INDEX_HEADER) + IndexRecord->IndexEntriesOffset)))
|
||||
{
|
||||
DEBUG ((DEBUG_INFO, "NTFS: $INDEX_ALLOCATION is corrupted.\n"));
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
FreePool (IndexRecord);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
mBufferSize = IndexRecordSize - (sizeof (INDEX_HEADER) + IndexRecord->IndexEntriesOffset);
|
||||
|
||||
Status = ListFile (
|
||||
Dir,
|
||||
(UINT8 *)IndexRecord + sizeof (INDEX_HEADER) + IndexRecord->IndexEntriesOffset,
|
||||
FileOrCtx,
|
||||
FunctionType
|
||||
);
|
||||
if (!EFI_ERROR (Status)) {
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
FreePool (IndexRecord);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
Bit <<= 1U;
|
||||
if (Bit == 0) {
|
||||
Bit = 1U;
|
||||
++BitIndex;
|
||||
}
|
||||
}
|
||||
|
||||
FreeAttr (&Attr);
|
||||
FreePool (BitMap);
|
||||
FreePool (IndexRecord);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
RelativeToAbsolute (
|
||||
OUT CHAR16 *Dest,
|
||||
IN CHAR16 *Source
|
||||
)
|
||||
{
|
||||
CHAR16 *Buffer;
|
||||
CHAR16 *BPointer;
|
||||
CHAR16 *Start;
|
||||
CHAR16 *End;
|
||||
UINT32 Skip;
|
||||
|
||||
ASSERT (Dest != NULL);
|
||||
ASSERT (Source != NULL);
|
||||
|
||||
Skip = 0;
|
||||
|
||||
Buffer = AllocateZeroPool (StrSize (Source));
|
||||
BPointer = Buffer;
|
||||
|
||||
End = Source + StrLen (Source);
|
||||
Start = End;
|
||||
while (Start > Source) {
|
||||
while (*Start != L'/') {
|
||||
--Start;
|
||||
}
|
||||
|
||||
if ((Start[1] == L'.') && ((Start[2] == L'/') || (Start[2] == L'\0'))) {
|
||||
End = Start;
|
||||
--Start;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((Start[1] == L'.') && (Start[2] == L'.') && ((Start[3] == L'/') || (Start[3] == L'\0'))) {
|
||||
End = Start;
|
||||
--Start;
|
||||
++Skip;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Skip > 0) {
|
||||
End = Start;
|
||||
--Start;
|
||||
--Skip;
|
||||
continue;
|
||||
}
|
||||
|
||||
CopyMem (
|
||||
BPointer,
|
||||
Start + 1U,
|
||||
(End - Start - 1U) * sizeof (CHAR16)
|
||||
);
|
||||
BPointer += End - Start - 1U;
|
||||
|
||||
*BPointer = L'/';
|
||||
++BPointer;
|
||||
|
||||
End = Start;
|
||||
--Start;
|
||||
}
|
||||
|
||||
if (Skip > 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Invalid path: root has no parent.\n"));
|
||||
FreePool (Buffer);
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
End = BPointer - 1U;
|
||||
Start = End - 1U;
|
||||
while (Start > Buffer) {
|
||||
while ((Start >= Buffer) && (*Start != L'/')) {
|
||||
--Start;
|
||||
}
|
||||
|
||||
*Dest = L'/';
|
||||
++Dest;
|
||||
|
||||
CopyMem (
|
||||
Dest,
|
||||
Start + 1U,
|
||||
(End - Start - 1U) * sizeof (CHAR16)
|
||||
);
|
||||
Dest += End - Start - 1U;
|
||||
|
||||
End = Start;
|
||||
--Start;
|
||||
}
|
||||
|
||||
FreePool (Buffer);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
NTFS Time is the number of 100ns units since Jan 1, 1601.
|
||||
The signifigance of this date is that it is the beginning
|
||||
of the first full century of the Gregorian Calendar.
|
||||
The time displayed in Shell will only match the time
|
||||
displayed in Windows if the Windows time zone is set to
|
||||
Coordinated Universal Time (UTC). Otherwise, it will be
|
||||
skewed by the time zone setting.
|
||||
**/
|
||||
VOID
|
||||
NtfsToEfiTime (
|
||||
EFI_TIME *EfiTime,
|
||||
UINT64 NtfsTime
|
||||
)
|
||||
{
|
||||
UINT64 Remainder64;
|
||||
UINT32 Remainder32;
|
||||
UINT16 Year;
|
||||
UINT8 Month;
|
||||
UINT32 Day;
|
||||
UINT32 LastDay;
|
||||
UINT32 PrevLD;
|
||||
UINT8 Index;
|
||||
UINT64 Temp;
|
||||
|
||||
ASSERT (EfiTime != NULL);
|
||||
|
||||
EfiTime->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
|
||||
EfiTime->Pad1 = 0;
|
||||
EfiTime->Daylight = 0;
|
||||
EfiTime->Pad2 = 0;
|
||||
//
|
||||
// Because calendars are 1-based (there is no day 0), we have to add
|
||||
// a day's worth of 100-ns units to make these calculations come out correct.
|
||||
//
|
||||
Year = GREGORIAN_START;
|
||||
for (Temp = NtfsTime + DAY_IN_100NS; Temp > YEAR_IN_100NS; Temp -= YEAR_IN_100NS) {
|
||||
if (LEAP_YEAR) {
|
||||
//
|
||||
// Subtract an extra day for leap year
|
||||
//
|
||||
Temp -= DAY_IN_100NS;
|
||||
}
|
||||
|
||||
++Year;
|
||||
}
|
||||
|
||||
//
|
||||
// From what's left, get the day, hour, minute, second and nanosecond.
|
||||
//
|
||||
Day = (UINT32)DivU64x64Remainder (Temp, DAY_IN_100NS, &Remainder64);
|
||||
if (Day == 0) {
|
||||
//
|
||||
// Special handling for last day of year
|
||||
//
|
||||
Year -= 1U;
|
||||
Day = LEAP_YEAR ? 366U : 365U;
|
||||
}
|
||||
|
||||
EfiTime->Year = Year;
|
||||
EfiTime->Hour = (UINT8)DivU64x64Remainder (Remainder64, HOUR_IN_100NS, &Remainder64);
|
||||
EfiTime->Minute = (UINT8)DivU64x32Remainder (Remainder64, MINUTE_IN_100NS, &Remainder32);
|
||||
EfiTime->Second = (UINT8)(Remainder32 / SECOND_IN_100NS);
|
||||
Remainder32 %= SECOND_IN_100NS;
|
||||
EfiTime->Nanosecond = Remainder32 * UNIT_IN_NS;
|
||||
//
|
||||
// "Day" now contains the ordinal date. We have to convert that to month and day.
|
||||
//
|
||||
Month = 1U;
|
||||
LastDay = 31U;
|
||||
PrevLD = 0;
|
||||
for (Index = 1U; Index < 13U; ++Index) {
|
||||
if (Day > LastDay) {
|
||||
Month += 1U;
|
||||
PrevLD = LastDay;
|
||||
LastDay += mDaysPerMonth[Index];
|
||||
if ((Index == 1U) && LEAP_YEAR) {
|
||||
++LastDay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EfiTime->Month = Month;
|
||||
EfiTime->Day = (UINT8)(Day - PrevLD);
|
||||
}
|
||||
251
Platform/OpenNtfsDxe/Info.c
Normal file
251
Platform/OpenNtfsDxe/Info.c
Normal file
@ -0,0 +1,251 @@
|
||||
/** @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"
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
GetLabel (
|
||||
IN CONST EFI_FS *FileSystem,
|
||||
IN OUT CHAR16 **Label
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
NTFS_FILE *BaseMftRecord;
|
||||
ATTR_HEADER_RES *Res;
|
||||
|
||||
ASSERT (FileSystem != NULL);
|
||||
ASSERT (Label != NULL);
|
||||
|
||||
*Label = NULL;
|
||||
BaseMftRecord = NULL;
|
||||
|
||||
Status = FsHelpFindFile (L"/$Volume", FileSystem->RootIndex, &BaseMftRecord, FSHELP_REG);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not find $Volume file.\n"));
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (!BaseMftRecord->InodeRead) {
|
||||
BaseMftRecord->FileRecord = AllocateZeroPool (FileSystem->FileRecordSize);
|
||||
if (BaseMftRecord->FileRecord == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Failed to allocate buffer for FileRecord.\n"));
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Status = ReadMftRecord (BaseMftRecord->File, BaseMftRecord->FileRecord, BaseMftRecord->Inode);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not read Mft Record #%d.\n", BaseMftRecord->Inode));
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
Status = InitAttr (&BaseMftRecord->Attr, BaseMftRecord);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not initialize attribute.\n"));
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Res = (ATTR_HEADER_RES *)FindAttr (&BaseMftRecord->Attr, AT_VOLUME_NAME);
|
||||
if ((Res != NULL) && (Res->NonResFlag == 0) && (Res->InfoLength != 0)) {
|
||||
//
|
||||
// The Volume Name is not terminated with a Unicode NULL.
|
||||
// Its name's length is the size of the attribute as stored in the header.
|
||||
//
|
||||
if (Res->InfoLength > (MAX_FILE_SIZE - sizeof (CHAR16))) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Volume Name is too huge.\n"));
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
*Label = AllocateZeroPool (Res->InfoLength + sizeof (CHAR16));
|
||||
if (*Label == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Failed to allocate buffer for *Label\n"));
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (*Label, (UINT8 *)Res + Res->InfoOffset, Res->InfoLength);
|
||||
}
|
||||
|
||||
FreeFile (BaseMftRecord);
|
||||
FreePool (BaseMftRecord);
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns file info, system info, or the label of a volume
|
||||
depending on information type specified.
|
||||
|
||||
@param This Pointer to this instance of file protocol
|
||||
@param Type Pointer to information type requested
|
||||
@param Len Pointer to size of buffer
|
||||
@param Data Pointer to buffer for returned info
|
||||
|
||||
@retval EFI_STATUS Status of the operation
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileGetInfo (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN EFI_GUID *Type,
|
||||
IN OUT UINTN *Len,
|
||||
OUT VOID *Data
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_NTFS_FILE *File;
|
||||
EFI_FILE_INFO *Info;
|
||||
EFI_FILE_SYSTEM_INFO *FSInfo;
|
||||
EFI_FILE_SYSTEM_VOLUME_LABEL *VLInfo;
|
||||
CHAR16 *Label;
|
||||
UINTN Length;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
ASSERT (Type != NULL);
|
||||
ASSERT (Len != NULL);
|
||||
ASSERT ((Data != NULL) || (*Len == 0));
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
Length = 0;
|
||||
|
||||
if (CompareMem (Type, &gEfiFileInfoGuid, sizeof (*Type)) == 0) {
|
||||
//
|
||||
// Fill in file information
|
||||
//
|
||||
Info = (EFI_FILE_INFO *)Data;
|
||||
|
||||
if (File->BaseName != NULL) {
|
||||
Length = sizeof (EFI_FILE_INFO) + StrSize (File->BaseName);
|
||||
} else if (File->Path != NULL) {
|
||||
Length = sizeof (EFI_FILE_INFO) + StrSize (File->Path);
|
||||
} else {
|
||||
return EFI_VOLUME_CORRUPTED;
|
||||
}
|
||||
|
||||
if (*Len < Length) {
|
||||
*Len = Length;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
ZeroMem (Data, sizeof (EFI_FILE_INFO));
|
||||
|
||||
Info->Size = (UINT64)Length;
|
||||
Info->Attribute = EFI_FILE_READ_ONLY;
|
||||
NtfsToEfiTime (&Info->CreateTime, File->RootFile.CreationTime);
|
||||
NtfsToEfiTime (&Info->LastAccessTime, File->RootFile.ReadTime);
|
||||
NtfsToEfiTime (&Info->ModificationTime, File->RootFile.AlteredTime);
|
||||
|
||||
if (File->IsDir) {
|
||||
Info->Attribute |= EFI_FILE_DIRECTORY;
|
||||
} else {
|
||||
Info->FileSize = File->RootFile.DataAttributeSize;
|
||||
Info->PhysicalSize = File->RootFile.DataAttributeSize;
|
||||
}
|
||||
|
||||
if (File->BaseName != NULL) {
|
||||
CopyMem (Info->FileName, File->BaseName, StrSize (File->BaseName));
|
||||
} else {
|
||||
CopyMem (Info->FileName, File->Path, StrSize (File->Path));
|
||||
}
|
||||
|
||||
*Len = Length;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (CompareMem (Type, &gEfiFileSystemInfoGuid, sizeof (*Type)) == 0) {
|
||||
//
|
||||
// Get file system information
|
||||
//
|
||||
FSInfo = (EFI_FILE_SYSTEM_INFO *)Data;
|
||||
|
||||
if (*Len < MINIMUM_FS_INFO_LENGTH) {
|
||||
*Len = MINIMUM_FS_INFO_LENGTH;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
FSInfo->ReadOnly = TRUE;
|
||||
FSInfo->BlockSize = File->FileSystem->BlockIo->Media->BlockSize;
|
||||
if (FSInfo->BlockSize == 0) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Corrected Media BlockSize\n"));
|
||||
FSInfo->BlockSize = 512;
|
||||
}
|
||||
|
||||
FSInfo->VolumeSize = (File->FileSystem->BlockIo->Media->LastBlock + 1U) * FSInfo->BlockSize;
|
||||
//
|
||||
// The device is Read Only
|
||||
//
|
||||
FSInfo->FreeSpace = 0;
|
||||
|
||||
Length = *Len - sizeof (EFI_FILE_SYSTEM_INFO);
|
||||
Status = FileGetInfo (
|
||||
This,
|
||||
&gEfiFileSystemVolumeLabelInfoIdGuid,
|
||||
&Length,
|
||||
FSInfo->VolumeLabel
|
||||
);
|
||||
|
||||
FSInfo->Size = (UINT64)Length + sizeof (EFI_FILE_SYSTEM_INFO);
|
||||
*Len = (UINTN)FSInfo->Size;
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (CompareMem (Type, &gEfiFileSystemVolumeLabelInfoIdGuid, sizeof (*Type)) == 0) {
|
||||
//
|
||||
// Get the volume label
|
||||
//
|
||||
VLInfo = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Data;
|
||||
|
||||
Status = GetLabel (File->FileSystem, &Label);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not read disk label - %r\n", Status));
|
||||
return EFI_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (Label == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Volume has no label\n"));
|
||||
*Len = 0;
|
||||
} else {
|
||||
if (*Len < StrSize (Label)) {
|
||||
*Len = StrSize (Label);
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
*Len = StrSize (Label);
|
||||
CopyMem (VLInfo->VolumeLabel, Label, *Len);
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileSetInfo (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN EFI_GUID *InformationType,
|
||||
IN UINTN BufferSize,
|
||||
IN VOID *Buffer
|
||||
)
|
||||
{
|
||||
return EFI_WRITE_PROTECTED;
|
||||
}
|
||||
403
Platform/OpenNtfsDxe/NTFS.c
Normal file
403
Platform/OpenNtfsDxe/NTFS.c
Normal file
@ -0,0 +1,403 @@
|
||||
/** @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"
|
||||
|
||||
#define LANGUAGE_CODE_ENGLISH "eng"
|
||||
|
||||
CHAR16 *gNTFSDriverName = L"NTFS Driver";
|
||||
|
||||
EFI_COMPONENT_NAME_PROTOCOL
|
||||
gNTFSDriverNames = {
|
||||
.GetDriverName = NTFSCtlDriverName,
|
||||
.GetControllerName = NTFSCtlGetControllerName,
|
||||
.SupportedLanguages = LANGUAGE_CODE_ENGLISH
|
||||
};
|
||||
|
||||
EFI_DRIVER_BINDING_PROTOCOL
|
||||
gNTFSDriverBinding = {
|
||||
.Supported = NTFSSupported,
|
||||
.Start = NTFSStart,
|
||||
.Stop = NTFSStop,
|
||||
.Version = 0x10U,
|
||||
.ImageHandle = NULL,
|
||||
.DriverBindingHandle = NULL
|
||||
};
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSEntryPoint (
|
||||
IN EFI_HANDLE ImageHandle,
|
||||
IN EFI_SYSTEM_TABLE *SystemTable
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
ImageHandle,
|
||||
&gEfiLoadedImageProtocolGuid,
|
||||
(VOID **)&LoadedImage,
|
||||
ImageHandle,
|
||||
NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not open loaded image protocol - %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
gNTFSDriverBinding.ImageHandle = ImageHandle;
|
||||
gNTFSDriverBinding.DriverBindingHandle = ImageHandle;
|
||||
|
||||
Status = gBS->InstallMultipleProtocolInterfaces (
|
||||
&ImageHandle,
|
||||
&gEfiDriverBindingProtocolGuid,
|
||||
&gNTFSDriverBinding,
|
||||
&gEfiComponentNameProtocolGuid,
|
||||
&gNTFSDriverNames,
|
||||
NULL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not bind driver - %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
LoadedImage->Unload = UnloadNTFSDriver;
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
UnloadNTFSDriver (
|
||||
IN EFI_HANDLE ImageHandle
|
||||
)
|
||||
{
|
||||
EFI_HANDLE *Buffer;
|
||||
UINTN NumOfHandles;
|
||||
UINTN Index;
|
||||
EFI_STATUS Status;
|
||||
|
||||
Status = gBS->LocateHandleBuffer (
|
||||
AllHandles,
|
||||
NULL,
|
||||
NULL,
|
||||
&NumOfHandles,
|
||||
&Buffer
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
for (Index = 0; Index < NumOfHandles; ++Index) {
|
||||
gBS->DisconnectController (Buffer[Index], ImageHandle, NULL);
|
||||
}
|
||||
|
||||
if (Buffer != NULL) {
|
||||
FreePool (Buffer);
|
||||
}
|
||||
|
||||
Status = gBS->UninstallMultipleProtocolInterfaces (
|
||||
ImageHandle,
|
||||
&gEfiDriverBindingProtocolGuid,
|
||||
&gNTFSDriverBinding,
|
||||
&gEfiComponentNameProtocolGuid,
|
||||
&gNTFSDriverNames,
|
||||
NULL
|
||||
);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSSupported (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_FS *Instance;
|
||||
|
||||
Instance = AllocateZeroPool (sizeof (EFI_FS));
|
||||
if (Instance == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Out of memory.\n"));
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
Controller,
|
||||
&gEfiBlockIoProtocolGuid,
|
||||
(VOID **)&Instance->BlockIo,
|
||||
This->DriverBindingHandle,
|
||||
Controller,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Instance);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
Controller,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
(VOID **)&Instance->DiskIo,
|
||||
This->DriverBindingHandle,
|
||||
Controller,
|
||||
EFI_OPEN_PROTOCOL_BY_DRIVER
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (Instance);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = NtfsMount (Instance);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: This is not NTFS Volume.\n"));
|
||||
Status = EFI_UNSUPPORTED;
|
||||
} else {
|
||||
FreeAttr (&Instance->RootIndex->Attr);
|
||||
FreeAttr (&Instance->MftStart->Attr);
|
||||
FreePool (Instance->RootIndex->FileRecord);
|
||||
FreePool (Instance->MftStart->FileRecord);
|
||||
FreePool (Instance->RootIndex->File);
|
||||
}
|
||||
|
||||
gBS->CloseProtocol (
|
||||
Controller,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
This->DriverBindingHandle,
|
||||
Controller
|
||||
);
|
||||
|
||||
FreePool (Instance);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
OpenVolume (
|
||||
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
|
||||
OUT EFI_FILE_PROTOCOL **Root
|
||||
)
|
||||
{
|
||||
EFI_FS *Instance;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
ASSERT (Root != NULL);
|
||||
|
||||
Instance = (EFI_FS *)This;
|
||||
|
||||
*Root = (EFI_FILE_PROTOCOL *)Instance->RootIndex->File;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Installs Simple File System Protocol
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSStart (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_FS *Instance;
|
||||
|
||||
Instance = AllocateZeroPool (sizeof (EFI_FS));
|
||||
if (Instance == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not allocate a new file system instance\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Instance->FileIoInterface.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
|
||||
Instance->FileIoInterface.OpenVolume = OpenVolume;
|
||||
|
||||
Instance->DevicePath = DevicePathFromHandle (Controller);
|
||||
if (Instance->DevicePath == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not get Device Path\n"));
|
||||
FreePool (Instance);
|
||||
return EFI_NO_MAPPING;
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
Controller,
|
||||
&gEfiBlockIoProtocolGuid,
|
||||
(VOID **)&Instance->BlockIo,
|
||||
This->DriverBindingHandle,
|
||||
Controller,
|
||||
|
||||
/*
|
||||
* EFI_OPEN_PROTOCOL_BY_DRIVER would return Access Denied here,
|
||||
* because the disk driver has that protocol already open. So use
|
||||
* EFI_OPEN_PROTOCOL_GET_PROTOCOL (which doesn't require us to close it).
|
||||
*/
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not access BlockIO protocol - %r\n", Status));
|
||||
FreePool (Instance);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
Controller,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
(VOID **)&Instance->DiskIo,
|
||||
This->DriverBindingHandle,
|
||||
Controller,
|
||||
EFI_OPEN_PROTOCOL_BY_DRIVER
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not access DiskIO protocol - %r\n", Status));
|
||||
FreePool (Instance);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Instance->EfiFile.Revision = EFI_FILE_PROTOCOL_REVISION2;
|
||||
Instance->EfiFile.Open = FileOpen;
|
||||
Instance->EfiFile.Close = FileClose;
|
||||
Instance->EfiFile.Delete = FileDelete;
|
||||
Instance->EfiFile.Read = FileRead;
|
||||
Instance->EfiFile.Write = FileWrite;
|
||||
Instance->EfiFile.GetPosition = FileGetPosition;
|
||||
Instance->EfiFile.SetPosition = FileSetPosition;
|
||||
Instance->EfiFile.GetInfo = FileGetInfo;
|
||||
Instance->EfiFile.SetInfo = FileSetInfo;
|
||||
Instance->EfiFile.Flush = FileFlush;
|
||||
|
||||
Status = NtfsMount (Instance);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not mount file system.\n"));
|
||||
FreePool (Instance);
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = gBS->InstallMultipleProtocolInterfaces (
|
||||
&Controller,
|
||||
&gEfiSimpleFileSystemProtocolGuid,
|
||||
&Instance->FileIoInterface,
|
||||
NULL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not install simple file system protocol - %r\n", Status));
|
||||
gBS->CloseProtocol (
|
||||
Controller,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
This->DriverBindingHandle,
|
||||
Controller
|
||||
);
|
||||
|
||||
FreeAttr (&Instance->RootIndex->Attr);
|
||||
FreeAttr (&Instance->MftStart->Attr);
|
||||
FreePool (Instance->RootIndex->FileRecord);
|
||||
FreePool (Instance->MftStart->FileRecord);
|
||||
FreePool (Instance->RootIndex->File);
|
||||
|
||||
FreePool (Instance);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
Uninstall Simple File system Protocol
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSStop (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN UINTN NumberOfChildren,
|
||||
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *NTFS;
|
||||
EFI_FS *Instance;
|
||||
|
||||
Status = gBS->OpenProtocol (
|
||||
Controller,
|
||||
&gEfiSimpleFileSystemProtocolGuid,
|
||||
(VOID **)&NTFS,
|
||||
This->DriverBindingHandle,
|
||||
Controller,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not locate our instance - %r\n", Status));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Instance = (EFI_FS *)NTFS;
|
||||
|
||||
FreeAttr (&Instance->RootIndex->Attr);
|
||||
FreeAttr (&Instance->MftStart->Attr);
|
||||
FreePool (Instance->RootIndex->FileRecord);
|
||||
FreePool (Instance->MftStart->FileRecord);
|
||||
FreePool (Instance->RootIndex->File);
|
||||
|
||||
Status = gBS->UninstallMultipleProtocolInterfaces (
|
||||
Controller,
|
||||
&gEfiSimpleFileSystemProtocolGuid,
|
||||
&NTFS,
|
||||
NULL
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
//
|
||||
// Close DISK_IO Protocol
|
||||
//
|
||||
Status = gBS->CloseProtocol (
|
||||
Controller,
|
||||
&gEfiDiskIoProtocolGuid,
|
||||
This->DriverBindingHandle,
|
||||
Controller
|
||||
);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSCtlDriverName (
|
||||
IN EFI_COMPONENT_NAME_PROTOCOL *This,
|
||||
IN CHAR8 *Language,
|
||||
OUT CHAR16 **DriverName
|
||||
)
|
||||
{
|
||||
*DriverName = gNTFSDriverName;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSCtlGetControllerName (
|
||||
IN EFI_COMPONENT_NAME_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_HANDLE ChildHandle OPTIONAL,
|
||||
IN CHAR8 *Language,
|
||||
OUT CHAR16 **ControllerName
|
||||
)
|
||||
{
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
160
Platform/OpenNtfsDxe/NTFS.h
Normal file
160
Platform/OpenNtfsDxe/NTFS.h
Normal file
@ -0,0 +1,160 @@
|
||||
/** @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
|
||||
**/
|
||||
|
||||
#ifndef NTFS_H
|
||||
#define NTFS_H
|
||||
|
||||
#include "Protocol/PciIo.h"
|
||||
#include "Protocol/DevicePath.h"
|
||||
#include "Protocol/DriverBinding.h"
|
||||
#include "Protocol/BlockIo.h"
|
||||
#include "Protocol/DiskIo.h"
|
||||
#include "Protocol/SimpleFileSystem.h"
|
||||
#include "Protocol/UnicodeCollation.h"
|
||||
#include "Protocol/LoadedImage.h"
|
||||
#include <Protocol/ComponentName.h>
|
||||
|
||||
#include <Guid/FileInfo.h>
|
||||
#include <Guid/FileSystemInfo.h>
|
||||
#include <Guid/FileSystemVolumeLabelInfo.h>
|
||||
|
||||
#include <Library/BaseLib.h>
|
||||
#include <Library/BaseMemoryLib.h>
|
||||
#include <Library/DebugLib.h>
|
||||
#include <Library/DevicePathLib.h>
|
||||
#include <Library/MemoryAllocationLib.h>
|
||||
#include <Library/UefiBootServicesTableLib.h>
|
||||
|
||||
#include <Library/OcStringLib.h>
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSSupported (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSStart (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSStop (
|
||||
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN UINTN NumberOfChildren,
|
||||
IN EFI_HANDLE *ChildHandleBuffer
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
UnloadNTFSDriver (
|
||||
IN EFI_HANDLE ImageHandle
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSCtlDriverName (
|
||||
IN EFI_COMPONENT_NAME_PROTOCOL *This,
|
||||
IN CHAR8 *Language,
|
||||
OUT CHAR16 **DriverName
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
NTFSCtlGetControllerName (
|
||||
IN EFI_COMPONENT_NAME_PROTOCOL *This,
|
||||
IN EFI_HANDLE Controller,
|
||||
IN EFI_HANDLE ChildHandle OPTIONAL,
|
||||
IN CHAR8 *Language,
|
||||
OUT CHAR16 **ControllerName
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileOpen (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
OUT EFI_FILE_PROTOCOL **NewHandle,
|
||||
IN CHAR16 *FileName,
|
||||
IN UINT64 OpenMode,
|
||||
IN UINT64 Attributes
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileClose (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileDelete (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileRead (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN OUT UINTN *BufferSize,
|
||||
OUT VOID *Buffer
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileWrite (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN OUT UINTN *BufferSize,
|
||||
IN VOID *Buffer
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileGetPosition (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
OUT UINT64 *Position
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileSetPosition (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN UINT64 Position
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileGetInfo (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN EFI_GUID *Type,
|
||||
IN OUT UINTN *Len,
|
||||
OUT VOID *Data
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileSetInfo (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN EFI_GUID *InformationType,
|
||||
IN UINTN BufferSize,
|
||||
IN VOID *Buffer
|
||||
);
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileFlush (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
);
|
||||
|
||||
#endif // NTFS_H
|
||||
378
Platform/OpenNtfsDxe/Open.c
Normal file
378
Platform/OpenNtfsDxe/Open.c
Normal file
@ -0,0 +1,378 @@
|
||||
/** @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"
|
||||
|
||||
extern INT64 mIndexCounter;
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileOpen (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
OUT EFI_FILE_PROTOCOL **NewHandle,
|
||||
IN CHAR16 *FileName,
|
||||
IN UINT64 OpenMode,
|
||||
IN UINT64 Attributes
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_NTFS_FILE *File;
|
||||
EFI_FS *FileSystem;
|
||||
EFI_NTFS_FILE *NewFile;
|
||||
CHAR16 Path[MAX_PATH];
|
||||
CHAR16 CleanPath[MAX_PATH];
|
||||
CHAR16 *DirName;
|
||||
UINTN Index;
|
||||
UINTN Length;
|
||||
BOOLEAN AbsolutePath;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
ASSERT (NewHandle != NULL);
|
||||
ASSERT (FileName != NULL);
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
FileSystem = File->FileSystem;
|
||||
AbsolutePath = (*FileName == L'\\');
|
||||
|
||||
ZeroMem (Path, sizeof (Path));
|
||||
|
||||
if (OpenMode != EFI_FILE_MODE_READ) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: File '%s' can only be opened in read-only mode\n", FileName));
|
||||
return EFI_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
if ((*FileName == 0) || (CompareMem (FileName, L".", sizeof (L".")) == 0)) {
|
||||
++File->RefCount;
|
||||
*NewHandle = This;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (AbsolutePath) {
|
||||
Length = 0;
|
||||
} else {
|
||||
Length = StrnLenS (File->Path, MAX_PATH);
|
||||
if (Length == MAX_PATH) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Path is too long.\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Status = StrCpyS (Path, MAX_PATH, File->Path);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not copy string.\n"));
|
||||
return Status;
|
||||
}
|
||||
|
||||
if ((Length == 0) || (Path[Length - 1U] != '/')) {
|
||||
Path[Length++] = L'/';
|
||||
}
|
||||
}
|
||||
|
||||
Status = StrCpyS (&Path[Length], MAX_PATH - Length, FileName);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not copy FileName `%s`.\n", FileName));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Index = StrnLenS (Path, MAX_PATH);
|
||||
while (Index > Length) {
|
||||
--Index;
|
||||
if (Path[Index] == L'\\') {
|
||||
Path[Index] = L'/';
|
||||
}
|
||||
}
|
||||
|
||||
ZeroMem (CleanPath, sizeof (CleanPath));
|
||||
Status = RelativeToAbsolute (CleanPath, Path);
|
||||
if (EFI_ERROR (Status)) {
|
||||
return Status;
|
||||
}
|
||||
|
||||
NewFile = AllocateZeroPool (sizeof (EFI_NTFS_FILE));
|
||||
if (NewFile == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
NewFile->FileSystem = FileSystem;
|
||||
CopyMem (&NewFile->EfiFile, &FileSystem->EfiFile, sizeof (EFI_FILE_PROTOCOL));
|
||||
|
||||
NewFile->Path = AllocateZeroPool (StrnSizeS (CleanPath, MAX_PATH));
|
||||
if (NewFile->Path == NULL) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not instantiate path\n"));
|
||||
FreePool (NewFile);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (NewFile->Path, CleanPath, StrnSizeS (CleanPath, MAX_PATH));
|
||||
|
||||
Index = StrnLenS (CleanPath, MAX_PATH);
|
||||
if (Index == MAX_PATH) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: CleanPath is too long.\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
while (Index > 0) {
|
||||
--Index;
|
||||
if (CleanPath[Index] == L'/') {
|
||||
CleanPath[Index] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DirName = (Index == 0) ? L"/" : CleanPath;
|
||||
NewFile->BaseName = (Index == 0) ? L"\0" : &NewFile->Path[Index + 1U];
|
||||
|
||||
Status = NtfsDir (FileSystem, DirName, NewFile, INFO_HOOK);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (NewFile->Path);
|
||||
FreePool (NewFile);
|
||||
return Status;
|
||||
}
|
||||
|
||||
if (!NewFile->IsDir) {
|
||||
Status = NtfsOpen (NewFile);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreePool (NewFile->Path);
|
||||
FreePool (NewFile);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
NewFile->RefCount++;
|
||||
*NewHandle = &NewFile->EfiFile;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
FileReadDir (
|
||||
IN EFI_NTFS_FILE *File,
|
||||
OUT VOID *Data,
|
||||
IN OUT UINTN *Size
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_FILE_INFO *Info;
|
||||
CHAR16 Path[MAX_PATH];
|
||||
EFI_NTFS_FILE *TmpFile = NULL;
|
||||
UINTN Length;
|
||||
|
||||
ASSERT (File != NULL);
|
||||
ASSERT (Size != NULL);
|
||||
ASSERT ((Data != NULL) || ((Data == NULL) && (*Size == 0)));
|
||||
|
||||
Info = (EFI_FILE_INFO *)Data;
|
||||
|
||||
if (*Size < MINIMUM_INFO_LENGTH) {
|
||||
*Size = MINIMUM_INFO_LENGTH;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
ZeroMem (Path, sizeof (Path));
|
||||
ZeroMem (Data, *Size);
|
||||
Info->Size = *Size;
|
||||
|
||||
Status = StrCpyS (Path, MAX_PATH, File->Path);
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Could not copy string.\n"));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Length = StrnLenS (Path, MAX_PATH);
|
||||
if (Length == MAX_PATH) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Path is too long.\n"));
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
if ((Length == 0) || (Path[Length - 1U] != L'/')) {
|
||||
Path[Length] = L'/';
|
||||
Length++;
|
||||
}
|
||||
|
||||
mIndexCounter = (INT64)File->DirIndex;
|
||||
if (Length == 0) {
|
||||
Status = NtfsDir (File->FileSystem, L"/", Data, DIR_HOOK);
|
||||
} else {
|
||||
Status = NtfsDir (File->FileSystem, File->Path, Data, DIR_HOOK);
|
||||
}
|
||||
|
||||
if (mIndexCounter >= 0) {
|
||||
//
|
||||
// No entries left
|
||||
//
|
||||
*Size = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (EFI_ERROR (Status)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: Directory listing failed.\n"));
|
||||
return Status;
|
||||
}
|
||||
|
||||
Info->FileSize = 0;
|
||||
Info->PhysicalSize = 0;
|
||||
|
||||
if ((Info->Attribute & EFI_FILE_DIRECTORY) == 0) {
|
||||
TmpFile = AllocateZeroPool (sizeof (EFI_NTFS_FILE));
|
||||
if (TmpFile == NULL) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
TmpFile->FileSystem = File->FileSystem;
|
||||
CopyMem (&TmpFile->EfiFile, &File->FileSystem->EfiFile, sizeof (EFI_FILE_PROTOCOL));
|
||||
|
||||
if ((MAX_PATH - Length) < StrSize (Info->FileName)) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: FileName is too long.\n"));
|
||||
FreePool (TmpFile);
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (&Path[Length], Info->FileName, StrSize (Info->FileName));
|
||||
TmpFile->Path = Path;
|
||||
|
||||
Status = NtfsOpen (TmpFile);
|
||||
if (EFI_ERROR (Status)) {
|
||||
// DEBUG ((DEBUG_INFO, "NTFS: Unable to obtain the size of '%s'\n", Info->FileName));
|
||||
} else {
|
||||
Info->FileSize = TmpFile->RootFile.DataAttributeSize;
|
||||
Info->PhysicalSize = TmpFile->RootFile.DataAttributeSize;
|
||||
}
|
||||
|
||||
FreePool (TmpFile);
|
||||
}
|
||||
|
||||
*Size = (UINTN)Info->Size;
|
||||
++File->DirIndex;
|
||||
|
||||
// DEBUG ((DEBUG_INFO, "NTFS: Entry[%d]: '%s' %s\n", File->DirIndex - 1U, Info->FileName,
|
||||
// (Info->Attribute & EFI_FILE_DIRECTORY) ? L"<DIR>" : L""));
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
STATIC
|
||||
EFI_STATUS
|
||||
Read (
|
||||
IN EFI_NTFS_FILE *File,
|
||||
OUT VOID *Data,
|
||||
IN UINTN *Size
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
UINT64 Remaining;
|
||||
NTFS_FILE *BaseMftRecord;
|
||||
|
||||
ASSERT (File != NULL);
|
||||
ASSERT (Size != NULL);
|
||||
ASSERT ((Data != NULL) || ((Data == NULL) && (*Size == 0)));
|
||||
|
||||
Remaining = (File->RootFile.DataAttributeSize > File->Offset) ?
|
||||
File->RootFile.DataAttributeSize - File->Offset : 0;
|
||||
|
||||
if (*Size > Remaining) {
|
||||
*Size = (UINTN)Remaining;
|
||||
}
|
||||
|
||||
BaseMftRecord = &File->RootFile;
|
||||
|
||||
Status = ReadAttr (&BaseMftRecord->Attr, Data, File->Offset, *Size);
|
||||
if (EFI_ERROR (Status)) {
|
||||
*Size = 0;
|
||||
FreeAttr (&BaseMftRecord->Attr);
|
||||
return Status;
|
||||
}
|
||||
|
||||
File->Offset += *Size;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileRead (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN OUT UINTN *BufferSize,
|
||||
OUT VOID *Buffer
|
||||
)
|
||||
{
|
||||
EFI_NTFS_FILE *File;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
ASSERT (BufferSize != NULL);
|
||||
ASSERT ((Buffer != NULL) || ((Buffer == NULL) && (*BufferSize == 0)));
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
|
||||
if (File->IsDir) {
|
||||
return FileReadDir (File, Buffer, BufferSize);
|
||||
}
|
||||
|
||||
return Read (File, Buffer, BufferSize);
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileWrite (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN OUT UINTN *BufferSize,
|
||||
IN VOID *Buffer
|
||||
)
|
||||
{
|
||||
return EFI_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileDelete (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
)
|
||||
{
|
||||
FileClose (This);
|
||||
|
||||
return EFI_WARN_DELETE_FAILURE;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileFlush (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
)
|
||||
{
|
||||
return EFI_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileClose (
|
||||
IN EFI_FILE_PROTOCOL *This
|
||||
)
|
||||
{
|
||||
EFI_NTFS_FILE *File;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
|
||||
if (&File->RootFile == File->FileSystem->RootIndex) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (--File->RefCount == 0) {
|
||||
FreeFile (&File->RootFile);
|
||||
FreeFile (&File->MftFile);
|
||||
|
||||
if (File->Path != NULL) {
|
||||
FreePool (File->Path);
|
||||
}
|
||||
|
||||
FreePool (File);
|
||||
}
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
66
Platform/OpenNtfsDxe/OpenNtfsDxe.inf
Normal file
66
Platform/OpenNtfsDxe/OpenNtfsDxe.inf
Normal file
@ -0,0 +1,66 @@
|
||||
## @file
|
||||
#
|
||||
# New Technologies File System (NTFS) read-only driver.
|
||||
#
|
||||
# Copyright (c) 2022, Mikhail Krichanov. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
##
|
||||
|
||||
[Defines]
|
||||
INF_VERSION = 0x00010005
|
||||
BASE_NAME = OpenNtfsDxe
|
||||
FILE_GUID = 5E18FB33-27FE-4613-86AD-10FABDA95BB0
|
||||
MODULE_TYPE = DXE_DRIVER
|
||||
VERSION_STRING = 1.0
|
||||
ENTRY_POINT = NTFSEntryPoint
|
||||
|
||||
#
|
||||
# The following information is for reference only and not required by the build tools.
|
||||
#
|
||||
# VALID_ARCHITECTURES = IA32 X64
|
||||
#
|
||||
|
||||
[Sources]
|
||||
NTFS.h
|
||||
Driver.h
|
||||
Helper.h
|
||||
NTFS.c
|
||||
Open.c
|
||||
Info.c
|
||||
Position.c
|
||||
Disc.c
|
||||
Data.c
|
||||
Index.c
|
||||
Compression.c
|
||||
|
||||
[Packages]
|
||||
MdePkg/MdePkg.dec
|
||||
OpenCorePkg/OpenCorePkg.dec
|
||||
|
||||
[LibraryClasses]
|
||||
BaseLib
|
||||
BaseMemoryLib
|
||||
DebugLib
|
||||
DevicePathLib
|
||||
MemoryAllocationLib
|
||||
UefiBootServicesTableLib
|
||||
UefiDriverEntryPoint
|
||||
OcStringLib
|
||||
|
||||
[Protocols]
|
||||
gEfiDriverBindingProtocolGuid ## PRODUCES
|
||||
gEfiSimpleFileSystemProtocolGuid ## PRODUCES
|
||||
gEfiComponentNameProtocolGuid ## CONSUMES
|
||||
gEfiDiskIoProtocolGuid ## CONSUMES
|
||||
gEfiBlockIoProtocolGuid ## CONSUMES
|
||||
gEfiUnicodeCollationProtocolGuid ## CONSUMES
|
||||
|
||||
[Guids]
|
||||
gEfiFileInfoGuid ## CONSUMES
|
||||
gEfiFileSystemInfoGuid ## CONSUMES
|
||||
gEfiFileSystemVolumeLabelInfoIdGuid ## CONSUMES
|
||||
|
||||
|
||||
[Depex]
|
||||
TRUE
|
||||
66
Platform/OpenNtfsDxe/Position.c
Normal file
66
Platform/OpenNtfsDxe/Position.c
Normal file
@ -0,0 +1,66 @@
|
||||
/** @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 "Driver.h"
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileGetPosition (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
OUT UINT64 *Position
|
||||
)
|
||||
{
|
||||
EFI_NTFS_FILE *File;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
ASSERT (Position != NULL);
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
|
||||
*Position = File->IsDir ? File->DirIndex : File->Offset;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FileSetPosition (
|
||||
IN EFI_FILE_PROTOCOL *This,
|
||||
IN UINT64 Position
|
||||
)
|
||||
{
|
||||
EFI_NTFS_FILE *File;
|
||||
UINT64 FileSize;
|
||||
|
||||
ASSERT (This != NULL);
|
||||
|
||||
File = (EFI_NTFS_FILE *)This;
|
||||
|
||||
if (File->IsDir) {
|
||||
if (Position != 0) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
File->DirIndex = 0;
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
FileSize = File->RootFile.DataAttributeSize;
|
||||
|
||||
if (Position == 0xFFFFFFFFFFFFFFFFULL) {
|
||||
Position = FileSize;
|
||||
} else if (Position > FileSize) {
|
||||
DEBUG ((DEBUG_INFO, "NTFS: '%s': Cannot seek to #%Lx of %Lx\n", File->Path, Position, FileSize));
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
File->Offset = Position;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
@ -72,6 +72,11 @@ extern EFI_GUID gEfiLegacyRegion2ProtocolGuid;
|
||||
extern EFI_GUID gEfiPciRootBridgeIoProtocolGuid;
|
||||
extern EFI_GUID gEfiSmbiosTableGuid;
|
||||
extern EFI_GUID gEfiUnicodeCollation2ProtocolGuid;
|
||||
extern EFI_GUID gEfiFileSystemInfoGuid;
|
||||
extern EFI_GUID gEfiDiskIoProtocolGuid;
|
||||
extern EFI_GUID gEfiBlockIoProtocolGuid;
|
||||
extern EFI_GUID gEfiDriverBindingProtocolGuid;
|
||||
extern EFI_GUID gEfiComponentNameProtocolGuid;
|
||||
|
||||
extern EFI_GUID gOcBootstrapProtocolGuid;
|
||||
extern EFI_GUID gOcVendorVariableGuid;
|
||||
|
||||
@ -160,6 +160,21 @@ EFI_GUID gEfiSmbiosTableGuid = {
|
||||
EFI_GUID gEfiUnicodeCollation2ProtocolGuid = {
|
||||
0xa4c751fc, 0x23ae, 0x4c3e, { 0x92, 0xe9, 0x49, 0x64, 0xcf, 0x63, 0xf3, 0x49 }
|
||||
};
|
||||
EFI_GUID gEfiFileSystemInfoGuid = {
|
||||
0x09576E93, 0x6D3F, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }
|
||||
};
|
||||
EFI_GUID gEfiDiskIoProtocolGuid = {
|
||||
0xCE345171, 0xBA0B, 0x11D2, { 0x8E, 0x4F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }
|
||||
};
|
||||
EFI_GUID gEfiBlockIoProtocolGuid = {
|
||||
0x964E5B21, 0x6459, 0x11D2, { 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }
|
||||
};
|
||||
EFI_GUID gEfiDriverBindingProtocolGuid = {
|
||||
0x18A031AB, 0xB443, 0x4D1A, { 0xA5, 0xC0, 0x0C, 0x09, 0x26, 0x1E, 0x9F, 0x71 }
|
||||
};
|
||||
EFI_GUID gEfiComponentNameProtocolGuid = {
|
||||
0x107A772C, 0xD5E1, 0x11D4, { 0x9A, 0x46, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }
|
||||
};
|
||||
|
||||
EFI_GUID gOcBootstrapProtocolGuid = {
|
||||
0xBA1EB455, 0xB182, 0x4F14, { 0x85, 0x21, 0xE4, 0x22, 0xC3, 0x25, 0xDE, 0xF6 }
|
||||
|
||||
15
Utilities/TestNtfsDxe/Makefile
Normal file
15
Utilities/TestNtfsDxe/Makefile
Normal file
@ -0,0 +1,15 @@
|
||||
## @file
|
||||
# Copyright (c) 2022, Mikhail Krichanov. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
##
|
||||
|
||||
PROJECT = TestNtfsDxe
|
||||
PRODUCT = $(PROJECT)$(INFIX)$(SUFFIX)
|
||||
OBJS = $(PROJECT).o
|
||||
OBJS += Compression.o Data.o Disc.o Index.o Info.o NTFS.o Open.o Position.o
|
||||
|
||||
include ../../User/Makefile
|
||||
|
||||
CFLAGS += -I../../Platform/OpenNtfsDxe
|
||||
|
||||
VPATH += ../../Platform/OpenNtfsDxe:$
|
||||
257
Utilities/TestNtfsDxe/TestNtfsDxe.c
Normal file
257
Utilities/TestNtfsDxe/TestNtfsDxe.c
Normal file
@ -0,0 +1,257 @@
|
||||
/** @file
|
||||
Copyright (c) 2022, Mikhail Krichanov. All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
**/
|
||||
|
||||
#include <NTFS.h>
|
||||
#include <Helper.h>
|
||||
|
||||
#include <UserFile.h>
|
||||
#include <UserGlobalVar.h>
|
||||
|
||||
UINTN mFuzzOffset;
|
||||
UINTN mFuzzSize;
|
||||
CONST UINT8 *mFuzzPointer;
|
||||
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
FuzzReadDisk (
|
||||
IN EFI_DISK_IO_PROTOCOL *This,
|
||||
IN UINT32 MediaId,
|
||||
IN UINT64 Offset,
|
||||
IN UINTN BufferSize,
|
||||
OUT VOID *Buffer
|
||||
)
|
||||
{
|
||||
if (Buffer == NULL) {
|
||||
return EFI_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if ((mFuzzSize - mFuzzOffset) < BufferSize) {
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
CopyMem (Buffer, mFuzzPointer, BufferSize);
|
||||
mFuzzPointer += BufferSize;
|
||||
mFuzzOffset += BufferSize;
|
||||
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
|
||||
VOID
|
||||
FreeAll (
|
||||
IN CHAR16 *FileName,
|
||||
IN EFI_FS *Instance
|
||||
)
|
||||
{
|
||||
FreePool (FileName);
|
||||
|
||||
if (Instance != NULL) {
|
||||
if (Instance->DiskIo != NULL) {
|
||||
FreePool (Instance->DiskIo);
|
||||
}
|
||||
|
||||
if (Instance->BlockIo != NULL) {
|
||||
if (Instance->BlockIo->Media != NULL) {
|
||||
FreePool (Instance->BlockIo->Media);
|
||||
}
|
||||
|
||||
FreePool (Instance->BlockIo);
|
||||
}
|
||||
|
||||
if (Instance->RootIndex != NULL) {
|
||||
FreeAttr (&Instance->RootIndex->Attr);
|
||||
FreeAttr (&Instance->MftStart->Attr);
|
||||
FreePool (Instance->RootIndex->FileRecord);
|
||||
FreePool (Instance->MftStart->FileRecord);
|
||||
FreePool (Instance->RootIndex->File);
|
||||
}
|
||||
|
||||
FreePool (Instance);
|
||||
}
|
||||
}
|
||||
|
||||
INT32
|
||||
LLVMFuzzerTestOneInput (
|
||||
CONST UINT8 *FuzzData,
|
||||
UINTN FuzzSize
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
EFI_FS *Instance;
|
||||
EFI_FILE_PROTOCOL *This;
|
||||
UINTN BufferSize;
|
||||
VOID *Buffer;
|
||||
EFI_FILE_PROTOCOL *NewHandle;
|
||||
CHAR16 *FileName;
|
||||
VOID *Info;
|
||||
UINTN Len;
|
||||
UINT64 Position;
|
||||
|
||||
mFuzzOffset = 0;
|
||||
mFuzzPointer = FuzzData;
|
||||
mFuzzSize = FuzzSize;
|
||||
|
||||
Instance = NULL;
|
||||
BufferSize = 100;
|
||||
|
||||
//
|
||||
// Construct File Name
|
||||
//
|
||||
FileName = AllocateZeroPool (BufferSize);
|
||||
if (FileName == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (FileName, BufferSize);
|
||||
|
||||
if ((mFuzzSize - mFuzzOffset) < BufferSize) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CopyMem (FileName, mFuzzPointer, BufferSize - 2);
|
||||
mFuzzPointer += BufferSize - 2;
|
||||
mFuzzOffset += BufferSize - 2;
|
||||
|
||||
//
|
||||
// Construct File System
|
||||
//
|
||||
Instance = AllocateZeroPool (sizeof (EFI_FS));
|
||||
if (Instance == NULL) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (Instance, sizeof (EFI_FS));
|
||||
|
||||
Instance->DiskIo = AllocateZeroPool (sizeof (EFI_DISK_IO_PROTOCOL));
|
||||
if (Instance->DiskIo == NULL) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (Instance->DiskIo, sizeof (EFI_DISK_IO_PROTOCOL));
|
||||
|
||||
Instance->DiskIo->ReadDisk = FuzzReadDisk;
|
||||
|
||||
Instance->BlockIo = AllocateZeroPool (sizeof (EFI_BLOCK_IO_PROTOCOL));
|
||||
if (Instance->BlockIo == NULL) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (Instance->BlockIo, sizeof (EFI_BLOCK_IO_PROTOCOL));
|
||||
|
||||
Instance->BlockIo->Media = AllocateZeroPool (sizeof (EFI_BLOCK_IO_MEDIA));
|
||||
if (Instance->BlockIo->Media == NULL) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (Instance->BlockIo->Media, sizeof (EFI_BLOCK_IO_MEDIA));
|
||||
|
||||
Instance->EfiFile.Revision = EFI_FILE_PROTOCOL_REVISION2;
|
||||
Instance->EfiFile.Open = FileOpen;
|
||||
Instance->EfiFile.Close = FileClose;
|
||||
Instance->EfiFile.Delete = FileDelete;
|
||||
Instance->EfiFile.Read = FileRead;
|
||||
Instance->EfiFile.Write = FileWrite;
|
||||
Instance->EfiFile.GetPosition = FileGetPosition;
|
||||
Instance->EfiFile.SetPosition = FileSetPosition;
|
||||
Instance->EfiFile.GetInfo = FileGetInfo;
|
||||
Instance->EfiFile.SetInfo = FileSetInfo;
|
||||
Instance->EfiFile.Flush = FileFlush;
|
||||
|
||||
Status = NtfsMount (Instance);
|
||||
if (EFI_ERROR (Status)) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
This = (EFI_FILE_PROTOCOL *)Instance->RootIndex->File;
|
||||
|
||||
//
|
||||
// Test Ntfs Driver
|
||||
//
|
||||
Status = FileOpen (This, &NewHandle, FileName, EFI_FILE_MODE_READ, 0);
|
||||
if (Status == EFI_SUCCESS) {
|
||||
Buffer = NULL;
|
||||
BufferSize = 0;
|
||||
Status = FileRead (NewHandle, &BufferSize, Buffer);
|
||||
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||
Buffer = AllocateZeroPool (BufferSize);
|
||||
if (Buffer == NULL) {
|
||||
FreeAll (FileName, Instance);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ASAN_CHECK_MEMORY_REGION (Buffer, BufferSize);
|
||||
|
||||
FileRead (NewHandle, &BufferSize, Buffer);
|
||||
|
||||
FileWrite (NewHandle, &BufferSize, Buffer);
|
||||
|
||||
FileFlush (NewHandle);
|
||||
|
||||
FreePool (Buffer);
|
||||
}
|
||||
|
||||
Len = 0;
|
||||
Info = NULL;
|
||||
Status = FileGetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
||||
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||
Info = AllocateZeroPool (Len);
|
||||
FileGetInfo (NewHandle, &gEfiFileInfoGuid, &Len, Info);
|
||||
FreePool (Info);
|
||||
}
|
||||
|
||||
Len = 0;
|
||||
Status = FileGetInfo (NewHandle, &gEfiFileSystemInfoGuid, &Len, Info);
|
||||
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||
Info = AllocateZeroPool (Len);
|
||||
FileGetInfo (NewHandle, &gEfiFileSystemInfoGuid, &Len, Info);
|
||||
FreePool (Info);
|
||||
}
|
||||
|
||||
Len = 0;
|
||||
Status = FileGetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, &Len, Info);
|
||||
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||
Info = AllocateZeroPool (Len);
|
||||
FileGetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, &Len, Info);
|
||||
FreePool (Info);
|
||||
}
|
||||
|
||||
FileSetInfo (NewHandle, &gEfiFileSystemVolumeLabelInfoIdGuid, Len, Info);
|
||||
|
||||
FileGetPosition (NewHandle, &Position);
|
||||
while (!EFI_ERROR (FileSetPosition (NewHandle, Position))) {
|
||||
++Position;
|
||||
}
|
||||
|
||||
FileDelete (NewHandle);
|
||||
}
|
||||
|
||||
FreeAll (FileName, Instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ENTRY_POINT (
|
||||
int argc,
|
||||
char **argv
|
||||
)
|
||||
{
|
||||
uint32_t f;
|
||||
uint8_t *b;
|
||||
|
||||
if ((b = UserReadFile ((argc > 1) ? argv[1] : "in.bin", &f)) == NULL) {
|
||||
DEBUG ((DEBUG_ERROR, "Read fail\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
LLVMFuzzerTestOneInput (b, f);
|
||||
FreePool (b);
|
||||
return 0;
|
||||
}
|
||||
@ -25,6 +25,7 @@ buildutil() {
|
||||
"TestKextInject"
|
||||
"TestMacho"
|
||||
"TestMp3"
|
||||
"TestNtfsDxe"
|
||||
"TestPeCoff"
|
||||
"TestRsaPreprocess"
|
||||
"TestSmbios"
|
||||
@ -164,6 +165,7 @@ package() {
|
||||
"OpenCanopy.efi"
|
||||
"OpenHfsPlus.efi"
|
||||
"OpenLinuxBoot.efi"
|
||||
"OpenNtfsDxe.efi"
|
||||
"OpenPartitionDxe.efi"
|
||||
"OpenRuntime.efi"
|
||||
"OpenUsbKbDxe.efi"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user