/** @file | |
File System Access for NvVarsFileLib | |
Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "NvVarsFileLib.h" | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/MemoryAllocationLib.h> | |
/** | |
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; | |
} |