/** @file
File System Access for NvVarsFileLib
Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "NvVarsFileLib.h"
#include
#include
#include
/**
Open the NvVars file for reading or writing
@param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
@param[in] ReadingFile - TRUE: open the file for reading. FALSE: writing
@param[out] NvVarsFile - If EFI_SUCCESS is returned, then this is updated
with the opened NvVars file.
@return EFI_SUCCESS if the file was opened
**/
EFI_STATUS
GetNvVarsFile (
IN EFI_HANDLE FsHandle,
IN BOOLEAN ReadingFile,
OUT EFI_FILE_HANDLE *NvVarsFile
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
EFI_FILE_HANDLE Root;
//
// Get the FileSystem protocol on that handle
//
Status = gBS->HandleProtocol (
FsHandle,
&gEfiSimpleFileSystemProtocolGuid,
(VOID **)&Fs
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the volume (the root directory)
//
Status = Fs->OpenVolume (Fs, &Root);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Attempt to open the NvVars file in the root directory
//
Status = Root->Open (
Root,
NvVarsFile,
L"NvVars",
ReadingFile ?
EFI_FILE_MODE_READ :
(
EFI_FILE_MODE_CREATE |
EFI_FILE_MODE_READ |
EFI_FILE_MODE_WRITE
),
0
);
return Status;
}
/**
Open the NvVars file for reading or writing
@param[in] File - The file to inspect
@param[out] Exists - Returns whether the file exists
@param[out] Size - Returns the size of the file
(0 if the file does not exist)
**/
VOID
NvVarsFileReadCheckup (
IN EFI_FILE_HANDLE File,
OUT BOOLEAN *Exists,
OUT UINTN *Size
)
{
EFI_FILE_INFO *FileInfo;
*Exists = FALSE;
*Size = 0;
FileInfo = FileHandleGetInfo (File);
if (FileInfo == NULL) {
return;
}
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
FreePool (FileInfo);
return;
}
*Exists = TRUE;
*Size = (UINTN)FileInfo->FileSize;
FreePool (FileInfo);
}
/**
Open the NvVars file for reading or writing
@param[in] File - The file to inspect
@param[out] Exists - Returns whether the file exists
@param[out] Size - Returns the size of the file
(0 if the file does not exist)
**/
EFI_STATUS
FileHandleEmpty (
IN EFI_FILE_HANDLE File
)
{
EFI_STATUS Status;
EFI_FILE_INFO *FileInfo;
//
// Retrieve the FileInfo structure
//
FileInfo = FileHandleGetInfo (File);
if (FileInfo == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// If the path is a directory, then return an error
//
if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) != 0) {
FreePool (FileInfo);
return EFI_INVALID_PARAMETER;
}
//
// If the file size is already 0, then it is empty, so
// we can return success.
//
if (FileInfo->FileSize == 0) {
FreePool (FileInfo);
return EFI_SUCCESS;
}
//
// Set the file size to 0.
//
FileInfo->FileSize = 0;
Status = FileHandleSetInfo (File, FileInfo);
FreePool (FileInfo);
return Status;
}
/**
Reads a file to a newly allocated buffer
@param[in] File - The file to read
@param[in] ReadSize - The size of data to read from the file
@return Pointer to buffer allocated to hold the file
contents. NULL if an error occurred.
**/
VOID *
FileHandleReadToNewBuffer (
IN EFI_FILE_HANDLE FileHandle,
IN UINTN ReadSize
)
{
EFI_STATUS Status;
UINTN ActualReadSize;
VOID *FileContents;
ActualReadSize = ReadSize;
FileContents = AllocatePool (ReadSize);
if (FileContents != NULL) {
Status = FileHandleRead (
FileHandle,
&ReadSize,
FileContents
);
if (EFI_ERROR (Status) || (ActualReadSize != ReadSize)) {
FreePool (FileContents);
return NULL;
}
}
return FileContents;
}
/**
Reads the contents of the NvVars file on the file system
@param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
@return EFI_STATUS based on the success or failure of the file read
**/
EFI_STATUS
ReadNvVarsFile (
IN EFI_HANDLE FsHandle
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;
UINTN FileSize;
BOOLEAN FileExists;
VOID *FileContents;
EFI_HANDLE SerializedVariables;
Status = GetNvVarsFile (FsHandle, TRUE, &File);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "FsAccess.c: Could not open NV Variables file on this file system\n"));
return Status;
}
NvVarsFileReadCheckup (File, &FileExists, &FileSize);
if (FileSize == 0) {
FileHandleClose (File);
return EFI_UNSUPPORTED;
}
FileContents = FileHandleReadToNewBuffer (File, FileSize);
if (FileContents == NULL) {
FileHandleClose (File);
return EFI_UNSUPPORTED;
}
DEBUG ((
DEBUG_INFO,
"FsAccess.c: Read %Lu bytes from NV Variables file\n",
(UINT64)FileSize
));
Status = SerializeVariablesNewInstanceFromBuffer (
&SerializedVariables,
FileContents,
FileSize
);
if (!RETURN_ERROR (Status)) {
Status = SerializeVariablesSetSerializedVariables (SerializedVariables);
}
FreePool (FileContents);
FileHandleClose (File);
return Status;
}
/**
Writes a variable to indicate that the NV variables
have been loaded from the file system.
**/
STATIC
VOID
SetNvVarsVariable (
VOID
)
{
BOOLEAN VarData;
UINTN Size;
//
// Write a variable to indicate we've already loaded the
// variable data. If it is found, we skip the loading on
// subsequent attempts.
//
Size = sizeof (VarData);
VarData = TRUE;
gRT->SetVariable (
L"NvVars",
&gEfiSimpleFileSystemProtocolGuid,
EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
Size,
(VOID *)&VarData
);
}
/**
Loads the non-volatile variables from the NvVars file on the
given file system.
@param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
@return EFI_STATUS based on the success or failure of load operation
**/
EFI_STATUS
LoadNvVarsFromFs (
EFI_HANDLE FsHandle
)
{
EFI_STATUS Status;
BOOLEAN VarData;
UINTN Size;
DEBUG ((DEBUG_INFO, "FsAccess.c: LoadNvVarsFromFs\n"));
//
// We write a variable to indicate we've already loaded the
// variable data. If it is found, we skip the loading.
//
// This is relevant if the non-volatile variable have been
// able to survive a reboot operation. In that case, we don't
// want to re-load the file as it would overwrite newer changes
// made to the variables.
//
Size = sizeof (VarData);
VarData = TRUE;
Status = gRT->GetVariable (
L"NvVars",
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&Size,
(VOID *)&VarData
);
if (Status == EFI_SUCCESS) {
DEBUG ((DEBUG_INFO, "NV Variables were already loaded\n"));
return EFI_ALREADY_STARTED;
}
//
// Attempt to restore the variables from the NvVars file.
//
Status = ReadNvVarsFile (FsHandle);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Error while restoring NV variable data\n"));
return Status;
}
//
// Write a variable to indicate we've already loaded the
// variable data. If it is found, we skip the loading on
// subsequent attempts.
//
SetNvVarsVariable ();
DEBUG ((
DEBUG_INFO,
"FsAccess.c: Read NV Variables file (size=%Lu)\n",
(UINT64)Size
));
return Status;
}
STATIC
RETURN_STATUS
EFIAPI
IterateVariablesCallbackAddAllNvVariables (
IN VOID *Context,
IN CHAR16 *VariableName,
IN EFI_GUID *VendorGuid,
IN UINT32 Attributes,
IN UINTN DataSize,
IN VOID *Data
)
{
EFI_HANDLE Instance;
Instance = (EFI_HANDLE)Context;
//
// Only save non-volatile variables
//
if ((Attributes & EFI_VARIABLE_NON_VOLATILE) == 0) {
return RETURN_SUCCESS;
}
return SerializeVariablesAddVariable (
Instance,
VariableName,
VendorGuid,
Attributes,
DataSize,
Data
);
}
/**
Saves the non-volatile variables into the NvVars file on the
given file system.
@param[in] FsHandle - Handle for a gEfiSimpleFileSystemProtocolGuid instance
@return EFI_STATUS based on the success or failure of load operation
**/
EFI_STATUS
SaveNvVarsToFs (
EFI_HANDLE FsHandle
)
{
EFI_STATUS Status;
EFI_FILE_HANDLE File;
UINTN WriteSize;
UINTN VariableDataSize;
VOID *VariableData;
EFI_HANDLE SerializedVariables;
SerializedVariables = NULL;
Status = SerializeVariablesNewInstance (&SerializedVariables);
if (EFI_ERROR (Status)) {
return Status;
}
Status = SerializeVariablesIterateSystemVariables (
IterateVariablesCallbackAddAllNvVariables,
(VOID *)SerializedVariables
);
if (EFI_ERROR (Status)) {
return Status;
}
VariableData = NULL;
VariableDataSize = 0;
Status = SerializeVariablesToBuffer (
SerializedVariables,
NULL,
&VariableDataSize
);
if (Status == RETURN_BUFFER_TOO_SMALL) {
VariableData = AllocatePool (VariableDataSize);
if (VariableData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
} else {
Status = SerializeVariablesToBuffer (
SerializedVariables,
VariableData,
&VariableDataSize
);
}
}
SerializeVariablesFreeInstance (SerializedVariables);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Open the NvVars file for writing.
//
Status = GetNvVarsFile (FsHandle, FALSE, &File);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "FsAccess.c: Unable to open file to saved NV Variables\n"));
return Status;
}
//
// Empty the starting file contents.
//
Status = FileHandleEmpty (File);
if (EFI_ERROR (Status)) {
FileHandleClose (File);
return Status;
}
WriteSize = VariableDataSize;
Status = FileHandleWrite (File, &WriteSize, VariableData);
if (EFI_ERROR (Status)) {
return Status;
}
FileHandleClose (File);
if (!EFI_ERROR (Status)) {
//
// Write a variable to indicate we've already loaded the
// variable data. If it is found, we skip the loading on
// subsequent attempts.
//
SetNvVarsVariable ();
DEBUG ((DEBUG_INFO, "Saved NV Variables to NvVars file\n"));
}
return Status;
}